Bonnes pratiques et modèles

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ésultat c
  • tool_2(arg_c) -> résultat d
  • tool_3(arg_d) -> résultat e

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 extrait arg_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 extrait arg_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 :

  1. Recherchez l'appel d'outil end_session dans la réponse de l'agent.
  2. Créez la partie d'outil post_call_logging.
  3. Insérez l'appel d'outil post_call_logging avant l'appel d'outil end_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.com doit explicitement disposer de l'autorisation storage.objects.get sur le bucket Cloud Storage cible.
  • Vous pouvez utiliser l'argument d'entrée interruptable pour configurer si l'utilisateur final peut interrompre l'audio préenregistré.
  • Pour la lecture de musique, vous pouvez utiliser l'argument d'entrée cancellable pour 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 :

  1. Rappel (déterministe) : force la réponse d'un rappel, comme indiqué dans l'exemple.
  2. Instructions (pilotées par l'agent) : demandez à l'agent d'utiliser l'outil customize_response dans 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)