Управление подсказками с помощью Dotprompt

Firebase Genkit предоставляет плагин Dotprompt и текстовый формат, которые помогут вам писать и организовывать генеративные подсказки ИИ.

Dotprompt разработан на основе предположения, что подсказки — это код . Вы пишете и поддерживаете свои подсказки в файлах специального формата, называемых файлами dotprompt, отслеживаете изменения в них, используя ту же систему контроля версий, которую вы используете для своего кода, и развертываете их вместе с кодом, который вызывает ваши генеративные модели ИИ.

Чтобы использовать Dotprompt, сначала создайте каталог prompts в корне вашего проекта, а затем создайте в этом каталоге файл .prompt . Вот простой пример, который вы можете назвать 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}}.

Чтобы использовать эту подсказку:

Идти

Установите плагин dotprompt :

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

Затем загрузите приглашение с помощью Open :

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

Вы можете вызвать метод Generate приглашения, чтобы отобразить шаблон и передать его в API модели за один шаг:

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

Или просто преобразуйте шаблон в строку:

Идти

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

Синтаксис Dotprompt основан на языке шаблонов Handlebars . Вы можете использовать помощники if , unless each для добавления условных частей в приглашение или для перебора структурированного содержимого. Формат файла использует фронтальную часть YAML для предоставления метаданных для подсказки, встроенной в шаблон.

Определение схем ввода/вывода с помощью Picoschema

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

Приведенная выше схема эквивалентна следующей схеме JSON:

{
  "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 поддерживает скалярные типы: string , integer , number , boolean и any . Для объектов, массивов и перечислений они обозначаются круглыми скобками после имени поля.

Объекты, определенные Picoschema, имеют все требуемые свойства, если не указано иное как ? и не разрешать дополнительные свойства. Когда свойство помечено как необязательное, оно также становится допускающим значение NULL, чтобы LLM было проще возвращать значение NULL вместо пропуска поля.

В определении объекта специальный ключ (*) можно использовать для объявления определения поля с подстановочным знаком. Это будет соответствовать любым дополнительным свойствам, не указанным явным ключом.

Picoschema не поддерживает многие возможности полной схемы JSON. Если вам нужны более надежные схемы, вы можете вместо этого предоставить схему JSON:

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

Переопределение метаданных подсказки

Хотя файлы .prompt позволяют встраивать метаданные, такие как конфигурация модели, в сам файл, вы также можете переопределить эти значения для каждого вызова:

Идти

// 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,
)

Подсказки с несколькими сообщениями

По умолчанию Dotprompt создает одно сообщение с ролью "user" . Некоторые подсказки лучше всего выражать в виде комбинации нескольких сообщений, например системное приглашение.

Помощник {% verbatim %}{% endverbatim %} предоставляет простой способ создания подсказок с несколькими сообщениями:

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

Мультимодальные подсказки

Для моделей, поддерживающих мультимодальный ввод, например изображения вместе с текстом, вы можете использовать помощник {% verbatim %}{% endverbatim %} :

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

Describe this image in a detailed paragraph:

{{media url=photoUrl}}

URL-адрес может быть https:// или data: URI для «встроенного» использования изображений. В коде это будет:

Идти

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,
)

Подскажите варианты

Поскольку файлы подсказок представляют собой просто текст, вы можете (и должны!) зафиксировать их в своей системе контроля версий, что позволит вам легко сравнивать изменения с течением времени. Зачастую измененные версии подсказок можно полностью протестировать только в производственной среде параллельно с существующими версиями. Dotprompt поддерживает это через функцию вариантов .

Чтобы создать вариант, создайте файл [name].[variant].prompt . Например, если вы использовали Gemini 1.0 Pro в своем приглашении, но хотели посмотреть, будет ли Gemini 1.5 Pro работать лучше, вы можете создать два файла:

  • my_prompt.prompt : «базовое» приглашение
  • my_prompt.gemini15.prompt : вариант под названием «gemini».

Чтобы использовать вариант подсказки, укажите вариант при загрузке:

Идти

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

Загрузчик приглашений попытается загрузить вариант с этим именем и вернется к базовому состоянию, если такового не существует. Это означает, что вы можете использовать условную загрузку на основе любых критериев, имеющих смысл для вашего приложения:

Идти

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

Имя варианта включается в метаданные трассировки генерации, поэтому вы можете сравнивать и сопоставлять фактическую производительность между вариантами в инспекторе трассировки Genkit.