Firebase Genkit fournit le plug-in Dotprompt et le format texte pour vous aider à écrire et organiser vos requêtes d'IA générative.
Dotprompt a été conçu autour du principe que 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
à la racine de votre projet, puis
puis créez un fichier .prompt
dans ce répertoire. Voici un exemple simple
pourrait 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"
g, err := genkit.Init(ctx, genkit.WithPromptDir("prompts"))
if err != nil {
log.Fatal(err)
}
prompt, err := dotprompt.Open(g, "greeting")
Vous pouvez appeler la méthode Generate
de l'invite 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, g, nil)
// The .prompt file specifies vertexai/gemini-2.0-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-2.0-flash", &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, g,
dotprompt.WithInput(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 dans une chaîne:
renderedPrompt, err := prompt.RenderText(map[string]any{
"location": "a restaurant",
"style": "a pirate",
})
La syntaxe de Dotprompt est basée sur le Handlebars
de création de modèles. Vous pouvez utiliser les assistants if
, unless
et each
pour ajouter
des portions conditionnelles à votre requête
ou d'effectuer des itérations dans le contenu structuré. La
utilise le frontal YAML pour fournir des métadonnées pour une requête intégrée
avec le 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, Un picoschéma pour définir facilement les attributs les plus importants d'un schéma pour l'utilisation des 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 est compatible avec les types scalaires string
, integer
, number
, boolean
et any
.
Pour les objets, les tableaux et les énumérations, ils sont indiqués par des parenthèses 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 un "caractère générique"
la définition d'un champ. 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 par appel :
// Make sure you set up the model you're using.
vertexai.DefineModel(g, "gemini-2.0-flash", nil)
response, err := prompt.Generate(
context.Background(),
g,
dotprompt.WithInput(GreetingPromptInput{
Location: "the beach",
Style: "a fancy pirate",
Name: "Ed",
}),
dotprompt.WithModelName("vertexai/gemini-2.0-flash"),
dotprompt.WithConfig(&ai.GenerationCommonConfig{
Temperature: 1.0,
}),
nil,
)
Requêtes multimessages
Par défaut, Dotprompt construit un seul message avec le rôle "user"
. Certaines invites sont mieux exprimées sous la forme d'une combinaison de plusieurs messages, comme 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 compatibles avec les entrées multimodales telles que les images et le texte, vous pouvez utiliser l'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". Dans le code, la réponse serait la suivante:
ctx := context.Background()
g, err := genkit.Init(ctx, genkit.WithPromptDir("prompts"))
if err != nil {
log.Fatal(err)
}
describeImagePrompt, err := dotprompt.Open(g, "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(), g,
dotprompt.WithInput(DescribeImagePromptInput{
PhotoUrl: dataURI,
}),
nil,
)
Variantes d'invites
Comme les fichiers de requête ne sont que du texte, vous pouvez (et devriez !) les valider dans votre de contrôle des versions, ce qui vous permet de comparer facilement les modifications au fil du temps. Souvent, les versions modifiées des requêtes ne peuvent être entièrement testées de production en parallèle 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
: valeur de référence requêtemy_prompt.geminipro.prompt
: une variante nommée "geminipro"
Pour utiliser une variante de requête, spécifiez-la lors du chargement:
describeImagePrompt, err := dotprompt.OpenVariant(g, "describe_image", "geminipro")
Le chargeur d'invites tentera de charger la variante de ce nom, et effectuera un repli à la valeur de référence s'il n'en existe aucune. Cela signifie que vous pouvez utiliser le chargement conditionnel basé sur en fonction de n'importe quel critère pertinent pour votre application:
var myPrompt *dotprompt.Prompt
if isBetaTester(user) {
myPrompt, err = dotprompt.OpenVariant(g, "describe_image", "geminipro")
} else {
myPrompt, err = dotprompt.Open(g, "describe_image")
}
Le nom de la variante est inclus dans les métadonnées des traces de génération. Vous pouvez ainsi comparer les performances réelles entre les variantes dans l'inspecteur de trace Genkit.