Mengelola prompt dengan Dotprompt

Firebase Genkit menyediakan plugin Dotprompt dan format teks untuk membantu Anda menulis dan mengatur perintah AI generatif Anda.

Dotprompt didesain berdasarkan premis bahwa perintahnya adalah kode. Anda menulis dan mempertahankan prompt Anda dalam file berformat khusus yang disebut file dotprompt, perubahannya menggunakan sistem kontrol versi yang sama dengan yang Anda gunakan untuk kode, dan men-deploy-nya bersama dengan kode yang memanggil AI generatif jaringan.

Untuk menggunakan Dotprompt, buat direktori prompts terlebih dahulu di root project Anda dan lalu buat file .prompt di direktori tersebut. Berikut adalah contoh sederhana yang mungkin memanggil greeting.prompt:

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

Untuk menggunakan perintah ini:

Go

Instal plugin dotprompt:

go get github.com/firebase/genkit/go/plugins/dotprompt

Kemudian, muat perintah menggunakan Open:

import "github.com/firebase/genkit/go/plugins/dotprompt"
dotprompt.SetDirectory("prompts")
prompt, err := dotprompt.Open("greeting")

Anda dapat memanggil metode Generate perintah untuk merender template dan meneruskannya ke API model dalam satu langkah:

ctx := context.Background()

// The .prompt file specifies vertexai/gemini-1.5-pro, so make sure it's set
// up.
// Default to the project in GCLOUD_PROJECT and the location "us-central1".
vertexai.Init(ctx, nil)
vertexai.DefineModel("gemini-1.5-pro", nil)

type GreetingPromptInput struct {
  Location string `json:"location"`
  Style    string `json:"style"`
  Name     string `json:"name"`
}
response, err := prompt.Generate(
  ctx,
  &dotprompt.PromptRequest{
      Variables: GreetingPromptInput{
          Location: "the beach",
          Style:    "a fancy pirate",
          Name:     "Ed",
      },
  },
  nil,
)
if err != nil {
  return err
}

if responseText, err := response.Text(); err == nil {
  fmt.Println(responseText)
}

Atau cukup render template ke string:

Go

renderedPrompt, err := prompt.RenderText(map[string]any{
  "location": "a restaurant",
  "style":    "a pirate",
})

Sintaksis titik prompt didasarkan pada Handlebar bahasa template. Anda dapat menggunakan helper if, unless, dan each untuk menambahkan bagian kondisional ke prompt Anda atau melakukan iterasi melalui konten terstruktur. Tujuan format file menggunakan frontmatter YAML untuk menyediakan metadata untuk prompt inline dengan template.

Menentukan Skema Input/Output dengan Picoschema

Dotprompt menyertakan format definisi skema berbasis YAML yang ringkas yang disebut Picoschema untuk memudahkan penentuan atribut terpenting dari suatu skema untuk penggunaan LLM. Berikut contoh skema artikel:

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

Skema di atas setara dengan skema JSON berikut:

{
  "properties": {
    "metadata": {
      "properties": {
        "updatedAt": {
          "type": "string",
          "description": "ISO timestamp of last update"
        },
        "approvedBy": {
          "type": "integer",
          "description": "id of approver"
        }
      },
      "type": "object"
    },
    "title": {
      "type": "string"
    },
    "subtitle": {
      "type": "string"
    },
    "draft": {
      "type": "boolean",
      "description": "true when in draft state"
    },
    "date": {
      "type": "string",
      "description": "the date of publication e.g. '2024-04-09'"
    },
    "tags": {
      "items": {
        "type": "string"
      },
      "type": "array",
      "description": "relevant tags for article"
    },
    "authors": {
      "items": {
        "properties": {
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        },
        "type": "object",
        "required": ["name"]
      },
      "type": "array"
    }
  },
  "type": "object",
  "required": ["title", "date", "tags", "authors"]
}

Picoschema mendukung jenis skalar string, integer, number, boolean, dan any. Untuk objek, array, dan enum, objek, array, dan enum dilambangkan dengan tanda kurung setelah nama kolom.

Objek yang didefinisikan oleh Picoschema memiliki semua properti sesuai kebutuhan kecuali dilambangkan opsional paling lambat ?, dan tidak mengizinkan properti tambahan. Ketika sebuah properti ditandai sebagai opsional, nullable untuk memberikan lebih banyak keringanan kepada LLM agar menampilkan null, menghilangkan {i>field<i}.

Dalam definisi objek, kunci khusus (*) dapat digunakan untuk mendeklarasikan "karakter pengganti" definisi bidang. Ini akan cocok dengan properti tambahan apa pun yang tidak disediakan oleh eksplisit.

Picoschema tidak mendukung banyak kemampuan skema JSON lengkap. Jika Anda memerlukan skema yang lebih andal, Anda dapat memberikan Skema JSON:

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

Mengganti Metadata Prompt

Meskipun file .prompt memungkinkan Anda menyematkan metadata seperti konfigurasi model di file itu sendiri, Anda juga dapat mengganti nilai-nilai ini per panggilan:

Go

// Make sure you set up the model you're using.
vertexai.DefineModel("gemini-1.5-flash", nil)

response, err := prompt.Generate(
  context.Background(),
  &dotprompt.PromptRequest{
      Variables: GreetingPromptInput{
          Location: "the beach",
          Style:    "a fancy pirate",
          Name:     "Ed",
      },
      Model: "vertexai/gemini-1.5-flash",
      Config: &ai.GenerationCommonConfig{
          Temperature: 1.0,
      },
  },
  nil,
)

Dialog multi-pesan

Secara default, Dotprompt membuat satu pesan dengan peran "user". Agak besar perintah sebaiknya dinyatakan sebagai kombinasi dari beberapa pesan, seperti prompt sistem.

Helper {{role}} menyediakan cara mudah untuk membuat prompt multi-pesan:

---
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}}

Perintah multi-modal

Untuk model yang mendukung input multimodal seperti gambar di samping teks, Anda bisa gunakan helper {{media}}:

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

Describe this image in a detailed paragraph:

{{media url=photoUrl}}

URL dapat berupa URI data: yang dienkode dengan https:// atau base64 untuk "inline" gambar tingkat penggunaan. Dalam kode, ini akan menjadi:

Go

dotprompt.SetDirectory("prompts")
describeImagePrompt, err := dotprompt.Open("describe_image")
if err != nil {
  return err
}

imageBytes, err := os.ReadFile("img.jpg")
if err != nil {
  return err
}
encodedImage := base64.StdEncoding.EncodeToString(imageBytes)
dataURI := "data:image/jpeg;base64," + encodedImage

type DescribeImagePromptInput struct {
  PhotoUrl string `json:"photo_url"`
}
response, err := describeImagePrompt.Generate(
  context.Background(),
  &dotprompt.PromptRequest{Variables: DescribeImagePromptInput{
      PhotoUrl: dataURI,
  }},
  nil,
)

Varian Prompt

Karena file prompt berupa teks, Anda bisa (dan harus) melakukan commit ke {i>version control system<i}, yang memungkinkan Anda untuk membandingkan perubahan dari waktu ke waktu dengan mudah. Sering kali, versi prompt yang sudah diutak-atik hanya bisa diuji sepenuhnya di lingkungan produksi secara berdampingan dengan versi yang sudah ada. Dukungan titik ini melalui fitur varian.

Untuk membuat varian, buat file [name].[variant].prompt. Misalnya, jika Anda menggunakan Gemini 1.0 Pro sesuai perintah Anda, tetapi ingin mengetahui apakah Gemini 1.5 Pro akan berperforma lebih baik, Anda dapat membuat dua file:

  • my_prompt.prompt: "dasar bawaan" perintah
  • my_prompt.gemini15.prompt: varian bernama "gemini"

Untuk menggunakan varian perintah, tentukan varian saat memuat:

Go

describeImagePrompt, err := dotprompt.OpenVariant("describe_image", "gemini15")

Loader prompt akan mencoba memuat varian dari nama tersebut, dan kembali ke dasar pengukuran jika tidak ada. Ini berarti Anda dapat menggunakan pemuatan bersyarat berdasarkan kriteria apa pun yang masuk akal untuk aplikasi Anda:

Go

var myPrompt *dotprompt.Prompt
var err error
if isBetaTester(user) {
  myPrompt, err = dotprompt.OpenVariant("describe_image", "gemini15")
} else {
  myPrompt, err = dotprompt.Open("describe_image")
}

Nama varian disertakan dalam metadata pelacakan pembuatan, sehingga Anda dapat membandingkan dan membedakan performa sebenarnya di antara varian dalam rekaman aktivitas Genkit pemeriksa.