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)-> outputctool_2(arg_c)-> outputdtool_3(arg_d)-> outpute
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 estraearg_c - Modello -> L'agente prevede
tool_2(arg_c) tool_2_response.json()viene restituito- L'agente interpreta
tool_2_response.json()ed estraearg_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:
- Controlla la chiamata allo strumento
end_sessionnella risposta dell'agente. - Crea la parte dello strumento
post_call_logging. - Inserisci la chiamata allo strumento
post_call_loggingprima della chiamata allo strumentoend_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.comdeve essere concessa esplicitamente l'autorizzazionestorage.objects.getper il bucket Cloud Storage di destinazione. - Puoi utilizzare l'argomento di input
interruptableper configurare se l'audio preregistrato può essere interrotto dall'utente finale. - Per la riproduzione di musica, puoi utilizzare l'argomento di input
cancellableper 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:
- Callback (deterministico): forza la risposta da un callback, come mostrato nell'esempio.
- Istruzioni (basate sull'agente): chiedi all'agente di utilizzare lo strumento
customize_responsenelle 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)