Best Practices und Muster

In diesem Leitfaden finden Sie bewährte Best Practices und Designmuster, mit denen Sie Ihre Agent-Anwendung optimieren und skalieren können. Diese Inhalte können Ihnen helfen, die Kosten für die Designzeit und die Laufzeit zu senken und die Zuverlässigkeit von Agents zu verbessern.

Allgemein

In diesem Abschnitt finden Sie allgemeine Best Practices für den Einstieg in die Entwicklung von Agents und das Schreiben von Anweisungen.

Einfacher Einstieg

Wenn Sie mit der Entwicklung Ihrer Agent-Anwendung beginnen, sollten Sie mit einfachen Anwendungsfällen starten. Sobald einfache Anwendungsfälle funktionieren, können Sie komplexere Anwendungsfälle entwickeln.

Anweisungen sollten spezifisch sein

Die Anweisungen für den Agenten sollten spezifisch und eindeutig sein. Die Anleitungen sollten gut strukturiert und nach Themen gruppiert sein. Vermeiden Sie es, Anleitungen zu bestimmten Themen wahllos zu verteilen. Die Anleitung sollte auch für Menschen leicht verständlich sein.

Strukturierte Anweisungen verwenden

Nachdem Sie die Anweisungen geschrieben haben, sollten Sie die Funktion Anweisungen umstrukturieren verwenden, um sie zu formatieren. In diesem Format ist Ihr Agent zuverlässiger.

Modulare Komponenten aus mehreren Modalitäten und Channels verwenden

Wenn Agents für verschiedene Modalitäten (Text oder Sprache) oder Kanäle (API, Telefonie oder Web) unterschiedlich reagieren sollen, sollten Sie modulare Komponenten und Python-Tools und ‑Callbacks auf Kanal und Modalität prüfen verwenden.

Tools

In diesem Abschnitt finden Sie Best Practices für das Definieren und Verwenden von Tools, einschließlich des Umschließens externer APIs und des Verknüpfens von Tool-Aufrufen.

APIs mit Python-Tools umschließen

Externe API-Schemas können viele Ein- und Ausgabeparameter definieren, die für Ihren KI-Agenten nicht relevant sind. Wenn Sie in solchen Fällen OpenAPI-Tools verwenden, stellen Sie dem Modell möglicherweise unnötigen Kontext zur Verfügung, was die Zuverlässigkeit verringern kann. Angenommen, ein OpenAPI-Spezifikationstool benötigt drei Parameter als Eingabe und gibt ein großes JSON-Objekt mit 100 Schlüssel/Wert-Paaren zurück. Der Agent sagt die drei Eingabeargumente für dieses Tool voraus. Wenn er zurückkehrt, sieht er die vollständige JSON-Nutzlast mit allen 100 Schlüssel/Wert-Paaren. Wenn nur 3 dieser Paare tatsächlich für die Unterhaltung relevant sind, sind die anderen 97 Paare irrelevante Daten, die der Unterhaltungsverlauf um Tokens ergänzen. Das mag harmlos erscheinen, kann aber unnötige Verwirrung beim Agent verursachen, die Denkzeit und die Latenz erhöhen.

Es empfiehlt sich, Python-Tools zum Umschließen von API-Aufrufen zu verwenden. Durch das Umschließen können Sie unnötige Daten vor dem Agenten und dem Kontextverlauf verbergen. Sie können den genauen Kontext, den der Agent sieht, steuern, indem Sie nur die Daten zurückgeben, die zu diesem Zeitpunkt für den Agenten relevant sind. So haben Sie die vollständige Kontrolle über die vom Tool definierten Ein- und Ausgabeparameter, die für das Modell freigegeben werden. Diese Vorgehensweise ist eine Form des Context Engineering mit Tools.

Beispielcode:

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

Tools und Callbacks für deterministisches Verhalten verwenden

In bestimmten Gesprächsszenarien benötigen Sie möglicherweise ein deterministischeres Verhalten Ihrer Agent-Anwendung. In diesen Fällen sollten Sie Tools oder Callbacks verwenden.

Callbacks sind in der Regel die beste Option für eine vollständig deterministische Steuerung. Rückrufe erfolgen außerhalb des Zuständigkeitsbereichs des Agenten, sodass der Agent nicht an der Ausführung von Rückrufen beteiligt ist.

Die internen Abläufe eines Tools sind vollständig deterministisch, ein von einem Agenten orchestrierter Tool-Aufruf jedoch nicht. Der Agent entscheidet, ein Tool aufzurufen, bereitet Tool-Eingabeargumente vor und interpretiert Tool-Ergebnisse. Es ist möglich, dass ein KI-Agent diese Orchestrierung halluziniert.

Verketten von Toolaufrufen

Ähnlich wie beim Umschließen von API-Aufrufen mit Tools sollten Sie, wenn während eines Gesprächs mehrere Tools ausgeführt werden müssen, den Agenten anweisen, ein Tool aufzurufen, und dieses Tool so implementieren, dass es die anderen aufruft. Alternativ können Sie den Agenten anweisen, das erste Tool aufzurufen, und einen after_tool_callback-Callback definieren, um die verbleibenden Tools aufzurufen.

Schlechtes Muster für die Verkettung von Tool-Aufrufen

Es gilt als schlechtes Muster, den KI-Agenten anzuweisen, während einer Unterhaltungsrunde mehrere Tools aufzurufen, um ein gemeinsames Ziel zu erreichen.

Das Modell muss jeden Toolaufruf und jeden Parameter in diesem Toolaufruf vorhersagen. Außerdem muss es sicherstellen, dass die Tool-Aufrufe in der richtigen Reihenfolge vorhergesagt werden. Das bedeutet, dass Sie sich stark auf das Modell verlassen, um eine deterministische Aufgabe auszuführen, obwohl das Modell von Natur aus nicht deterministisch ist.

Betrachten Sie beispielsweise die folgende Tool-Sequenz:

  • tool_1(arg_a, arg_b) -> Ausgabe c
  • tool_2(arg_c) -> Ausgabe d
  • tool_3(arg_d) -> Ausgabe e

Wenn Sie Anweisungen für diese drei Tool-Aufrufe definieren, erhalten Sie eine Laufzeitsequenz von Ereignissen wie die folgende:

  • Nutzereingabe
  • Modell –> Agent sagt tool_1(arg_a, arg_b) voraus
  • tool_1_response.json() wird zurückgegeben
  • Der Agent interpretiert tool_1_response.json() und extrahiert arg_c
  • Modell –> Agent sagt tool_2(arg_c) voraus
  • tool_2_response.json() wird zurückgegeben
  • Der Agent interpretiert tool_2_response.json() und extrahiert arg_d
  • Modell –> Agent sagt tool_3(arg_d) voraus
  • tool_3_response.json() wird zurückgegeben
  • Modell -> Agent gibt endgültige Antwort

Es gibt 4 Modellaufrufe, 3 Tool-Vorhersagen und 4 Eingabeargumente.

Gutes Muster für das Verketten von Tool-Aufrufen

Wenn Sie mehrere Tools aufrufen müssen, ist es empfehlenswert, den Agenten anzuweisen, ein einzelnes Tool aufzurufen, und dieses Tool so zu implementieren, dass es die anderen aufruft.

Der folgende Tool-Aufruf ruft drei andere Tools auf:

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

Betrachten wir die Abfolge der Ereignisse für einen einzelnen Tool-Aufruf:

  • Nutzereingabe
  • Modell –> Agent sagt python_wrapper(arg_a, arg_b) voraus
  • python_wrapper_response.json() wird zurückgegeben
  • Modell -> Agent gibt endgültige Antwort

Dieser Ansatz reduziert die Anzahl der Tokens und die Wahrscheinlichkeit von Halluzinationen.

Klare und eindeutige Tooldefinitionen

Für Tooldefinitionen sollten die folgenden Best Practices angewendet werden:

  • Verschiedene Tools sollten keine ähnlichen Namen haben. Die Namen der Tools sollten sich deutlich voneinander unterscheiden.
  • Tools, die nicht verwendet werden, sollten aus dem Agent-Knoten entfernt werden.
  • Verwenden Sie für Parameternamen Snake Case, aussagekräftige Namen und vermeiden Sie ungewöhnliche Abkürzungen.

    Gute Beispiele: first_name, phone_number, url.

    Negativbeispiele: i, arg1, fn, pnum, rqst.

  • Parameter sollten flache statt verschachtelter Strukturen verwenden. Je stärker eine Struktur verschachtelt ist, desto mehr verlassen Sie sich darauf, dass das Modell Schlüssel/Wert-Paare und deren korrekte Typisierung vorhersagt.

Entwicklungs-Workflow

Dieser Abschnitt enthält Best Practices für die Zusammenarbeit im Team, die Versionsverwaltung und das Testen während der Entwicklung von KI-Agenten.

Entwicklungsprozess für die Zusammenarbeit von KI-Agenten definieren

Wenn Sie mit einem Team an der Entwicklung von Agent-Anwendungen zusammenarbeiten, sollten Sie einen Entwicklungsprozess definieren. Hier einige Beispiele für mögliche Zusammenarbeitsmethoden:

  • Versionsverwaltung von Drittanbietern verwenden: Mit der Import- und Wiederherstellungsfunktion können Sie Änderungen mit Ihrem Versionsverwaltungssystem von Drittanbietern synchronisieren. Legen Sie den Prozess für die Synchronisierung, Überprüfung und Zusammenführung fest. Definieren Sie klare Verantwortlichkeiten und klare Schritte zum Akzeptieren von Änderungen (z. B. anhand von Bewertungsergebnissen).
  • Integrierte Versionsverwaltung verwenden: Richten Sie einen Prozess ein, um die integrierte Versionsverwaltung zu nutzen. Legen Sie fest, wie Snapshots für die Versionsverwaltung verwendet werden sollen. Sie können beispielsweise einen Snapshot anfordern, wenn ein Meilenstein erreicht wird (eine Reihe von Tests wird bestanden) oder bevor neue Funktionen entwickelt werden. Legen Sie einen Prozess für die Synchronisierung, Überprüfung und Zusammenführung von Änderungen fest.

Versionen zum Speichern des Agentenstatus verwenden

Mit Versionen können Sie abgeschlossene Arbeiten oder Änderungen in Ihrer Agent-Anwendung festhalten. Nachdem Sie Änderungen an Anweisungen, Tools, Variablen und anderen Elementen vorgenommen haben, können Sie diesen Zustand speichern, bevor Sie weitere Änderungen vornehmen. Versionen sind unveränderliche Momentaufnahmen des Agenten. Sie sollten eine Version erstellen, wenn Sie mit einigen Änderungen zufrieden sind und die Agent-Anwendung wie geplant funktioniert, insbesondere nachdem Sie Änderungen mit Tests validiert haben. Nachdem Sie eine Version erstellt haben, können Sie jederzeit zu dieser Version zurückkehren.

Sie sollten häufig Versionen erstellen, vielleicht nach jeweils 10 bis 15 wichtigen Änderungen. Es ist auch hilfreich, Versionen semantisch zu benennen. Sie sollten die Namenskonvention mit Ihrem Entwicklungsteam festlegen. Beispiele sind beschreibende Namen wie pre-prod-instruction-changes oder prod-ready-for-testing. Sie können auch Standards wie Semantic Versioning verwenden und Namen wie v1.0.0, v1.0.1 usw. vergeben. Versionen haben auch ein Beschreibungsfeld, in dem Sie weitere Details hinzufügen können, ähnlich wie in einem Commit-Nachrichtentext. Der Versionsname und die Beschreibung sollten kurz, aussagekräftig und leicht verständlich sein, falls Sie ein Rollback zu dieser Version durchführen müssen.

End-to-End-Tests durchführen

Bei der Entwicklung Ihrer Agent-Anwendung sollten Sie End-to-End-Tests durchführen, um die Integrationen mit externen Systemen zu überprüfen.

Bewertungen

In diesem Abschnitt finden Sie Best Practices für die Verwendung von Evaluierungen, um die Zuverlässigkeit von Agents zu gewährleisten.

Bewertungen verwenden

Bewertungen tragen dazu bei, dass Ihre KI-Agenten zuverlässig bleiben. Verwenden Sie sie, um die Erwartungen an Ihre Agents und die von Ihren Agents aufgerufenen APIs festzulegen.

Sitzungsverwaltung

In diesem Abschnitt finden Sie Muster für die Verwaltung des Sitzungslebenszyklus.

Deterministische Begrüßungen und reduzierte Latenz mit statischen Antworten

Sie können Ihren Agent so konfigurieren, dass er zu Beginn einer Sitzung eine deterministische Antwort gibt. So können Sie Modellaufrufe und Tokens sparen und die Latenz reduzieren.

Mit before_model_callback können Sie die eingehende Eingabe abfangen und dann mit einer statischen Begrüßungsnachricht antworten.

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

Mit Präfixnachrichten schnell antworten, während das Modell arbeitet

Wenn eine Sitzung beginnt, sollten Sie den Nutzer nicht dazu zwingen, auf die Modellgenerierung zu warten. Sie können Antworten mit einem Präfix versehen, damit der Agent schnell eine freundliche, markenspezifische Begrüßung ausgeben kann (z. B. „Hallo, ich bin Gemini, dein persönlicher Assistent“), während das Modell gleichzeitig die Hauptanfrage des Nutzers im Hintergrund verarbeitet.

Dies hängt von der Einstellung partial = True ab. Normalerweise gilt eine Antwort, die nicht FuctionCall ist, als endgültige Antwort. Wenn Sie partial = True verwenden, wird der Agent gezwungen, die Verarbeitung nach der Antwort fortzusetzen.

Im folgenden Beispiel liefert die Agent-Anwendung schnell die Willkommensnachricht und verarbeitet dann die Hauptanfrage weiter. Dadurch wird die unangenehme Tipppause vermieden und der KI-Agent wirkt reaktionsschneller.

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

Pflichtinhalte überprüfen und durchsetzen

In einigen Fällen möchten Sie den Agenten möglicherweise anweisen, bestimmte obligatorische Inhalte (z. B. einen rechtlichen Haftungsausschluss) bereitzustellen, und gleichzeitig überprüfen, ob der Agent diese Inhalte tatsächlich eingefügt hat. Mit diesem Muster können Sie sich auf die natürliche Generierung des Modells verlassen, wenn sie funktioniert, aber den Inhalt deterministisch erzwingen, wenn sie fehlschlägt.

Sie können einen after_model_callback verwenden, um die Ausgabe des Modells zu prüfen. Wenn die erforderlichen Inhalte vorhanden sind, gibt der Callback None zurück (die Antwort des Modells wird also weitergeleitet). Wenn sie fehlt, wird in der Rückruffunktion eine neue Antwort mit den erforderlichen Inhalten erstellt.

Beispielvariablen:

Variablenname Standardwert
first_turn Wahr
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

Benutzerdefiniertes Tool bei Sitzungsende aufrufen

Sie können Ihren Agent so konfigurieren, dass er am Ende einer Sitzung ein bestimmtes Tool aufruft. Das kann für Ereignisse nach dem Anruf nützlich sein, z. B. zum Synchronisieren von Daten beim Beenden, zum Senden von Daten an eine externe API, zum Abschließen von Backend-Aufgaben oder zum Protokollieren von Anrufmetadaten.

Angenommen, Sie haben ein vorhandenes Tool wie post_call_logging, das Sie kurz vor dem Ende der Sitzung aufrufen möchten:

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

Mit after_model_callback können Sie die folgende Sequenz ausführen:

  1. Suchen Sie in der Antwort des Agenten nach dem Tool-Aufruf end_session.
  2. Erstellen Sie den Tool-Teil post_call_logging.
  3. Fügen Sie den post_call_logging-Toolaufruf vor dem end_session-Toolaufruf ein.

So wird sichergestellt, dass der Agent das Protokollierungstool ausführt, bevor er die Sitzung beendet.

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

Teilantworten für Echtzeit-Updates der Benutzeroberfläche verwenden

Wenn ein Kundenservicemitarbeiter eine Aktion ausführt (z. B. den Bestellstatus aktualisiert), kann es zu einer Verzögerung kommen, da das Modell die endgültige Antwort verarbeitet. Mit Teilantworten können Sie Benachrichtigungen an die Client-Benutzeroberfläche senden, wenn ein Tool die Ausführung abgeschlossen hat. So wird die visuelle Aktualisierung von der Textgenerierung des Modells entkoppelt.

Ihre Benutzeroberfläche kann Statusleisten, Tracker oder Belege in Echtzeit aktualisieren.

Dies hängt von der Einstellung partial = True ab. Normalerweise gilt eine Antwort, die nicht FuctionCall ist, als endgültige Antwort. Wenn Sie partial = True verwenden, wird der Agent gezwungen, die Verarbeitung nach der Antwort fortzusetzen.

Die JSON-Nutzlast wird nicht an das Modell gesendet. Der Agent ist sich daher der Existenz der Nutzlast während der Generierung von Antworten nicht bewusst.

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

Clientseitige Integration

In diesem Abschnitt finden Sie Muster für die Integration in clientseitige Anwendungen.

Benutzerdefinierte Nutzlasten zur Steuerung der Benutzeroberfläche verwenden

Nutzer erwarten eine dynamische, interaktive Oberfläche. Mit benutzerdefinierten Nutzlasten können Sie das clientseitige Rendern steuern und so die Lücke zwischen dem Agent und einer ausgereiften Anwendung schließen. Anstatt Optionen in einfachem Text zu präsentieren, können Sie Ihren Agenten so konfigurieren, dass er bestimmte Muster in einer Antwort erkennt (z. B. eine Liste mit Auswahlmöglichkeiten) und sie in interaktive Benutzeroberflächenelemente mit hoher Conversion-Rate umwandelt, z. B. in klickbare Chips oder Schaltflächen.

Verwenden Sie einen after_model_callback, um Agent-Antworten nach bestimmten Triggern zu durchsuchen. Wenn die Modellausgabe beispielsweise „Verfügbare Optionen: Erstattung, Bestellung verfolgen, mit Kundenservicemitarbeiter sprechen“ lautet, werden diese Optionen durch den folgenden Callback abgefangen und als JSON-Nutzlast extrahiert, die für das Rendern der Benutzeroberfläche verwendet werden kann.

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)

Markdown und HTML anzeigen

Wenn Ihre konversationelle Schnittstelle Markdown und HTML für Agentenantworten unterstützt, können Sie den Simulator verwenden, um diese Antworten zu testen, da der Simulator auch Markdown und HTML unterstützt.

Beispielanweisungen:

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

Python-Beispieltool:

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

Sprach- und Audiochannel-Steuerung

In diesem Abschnitt finden Sie Muster zum Steuern des Sprach- und Audio-Channels, einschließlich vorab aufgezeichneter Audioinhalte, Wartemusik und Einstellungen für das Unterbrechen von Gesprächen.

Hinweise:

  • Als Audiodatei wird die Audio-Codierung Linear16, mulaw und alaw unterstützt.
  • Wenn Sie einen Cloud Storage-Bucket verwenden, der zu einem anderen Cloud-Projekt gehört, muss dem Customer Engagement Suite-Dienstkonto service-<PROJECT-NUMBER>@gcp-sa-ces.iam.gserviceaccount.com explizit die Berechtigung storage.objects.get für den Ziel-Cloud Storage-Bucket erteilt werden.
  • Mit dem Eingabeargument interruptable können Sie konfigurieren, ob die vorab aufgezeichneten Audioinhalte vom Endnutzer unterbrochen werden können.
  • Für die Musikwiedergabe können Sie das Eingabeargument cancellable verwenden, um anzugeben, dass die Musikwiedergabe beendet werden soll, wenn der Agent eine neue Antwort generiert.

Markenspezifische vorab aufgenommene Audioinhalte abspielen

Sie können Ihren Agent so konfigurieren, dass er eine vorab aufgezeichnete Audiodatei abspielt, bevor er die Nutzeranfrage verarbeitet. Sie können diese Funktion für markenkonforme Begrüßungen oder obligatorische rechtliche Offenlegungen zu Beginn einer Sitzung verwenden.

Verwenden Sie "transcript": "yyy", um dem Agenten den Text für die Audiowiedergabe zur Verfügung zu stellen. So hat er den erforderlichen Kontext, um nachfolgende Antworten zu generieren.

Mit "interrupable": false wird sichergestellt, dass der Nutzer die Audiowiedergabe nicht unterbrechen kann.

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

Vorgefertigte Musik abspielen, wenn langsame Tools ausgeführt werden (kein Barge-in)

Sie können den Agent so konfigurieren, dass er Musik abspielt, während ein langsames, „blockierendes“ Tool (z. B. zur Kontovalidierung und -aktivierung) ausgeführt wird. Die Musik wird automatisch beendet, sobald das Tool ausgeführt wurde. Nutzer können nicht mit dem KI-Agenten interagieren, während die Musik läuft.

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

Vorgefertigte Musik abspielen, wenn asynchrone Tools ausgeführt werden (Unterbrechung zulassen)

Sie können einen Agent so konfigurieren, dass er Musik abspielt, während er ein Tool asynchron ausführt, z. B. während der Validierung und Aktivierung des Nutzerkontos. Die Musik wird automatisch beendet, wenn das asynchrone Tool abgeschlossen ist, sofern der Nutzer sie nicht bereits unterbrochen hat. Endnutzer können die Musik jederzeit unterbrechen, um mit dem Agenten zu interagieren.

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

Unterbrechen von bestimmten Antworten durch Nutzer nicht zulassen

Sie können dem Nutzer untersagen, den Agent zu unterbrechen, wenn der Agent wichtige Informationen (z. B. einen rechtlichen Haftungsausschluss) vorliest, aber das Barge-in für den restlichen Teil der Agent-Antwort zulassen.

Dazu wird das Systemtool customize_response verwendet.

Sie können dieses Verhalten auf zwei Arten implementieren, je nachdem, ob Sie ein deterministisches Ergebnis wünschen:

  1. Callback (deterministisch): Erzwingen Sie die Antwort aus einem Callback, wie im Beispiel gezeigt.
  2. Anweisungen (Agent-gesteuert): Fordern Sie den Agenten auf, das Tool customize_response in seinen Anweisungen zu verwenden.
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

Benutzerdefinierte Antwort für „Keine Eingabe“

Wenn ein Agent aufgrund einer Zeitüberschreitung auf Eingaben wartet (siehe Zeitüberschreitung bei Inaktivität in den Agent-Anwendungseinstellungen), wird standardmäßig eine generative Antwort verwendet. Sie können jedoch in einem „before model“-Callback prüfen, ob eine Eingabe vom Nutzer empfangen wurde, und bedingt eine Antwort geben.

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

Fehlerbehandlung

In diesem Abschnitt finden Sie Muster für die Behandlung von Tool-Fehlern.

Bei Tool-Fehlern an einen anderen Kundenservicemitarbeiter weiterleiten

Wenn die Ausführung eines bestimmten Tools fehlschlägt, können Sie die Unterhaltung deterministisch an einen anderen Agenten übergeben. Dies ist ein wichtiges Sicherheitsnetz, um die Nutzerfreundlichkeit bei Laufzeitfehlern zu schützen.

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

Sitzung bei Tool-Fehlern ordnungsgemäß beenden

Wenn die Ausführung eines bestimmten Tools fehlschlägt, können Sie die Sitzung ordnungsgemäß beenden. So können Endlosschleifen und verwirrende Antworten bei kritischen Tool-Fehlern vermieden werden.

Beispiel-Callback:

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

Kontext und Variablen

In diesem Abschnitt finden Sie Muster für die Verwendung von Kontextvariablen.

Kontextvariablen an OpenAPI-Tools übergeben

Für personalisierte KI sind Tools erforderlich, mit denen auf Nutzer-Sitzungsdaten zugegriffen werden kann. Wenn Sie sich auf das Modell verlassen, um wichtige Details wie Sitzungs-IDs oder Nutzervariablen manuell abzurufen und zu übergeben, ist das von Natur aus unzuverlässig und langsam. Stattdessen kann der Agent bestimmte Kontextvariablen an OpenAPI-Tools übergeben. Mit x-ces-session-context können Sie angeben, dass der Wert nicht vom Modell generiert werden muss (und dessen Schema für das Modell unsichtbar ist), sondern aus Kontextvariablen stammt.

In der folgenden Tabelle sind die verfügbaren Werte aufgeführt:

Wert Beschreibung
$context.project_id Die Google Cloud Projekt-ID.
$context.project_number Die Google Cloud -Projektnummer.
$context.location Der Standort (die Region) des KI-Agents.
$context.app_id Die Anwendungs-ID des Agenten.
$context.session_id Die eindeutige Kennung für die Sitzung.
$context.variables Alle Werte der Kontextvariable als Objekt.
$context.variables.variable_name Der Wert einer bestimmten Kontextvariable. Ersetzen Sie variable_name durch den Namen der Variablen.
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

Dynamische Prompts

Sie können Agents erstellen, bei denen dynamische Prompts an das Modell gesendet werden, indem Sie Folgendes verwenden:

  • Variablen, mit denen sich Prompt-Variationen steuern lassen.
  • Anleitungen, die Verweise auf Variablen enthalten.
  • Ein Tool, mit dem die Variablen anhand von Unterhaltungsdetails aktualisiert werden können.
  • Ein before_model_callback.

Sie können die Anweisungen für den Agenten beispielsweise daran anpassen, ob der Nutzer ein Anwalt oder ein Pirat ist:

Variablen:

Variablenname Standardwert
current_instructions Du bist Gemini und arbeitest für Google.
lawyer_instructions Du bist Anwalt und deine Aufgabe ist es, Witze im Stil von „Papawitzen“ zu erzählen, aber mit einem juristischen Touch.
pirate_instructions Du bist ein Pirat und deine Aufgabe ist es, einen Witz als Pirat zu erzählen.
Nutzername Unbekannt

Anleitung:

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}

Python-Tool:

from typing import Optional

def update_username(username: str) -> Optional[str]:
  """Updates the current user's name."""
  set_variable("username", username)

Callback:

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)