このガイドでは、エージェント アプリケーションを最適化してスケーリングするための実績のあるベスト プラクティスと設計パターンを紹介します。このコンテンツは、設計時のコストの削減、実行時のコストの削減、エージェントの信頼性の向上に役立ちます。
全般
このセクションでは、エージェント開発と指示の記述を開始するための一般的なベスト プラクティスについて説明します。
シンプルなものから始める
エージェント アプリケーションの構築を初めて行う場合は、簡単なユースケースから始める必要があります。シンプルなユースケースが機能したら、より複雑なユースケースの構築を続けます。
指示は具体的である必要があります
エージェントの手順は具体的で、あいまいなものであってはなりません。手順は、トピック別に整理してグループ化する必要があります。特定のトピックに関する手順が散在しないようにします。手順は人間が簡単に実行できるものでなければなりません。
構造化された指示を使用する
指示の作成が完了したら、指示の再構成機能を使用して指示の形式を設定します。この形式にすると、エージェントの信頼性が高まります。
複数のモダリティとチャネルのモジュール式コンポーネントを使用する
エージェントがさまざまなモダリティ(テキストまたは音声)やチャネル(API、電話、ウェブ)に対して異なる方法で応答する必要がある場合は、モジュラー コンポーネントと Python ツールとコールバックでチャネルとモダリティを確認するを使用する必要があります。
ツール
このセクションでは、外部 API のラップやツール呼び出しのチェーンなど、ツールの定義と使用に関するベスト プラクティスについて説明します。
Python ツールで API をラップする
外部 API スキーマでは、エージェントに関係のない入力パラメータと出力パラメータが多数定義されている場合があります。このようなケースで OpenAPI ツールを使用すると、モデルに不要なコンテキストが提供され、信頼性が低下する可能性があります。たとえば、OpenAPI 仕様ツールが 3 つのパラメータを入力として受け取り、100 個の Key-Value ペアを含む大きな JSON オブジェクトを返すとします。エージェントはこのツールの 3 つの入力引数を予測し、戻り値として 100 個の Key-Value ペアを含む完全な JSON ペイロードを取得します。これらのペアのうち 3 つだけが会話に関連している場合、残りの 97 個のペアは会話履歴にトークンを追加する無関係なデータです。これは無害に見えるかもしれませんが、エージェントに不要な混乱が生じ、推論時間が長くなり、レイテンシが増加する可能性があります。
Python ツールを使用して API 呼び出しをラップすることをおすすめします。ラッピングを使用すると、エージェントとコンテキスト履歴から不要なデータを難読化できます。その時点のエージェントに関連するデータのみを返すことで、エージェントが認識するコンテキストを正確に制御できます。これにより、ツールで定義され、モデルと共有される入力パラメータと出力パラメータを完全に制御できます。この手法は、ツールを使用したコンテキスト エンジニアリングの一種です。
サンプルコード:
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
決定論的動作にツールとコールバックを使用する
特定の会話シナリオでは、エージェント アプリケーションのより決定論的な動作が必要になることがあります。このような場合は、ツールまたはコールバックを使用する必要があります。
通常、コールバックは完全な決定論的制御に最適なオプションです。コールバックはエージェントの権限外で行われるため、エージェントはコールバックの実行に関与しません。
ツールの内部は完全に決定論的ですが、エージェントによってオーケストレートされたツール呼び出しは決定論的ではありません。エージェントがツールを呼び出すことを決定し、ツールの入力引数を準備して、ツールの結果を解釈します。エージェントがこのオーケストレーションをハルシネーションする可能性があります。
ツールの呼び出しを連鎖させる
ツールで API 呼び出しをラップすると同様に、会話のターン中に複数のツールを実行する必要がある場合は、1 つのツールを呼び出すようにエージェントに指示し、他のツールを呼び出すようにそのツールを実装する必要があります。または、最初の手順でエージェントに最初の手順を呼び出すよう指示し、残りの手順を呼び出す after_tool_callback コールバックを定義することもできます。
ツール呼び出しのチェーンの悪いパターン
会話のターン中に複数のツールを呼び出して共通の目標を達成するようにエージェントに指示することは、悪いパターンと見なされます。
モデルは、すべてのツール呼び出しと、そのツール呼び出しのすべてのパラメータを予測する必要があります。また、ツール呼び出しが順番どおりに予測されるようにする必要があります。つまり、決定論的なタスクを実行するために、モデル(本質的に非決定的)に大きく依存していることになります。
たとえば、次のツール シーケンスについて考えてみましょう。
tool_1(arg_a, arg_b)-> 出力ctool_2(arg_c)-> 出力dtool_3(arg_d)-> 出力e
これらの 3 つのツール呼び出しの指示を定義すると、次のような実行時のイベント シーケンスになります。
- ユーザー入力
- モデル -> エージェントが
tool_1(arg_a, arg_b)を予測 tool_1_response.json()が返却されます- エージェントが
tool_1_response.json()を解釈してarg_cを抽出する - モデル -> エージェントが
tool_2(arg_c)を予測 tool_2_response.json()が返却されます- エージェントが
tool_2_response.json()を解釈してarg_dを抽出する - モデル -> エージェントが
tool_3(arg_d)を予測 tool_3_response.json()が返却されます- モデル -> エージェントが最終レスポンスを提供
モデル呼び出しが 4 回、ツール予測が 3 回、入力引数が 4 個あります。
ツール呼び出しをチェーンする際の適切なパターン
複数のツールを呼び出す必要がある場合は、エージェントに 1 つのツールを呼び出すよう指示し、そのツールに他のツールを呼び出すよう実装するのが良いパターンとされています。
次のツールは、他の 3 つのツールを呼び出します。
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()
1 つのツール呼び出しのイベントのシーケンスについて考えてみましょう。
- ユーザー入力
- モデル -> エージェントが
python_wrapper(arg_a, arg_b)を予測 python_wrapper_response.json()が返却されます- モデル -> エージェントが最終レスポンスを提供
このアプローチにより、トークンが削減され、ハルシネーションの可能性が低くなります。
明確で区別可能なツール定義
ツールの定義には、次のベスト プラクティスを適用する必要があります。
- 異なるツールに類似した名前を付けないでください。ツール名が互いに明確に区別できるようにします。
- 使用されていないツールは、エージェント ノードから削除する必要があります。
パラメータ名にはスネークケースを使用し、わかりやすい名前を使用し、一般的でない略語は避けてください。
良い例:
first_name、phone_number、url。悪い例:
i、arg1、fn、pnum、rqst。パラメータには、ネストされた構造ではなく、フラット化された構造を使用する必要があります。構造がネストされているほど、モデルがキーと値のペアとその適切な型を予測することに依存します。
開発ワークフロー
このセクションでは、エージェント開発中のチーム コラボレーション、バージョン管理、テストに関するベスト プラクティスについて説明します。
エージェント コラボレーションの開発プロセスを定義する
エージェント アプリケーションの開発でチームと共同作業を行う場合は、開発プロセスを定義する必要があります。コラボレーションの例を次に示します。
- サードパーティのバージョン管理を使用する: インポートと復元を使用して、変更をサードパーティのバージョン管理システムと同期します。同期、レビュー、統合のプロセスについて合意します。変更を承認するための明確なオーナーと明確な手順(評価結果など)を定義します。
- 組み込みのバージョン管理を使用する: 組み込みのバージョン管理を使用するプロセスを設定します。バージョニングにスナップショットを使用する方法について合意します。たとえば、マイルストーンに到達したとき(一連の評価に合格したとき)や、新機能の開発が完了する前にスナップショットを要求できます。変更の同期、レビュー、統合のプロセスについて合意します。
バージョンを使用してエージェントの状態を保存する
バージョンを使用すると、エージェント アプリケーション内で完了した作業や変更を記録できます。手順、ツール、変数などの項目を変更した後、他の変更を行う前にその状態を保存できます。バージョンは、エージェントの不変のタイム スナップショットです。特に評価で変更を検証した後、変更に満足し、エージェント アプリケーションが設計どおりに動作している場合は、バージョンを作成する必要があります。バージョンを作成すると、いつでもそのバージョンにロールバックできます。
バージョンは頻繁に作成する必要があります(10 ~ 15 回の大きな変更ごとに作成するなど)。バージョンにセマンティックな名前を付けることも有効です。使用する命名規則は、開発チームと決定する必要があります。たとえば、pre-prod-instruction-changes や prod-ready-for-testing などの説明的な名前があります。セマンティック バージョニングなどの標準を使用して、v1.0.0 や v1.0.1 などの名前を使用することもできます。バージョンには、コミット メッセージのメッセージ本文と同様に、詳細を追加できる説明フィールドもあります。バージョン名と説明は、そのバージョンにロールバックする必要がある場合に備えて、短く、意味があり、わかりやすいものにする必要があります。
エンドツーエンド テストを実行する
エージェント アプリケーションの開発プロセスには、外部システムとの統合を検証するためのエンドツーエンド テストを含める必要があります。
評価
このセクションでは、エージェントの信頼性を確保するために評価を使用する際のベスト プラクティスについて説明します。
評価を使用する
評価は、エージェントの信頼性を維持するのに役立ちます。これらを使用して、エージェントとエージェントが呼び出す API の期待値を設定します。
セッション処理
このセクションでは、セッションのライフサイクルを管理するためのパターンについて説明します。
静的レスポンスによる決定論的な挨拶とレイテンシの低減
セッションの開始時に確定的なレスポンスを話すようにエージェントを構成できます。このアプローチにより、モデルの呼び出しとトークンを節約し、レイテンシを短縮できます。
before_model_callback を使用すると、受信した入力をインターセプトし、静的な挨拶メッセージで応答できます。
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
モデルの処理中に接頭辞メッセージで迅速に対応する
セッションが開始されたら、ユーザーがモデルの生成を待つことを強制しないでください。回答に接頭辞を付けると、エージェントはフレンドリーなブランドの挨拶(「こんにちは。私は Gemini です。あなたのパーソナル アシスタントです」など)をすばやく提供できます。その間、モデルはユーザーのメイン リクエストをバックグラウンドで同時に処理します。
これは partial = True 設定に依存します。通常、FuctionCall 以外のレスポンスは終了レスポンスと見なされます。partial = True を使用すると、エージェントはレスポンスの後も処理を続行します。
次の例では、エージェント アプリケーションがウェルカム メッセージをすばやく配信し、メインのリクエストの処理を続行します。これにより、入力のぎこちない一時停止が解消され、エージェントが応答しているように感じられます。
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
必須コンテンツを確認して適用する
場合によっては、エージェントに特定の必須コンテンツ(法的免責条項など)を提供するよう指示し、エージェントが実際にそのコンテンツを含めたことを確認する必要があります。このパターンを使用すると、モデルが機能している場合はモデルの自然な生成に依存し、失敗した場合はコンテンツを決定論的に適用できます。
after_model_callback を使用して、モデルの出力を確認できます。必須コンテンツが存在する場合、コールバックは None を返します(モデルのレスポンスを通過させます)。不足している場合は、コールバックは必須コンテンツを含む新しいレスポンスを構築します。
サンプル変数:
| 変数名 | デフォルト値 |
|---|---|
| first_turn | 正しい |
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
セッション終了時にカスタムツールを呼び出す
セッションが終了したときに特定ツールを呼び出すようにエージェントを構成できます。これは、終了時のデータの同期、外部 API へのデータの送信、バックエンド タスクの完了、通話メタデータのロギングなどの通話後のラップアップ イベントに役立ちます。
たとえば、セッションが終了する直前に呼び出す既存のツール(post_call_logging など)があるとします。
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()
after_model_callback を使用すると、次のシーケンスを実行できます。
- エージェントのレスポンスで
end_sessionツール呼び出しを確認します。 post_call_loggingツールパーツを作成します。end_sessionツール呼び出しの前にpost_call_loggingツール呼び出しを挿入します。
これにより、エージェントはセッションを終了する前にロギング ツールを実行します。
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
リアルタイムのユーザー インターフェースの更新に部分レスポンスを使用する
エージェントがアクション(注文ステータスの更新など)を実行すると、モデルが最終的なレスポンスを処理する際に遅延が発生する可能性があります。部分レスポンスを使用すると、ツールが実行を完了したときにクライアント ユーザー インターフェースに通知を送信し、ビジュアルの更新をモデルのテキスト生成から切り離すことができます。
ユーザー インターフェースで、ステータスバー、トラッカー、領収書をリアルタイムで更新できます。
これは partial = True 設定に依存します。通常、FuctionCall 以外のレスポンスは終了レスポンスと見なされます。partial = True を使用すると、エージェントはレスポンスの後も処理を続行します。
JSON ペイロードはモデルに送信されません。そのため、エージェントは回答の生成中にペイロードの存在を認識しません。
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
クライアントサイドの統合
このセクションでは、クライアントサイド アプリケーションとの統合パターンについて説明します。
カスタム ペイロードを使用してユーザー インターフェースを駆動する
ユーザーは動的でインタラクティブなインターフェースを期待しています。カスタム ペイロードを使用して、クライアントサイド レンダリングを駆動し、エージェントと洗練されたアプリケーション間のギャップを埋めることができます。書式なしテキスト オプションを提供する代わりに、レスポンス内の特定のパターン(選択肢のリストなど)を検出して、クリック可能なチップやボタンなどのコンバージョン率の高いインタラクティブなユーザー インターフェース要素に変換するようにエージェントを構成します。
after_model_callback を使用して、特定のアクティベーションをエージェントの回答からスキャンします。たとえば、モデルの出力が「利用可能なオプションは、払い戻し、注文の追跡、エージェントへの問い合わせです」の場合、次のコールバックはこれらのオプションを JSON ペイロードとしてインターセプトして抽出します。これは、ユーザー インターフェースのレンダリングに使用できます。
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)
マークダウンと HTML の表示
会話型インターフェースがエージェント レスポンスの Markdown と HTML をサポートしている場合は、シミュレータを使用してこれらのレスポンスをテストできます。シミュレータも Markdown と HTML をサポートしているためです。
指示の例:
<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 ツールの例:
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)}
音声チャンネルとオーディオ チャンネルのコントロール
このセクションでは、録音済み音声、保留音、割り込み設定など、音声チャンネルとオーディオ チャンネルを制御するためのパターンについて説明します。
注:
- 音声ファイルとして Linear16、mulaw、alaw の音声エンコードがサポートされています。
- 別のクラウド プロジェクトに属する Cloud Storage バケットを使用している場合、Customer Engagement Suite サービス アカウント
service-<PROJECT-NUMBER>@gcp-sa-ces.iam.gserviceaccount.comに、ターゲット Cloud Storage バケットに対するstorage.objects.get権限を明示的に付与する必要があります。 interruptable入力引数を使用して、エンドユーザーが録音済み音声割り込みを許可するかどうかを設定できます。- 音楽再生の場合、
cancellable入力引数を使用して、エージェントが新しいレスポンスを生成したときに音楽再生を停止する必要があることを示すことができます。
ブランド固有の事前録音された音声を再生する
ユーザー リクエストを処理する前に録音済みの音声ファイルを再生するようにエージェントを構成できます。セッション開始時に、ブランドが承認した挨拶や法的に義務付けられた開示を行うために使用できます。
"transcript": "yyy" を使用して、エージェントに音声再生テキストを提供します。これにより、エージェントは次のレスポンスを生成するために必要なコンテキストを取得できます。
"interrupable": false を使用すると、ユーザーが音声再生を中断できなくなります。
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
低速ツール実行時に録音済みの音楽を再生する(割り込みなし)
遅い「ブロック」ツール(アカウントの検証や有効化など)の実行中に音楽を再生するようにエージェントを構成できます。ツールが実行を完了すると、音楽は自動的に停止します。音楽の再生中は、エージェントとやり取りできません。
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
非同期ツールを実行するときに録音済みの音楽を再生する(割り込みを許可)
ユーザー アカウントの検証や有効化など、ツールを非同期で実行している間、音楽を再生するようにエージェントを構成できます。非同期ツールの完了時に、ユーザーがすでに中断していない限り、音楽は自動的に終了します。エンドユーザーは、いつでも音楽を中断してエージェントとのエンゲージメントを継続できます。
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
特定の応答に対するユーザーの割り込みを禁止する
エージェントが重要な情報(法的免責条項など)を読み上げている間はユーザーがエージェントを中断できないようにし、エージェントの応答の残りの部分では割り込みを許可することができます。
これは customize_response システムツールを使用します。
この動作は、決定論的な結果が必要かどうかに応じて、次の 2 つの方法で実装できます。
- コールバック(決定論的): サンプルに示すように、コールバックからのレスポンスを強制します。
- 手順(エージェント主導): 指示の中で
customize_responseツールを使用するようにエージェントに促します。
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
入力なしのカスタム レスポンス
エージェントが入力待ちでタイムアウトした場合(エージェント アプリケーションの設定の無音タイムアウトを参照)、デフォルトで生成レスポンスが使用されます。ただし、ユーザーが入力したかどうかを before モデル コールバックで確認し、条件付きでレスポンスを提供することはできます。
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
エラー処理
このセクションでは、ツールのエラーを処理するためのパターンについて説明します。
ツールの失敗時に別のエージェントに転送する
特定のツールの実行が失敗した場合は、別のエージェントに確実に引き継いで会話を処理できます。これは、実行時エラーが発生したときにユーザー エクスペリエンスを保護するための重要な安全ネットです。
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
ツールの失敗時にセッションを正常に終了する
特定のツールの実行が失敗した場合は、セッションを正常に終了できます。これにより、重要なツールの障害が発生した場合に、無限ループや混乱を招くレスポンスを防ぐことができます。
コールバックの例:
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
コンテキストと変数
このセクションでは、コンテキスト変数を使用するパターンについて説明します。
コンテキスト変数を OpenAPI ツールに渡す
パーソナライズされた AI では、ユーザー セッション データにアクセスするためのツールが必要です。モデルに依存してセッション ID やユーザー変数などの重要な詳細を手動で呼び出して渡すことは、本質的に信頼性が低く、遅くなります。代わりに、エージェントは特定のコンテキスト変数を OpenAPI ツールに渡すことができます。x-ces-session-context を使用すると、値がモデルによって生成される必要はなく(モデルにはスキーマが表示されません)、コンテキスト変数から取得されることを示すことができます。
次の表に、使用可能な値を示します。
| 値 | 説明 |
|---|---|
$context.project_id |
Google Cloud プロジェクト ID |
$context.project_number |
Google Cloud プロジェクト番号。 |
$context.location |
エージェントのロケーション(リージョン)。 |
$context.app_id |
エージェント アプリケーション ID。 |
$context.session_id |
セッションの固有識別子。 |
$context.variables |
すべてのコンテキスト変数値をオブジェクトとして取得します。 |
$context.variables.variable_name |
特定のコンテキスト変数の値。variable_name は、変数の名前に置き換えます。 |
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
動的プロンプト
次のものを使用して、モデルに動的プロンプトを送信するエージェントを構築できます。
- プロンプトのバリエーションを制御するために使用できる変数。
- 変数への参照を含む命令。
- 会話の詳細に基づいて変数を更新できるツール。
- before_model_callback。
たとえば、ユーザーが弁護士か海賊かに基づいて、エージェントの指示を変更できます。
変数:
| 変数名 | デフォルト値 |
|---|---|
| current_instructions | あなたは Gemini で、Google で働いています。 |
| lawyer_instructions | あなたは弁護士で、弁護士らしいジョークを親父ギャグ風に言うのが仕事です。 |
| pirate_instructions | あなたは海賊です。海賊としてジョークを言うのがあなたの仕事です。 |
| ユーザー名 | 不明 |
手順:
The current user is: {username}
You can use {@TOOL: update_username} to update the user's name if they provide
it.
Follow the current instruction set below exactly.
{current_instructions}
Python ツール:
from typing import Optional
def update_username(username: str) -> Optional[str]:
"""Updates the current user's name."""
set_variable("username", username)
コールバック:
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)