Ce guide présente des bonnes pratiques et des modèles de conception éprouvés pour optimiser et faire évoluer votre application d'agent. Ce contenu peut vous aider à réduire les coûts de conception et d'exécution, et à améliorer la fiabilité des agents.
Général
Cette section présente les bonnes pratiques générales pour commencer à développer des agents et à rédiger des instructions.
Restez simple
Lorsque vous commencez à créer votre application d'agent, vous devez commencer par des cas d'utilisation simples. Une fois que vous avez fait fonctionner des cas d'utilisation simples, continuez à en créer de plus complexes.
Les instructions doivent être spécifiques
Les instructions de l'agent doivent être spécifiques et univoques. Les instructions doivent être bien organisées et regroupées par thème. Évitez de disperser les instructions sur des thèmes spécifiques de manière aléatoire. Les instructions doivent également être faciles à suivre pour un humain.
Utiliser des instructions structurées
Une fois que vous avez terminé de rédiger vos instructions, vous devez utiliser la fonctionnalité Restructurer les instructions pour les mettre en forme. Dans ce format, votre agent sera plus fiable.
Utiliser des composants modulaires provenant de plusieurs modalités et canaux
Lorsque les agents doivent répondre différemment selon les modalités (texte ou voix) ou les canaux (API, téléphonie ou Web), vous devez utiliser des composants modulaires et vérifier le canal et la modalité dans les outils et les rappels Python.
Outils
Cette section fournit des bonnes pratiques pour définir et utiliser des outils, y compris pour encapsuler des API externes et enchaîner des appels d'outils.
Encapsuler des API avec des outils Python
Les schémas d'API externes peuvent définir de nombreux paramètres d'entrée et de sortie qui ne sont pas pertinents pour votre agent. Si vous utilisez des outils OpenAPI dans ce type de cas, vous risquez de fournir au modèle un contexte inutile, ce qui peut réduire la fiabilité. Par exemple, supposons qu'un outil de spécification OpenAPI accepte trois paramètres en entrée et renvoie un grand objet JSON avec 100 paires clé/valeur. L'agent prédit les trois arguments d'entrée pour cet outil. Lorsqu'il renvoie la réponse, il voit la charge utile JSON complète avec les 100 paires clé/valeur. Si seulement trois de ces paires sont réellement pertinentes pour la conversation, les 97 autres paires sont des données non pertinentes qui ajoutent des jetons à l'historique de la conversation. Bien que cela puisse sembler inoffensif, cela peut créer une confusion inutile pour l'agent, augmenter le temps de raisonnement et la latence.
Il est recommandé d'utiliser des outils Python pour encapsuler les appels d'API. L'encapsulation vous permet d'obscurcir les données inutiles de l'agent et de l'historique du contexte. Vous pouvez contrôler le contexte exact que l'agent voit en ne renvoyant que les données qui lui sont pertinentes à ce moment-là. Vous disposez ainsi d'un contrôle total sur les paramètres d'entrée et de sortie définis par l'outil, qui sont partagés avec le modèle. Cette pratique est une forme d'ingénierie du contexte avec des outils.
Exemple de code :
def python_wrapper(arg_a: str, arg_b: str) -> dict:
"""
Call the scheduling service to schedule an appointment,
returning only relevant fields.
"""
res = complicated_external_api_call(...)
# Process result to extract only relevant key-value pairs.
processed_res = {
"appointment_time": res.json()["appointment_time"],
"appointment_location": res.json()["appointment_location"],
"confirmation_id": res.json()["confirmation_id"],
}
return processed_res
Utiliser des outils et des rappels pour un comportement déterministe
Dans certains scénarios conversationnels, vous pouvez avoir besoin d'un comportement plus déterministe de la part de votre application d'agent. Dans ce cas, vous devez utiliser des outils ou des rappels.
Les rappels sont généralement la meilleure option pour un contrôle déterministe complet. Les rappels ont lieu en dehors du champ d'application de l'agent. L'agent n'est donc pas impliqué dans l'exécution des rappels.
Les composants internes d'un outil sont entièrement déterministes, mais un appel d'outil orchestré par un agent ne l'est pas. L'agent décide d'appeler un outil, prépare les arguments d'entrée de l'outil et interprète les résultats de l'outil. Il est possible qu'un agent hallucine cette orchestration.
Enchaînement des appels d'outils
Comme pour l'encapsulation des appels d'API avec des outils, si plusieurs outils doivent être exécutés au cours d'un tour de conversation, vous devez demander à l'agent d'appeler un outil et implémenter cet outil pour appeler les autres.
Vous pouvez également demander à l'agent d'appeler le premier outil et définir un rappel after_tool_callback pour appeler les outils restants.
Mauvais modèle pour l'enchaînement des appels d'outils
Il est considéré comme une mauvaise pratique d'inviter l'agent à appeler plusieurs outils au cours d'un tour de conversation afin d'atteindre un objectif commun.
Le modèle doit prédire chaque appel d'outil et chaque paramètre de cet appel d'outil. Il doit ensuite s'assurer de prédire les appels d'outils dans l'ordre. Cela signifie que vous vous appuyez fortement sur le modèle (qui n'est pas déterministe par nature) pour effectuer une tâche déterministe.
Par exemple, considérons la séquence d'outils suivante :
tool_1(arg_a, arg_b)-> résultatctool_2(arg_c)-> résultatdtool_3(arg_d)-> résultate
Si vous définissez des instructions pour ces trois appels d'outils, vous obtenez une séquence d'événements d'exécution semblable à celle-ci :
- Entrée utilisateur
- Modèle -> L'agent prédit
tool_1(arg_a, arg_b) tool_1_response.json()est renvoyé- L'agent interprète
tool_1_response.json()et extraitarg_c - Modèle -> L'agent prédit
tool_2(arg_c) tool_2_response.json()est renvoyé- L'agent interprète
tool_2_response.json()et extraitarg_d - Modèle -> L'agent prédit
tool_3(arg_d) tool_3_response.json()est renvoyé- Modèle → L'agent fournit la réponse finale
Il y a quatre appels de modèle, trois prédictions d'outil et quatre arguments d'entrée.
Bon modèle pour enchaîner les appels d'outils
Lorsque vous devez appeler plusieurs outils, il est recommandé d'indiquer à l'agent d'appeler un seul outil et d'implémenter cet outil pour appeler les autres.
L'outil suivant appelle trois autres outils :
def python_wrapper(arg_a: str, arg_b: str) -> dict:
"""Makes some sequential API calls."""
res1 = tools.tool_1({"arg_a": arg_a, "arg_b": arg_b})
res2 = tools.tool_2(res1.json())
res3 = tools.tool_3(res2.json())
return res3.json()
Prenons l'exemple d'une séquence d'événements pour un seul appel d'outil :
- Entrée utilisateur
- Modèle -> L'agent prédit
python_wrapper(arg_a, arg_b) python_wrapper_response.json()est renvoyé- Modèle → L'agent fournit la réponse finale
Cette approche réduit le nombre de jetons et la probabilité d'hallucination.
Définitions d'outils claires et distinctes
Pour les définitions d'outils, les bonnes pratiques suivantes doivent être appliquées :
- Les outils ne doivent pas avoir des noms similaires. Veillez à ce que les noms de vos outils soient clairement distincts les uns des autres.
- Les outils qui ne sont pas utilisés doivent être supprimés du nœud de l'agent.
Pour les noms de paramètres, utilisez la casse snake, des noms descriptifs et évitez les abréviations peu courantes.
Exemples de bonnes réponses :
first_name,phone_number,url.Exemples à ne pas suivre :
i,arg1,fn,pnum,rqst.Les paramètres doivent utiliser des structures aplaties plutôt que des structures imbriquées. Plus une structure est imbriquée, plus vous comptez sur le modèle pour prédire les paires clé/valeur et leur typage approprié.
Workflow de développement
Cette section présente les bonnes pratiques en matière de collaboration en équipe, de contrôle des versions et de tests lors du développement d'agents.
Définir un processus de développement pour la collaboration entre agents
Lorsque vous collaborez avec une équipe au développement d'une application d'agent, vous devez définir un processus de développement. Voici quelques exemples de pratiques de collaboration possibles :
- Utiliser un système de contrôle des versions tiers : utilisez l'importation et la restauration pour synchroniser les modifications avec votre système de contrôle des versions tiers. Définissez le processus de synchronisation, d'examen et de fusion. Définissez clairement les propriétaires et les étapes à suivre pour accepter les modifications (par exemple, en fournissant les résultats de l'évaluation).
- Utilisez le contrôle des versions intégré : configurez un processus pour utiliser le contrôle des versions intégré. Définissez la manière dont vous utiliserez les instantanés pour la gestion des versions. Par exemple, vous pouvez exiger un instantané lorsqu'une étape est franchie (un ensemble d'évaluations est réussi) ou avant le développement d'une nouvelle fonctionnalité. Définissez un processus de synchronisation, d'examen et de fusion des modifications.
Utiliser des versions pour enregistrer l'état de l'agent
Les versions vous permettent de conserver une trace du travail ou des modifications que vous avez effectués dans votre application d'agent. Après avoir modifié des instructions, des outils, des variables et d'autres éléments, vous pouvez enregistrer cet état avant d'apporter d'autres modifications. Les versions sont des instantanés immuables de l'agent à un moment donné. Vous devez créer une version lorsque vous êtes satisfait de certaines modifications et que l'application d'agent fonctionne comme vous l'avez conçue, en particulier après avoir validé les modifications avec des évaluations. Une fois que vous avez créé une version, vous pouvez toujours y revenir à tout moment.
Vous devez créer des versions fréquemment, par exemple après chaque série de 10 à 15 modifications majeures.
Il est également utile de nommer les versions de manière sémantique. Vous devez décider de la convention de nommage à utiliser avec votre équipe de développement. Il peut s'agir de noms descriptifs tels que pre-prod-instruction-changes ou prod-ready-for-testing. Vous pouvez également utiliser des normes telles que la gestion sémantique des versions, en utilisant des noms tels que v1.0.0, v1.0.1, etc. Les versions disposent également d'un champ de description qui vous permet d'ajouter plus de détails, comme le corps d'un message de commit. Le nom et la description de la version doivent être courts, pertinents et faciles à comprendre au cas où vous devriez effectuer un rollback vers cette version.
Effectuer des tests de bout en bout
Le processus de développement de votre application d'agent doit inclure des tests de bout en bout pour vérifier les intégrations avec les systèmes externes.
Évaluations
Cette section présente les bonnes pratiques pour utiliser les évaluations afin de garantir la fiabilité de l'agent.
Utiliser les évaluations
Les évaluations permettent de garantir la fiabilité de vos agents. Utilisez-les pour définir les attentes concernant vos agents et les API appelées par vos agents.
Gestion des sessions
Cette section fournit des modèles pour gérer le cycle de vie des sessions.
Salutations déterministes et latence réduite avec les réponses statiques
Vous pouvez configurer votre agent pour qu'il prononce une réponse déterministe au début d'une session. Cette approche peut permettre d'économiser des appels de modèle et des jetons, et de réduire la latence.
L'utilisation de before_model_callback vous permet d'intercepter l'entrée entrante, puis de répondre avec un message d'accueil statique.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in callback_context.get_last_user_input():
# Or other events or texts
if part.text == "<event>session start</event>":
return LlmResponse.from_parts(parts=[
Part.from_text(text="Hello how can I help you today?")
])
return None
Répondre rapidement avec des messages préfixés pendant que le modèle travaille
Lorsqu'une session commence, évitez de forcer l'utilisateur à attendre la génération du modèle. Vous pouvez ajouter un préfixe aux réponses pour que l'agent puisse rapidement fournir un message d'accueil convivial et personnalisé (par exemple, "Bonjour, je suis Gemini, votre assistant personnel"), tandis que le modèle traite simultanément la requête principale de l'utilisateur en arrière-plan.
Cela dépend du paramètre partial = True.
En règle générale, une réponse non FuctionCall est considérée comme une réponse terminale.
L'utilisation de partial = True oblige l'agent à poursuivre le traitement après la réponse.
Dans l'exemple suivant, l'application d'agent envoie rapidement le message de bienvenue, puis continue de traiter la requête principale. Cela élimine la pause de saisie maladroite, ce qui donne l'impression que l'agent est réactif.
def before_model_callback(callback_context: CallbackContext, llm_request: LlmRequest) -> Optional[LlmResponse]:
for part in callback_context.get_last_user_input():
if part.text == "<event>session start</event>":
response = LlmResponse.from_parts([Part.from_text("Hello, I'm Gemini, your personal AI assistant.")])
response.partial = True
return response
return None
Valider et appliquer le contenu obligatoire
Dans certains cas, vous pouvez demander à l'agent de fournir un contenu obligatoire spécifique (comme un avis de non-responsabilité juridique), mais aussi vérifier que l'agent l'a bien inclus. Ce modèle vous permet de vous appuyer sur la génération naturelle du modèle lorsqu'elle fonctionne, mais d'appliquer le contenu de manière déterministe lorsqu'elle échoue.
Vous pouvez utiliser un after_model_callback pour vérifier la sortie du modèle. Si le contenu obligatoire est présent, le rappel renvoie None (laissant passer la réponse du modèle). S'il manque, le rappel construit une nouvelle réponse contenant le contenu obligatoire.
Exemples de variables :
| Nom de la variable | Valeur par défaut |
|---|---|
| first_turn | Vrai |
DISCLAIMER = "THIS CONVERSATION MAY BE RECORDED FOR LEGAL PURPOSES."
def after_model_callback(
callback_context: CallbackContext,
llm_response: LlmResponse
) -> Optional[LlmResponse]:
if callback_context.variables.get("first_turn"):
callback_context.variables["first_turn"] = False
# Check if the agent's response already contains the disclaimer.
# The agent might have produced it based on instructions.
for part in callback_context.get_last_agent_output():
if part.text and DISCLAIMER in part.text:
return None
# If the agent failed to produce the disclaimer, force it.
return LlmResponse.from_parts(parts=[
Part.from_text(DISCLAIMER),
*llm_response.content.parts
])
return None
Appeler un outil personnalisé à la fin de la session
Vous pouvez configurer votre agent pour qu'il appelle un outil spécifique à la fin d'une session. Cela peut être utile pour les événements de clôture d'appel, comme la synchronisation des données à la sortie, l'envoi de données à une API externe, l'exécution de tâches de backend ou la journalisation des métadonnées d'appel.
Par exemple, supposons que vous disposiez d'un outil existant tel que post_call_logging que vous souhaitez appeler juste avant la fin de la session :
def post_call_logging(session_id: str) -> dict:
"""Logs the session ID to external API."""
API_URL = "https://api.example.com"
response = ces_requests.post(
url=API_URL,
data={"session_id": session_id}
)
return response.json()
Vous pouvez utiliser after_model_callback pour effectuer la séquence suivante :
- Recherchez l'appel d'outil
end_sessiondans la réponse de l'agent. - Créez la partie d'outil
post_call_logging. - Insérez l'appel d'outil
post_call_loggingavant l'appel d'outilend_session.
Cela garantit que l'agent exécute l'outil de journalisation avant de mettre fin à la session.
def after_model_callback(
callback_context: CallbackContext,
llm_response: LlmResponse
) -> Optional[LlmResponse]:
for index, part in enumerate(llm_response.content.parts):
if part.has_function_call('end_session'):
# Add an additional "post_call_logging" function call before "end_session",
# so the agent will execute the tool before ending the session.
tool_call = Part.from_function_call(
name="post_call_logging",
args={"sessionId": callback_context.session_id}
)
return LlmResponse.from_parts(
parts=llm_response.content.parts[:index] + [tool_call] + llm_response.content.parts[index:]
)
return None
Utiliser des réponses partielles pour les mises à jour de l'interface utilisateur en temps réel
Lorsqu'un agent exécute une action (par exemple, la mise à jour de l'état d'une commande), un délai peut s'écouler pendant que le modèle traite la réponse finale. L'utilisation de réponses partielles vous permet d'envoyer des notifications à l'interface utilisateur du client lorsqu'un outil termine son exécution, ce qui découple la mise à jour visuelle de la génération de texte du modèle.
Votre interface utilisateur peut actualiser les barres d'état, les trackers ou les reçus en temps réel.
Cela dépend du paramètre partial = True.
En règle générale, une réponse non FuctionCall est considérée comme une réponse terminale.
L'utilisation de partial = True oblige l'agent à poursuivre le traitement après la réponse.
La charge utile JSON ne sera pas envoyée au modèle. L'agent n'aura donc pas connaissance de l'existence de la charge utile lors de la génération de la réponse.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
if llm_request.contents[-1].parts[-1].has_function_response('update_order'):
order_state = llm_request.contents[-1].parts[-1].function_response.response['result']['order_state']
# Return a custom JSON payload before calling the model to generate the final agent response.
response = LlmResponse.from_parts([Part.from_json(data=json.dumps(order_state))])
response.partial = True
return response
return None
Intégration côté client
Cette section fournit des modèles d'intégration aux applications côté client.
Utiliser des charges utiles personnalisées pour piloter votre interface utilisateur
Les utilisateurs s'attendent à une interface dynamique et interactive. Vous pouvez utiliser des charges utiles personnalisées pour piloter le rendu côté client, ce qui permet de combler le fossé entre l'agent et une application soignée. Au lieu de fournir des options en texte brut, configurez votre agent pour qu'il détecte des schémas spécifiques dans une réponse (par exemple, une liste de choix) et les transforme en éléments d'interface utilisateur interactifs à fort taux de conversion, tels que des chips ou des boutons cliquables.
Utilisez un after_model_callback pour analyser les réponses de l'agent et détecter des déclencheurs spécifiques.
Par exemple, si la sortie du modèle est la suivante :
"Available options are: Refund, Track Order, Speak to Agent",
le rappel suivant intercepte et extrait ces options sous forme de charge utile JSON, qui peut être utilisée pour le rendu de l'interface utilisateur.
import json
def after_model_callback(
callback_context: CallbackContext,
llm_response: LlmResponse
) -> Optional[LlmResponse]:
prefix = 'Available options are:'
payload = {}
for part in llm_response.content.parts:
if part.text is not None and part.text.startswith(prefix):
# Return available options as chip list
payload['chips'] = part.text[len(prefix):].split(',')
break
new_parts = []
# Keep the original agent response part, as the custom payload won't be sent
# back to the model in the next turn.
new_parts.extend(llm_response.content.parts)
new_parts.append(Part.from_json(data=json.dumps(payload)))
return LlmResponse.from_parts(parts=new_parts)
Afficher du code Markdown et HTML
Si votre interface conversationnelle est compatible avec Markdown et HTML pour les réponses des agents, vous pouvez utiliser le simulateur pour tester ces réponses, car le simulateur est également compatible avec Markdown et HTML.
Exemples d'instructions :
<role>
You are a "Markdown Display Assistant," an AI agent designed to demonstrate
various rich content formatting options like images, videos, and deep links
using HTML-style markdown. Your purpose is to generate and display this
content directly to the user based on their requests.
</role>
<persona>
Your primary goal is to showcase the rich content rendering capabilities of
the platform by generating HTML markdown for elements like images, videos,
and hyperlinks. You are a helpful and direct assistant. When asked to show
something, you generate the markdown for it and present it.
You should not engage in conversations outside the scope of generating and
displaying markdown. If the user asks for something unrelated, politely
state that you can only help with displaying rich content. Adhere strictly
to the defined constraints and task flow.
</persona>
<constraints>
1. **Scope Limitation:** Only handle requests related to displaying
markdown content (images, videos, links, etc.). Do not answer general
knowledge questions or perform other tasks.
2. **Tool Interaction Protocol:** You must use the \`display_markdown\`
tool to generate the formatted content string.
3. **Direct Output:** Your final response to the user must be the raw
markdown string returned by the \`display_markdown\` tool. Do not add
any conversational text around it unless the tool returns an error.
For example, if the tool returns \`"<img src='...'>"\`, your response
should be exactly \`"<img src='...'>"\`.
4. **Clarity and Defaults:** If a user's request is vague (e.g., "show me
an image"), use the tool's default values to generate a response. There
is no need to ask for clarification.
5. **Error Handling:** If the tool call fails or returns an error, inform
the user about the issue in a conversational manner.
</constraints>
<taskflow>
These define the conversational subtasks that you can take. Each subtask
has a sequence of steps that should be taken in order.
<subtask name="Generate and Display Markdown">
<step name="Parse Request and Call Tool">
<trigger>
User initiates a request to see any form of rich content (image,
video, link, etc.).
</trigger>
<action>
1. Identify the types of content the user wants to see (e.g.,
image, video, deep link).
2. Call the \`display_markdown\` tool. Set the corresponding
boolean arguments to \`True\` based on the user's request.
For example, if the user asks for a video and a link, call
\`display_markdown(show_video=True, show_deep_link=True)\`.
3. If the user makes a general request like "show me something
cool", you can enable all flags.
</action>
</step>
<step name="Output Tool Response">
<trigger>
The \`display_markdown\` tool returns a successful response
containing a \`markdown_string\`.
</trigger>
<action>
1. Extract the value of the \`markdown_string\` key from the
tool's output.
2. Use this value as your direct and final response to the
user, without any additional text or formatting.
</action>
</step>
</subtask>
</taskflow>
Exemple d'outil Python :
from typing import Any
def display_markdown(show_image: bool, show_video: bool, show_deep_link: bool) -> dict[str, Any]:
"""
Constructs a markdown string containing HTML for various rich media elements.
This function generates an HTML-formatted string based on the boolean flags provided.
It can include an image, a video, and a hyperlink (deep link). The content for
these elements is pre-defined.
Args:
show_image (bool): If True, an <img> tag will be included in the output.
show_video (bool): If True, a <video> tag will be included in the output.
show_deep_link (bool): If True, an <a> tag will be included in the output.
Returns:
dict[str, Any]: A dictionary with a single key 'markdown_string' containing the
generated HTML markdown. If no flags are set, it returns a
message indicating nothing was requested.
"""
# MOCK: This is a mock implementation. It does not fetch any dynamic content.
# It assembles a markdown string from hardcoded HTML snippets to demonstrate
# the agent's ability to render rich content.
markdown_parts = []
if show_image:
image_html = "This is a sample image:\n<img src='https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png' alt='Google Logo' width='272' height='92' />"
markdown_parts.append(image_html)
if show_video:
video_html = "This is a sample video:\n<video controls width='320' height='240'><source src='https://www.w3schools.com/html/mov_bbb.mp4' type='video/mp4'>Sorry, your browser does not support embedded videos.</video>"
markdown_parts.append(video_html)
if show_deep_link:
link_html = "This is a sample deep link:\n<a href='https://www.google.com'>Click here to go to Google</a>"
markdown_parts.append(link_html)
if not markdown_parts:
return {"markdown_string": "You did not request any content to be displayed. Please specify if you want to see an image, video, or link."}
return {"markdown_string": "\n\n".join(markdown_parts)}
Commandes des canaux audio et vocaux
Cette section fournit des modèles pour contrôler le canal vocal et audio, y compris l'audio préenregistré, la musique d'attente et les paramètres d'interruption.
Remarques :
- Les fichiers audio peuvent être encodés au format Linear16, mulaw ou alaw.
- Si vous utilisez un bucket Cloud Storage appartenant à un autre projet cloud, le compte de service Customer Engagement Suite
service-<PROJECT-NUMBER>@gcp-sa-ces.iam.gserviceaccount.comdoit explicitement disposer de l'autorisationstorage.objects.getsur le bucket Cloud Storage cible. - Vous pouvez utiliser l'argument d'entrée
interruptablepour configurer si l'utilisateur final peut interrompre l'audio préenregistré. - Pour la lecture de musique, vous pouvez utiliser l'argument d'entrée
cancellablepour indiquer que la lecture de musique doit cesser lorsqu'une nouvelle réponse est générée par l'agent.
Lire un contenu audio préenregistré spécifique à une marque
Vous pouvez configurer votre agent pour qu'il lise un fichier audio préenregistré avant de traiter la requête de l'utilisateur. Vous pouvez l'utiliser pour les salutations approuvées par la marque ou les mentions légales obligatoires au début de la session.
Utilisez "transcript": "yyy" pour fournir à l'agent le texte de lecture audio, en vous assurant qu'il dispose du contexte nécessaire pour générer les réponses suivantes.
L'utilisation de "interrupable": false garantit que l'utilisateur ne peut pas interrompre la lecture audio.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in callback_context.get_last_user_input():
if part.text == "<event>session start</event>":
return LlmResponse.from_parts(parts=[
Part.from_json(data='{"audioUri": "gs://path/to/audio/file", "transcript": "transcript for the audio file", "interruptable": false}')
])
return None
Écouter de la musique préenregistrée lors de l'exécution d'outils lents (sans interruption)
Vous pouvez configurer l'agent pour qu'il diffuse de la musique pendant l'exécution d'un outil lent et "bloquant" (tel que la validation et l'activation de compte). La musique s'arrête automatiquement une fois l'exécution de l'outil terminée. Les utilisateurs ne peuvent pas interagir avec l'agent pendant la lecture de musique.
def after_model_callback(
callback_context: CallbackContext,
llm_response: LlmResponse
) -> Optional[LlmResponse]:
for index, part in enumerate(llm_response.content.parts):
if part.has_function_call("slow_tool"):
play_music = Part.from_json(
data='{"audioUri": "gs://path/to/music/file", "cancellable": true}'
)
return LlmResponse.from_parts(
parts=llm_response.content.parts[:index] +
[play_music] + llm_response.content.parts[index:]
)
return None
Lire de la musique préenregistrée lors de l'exécution d'outils asynchrones (autoriser l'interruption)
Vous pouvez configurer un agent pour qu'il lise de la musique tout en exécutant un outil de manière asynchrone, par exemple lors de la validation et de l'activation d'un compte utilisateur. La musique s'arrête automatiquement à la fin de l'outil asynchrone, à condition que l'utilisateur ne l'ait pas déjà interrompue. Les utilisateurs finaux peuvent interrompre la musique à tout moment pour continuer à interagir avec l'agent.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in llm_request.contents[-1].parts:
if part.has_function_response("async_tool"):
text = Part.from_text(text="I'm submitting your order, it may take a while.")
music = Part.from_json(
data='{"audioUri": "gs://path/to/music/file", "cancellable": true}'
)
return LlmResponse.from_parts(parts=[text, music])
return None
Interdire à l'utilisateur d'interrompre certaines réponses
Vous pouvez interdire à l'utilisateur d'interrompre l'agent lorsque celui-ci lit des informations importantes (comme un avis juridique), mais autoriser l'interruption pour le reste de la réponse de l'agent.
Il utilise l'outil système customize_response.
Vous pouvez implémenter ce comportement de deux manières, selon que vous souhaitez un résultat déterministe ou non :
- Rappel (déterministe) : force la réponse d'un rappel, comme indiqué dans l'exemple.
- Instructions (pilotées par l'agent) : demandez à l'agent d'utiliser l'outil
customize_responsedans ses instructions.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in callback_context.get_last_user_input():
if part.text == "<event>session start</event>":
return LlmResponse.from_parts(parts=[
Part.from_customized_response(
content=("Hello, I'm Gemini. Please listen to the following legal "
"disclaimer: <LEGAL_DISCLAIMER>"),
disable_barge_in=True
),
Part.from_text("How can I help you today?")
])
return None
Réponse personnalisée en cas d'absence de saisie
Lorsqu'un agent expire en attendant une entrée (voir Délai avant expiration du silence dans les paramètres de l'application de l'agent), une réponse générative est utilisée par défaut. Toutefois, vous pouvez vérifier si l'utilisateur a saisi une entrée dans un rappel de modèle "before" et fournir une réponse de manière conditionnelle.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in callback_context.get_last_user_input():
if part.text:
if "no user activity detected" in part.text:
return LlmResponse.from_parts(parts=[
Part.from_text(text="Hi, are you still there?")
])
return None
Gestion des exceptions
Cette section fournit des modèles pour gérer les erreurs d'outil.
Transférer vers un autre agent en cas d'échec de l'outil
Lorsqu'une exécution d'outil spécifique échoue, vous pouvez transférer de manière déterministe la conversation à un autre agent. Il s'agit d'un filet de sécurité essentiel pour protéger l'expérience utilisateur en cas d'erreurs d'exécution.
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in llm_request.contents[-1].parts:
if (part.has_function_response('authentication') and
'error' in part.function_response.response['result']):
return LlmResponse.from_parts(parts=[
Part.from_text('Sorry something went wrong, let me transfer you to another agent.'),
Part.from_agent_transfer(agent='escalation agent')
])
return None
Arrêter la session de manière fluide en cas d'échec de l'outil
Lorsqu'une exécution d'outil spécifique échoue, vous pouvez mettre fin à la session de manière fluide. Cela peut éviter les boucles infinies et les réponses déroutantes en cas de défaillance d'un outil critique.
Exemple de rappel :
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
for part in llm_request.contents[-1].parts:
if (part.has_function_response('authentication') and
'error' in part.function_response.response['result']):
return LlmResponse.from_parts(parts=[
Part.from_text('Sorry something went wrong, please call back later.'),
Part.from_end_session(reason='Failure during user authentication.')
])
return None
Contexte et variables
Cette section fournit des modèles d'utilisation des variables de contexte.
Transmettre des variables de contexte aux outils OpenAPI
L'IA personnalisée nécessite des outils pour accéder aux données de session utilisateur.
Il est intrinsèquement peu fiable et lent de s'appuyer sur le modèle pour rappeler et transmettre manuellement des détails importants tels que les ID de session ou les variables utilisateur.
L'agent peut plutôt transmettre des variables de contexte spécifiques aux outils OpenAPI.
Vous pouvez utiliser x-ces-session-context pour indiquer que la valeur n'a pas besoin d'être produite par le modèle (et dont le schéma est invisible pour le modèle), mais qu'elle provient plutôt des variables de contexte.
Le tableau suivant liste les valeurs disponibles :
| Valeur | Description |
|---|---|
$context.project_id |
ID du projet Google Cloud . |
$context.project_number |
Numéro du projet Google Cloud . |
$context.location |
Région de l'agent. |
$context.app_id |
ID application de l'agent. |
$context.session_id |
Identifiant unique de la session. |
$context.variables |
Toutes les valeurs des variables de contexte sous forme d'objet. |
$context.variables.variable_name |
Valeur d'une variable de contexte spécifique. Remplacez variable_name par le nom de la variable. |
openapi: 3.0.0
info:
title: test-title
description: test-description
version: 1.0.0
paths:
/test-path/{session_id}:
post:
parameters:
- name: session_id
in: path
description: The session ID.
required: true
schema:
type: string
x-ces-session-context: $context.session_id
- name: test_variable
in: query
description: Specific session variable.
required: true
schema:
type: string
x-ces-session-context: $context.variables.test_variable
requestBody:
description: test-description
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SessionParams'
responses:
'200':
description: test-response-description
content:
application/json:
schema:
type: object
properties:
result:
type: string
components:
schemas:
SessionParams:
type: object
description: all context variables
x-ces-session-context: $context.variables
Requêtes dynamiques
Vous pouvez créer des agents qui envoient des requêtes dynamiques au modèle en utilisant :
- Variables pouvant être utilisées pour contrôler les variantes de requêtes.
- Instructions qui incluent des références à des variables.
- Un outil capable de mettre à jour les variables en fonction des détails de la conversation.
- Un before_model_callback.
Par exemple, vous pouvez modifier les instructions de l'agent selon que l'utilisateur est un avocat ou un pirate :
Variables :
| Nom de la variable | Valeur par défaut |
|---|---|
| current_instructions | Vous êtes Gemini et vous travaillez pour Google. |
| lawyer_instructions | Tu es avocat et ton travail consiste à raconter des blagues à deux balles, mais avec une touche juridique. |
| pirate_instructions | Vous êtes un pirate et votre travail consiste à raconter une blague en tant que pirate. |
| nom d'utilisateur | Inconnu |
Instructions :
The current user is: {username}
You can use {@TOOL: update_username} to update the user's name if they provide
it.
Follow the current instruction set below exactly.
{current_instructions}
Outil Python :
from typing import Optional
def update_username(username: str) -> Optional[str]:
"""Updates the current user's name."""
set_variable("username", username)
Rappel :
def before_model_callback(
callback_context: CallbackContext,
llm_request: LlmRequest
) -> Optional[LlmResponse]:
username = callback_context.get_variable("username", None)
if username == "Jenn":
new_instructions = callback_context.get_variable("pirate_instructions")
elif username == "Gary":
new_instructions = callback_context.get_variable("lawyer_instructions")
callback_context.set_variable("current_instructions", new_instructions)