Firebase Genkit fournit le plug-in Dotprompt et le format de texte pour vous aider à rédiger et à organiser vos requêtes d'IA générative.
Dotprompt est conçu sur la base du principe selon lequel les requêtes sont du code. Vous écrivez et gérez vos invites dans des fichiers au format spécial appelés fichiers dotprompt, suivez les modifications apportées à l'aide du même système de contrôle des versions que celui que vous utilisez pour votre code, et les déployez avec le code qui appelle vos modèles d'IA générative.
Pour utiliser Dotprompt, créez d'abord un répertoire prompts
dans le répertoire racine de votre projet, puis un fichier .prompt
dans ce répertoire. Voici un exemple simple que vous pouvez appeler 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}}.
Pour utiliser cette invite, installez le plug-in dotprompt
:
go get github.com/firebase/genkit/go/plugins/dotprompt
Ensuite, chargez l'invite à l'aide de Open
:
import "github.com/firebase/genkit/go/plugins/dotprompt"
dotprompt.SetDirectory("prompts")
prompt, err := dotprompt.Open("greeting")
Vous pouvez appeler la méthode Generate
de la requête pour afficher le modèle et le transmettre à l'API du modèle en une seule étape:
ctx := context.Background()
// Default to the project in GCLOUD_PROJECT and the location "us-central1".
vertexai.Init(ctx, nil)
// The .prompt file specifies vertexai/gemini-1.5-flash, which is
// automatically defined by Init(). However, if it specified a model that
// isn't automatically loaded (such as a specific version), you would need
// to define it here:
// vertexai.DefineModel("gemini-1.0-pro-002", &ai.ModelCapabilities{
// Multiturn: true,
// Tools: true,
// SystemRole: true,
// Media: false,
// })
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
}
fmt.Println(response.Text())
Vous pouvez également afficher le modèle sous forme de chaîne :
renderedPrompt, err := prompt.RenderText(map[string]any{
"location": "a restaurant",
"style": "a pirate",
})
La syntaxe de Dotprompt est basée sur le langage de création de modèles Handlebars. Vous pouvez utiliser les assistants if
, unless
et each
pour ajouter des parties conditionnelles à votre requête ou itérer sur du contenu structuré. Le format de fichier utilise la partie YAML pour fournir des métadonnées pour une requête intégrée au modèle.
Définir des schémas d'entrée/sortie avec Picoschema
Dotprompt inclut un format de définition de schéma compact basé sur YAML appelé Picoschema pour faciliter la définition des attributs les plus importants d'un schéma pour l'utilisation de LLM. Voici un exemple de schéma pour un article :
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
Le schéma ci-dessus équivaut au schéma JSON suivant :
{
"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 accepte les types scalaires string
, integer
, number
, boolean
et any
.
Pour les objets, les tableaux et les énumérations, ils sont indiqués par une parenthèse après le nom du champ.
Les objets définis par Picoschema ont toutes les propriétés obligatoires, sauf si elles sont indiquées comme facultatives par ?
, et n'autorisent pas de propriétés supplémentaires. Lorsqu'une propriété est marquée comme facultative, elle est également rendue nullable pour permettre aux LLM de renvoyer "null" au lieu d'omettre un champ.
Dans une définition d'objet, la clé spéciale (*)
peut être utilisée pour déclarer une définition de champ "générique". Cela correspond à toutes les propriétés supplémentaires qui ne sont pas fournies par une clé explicite.
Picoschema n'est pas compatible avec de nombreuses fonctionnalités du schéma JSON complet. Si vous avez besoin de schémas plus robustes, vous pouvez fournir un schéma JSON à la place:
output:
schema:
type: object
properties:
field1:
type: number
minimum: 20
Remplacer les métadonnées de requête
Bien que les fichiers .prompt
vous permettent d'intégrer des métadonnées telles que la configuration du modèle dans le fichier lui-même, vous pouvez également remplacer ces valeurs pour chaque appel:
// 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,
)
Invites à plusieurs messages
Par défaut, Dotprompt crée un seul message avec un rôle "user"
. Il est préférable d'exprimer certaines invites sous la forme d'une combinaison de plusieurs messages, telle qu'une invite système.
L'assistant {{role}}
permet de créer facilement des invites multimessages :
---
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}}
Requêtes multimodales
Pour les modèles qui acceptent la saisie multimodale, comme les images à côté de texte, vous pouvez utiliser l'outil d'aide {{media}}
:
---
model: vertexai/gemini-1.5-flash
input:
schema:
photoUrl: string
---
Describe this image in a detailed paragraph:
{{media url=photoUrl}}
L'URL peut être un URI https://
ou data:
encodé en base64 pour une utilisation d'image "inline". En code, cela se présente comme suit :
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,
)
Variantes d'invites
Étant donné que les fichiers d'invite ne sont que du texte, vous pouvez (et devez) les enregistrer dans votre système de contrôle des versions. Vous pourrez ainsi comparer facilement les modifications au fil du temps. Souvent, les versions modifiées des requêtes ne peuvent être entièrement testées dans un environnement de production que côte à côte avec les versions existantes. Dotprompt prend en charge cette fonctionnalité via sa fonctionnalité de variantes.
Pour créer une variante, créez un fichier [name].[variant].prompt
. Par exemple, si vous utilisiez Gemini 1.5 Flash dans votre requête, mais que vous vouliez voir si Gemini 1.5 Pro était plus performant, vous pouvez créer deux fichiers :
my_prompt.prompt
: invite "baseline" (ligne de base)my_prompt.geminipro.prompt
: une variante nommée "geminipro"
Pour utiliser une variante d'invite, spécifiez-la lors du chargement :
describeImagePrompt, err := dotprompt.OpenVariant("describe_image", "geminipro")
Le chargeur d'invite tente de charger la variante de ce nom et revient à la référence si aucune n'existe. Cela signifie que vous pouvez utiliser le chargement conditionnel en fonction des critères pertinents pour votre application :
var myPrompt *dotprompt.Prompt
var err error
if isBetaTester(user) {
myPrompt, err = dotprompt.OpenVariant("describe_image", "geminipro")
} else {
myPrompt, err = dotprompt.Open("describe_image")
}
Le nom de la variante est inclus dans les métadonnées des traces de génération afin que vous puissiez comparer les performances réelles entre les variantes dans l'outil d'inspection de traces de Genkit.