Best practice e pattern

Questa guida condivide best practice e pattern di progettazione comprovati per ottimizzare e scalare l'applicazione dell'agente. Questi contenuti possono aiutarti a ridurre i costi di progettazione, ridurre i costi di runtime e migliorare l'affidabilità degli agenti.

Generale

Questa sezione fornisce le best practice generali per iniziare a sviluppare agenti e scrivere istruzioni.

Inizia dalle cose semplici

Quando inizi a creare l'applicazione dell'agente, devi iniziare con casi d'uso semplici. Una volta che i casi d'uso semplici funzionano, continua a creare casi d'uso più complessi.

Le istruzioni devono essere specifiche

Le istruzioni dell'agente devono essere specifiche e non ambigue. Le istruzioni devono essere ben organizzate e raggruppate per argomenti. Evita di spargere istruzioni su argomenti specifici in modo casuale. Le istruzioni devono essere facili da seguire anche per una persona.

Utilizzare istruzioni strutturate

Una volta terminato di scrivere le istruzioni, devi utilizzare la funzionalità Ristruttura istruzioni per formattarle. In questo formato, il tuo agente sarà più affidabile.

Strumenti

Questa sezione fornisce le best practice per definire e utilizzare gli strumenti, inclusi il wrapping delle API esterne e l'incatenamento delle chiamate agli strumenti.

Aggregare le API con gli strumenti Python

Gli schemi delle API esterne possono definire molti parametri di input e output che non sono pertinenti per il tuo agente. Se utilizzi strumenti OpenAPI per casi come questo, potresti fornire un contesto non necessario al modello, il che può ridurre l'affidabilità. Ad esempio, supponiamo che uno strumento di specifica OpenAPI accetti 3 parametri come input e restituisca un grande oggetto JSON con 100 coppie chiave/valore. L'agente prevede i tre argomenti di input per questo strumento e, quando viene restituito, visualizza il payload JSON completo con tutte le 100 coppie chiave/valore. Se solo 3 di queste coppie sono effettivamente pertinenti alla conversazione, le altre 97 coppie sono dati irrilevanti che aggiungono token alla cronologia della conversazione. Sebbene possa sembrare innocuo, potrebbe creare confusione inutile per l'agente, aumentare il tempo di ragionamento e la latenza.

È una best practice utilizzare gli strumenti Python per eseguire il wrapping delle chiamate API. Il wrapping consente di offuscare i dati non necessari dell'agente e della cronologia del contesto. Puoi controllare il contesto esatto che l'agente vede restituendo solo i dati pertinenti per l'agente in quel momento. In questo modo hai il controllo completo sui parametri di input e output definiti dallo strumento, che vengono condivisi con il modello. Questa pratica è una forma di ingegneria del contesto con gli strumenti.

Codice campione:

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

Utilizzare strumenti e callback per un comportamento deterministico

In alcuni scenari conversazionali, potresti richiedere un comportamento più deterministico dalla tua applicazione agente. In questi casi, devi utilizzare strumenti o callback.

I callback sono in genere l'opzione migliore per un controllo deterministico completo. I richiami avvengono al di fuori della competenza dell'agente, quindi l'agente non è coinvolto nella loro esecuzione.

Gli elementi interni di uno strumento sono completamente deterministici, ma una chiamata allo strumento orchestrata da un agente non è deterministica. L'agente decide di chiamare uno strumento, prepara gli argomenti di input dello strumento e interpreta i risultati dello strumento. È possibile che un agente abbia allucinazioni in questa orchestrazione.

Concatenamento delle chiamate allo strumento

Analogamente all'wrapping delle chiamate API con gli strumenti, se devono essere eseguiti più strumenti durante un turno conversazionale, devi istruire l'agente a chiamare uno strumento e implementare quello strumento per chiamare gli altri. In alternativa, puoi chiedere all'agente di chiamare il primo strumento e definire un after_tool_callback callback per chiamare gli strumenti rimanenti.

Pattern errato per il concatenamento delle chiamate agli strumenti

È considerato un pattern errato istruire l'agente a chiamare più strumenti durante un turno conversazionale per raggiungere un obiettivo comune.

Il modello deve prevedere ogni chiamata di funzione e ogni parametro in quella chiamata di funzione. Poi deve assicurarsi di prevedere le chiamate di strumenti in ordine. Ciò significa che ti affidi molto al modello (che è intrinsecamente non deterministico) per eseguire un'attività deterministica.

Ad esempio, considera la seguente sequenza di strumenti:

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

Se definisci le istruzioni per queste tre chiamate di strumenti, ottieni una sequenza di eventi di runtime come la seguente:

  • Input utente
  • Modello -> L'agente prevede tool_1(arg_a, arg_b)
  • tool_1_response.json() viene restituito
  • L'agente interpreta tool_1_response.json() ed estrae arg_c
  • Modello -> L'agente prevede tool_2(arg_c)
  • tool_2_response.json() viene restituito
  • L'agente interpreta tool_2_response.json() ed estrae arg_d
  • Modello -> L'agente prevede tool_3(arg_d)
  • tool_3_response.json() viene restituito
  • Modello -> L'agente fornisce la risposta finale

Ci sono 4 chiamate al modello, 3 previsioni di strumenti e 4 argomenti di input.

Buon pattern per concatenare le chiamate di strumenti

Quando devi chiamare più strumenti, è considerato un buon pattern istruire l'agente a chiamare un singolo strumento e a implementare quello strumento per chiamare gli altri.

Il seguente richiamo dello strumento chiama altri tre strumenti:

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

Considera la sequenza di eventi per una singola chiamata allo strumento:

  • Input utente
  • Modello -> L'agente prevede python_wrapper(arg_a, arg_b)
  • python_wrapper_response.json() viene restituito
  • Modello -> L'agente fornisce la risposta finale

Questo approccio riduce i token e la probabilità di allucinazioni.

Definizioni chiare e distinte degli strumenti

Per le definizioni degli strumenti, devono essere applicate le seguenti best practice:

  • Strumenti diversi non devono avere nomi simili. Rendi i nomi degli strumenti nettamente distinti l'uno dall'altro.
  • Gli strumenti non utilizzati devono essere rimossi dal nodo dell'agente.
  • Per i nomi dei parametri, utilizza snake case, nomi descrittivi ed evita abbreviazioni non comuni.

    Esempi corretti: first_name, phone_number, url.

    Esempi errati: i, arg1, fn, pnum, rqst.

  • I parametri devono utilizzare strutture piatte anziché nidificate. Più una struttura è nidificata, più ti affidi al modello per prevedere le coppie chiave/valore e la loro corretta digitazione.

Flusso di lavoro di sviluppo

Questa sezione fornisce le best practice per la collaborazione del team, il controllo delle versioni e i test durante lo sviluppo dell'agente.

Definisci un processo di sviluppo per la collaborazione tra agenti

Quando collabori con un team allo sviluppo di applicazioni di agenti, devi definire un processo di sviluppo. Di seguito sono riportati alcuni esempi di possibili pratiche di collaborazione:

  • Utilizza il controllo della versione di terze parti: utilizza l'importazione e il ripristino per sincronizzare le modifiche con il sistema di controllo della versione di terze parti. Concorda la procedura di sincronizzazione, revisione e unione. Definisci proprietari e passaggi chiari per accettare le modifiche (ad esempio, avere i risultati della valutazione).
  • Utilizza il controllo delle versioni integrato: configura un processo per utilizzare il controllo delle versioni integrato. Concorda la modalità di utilizzo degli snapshot per il controllo delle versioni. Ad esempio, potresti richiedere uno snapshot quando viene raggiunto un traguardo (un insieme di valutazioni viene superato) o prima che venga completato lo sviluppo di nuove funzionalità. Concorda la procedura per sincronizzare, rivedere e unire le modifiche.

Utilizzare le versioni per salvare lo stato dell'agente

Le versioni ti consentono di memorizzare il lavoro o le modifiche che hai completato all'interno della tua applicazione dell'agente. Dopo aver apportato modifiche a istruzioni, strumenti, variabili e altri elementi, puoi salvare lo stato prima di apportare altre modifiche. Le versioni sono snapshot immutabili dell'agente nel tempo. Devi creare una versione quando sei soddisfatto di alcune modifiche e l'applicazione dell'agente funziona come l'hai progettata, soprattutto dopo aver convalidato le modifiche con le valutazioni. Una volta creata una versione, puoi sempre eseguire il rollback a quella versione in qualsiasi momento.

Dovresti creare spesso delle versioni, magari dopo ogni 10-15 modifiche importanti. Anche la denominazione semantica delle versioni è utile e devi decidere la convenzione di denominazione da utilizzare con il tuo team di sviluppo. Alcuni esempi sono nomi descrittivi come pre-prod-instruction-changes o prod-ready-for-testing. Puoi anche utilizzare standard come il controllo delle versioni semantico, utilizzando nomi come v1.0.0, v1.0.1 e così via. Le versioni hanno anche un campo di descrizione che ti consente di aggiungere ulteriori dettagli, in modo simile al corpo di un messaggio di commit. Il nome e la descrizione della versione devono essere brevi, significativi e facili da capire nel caso in cui tu debba eseguire il rollback a quella versione.

Eseguire test end-to-end

Il processo di sviluppo dell'applicazione dell'agente deve includere test end-to-end per verificare le integrazioni con sistemi esterni.

Valutazioni

Questa sezione fornisce le best practice per l'utilizzo delle valutazioni per garantire l'affidabilità dell'agente.

Utilizzare le valutazioni

Le valutazioni contribuiscono a mantenere l'affidabilità dei tuoi agenti. Utilizzale per definire le aspettative per gli agenti e le API chiamate dagli agenti.

Gestione delle sessioni

Questa sezione fornisce pattern per la gestione del ciclo di vita della sessione.

Saluti deterministici e latenza ridotta con risposte statiche

Puoi configurare l'agente in modo che fornisca una risposta deterministica all'inizio di una sessione. Questo approccio può salvare chiamate e token del modello e ridurre la latenza.

L'utilizzo di before_model_callback ti consente di intercettare l'input in arrivo e rispondere con un messaggio di saluto statico.

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

Rispondere rapidamente con messaggi con prefisso mentre il modello è in funzione

Quando inizia una sessione, evita di costringere l'utente ad attendere la generazione del modello. Puoi aggiungere un prefisso alle risposte in modo che l'agente possa fornire rapidamente un saluto amichevole con il brand (ad esempio, "Ciao, sono Gemini, il tuo assistente personale"), mentre il modello elabora simultaneamente la richiesta principale dell'utente in background.

Questa operazione si basa sull'impostazione partial = True. Normalmente, una risposta non FuctionCall è considerata una risposta terminale. L'utilizzo di partial = True costringe l'agente a continuare l'elaborazione dopo la risposta.

Nell'esempio seguente, l'applicazione dell'agente invia rapidamente il messaggio di benvenuto e poi continua a elaborare la richiesta principale. In questo modo si elimina la pausa di digitazione, rendendo l'agente più reattivo.

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

Verificare e applicare i contenuti obbligatori

In alcuni casi, potresti voler chiedere all'agente di fornire contenuti obbligatori specifici (come una limitazione di responsabilità legale), ma anche verificare che l'agente li abbia effettivamente inclusi. Questo pattern ti consente di fare affidamento sulla generazione naturale del modello quando funziona, ma di applicare i contenuti in modo deterministico quando non funziona.

Puoi utilizzare un after_model_callback per controllare l'output del modello. Se i contenuti obbligatori sono presenti, il callback restituisce None (consentendo il passaggio della risposta del modello). Se manca, il callback crea una nuova risposta contenente i contenuti obbligatori.

Variabili di esempio:

Nome variabile Valore predefinito
first_turn Vero
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

Chiama lo strumento personalizzato al termine della sessione

Puoi configurare l'agente in modo che chiami uno strumento specifico al termine di una sessione. Questo può essere utile per gli eventi di riepilogo post-chiamata, ad esempio la sincronizzazione dei dati all'uscita, l'invio di dati a un'API esterna, il completamento delle attività di backend o la registrazione dei metadati delle chiamate.

Ad esempio, supponiamo che tu abbia uno strumento esistente come post_call_logging che vuoi chiamare poco prima della fine della sessione:

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

Puoi utilizzare after_model_callback per eseguire la seguente sequenza:

  1. Controlla la chiamata allo strumento end_session nella risposta dell'agente.
  2. Crea la parte dello strumento post_call_logging.
  3. Inserisci la chiamata allo strumento post_call_logging prima della chiamata allo strumento end_session.

In questo modo, l'agente esegue lo strumento di logging prima di terminare la sessione.

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

Utilizzo di risposte parziali per aggiornamenti dell'interfaccia utente in tempo reale

Quando un agente esegue un'azione (ad esempio, l'aggiornamento dello stato dell'ordine), potrebbe verificarsi un ritardo durante l'elaborazione della risposta finale da parte del modello. L'utilizzo delle risposte parziali consente di inviare notifiche all'interfaccia utente client quando un tool termina l'esecuzione, disaccoppiando l'aggiornamento visivo dalla generazione di testo del modello.

L'interfaccia utente può aggiornare barre di stato, tracker o ricevute in tempo reale.

Questa operazione si basa sull'impostazione partial = True. Normalmente, una risposta non FuctionCall è considerata una risposta terminale. L'utilizzo di partial = True costringe l'agente a continuare l'elaborazione dopo la risposta.

Il payload JSON non verrà inviato al modello. Pertanto, l'agente non sarà a conoscenza dell'esistenza del payload durante la generazione della risposta.

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

Integrazione lato client

Questa sezione fornisce pattern per l'integrazione con applicazioni lato client.

Utilizzo di payload personalizzati per gestire l'interfaccia utente

Gli utenti si aspettano un'interfaccia dinamica e interattiva. Puoi utilizzare payload personalizzati per attivare il rendering lato client, colmare il divario tra l'agente e un'applicazione raffinata. Anziché fornire opzioni di testo normale, configura l'agente in modo che rilevi pattern specifici in una risposta (ad esempio, un elenco di scelte) e trasformali in elementi dell'interfaccia utente interattivi ad alta conversione, come chip o pulsanti cliccabili.

Utilizza un after_model_callback per analizzare le risposte degli agenti alla ricerca di trigger specifici. Ad esempio, se l'output del modello è: "Le opzioni disponibili sono: Rimborso, Monitoraggio ordine, Parla con un operatore", il seguente callback intercetta ed estrae queste opzioni come payload JSON, che può essere utilizzato per il rendering dell'interfaccia utente.

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)

Visualizzazione di Markdown e HTML

Se l'interfaccia conversazionale supporta Markdown e HTML per le risposte dell'agente, puoi utilizzare il simulatore per testare queste risposte, perché il simulatore supporta anche Markdown e HTML.

Esempio di istruzioni:

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

Strumento Python di esempio:

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

Controlli del canale vocale e audio

Questa sezione fornisce pattern per il controllo del canale vocale e audio, incluse le impostazioni per audio preregistrato, musica di attesa e interruzione.

Note:

  • Sono supportate le codifiche audio Linear16, mulaw e alaw come file audio.
  • Se utilizzi un bucket Cloud Storage appartenente a un altro progetto cloud, al service account Customer Engagement Suite service-<PROJECT-NUMBER>@gcp-sa-ces.iam.gserviceaccount.com deve essere concessa esplicitamente l'autorizzazione storage.objects.get per il bucket Cloud Storage di destinazione.
  • Puoi utilizzare l'argomento di input interruptable per configurare se l'audio preregistrato può essere interrotto dall'utente finale.
  • Per la riproduzione di musica, puoi utilizzare l'argomento di input cancellable per indicare che la riproduzione di musica deve interrompersi quando l'agente genera una nuova risposta.

Riprodurre un audio preregistrato specifico per il brand

Puoi configurare l'agente in modo che riproduca un file audio preregistrato prima di elaborare la richiesta dell'utente. Puoi utilizzarlo per i saluti approvati dal brand o per le informative legali obbligatorie all'inizio della sessione.

Utilizza "transcript": "yyy" per fornire all'agente il testo della riproduzione audio, assicurandoti che abbia il contesto necessario per generare le risposte successive.

L'utilizzo di "interrupable": false garantisce che l'utente non possa interrompere la riproduzione 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

Riproduci musica preregistrata quando esegui strumenti lenti (nessun barge-in)

Puoi configurare l'agente per riprodurre musica mentre è in esecuzione uno strumento lento e "bloccante" (come la convalida e l'attivazione dell'account). La musica si interrompe automaticamente al termine dell'esecuzione dello strumento. Gli utenti non possono interagire con l'agente mentre la musica è in riproduzione.

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

Riprodurre musica preregistrata durante l'esecuzione di strumenti asincroni (consenti interruzione)

Puoi configurare un agente per riprodurre musica durante l'esecuzione asincrona di uno strumento, ad esempio durante la convalida e l'attivazione dell'account utente. La musica termina automaticamente al termine dello strumento asincrono, a condizione che l'utente non l'abbia già interrotta. Gli utenti finali mantengono la possibilità di interrompere la musica in qualsiasi momento per continuare l'interazione con l'agente.

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

Non consentire l'interruzione da parte dell'utente per determinate risposte

Puoi impedire all'utente di interrompere l'agente quando sta leggendo informazioni importanti (come una limitazione di responsabilità legale), ma consentire l'interruzione per la restante parte della risposta dell'agente.

Utilizza lo strumento di sistema customize_response.

Puoi implementare questo comportamento in due modi, a seconda che tu voglia un risultato deterministico:

  1. Callback (deterministico): forza la risposta da un callback, come mostrato nell'esempio.
  2. Istruzioni (basate sull'agente): chiedi all'agente di utilizzare lo strumento customize_response nelle sue istruzioni.
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

Risposta personalizzata per nessun input

Quando un agente va in timeout in attesa di input (vedi Timeout silenzio in Impostazioni dell'applicazione agente), viene utilizzata una risposta generativa per impostazione predefinita. Tuttavia, puoi verificare se l'input è stato ricevuto dall'utente in un callback before model e fornire una risposta in modo condizionale.

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

Gestione degli errori

Questa sezione fornisce pattern per la gestione degli errori degli strumenti.

Trasferimento a un altro agente in caso di errori dello strumento

Quando l'esecuzione di uno strumento specifico non va a buon fine, puoi trasferire in modo deterministico la conversazione a un altro agente. Si tratta di una rete di sicurezza fondamentale per proteggere l'esperienza utente durante gli errori di runtime.

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

Termina la sessione in modo controllato in caso di errori dello strumento

Quando l'esecuzione di uno strumento specifico non va a buon fine, puoi terminare la sessione in modo controllato. Ciò può impedire loop infiniti e risposte confuse quando si verificano errori critici degli strumenti.

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

Contesto e variabili

Questa sezione fornisce pattern per l'utilizzo delle variabili di contesto.

Trasferire le variabili di contesto agli strumenti OpenAPI

L'AI personalizzata richiede strumenti per accedere ai dati delle sessioni utente. Affidarsi al modello per richiamare e trasmettere manualmente dettagli importanti come ID sessione o variabili utente è intrinsecamente inaffidabile e lento. L'agente può invece passare variabili di contesto specifiche agli strumenti OpenAPI. Puoi utilizzare x-ces-session-context per indicare che il valore non deve essere prodotto dal modello (e il cui schema è invisibile al modello), ma proviene dalle variabili di contesto.

La tabella seguente elenca i valori disponibili:

Valore Descrizione
$context.project_id L'ID progetto Google Cloud .
$context.project_number Il numero di progetto Google Cloud .
$context.location La località (regione) dell'agente.
$context.app_id L'ID applicazione dell'agente.
$context.session_id L'identificatore univoco della sessione.
$context.variables Tutti i valori delle variabili di contesto come oggetto.
$context.variables.variable_name Il valore di una variabile di contesto specifica. Sostituisci variable_name con il nome della variabile.
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

Prompt dinamici

Puoi creare agenti con prompt dinamici inviati al modello utilizzando:

  • Variabili che possono essere utilizzate per controllare le varianti del prompt.
  • Istruzioni che includono riferimenti a variabili.
  • Uno strumento che può aggiornare le variabili in base ai dettagli della conversazione.
  • Un before_model_callback.

Ad esempio, puoi modificare le istruzioni dell'agente in base al fatto che l'utente sia un avvocato o un pirata:

Variabili:

Nome variabile Valore predefinito
current_instructions Sei Gemini e lavori per Google.
lawyer_instructions Sei un avvocato e il tuo lavoro è raccontare barzellette in stile "papà", ma con un tocco da avvocato.
pirate_instructions Sei un pirata e il tuo compito è raccontare una barzelletta come pirata.
nome utente Sconosciuto

Istruzioni:

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}

Strumento Python:

from typing import Optional

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

Richiamata:

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)