การจัดการพรอมต์ด้วย Dotprompt

Firebase Genkit มีปลั๊กอิน Dotprompt และรูปแบบข้อความเพื่อช่วยคุณเขียน และจัดระเบียบพรอมต์ Generative AI

Dotprompt ออกแบบมารอบๆ พื้นที่ที่พรอมต์เป็นโค้ด คุณเขียนและ จะคงข้อความแจ้งของคุณในไฟล์รูปแบบพิเศษที่เรียกว่า ไฟล์ Dotprompt, ติดตาม การเปลี่ยนแปลงโดยใช้ระบบควบคุมเวอร์ชัน เดียวกันกับที่คุณใช้สำหรับ โค้ดของคุณ แล้วทำให้ใช้งานได้พร้อมกับโค้ดที่เรียกใช้ Generative AI

หากต้องการใช้ Dotprompt ให้สร้างไดเรกทอรี prompts ในรูทของโปรเจ็กต์ก่อนและ จากนั้นสร้างไฟล์ .prompt ในไดเรกทอรีนั้น ลองดูตัวอย่างง่ายๆ นี้ อาจโทรหา greeting.prompt:

---
model: vertexai/gemini-1.5-flash
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}}.

หากต้องการใช้พรอมต์นี้ ให้ติดตั้งปลั๊กอิน dotprompt และนำเข้าฟังก์ชัน prompt จาก ไลบรารี @genkit-ai/dotprompt:

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

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

จากนั้นโหลดข้อความแจ้งโดยใช้ 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());

ไวยากรณ์ของ Dotprompt จะอิงตามแฮนด์บาร์ ภาษาเทมเพลต คุณสามารถใช้ตัวช่วยของ if, unless และ each เพื่อเพิ่ม ตามเงื่อนไขในพรอมต์หรือทำซ้ำผ่านเนื้อหาที่มีโครงสร้าง รูปแบบไฟล์ใช้ฟรอนท์เมเตอร์ YAML เพื่อมอบข้อมูลเมตาสำหรับพรอมต์ในบรรทัด ด้วยเทมเพลต

การกำหนดสคีมาอินพุต/เอาต์พุต

Dotprompt มีรูปแบบคำจำกัดความของสคีมาที่เพิ่มประสิทธิภาพ YAML อย่างกะทัดรัดที่เรียกว่า Picoschema ช่วยให้กำหนดแอตทริบิวต์ที่สำคัญที่สุดของสคีมาได้อย่างง่ายดาย สำหรับการใช้งาน LLM ต่อไปนี้คือตัวอย่างสคีมาของบทความ

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
  extra?: any, arbitrary extra data
  (*): string, wildcard field

สคีมาข้างต้นเทียบเท่ากับอินเทอร์เฟซ TypeScript ต่อไปนี้

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

}

Picoschema รองรับประเภทสเกลาร์ string, integer, number, boolean และ any สำหรับออบเจ็กต์ อาร์เรย์ และ enum จะมีวงเล็บกำกับไว้หลังชื่อช่อง

ออบเจ็กต์ที่ Picoschema กำหนดมีพร็อพเพอร์ตี้ทั้งหมดตามที่กำหนด ยกเว้นจะแสดงตัวเลือก ภายในวันที่ ? และไม่อนุญาตให้มีพร็อพเพอร์ตี้เพิ่มเติม เมื่อมีการทำเครื่องหมายพร็อพเพอร์ตี้ว่าไม่บังคับ ยังปรับให้ไม่มีข้อมูลได้เพื่อให้ LLM แสดงผลค่า Null มากขึ้นแทนการให้ การข้ามช่องไป

ในคำจำกัดความของออบเจ็กต์ คุณใช้คีย์พิเศษ (*) เพื่อประกาศ "ไวลด์การ์ด" ได้ ที่มีอยู่ การทำงานนี้จะจับคู่กับที่พักอื่นๆ ที่ไม่ได้ระบุไว้โดย คีย์ที่ชัดเจน

Picoschema ไม่รองรับความสามารถหลายอย่างของสคีมา JSON แบบเต็ม หากคุณ ต้องการสคีมาที่แข็งแกร่งมากขึ้น คุณอาจส่งสคีมา JSON แทน:

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

การใช้ประโยชน์จากสคีมาที่นำมาใช้ใหม่ได้

นอกจากการกำหนดสคีมาโดยตรงในไฟล์ .prompt แล้ว คุณยังอ้างอิง สคีมาที่ลงทะเบียนกับ defineSchema ตามชื่อ วิธีลงทะเบียนสคีมา

import { defineSchema } from '@genkit-ai/core';
import { z } from 'zod';

const MySchema = defineSchema(
  'MySchema',
  z.object({
    field1: z.string(),
    field2: z.number(),
  })
);

คุณสามารถระบุชื่อของสคีมาที่ลงทะเบียนภายในพรอมต์ได้ ดังนี้

# myPrompt.prompt
---
model: vertexai/gemini-1.5-flash
output:
  schema: MySchema
---

ไลบรารี Dotprompt จะแปลงชื่อเป็น Zod Schema ที่ลงทะเบียนแล้ว จากนั้นคุณสามารถใช้สคีมาเพื่อพิมพ์ เอาต์พุตของ Dotprompt:

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

const myPrompt = await prompt("myPrompt");

const result = await myPrompt.generate<typeof MySchema>({...});

// now strongly typed as MySchema
result.output();

การลบล้างข้อมูลเมตาของข้อความแจ้ง

ในขณะที่ไฟล์ .prompt ให้คุณฝังข้อมูลเมตา เช่น การกำหนดค่าโมเดลใน นอกจากนี้ คุณยังสามารถลบล้างค่าเหล่านี้ตามการเรียกแต่ละครั้ง ดังนี้

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

เอาต์พุตที่มีโครงสร้าง

คุณสามารถตั้งค่ารูปแบบและสคีมาเอาต์พุตของพรอมต์เพื่อบังคับให้อยู่ในรูปแบบ JSON ได้ ดังนี้

---
model: vertexai/gemini-1.5-flash
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.

เมื่อสร้างพรอมต์ที่มีเอาต์พุตที่มีโครงสร้าง ให้ใช้ตัวช่วย output() เพื่อ ดึงข้อมูลและตรวจสอบ:

const createMenuPrompt = await prompt('create_menu');

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

console.log(menu.output());

ความสอดคล้องของเอาต์พุตทำได้โดยการแทรกคำแนะนำเพิ่มเติมใน ปรากฏขึ้น โดยค่าเริ่มต้น จะต่อท้ายข้อความล่าสุดที่สร้าง จากข้อความแจ้ง คุณเปลี่ยนตำแหน่งด้วยตนเองได้โดยใช้ {{section "output"}} ผู้ช่วยอัตโนมัติ

This is a prompt that manually positions output instructions.

== Output Instructions

{{section "output"}}

== Other Instructions

This will come after the output instructions.

พรอมต์หลายข้อความ

โดยค่าเริ่มต้น Dotprompt จะสร้างข้อความเดียวที่มีบทบาท "user" ใช้บ้าง พรอมต์ที่ดีที่สุดคือการระบุที่ประกอบด้วยข้อความหลายๆ ข้อความ เช่น ข้อความแจ้งของระบบ

โปรแกรมช่วย {{role}} มีวิธีง่ายๆ ในการสร้างพรอมต์หลายข้อความ ดังนี้

---
model: vertexai/gemini-1.5-flash
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}}

ประวัติและข้อความแจ้งแบบหลายเลี้ยว

Dotprompt รองรับข้อความแจ้งแบบหลายเลี้ยวโดยการส่งตัวเลือก history ไปยัง เมธอด generate:

const result = await multiTurnPrompt.generate({
  history: [
    { role: 'user', content: [{ text: 'Hello.' }] },
    { role: 'model', content: [{ text: 'Hi there!' }] },
  ],
});

ตามค่าเริ่มต้น ประวัติจะถูกแทรกก่อนข้อความสุดท้ายที่สร้างโดย ข้อความแจ้ง อย่างไรก็ตาม คุณสามารถวางตำแหน่งประวัติด้วยตนเองได้โดยใช้ {{history}} ผู้ช่วย:

{{role "system"}}
This is the system prompt.
{{history}}
{{role "user"}}
This is a user message.
{{role "model"}}
This is a model message.
{{role "user"}}
This is the final user message.

พรอมต์แบบหลายโมดัล

สำหรับโมเดลที่รองรับการป้อนข้อมูลหลายรูปแบบ เช่น รูปภาพที่อยู่ข้างๆ ข้อความ คุณสามารถ ใช้ตัวช่วย {{media}} ดังนี้

---
model: vertexai/gemini-1.5-flash
input:
  schema:
    photoUrl: string
---

Describe this image in a detailed paragraph:

{{media url=photoUrl}}

URL จะเป็น URI แบบ https:// หรือ data: ที่เข้ารหัสฐาน 64 สำหรับ "ในบรรทัด" ก็ได้ ภาพ ในโค้ดจะเป็น

const describeImagePrompt = await prompt('describe_image');

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

console.log(result.text());

บางส่วน

"บางส่วน" เป็นเทมเพลตที่ใช้ซ้ำได้ซึ่งสามารถใส่ในพรอมต์ใดก็ได้ บางส่วน อาจเป็นประโยชน์อย่างยิ่งสำหรับพรอมต์ที่เกี่ยวข้องซึ่งมีลักษณะการทำงานเดียวกัน

เมื่อโหลดไดเรกทอรีพรอมต์ ไฟล์ใดก็ตามที่ขึ้นต้นด้วย _ จะถือว่าเป็น บางส่วน ดังนั้นไฟล์ _personality.prompt อาจมี

You should speak like a {{#if style}}{{style}}{{else}}helpful assistant.{{/else}}.

จึงอาจรวมอยู่ในพรอมต์อื่นๆ ด้วย

---
model: vertexai/gemini-1.5-flash
input:
  schema:
    name: string
    style?: string
---

{{ role "system" }}
{{>personality style=style}}

{{ role "user" }}
Give the user a friendly greeting.

User's Name: {{name}}

ระบบจะแทรกบางส่วนโดยใช้ไวยากรณ์ {{>NAME_OF_PARTIAL args...}} หากไม่ใช่ บางส่วนจะดำเนินการกับอาร์กิวเมนต์ โดยจะทำงานในบริบทเดียวกันกับ ข้อความแจ้งระดับบนสุด

ยอมรับบางส่วนทั้งสำหรับอาร์กิวเมนต์ที่มีชื่อเป็นด้านบนหรืออาร์กิวเมนต์ตำแหน่งเดี่ยว ที่แสดงถึงบริบท ซึ่งอาจเป็นประโยชน์เช่น การแสดงผลสมาชิกของรายการ

# _destination.prompt
- {{name}} ({{country}})

# chooseDestination.prompt
Help the user decide between these vacation destinations:
{{#each destinations}}
{{>destination this}}{{/each}}

การกำหนดบางส่วนในโค้ด

คุณอาจกำหนดบางส่วนของโค้ดโดยใช้ definePartial ได้ด้วย ดังนี้

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

definePartial(
  'personality',
  'Talk like a {{#if style}}{{style}}{{else}}helpful assistant{{/if}}.'
);

ส่วนที่กำหนดด้วยโค้ดจะพร้อมใช้งานในพรอมต์ทั้งหมด

ตัวแปรพรอมต์

เนื่องจากไฟล์พรอมต์เป็นเพียงข้อความ คุณสามารถ (และควร!) คอมมิตไฟล์เหล่านั้นไว้ใน ระบบควบคุมเวอร์ชัน ช่วยให้คุณเปรียบเทียบการเปลี่ยนแปลงในช่วงเวลาที่ผ่านมาได้โดยง่าย บ่อยครั้งที่พรอมต์เวอร์ชันที่ได้รับการปรับแก้สามารถทดสอบอย่างสมบูรณ์ได้ใน สภาพแวดล้อมเวอร์ชันที่ใช้งานจริงควบคู่กับเวอร์ชันที่มีอยู่ Dotprompt สนับสนุน ผ่านฟีเจอร์รูปแบบ

หากต้องการสร้างตัวแปร ให้สร้างไฟล์ [name].[variant].prompt ตัวอย่างเช่น หาก คุณกำลังใช้ Gemini 1.5 Flash ในพรอมต์ของคุณ แต่ต้องการดูว่า Gemini 1.5 หรือไม่ เวอร์ชัน Pro ทำงานได้ดีกว่า คุณอาจสร้างไฟล์ 2 ไฟล์ดังนี้

  • my_prompt.prompt: "เกณฑ์พื้นฐาน" ข้อความแจ้ง
  • my_prompt.gemini15pro.prompt: ผลิตภัณฑ์ย่อยที่ชื่อ "gemini15pro"

หากต้องการใช้รูปแบบพรอมต์ ให้ระบุตัวเลือก variant ขณะโหลด ดังนี้

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

ชื่อของตัวแปรจะรวมอยู่ในข้อมูลเมตาของการติดตามการสร้าง ดังนั้นคุณ สามารถเปรียบเทียบประสิทธิภาพจริงระหว่างตัวแปรต่างๆ ในการติดตาม Genkit เครื่องมือตรวจสอบ

การกำหนดตัวช่วยที่กำหนดเอง

คุณกำหนดผู้ช่วยที่กำหนดเองเพื่อประมวลผลและจัดการข้อมูลภายในพรอมต์ได้ ผู้ช่วย จดทะเบียนทั่วโลกโดยใช้ defineHelper:

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

defineHelper('shout', (text: string) => text.toUpperCase());

เมื่อกำหนดตัวช่วยแล้ว คุณจะใช้ผู้ช่วยในพรอมต์ใดก็ได้

---
model: vertexai/gemini-1.5-flash
input:
  schema:
    name: string
---

HELLO, {{shout name}}!!!

ดูข้อมูลเพิ่มเติมเกี่ยวกับอาร์กิวเมนต์ที่ส่งผ่านไปยังตัวช่วยได้ที่ เอกสารประกอบเกี่ยวกับแถบควบคุมเกี่ยวกับการสร้าง ผู้ช่วยที่กำหนดเอง

วิธีอื่นๆ ในการโหลดและกำหนดข้อความแจ้ง

Dotprompt ได้รับการเพิ่มประสิทธิภาพเพื่อองค์กรในไดเรกทอรีพรอมต์ อย่างไรก็ตาม วิธีอื่นๆ ในการโหลดและกำหนดข้อความแจ้งมีดังนี้

  • loadPromptFile: โหลดข้อความแจ้งจากไฟล์ในไดเรกทอรีของข้อความแจ้ง
  • loadPromptUrl: โหลดข้อความแจ้งจาก URL
  • defineDotprompt: กำหนดข้อความแจ้งในโค้ด

ตัวอย่าง

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.5-flash',
    input: {
      schema: z.object({
        name: z.string(),
      }),
    },
  },
  `Hello {{name}}, how are you today?`
);