コンテキスト管理
コンテキストという用語は複数の意味で使われます。考慮すべきコンテキストは、大きく次の 2 種類に分けられます。
- コードがローカルで利用できるコンテキスト:ツール関数の実行時、
on_handoffなどのコールバック内、ライフサイクルフック内などで必要になる可能性があるデータや依存関係です。 - LLM が利用できるコンテキスト:レスポンスを生成する際に LLM が参照するデータです。
ローカルコンテキスト
これは、RunContextWrapper クラスと、その中の context プロパティで表されます。仕組みは次のとおりです。
- 任意の Python オブジェクトを作成します。一般的には、データクラスまたは Pydantic オブジェクトを使用します。
- そのオブジェクトを各種の実行メソッド(例:
Runner.run(..., context=whatever))に渡します。 - すべてのツール呼び出しやライフサイクルフックなどには、ラッパーオブジェクト
RunContextWrapper[T]が渡されます。ここでTはコンテキストオブジェクトの型を表し、オブジェクト自体にはwrapper.contextを介してアクセスできます。
ランタイム固有の一部のコールバックでは、SDK が RunContextWrapper[T] のより特化したサブクラスを渡す場合があります。たとえば、FunctionTool インスタンスのライフサイクルフックは通常、ToolContext を受け取ります。これは、tool_call_id、tool_name、tool_arguments などのツール呼び出しメタデータも公開します。
注意すべき 最も重要な 点は、特定のエージェント実行に関わるすべてのエージェント、ツール関数、ライフサイクル処理などで、同じ 型 のコンテキストを使用する必要があることです。
コンテキストは、次のような用途に使用できます。
- 実行に関するコンテキストデータ(例:ユーザー名、UID、その他のユーザー情報)
- 依存関係(例:ロガーオブジェクト、データフェッチャーなど)
- ヘルパー関数
注記
コンテキストオブジェクトは、LLM に 送信されない ローカル専用のオブジェクトです。その値の読み取りや書き込み、メソッドの呼び出しが可能です。
1 回の実行内では、派生したラッパーが同じ基盤のアプリケーションコンテキスト、承認状態、使用量追跡を共有します。ネストされた Agent.as_tool() の実行では、異なる tool_input が付与される場合がありますが、デフォルトではアプリケーション状態の独立したコピーは作成されません。
機能の公開制御におけるローカルコンテキストの使用
関数ツール、MCP ツール、ハンドオフが同じリクエストポリシーに依存する場合は、ポリシーの入力値またはヘルパーをアプリケーションコンテキストに保持してください。SDK の各インターフェースは、それぞれのコールバックを介して現在の実行コンテキストを公開します。
FunctionTool.is_enabledはRunContextWrapperを受け取ります。Handoff.is_enabledはRunContextWrapperを受け取ります。- MCP の
tool_filterはToolFilterContextを受け取ります。そのrun_contextプロパティには、現在のRunContextWrapperが含まれます。
個別の機能リストを管理するのではなく、共有アプリケーションポリシーをこれらのコールバックに合わせて適用してください。これらのコールバックは、現在の実行に対して SDK が公開する機能を制御しますが、モデルが生成した引数やリソース選択を認可することはできません。関数ツールでは、ツール実装内で認可に関する判断を適用するか、必要に応じてツール入力ガードレールや承認を使用してください。MCP サーバーは、自身の保護対象の操作を認可する必要があります。input_type を持つハンドオフでは、アプリケーションに副作用が生じる前に、on_handoff の冒頭で解析済みの入力を確認し、認可に失敗した場合は値を返さずに例外を送出してください。ツール入力ガードレールは、ハンドオフでは実行されません。コールバックのライフサイクルについては、ハンドオフ入力を参照してください。
RunContextWrapper で公開される情報
RunContextWrapper は、アプリケーションで定義したコンテキストオブジェクトのラッパーです。実際には、主に次の項目を使用します。
- 独自の変更可能なアプリケーション状態と依存関係には、
wrapper.contextを使用します。 - 現在の実行全体で集計されたリクエストとトークンの使用量には、
wrapper.usageを使用します。 - 現在の実行が
Agent.as_tool()内で行われている場合の構造化入力には、wrapper.tool_inputを使用します。 - 承認状態をプログラムで更新する必要がある場合は、
wrapper.approve_tool(...)/wrapper.reject_tool(...)を使用します。
アプリケーションで定義したオブジェクトは wrapper.context だけです。その他のフィールドは、SDK が管理するランタイムメタデータです。
後で Human-in-the-loop または永続ジョブのワークフロー向けに RunState をシリアライズする場合、このランタイムメタデータも状態とともに保存されます。シリアライズした状態を永続化または送信する予定がある場合は、RunContextWrapper.context にシークレットを格納しないでください。
会話状態は別の検討事項です。ターンを引き継ぐ方法に応じて、result.to_input_list()、session、conversation_id、または previous_response_id を使用してください。この判断については、実行結果、エージェントの実行、セッションを参照してください。
import asyncio
from dataclasses import dataclass
from agents import Agent, RunContextWrapper, Runner
from agents.decorators import tool
@dataclass
class UserInfo: # (1)!
name: str
uid: int
@tool
async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)!
"""Fetch the age of the user. Call this function to get user's age information."""
return f"The user {wrapper.context.name} is 47 years old"
async def main():
user_info = UserInfo(name="John", uid=123)
agent = Agent[UserInfo]( # (3)!
name="Assistant",
tools=[fetch_user_age],
)
result = await Runner.run( # (4)!
starting_agent=agent,
input="What is the age of the user?",
context=user_info,
)
print(result.final_output) # (5)!
# The user John is 47 years old.
if __name__ == "__main__":
asyncio.run(main())
- これはコンテキストオブジェクトです。ここではデータクラスを使用していますが、任意の型を使用できます。
- これはツールです。このツールが
RunContextWrapper[UserInfo]を受け取ることが分かります。ツール実装はコンテキストから値を読み取ります。 - エージェントにジェネリック
UserInfoを指定し、型チェッカーがエラーを検出できるようにします(たとえば、異なるコンテキスト型を受け取るツールを渡そうとした場合)。 - コンテキストは
run関数に渡されます。 - エージェントはツールを正しく呼び出し、年齢を取得します。
高度な機能: ToolContext
場合によっては、実行中のツールに関する追加のメタデータ(名前、呼び出し ID、生の引数文字列など)へアクセスする必要があります。
その場合は、RunContextWrapper を拡張する ToolContext クラスを使用できます。
from typing import Annotated
from pydantic import BaseModel, Field
from agents import Agent
from agents.decorators import tool
from agents.tool_context import ToolContext
class WeatherContext(BaseModel):
user_id: str
class Weather(BaseModel):
city: str = Field(description="The city name")
temperature_range: str = Field(description="The temperature range in Celsius")
conditions: str = Field(description="The weather conditions")
@tool
def get_weather(ctx: ToolContext[WeatherContext], city: Annotated[str, "The city to get the weather for"]) -> Weather:
print(f"[debug] Tool context: (name: {ctx.tool_name}, call_id: {ctx.tool_call_id}, args: {ctx.tool_arguments})")
return Weather(city=city, temperature_range="14-20C", conditions="Sunny with wind.")
agent = Agent(
name="Weather Agent",
instructions="You are a helpful agent that can tell the weather of a given city.",
tools=[get_weather],
)
ToolContext は、RunContextWrapper と同じ .context プロパティを提供し、
さらに現在のツール呼び出しに固有の次のフィールドも提供します。
tool_name– 呼び出されるツールの名前tool_call_id– このツール呼び出しの一意の識別子tool_arguments– ツールに渡された生の引数文字列tool_namespace– ツールがtool_namespace()または名前空間を持つ別のインターフェースを介して読み込まれた場合の、ツール呼び出し用 Responses 名前空間qualified_tool_name– 名前空間が利用できる場合に、その名前空間で修飾されたツール名
実行中にツール単位のメタデータが必要な場合は、ToolContext を使用してください。
エージェントとツール間で一般的なコンテキストを共有する場合は、引き続き RunContextWrapper で十分です。ToolContext は RunContextWrapper を拡張しているため、ネストされた Agent.as_tool() の実行で構造化入力が指定された場合は、.tool_input も公開できます。
エージェント / LLM コンテキスト
LLM が呼び出されたとき、LLM が確認できる 唯一の データは会話履歴に含まれるデータです。つまり、新しいデータを LLM が利用できるようにするには、そのデータを会話履歴に含める必要があります。これには、次のような方法があります。
- エージェントの
instructionsに追加できます。これは「システムプロンプト」または「developer message」とも呼ばれます。システムプロンプトには静的な文字列を指定できるほか、コンテキストを受け取って文字列を出力する動的関数も使用できます。これは、常に有用な情報(たとえば、ユーザー名や現在の日付)に対してよく使われる方法です。 Runner.run関数を呼び出す際に、inputに追加します。これはinstructionsを使用する方法と似ていますが、指示の優先順位がより低いメッセージを使用できます。FunctionToolインスタンスを介して公開します。これは オンデマンド コンテキストに便利です。LLM がデータを必要とするタイミングを判断し、ツールを呼び出してそのデータを取得できます。- 検索または Web 検索を使用します。これらは、ファイルやデータベースから関連データを取得(検索)したり、Web から取得(Web 検索)したりできる特別なツールです。これは、関連するコンテキストデータに基づいてレスポンスを根拠付ける場合に便利です。