Quản lý lời nhắc bằng Dotprompt

Firebase Genkit cung cấp trình bổ trợ Dotprompt và định dạng văn bản để giúp bạn viết và sắp xếp các câu lệnh bằng AI tạo sinh của mình.

Dotprompt được thiết kế dựa trên cơ sở lời nhắc là mã. Bạn viết và duy trì lời nhắc trong các tệp có định dạng đặc biệt được gọi là tệp dấu chấm câu, theo dõi các thay đổi đối với chúng bằng chính hệ thống quản lý phiên bản mà bạn dùng cho mã của mình, sau đó triển khai chúng cùng với mã gọi mô hình AI tạo sinh.

Để sử dụng Dotprompt, trước tiên, hãy tạo thư mục prompts trong thư mục gốc của dự án, sau đó tạo tệp .prompt trong thư mục đó. Dưới đây là một ví dụ đơn giản mà bạn có thể gọi greeting.prompt:

---
model: vertexai/gemini-1.0-pro
config:
  temperature: 0.9
input:
  schema:
    location: string
    style?: string
    name?: string
  default:
    location: a restaurant
---

You are the world's most welcoming AI assistant and are currently working at {{location}}.

Greet a guest{{#if name}} named {{name}}{{/if}}{{#if style}} in the style of {{style}}{{/if}}.

Để sử dụng lời nhắc này, hãy cài đặt trình bổ trợ dotprompt và nhập hàm prompt từ thư viện @genkit-ai/dotprompt:

import { dotprompt, prompt } from '@genkit-ai/dotprompt';

configureGenkit({ plugins: [dotprompt()] });

Sau đó, hãy tải lời nhắc bằng prompt('file_name'):

const greetingPrompt = await prompt('greeting');

const result = await greetingPrompt.generate({
  input: {
    location: 'the beach',
    style: 'a fancy pirate',
  },
});

console.log(result.text());

Cú pháp của dấu nhắc dựa trên ngôn ngữ tạo mẫu Thanh điều khiển. Bạn có thể sử dụng trình trợ giúp if, unlesseach để thêm các phần có điều kiện vào câu lệnh hoặc lặp lại qua nội dung có cấu trúc. Định dạng tệp sử dụng trình giao diện trước YAML để cung cấp siêu dữ liệu cho một lời nhắc cùng dòng với mẫu.

Xác định giản đồ đầu vào/đầu ra bằng Picoschema

Dotprompt có một định dạng định nghĩa giản đồ nhỏ gọn và được tối ưu hoá YAML có tên là Picoschema. Điều này giúp bạn dễ dàng xác định các thuộc tính quan trọng nhất của một giản đồ cho việc sử dụng LLM. Dưới đây là ví dụ về giản đồ cho một bài viết:

schema:
  title: string # string, number, and boolean types are defined like this
  subtitle?: string # optional fields are marked with a `?`
  draft?: boolean, true when in draft state
  status?(enum, approval status): [PENDING, APPROVED]
  date: string, the date of publication e.g. '2024-04-09' # descriptions follow a comma
  tags(array, relevant tags for article): string # arrays are denoted via parentheses
  authors(array):
    name: string
    email?: string
  metadata?(object): # objects are also denoted via parentheses
    updatedAt?: string, ISO timestamp of last update
    approvedBy?: integer, id of approver

Giản đồ trên tương đương với giao diện TypeScript sau:

interface Article {
  title: string;
  subtitle?: string;
  /** true when in draft state */
  draft?: boolean;
  /** approval status */
  status?: 'PENDING' | 'APPROVED';
  /** the date of publication e.g. '2024-04-09' */
  date: string;
  /** relevant tags for article */
  tags: string[];
  authors: {
    name: string;
    email?: string;
  }[];
  metadata?: {
    /** ISO timestamp of last update */
    updatedAt?: string;
    /** id of approver */
    approvedBy?: number;
  };
}

Picoschema hỗ trợ các loại đại lượng vô hướng string, integer, numberboolean. Đối với các đối tượng, mảng và enum, chúng được biểu thị bằng dấu ngoặc đơn sau tên trường.

Các đối tượng do Picoschema xác định có tất cả các thuộc tính theo yêu cầu trừ phi được biểu thị là không bắt buộc bằng ? và không cho phép các thuộc tính khác.

Picoschema không hỗ trợ nhiều tính năng của giản đồ JSON đầy đủ. Nếu cần có giản đồ mạnh mẽ hơn, bạn có thể cung cấp Giản đồ JSON:

output:
  schema:
    type: object
    properties:
      field1:
        type: number
        minimum: 20

Siêu dữ liệu ghi đè lời nhắc

Mặc dù tệp .prompt cho phép bạn nhúng siêu dữ liệu chẳng hạn như cấu hình mô hình trong chính tệp, nhưng bạn cũng có thể ghi đè các giá trị này trên cơ sở mỗi lệnh gọi:

const result = await greetingPrompt.generate({
  model: 'google-genai/gemini-pro',
  config: {
    temperature: 1.0,
  },
  input: {
    location: 'the beach',
    style: 'a fancy pirate',
  },
});

Kết quả có cấu trúc

Bạn có thể đặt giản đồ định dạng và giản đồ đầu ra của câu lệnh để chuyển đổi thành JSON:

---
model: vertexai/gemini-1.0-pro
input:
  schema:
    theme: string
output:
  format: json
  schema:
    name: string
    price: integer
    ingredients(array): string
---

Generate a menu item that could be found at a {{theme}} themed restaurant.

Khi tạo lời nhắc có kết quả có cấu trúc, hãy sử dụng trình trợ giúp output() để truy xuất và xác thực lời nhắc:

const createMenuPrompt = await prompt('create_menu');

const menu = await createMenuPrompt.generate({
  input: {
    theme: 'banana',
  },
});

console.log(menu.output());

Lời nhắc gửi nhiều thư

Theo mặc định, Dotprompt tạo một thông báo duy nhất có vai trò "user". Một số lời nhắc được biểu thị hiệu quả nhất bằng cách kết hợp nhiều thông báo, chẳng hạn như lời nhắc của hệ thống.

Trình trợ giúp {{role}} cung cấp một cách đơn giản để tạo lời nhắc có nhiều thư:

---
model: vertexai/gemini-1.0-pro
input:
  schema:
    userQuestion: string
---

{{role "system"}}
You are a helpful AI assistant that really loves to talk about food. Try to work
food items into all of your conversations.
{{role "user"}}
{{userQuestion}}

Lời nhắc nhiều phương thức

Đối với các mô hình hỗ trợ phương thức nhập đa phương thức, chẳng hạn như hình ảnh cùng với văn bản, bạn có thể sử dụng trình trợ giúp {{media}}:

---
model: vertexai/gemini-1.0-pro-vision
input:
  schema:
    photoUrl: string
---

Describe this image in a detailed paragraph:

{{media url=photoUrl}}

URL có thể là các URI data: được mã hoá https:// hoặc base64 để sử dụng hình ảnh "cùng dòng". Trong mã, đây sẽ là:

const describeImagePrompt = await prompt('describe_image');

const result = await describeImagePrompt.generate({
  input: {
    photoUrl: 'https://example.com/image.png',
  },
});

console.log(result.text());

Biến thể lời nhắc

Vì tệp lời nhắc chỉ là văn bản nên bạn có thể (và nên!) đưa chúng vào hệ thống quản lý phiên bản của mình, cho phép bạn dễ dàng so sánh các thay đổi theo thời gian. Thông thường, bạn chỉ có thể kiểm thử đầy đủ các phiên bản câu lệnh được điều chỉnh trong môi trường sản xuất song song với các phiên bản hiện có. Dotprompt hỗ trợ việc này thông qua tính năng biến thể.

Để tạo một biến thể, hãy tạo tệp [name].[variant].prompt. Ví dụ: nếu bạn đang sử dụng Gemini 1.0 Pro trong câu lệnh nhưng muốn xem liệu Gemini 1.5 Pro có hoạt động tốt hơn hay không, bạn có thể tạo hai tệp:

  • my_prompt.prompt: lời nhắc "cơ sở"
  • my_prompt.gemini15.prompt: một biến thể có tên là "gemini"

Để sử dụng một biến thể lời nhắc, hãy chỉ định tuỳ chọn variant khi tải:

const myPrompt = await prompt('my_prompt', { variant: 'gemini15' });

Trình tải lời nhắc sẽ cố gắng tải biến thể của tên đó và quay lại sử dụng đường cơ sở nếu không có biến thể nào. Điều này có nghĩa là bạn có thể sử dụng tính năng tải có điều kiện dựa trên bất kỳ tiêu chí nào phù hợp với ứng dụng của mình:

const myPrompt = await prompt('my_prompt', {
  variant: isBetaTester(user) ? 'gemini15' : null,
});

Tên của biến thể được đưa vào siêu dữ liệu của dấu vết tạo dữ liệu để bạn có thể so sánh và đối chiếu hiệu suất thực tế giữa các biến thể trong công cụ kiểm tra dấu vết Genkit.

Những cách khác để tải và xác định lời nhắc

Dotprompt được tối ưu hoá cho việc sắp xếp trong thư mục lời nhắc. Tuy nhiên, có một số cách khác để tải và xác định lời nhắc:

  • loadPromptFile: Tải lời nhắc từ một tệp trong thư mục lời nhắc.
  • loadPromptUrl: Tải lời nhắc từ URL.
  • defineDotprompt: Xác định lời nhắc trong mã.

Ví dụ:

import {
  loadPromptFile,
  loadPromptUrl,
  defineDotprompt,
} from '@genkit-ai/dotprompt';
import path from 'path';
import { z } from 'zod';

// Load a prompt from a file
const myPrompt = await loadPromptFile(
  path.resolve(__dirname, './path/to/my_prompt.prompt')
);

// Load a prompt from a URL
const myPrompt = await loadPromptUrl('https://example.com/my_prompt.prompt');

// Define a prompt in code
const myPrompt = defineDotprompt(
  {
    model: 'vertexai/gemini-1.0-pro',
    input: {
      schema: z.object({
        name: z.string(),
      }),
    },
  },
  `Hello {{name}}, how are you today?`
);