openai-agents-python 的 ToolContext 全解析:在工具调用中访问 tool_name、call_id 与原始参数

openai-agents-python 的 ToolContext 全解析:在工具调用中访问 tool_name、call_id 与原始参数 openai-agents-python 的 ToolContext 全解析在工具调用中访问 tool_name、call_id 与原始参数【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-pythonToolContext是 openai-agents-python 中面向工具调用场景的上下文包装器它在RunContextWrapper的基础上补充了工具名称、调用 ID、原始参数字符串等一次性元数据让工具实现、生命周期钩子与审批回调能够感知当前正在执行哪个工具调用。读完本文你将掌握ToolContext的全部字段与构造方式、命名空间限定名的解析规则、嵌套 Agent 工具场景下的审批路由机制以及它在框架内部的真实调用链与测试验证。从 RunContextWrapper 到 ToolContext为什么需要工具级元数据在 openai-agents-python 中本地上下文local context通过 RunContextWrapper 类传递你创建任意 Python 对象常见的是 dataclass 或 Pydantic 对象把它传给Runner.run(..., contextwhatever)之后所有工具调用、生命周期钩子都会收到这个包装器通过wrapper.context访问你的应用状态。但RunContextWrapper只携带应用上下文、用量统计usage等运行级信息。当你的函数工具FunctionTool真正被模型调用时你往往还需要知道模型调用的是哪个工具tool_name这次调用在对话流中的唯一标识tool_call_id模型传入的原始 JSON 参数字符串tool_arguments工具是否来自某个命名空间tool_namespace。这些信息无法从wrapper.context中得到。为此SDK 提供了ToolContext——它是RunContextWrapper的子类专门承载某一次具体工具调用的元数据。官方指南在 docs/context.md 的 Advanced: ToolContext 一节明确指出当某些运行时回调需要工具级信息时SDK 会传递ToolContext这个更特化的包装器例如FunctionTool的生命周期钩子与on_invoke_tool回调。类定义位于 src/agents/tool_context.py核心声明如下dataclass(eqFalse) class ToolContext(RunContextWrapper[TContext]): The context of a tool call.eqFalse保证了实例按身份identity哈希这与RunContextWrapper的契约一致见 tests/test_tool_context.py 中test_tool_context_is_hashable_like_run_context_wrapper的说明。ToolContext 字段详解ToolContext在继承RunContextWrapper全部字段context、usage、turn_input、tool_input等的基础上新增了以下工具调用专属字段字段类型说明tool_namestr被调用工具的名称必填缺失时构造器抛出ValueError(tool_name must be passed to ToolContext)tool_call_idstr本次工具调用的唯一 ID必填缺失时抛出ValueError(tool_call_id must be passed to ToolContext)tool_argumentsstr模型传入的原始参数字符串JSON 文本必填缺失时抛出ValueError(tool_arguments must be passed to ToolContext)tool_callResponseFunctionToolCall \| None与该次调用关联的工具调用对象OpenAI Responses API 类型可为空tool_namespacestr \| None该工具调用所属的 Responses API 命名空间当工具经由tool_namespace()等命名空间化接口加载时存在agentAgentBase[Any] \| None当前工具调用对应的活动 Agent可用时填充run_configRunConfig \| None当前运行配置可用时填充三个必填字段通过default_factory绑定到三个断言函数实现强制校验src/agents/tool_context.pydef _assert_must_pass_tool_call_id() - str: raise ValueError(tool_call_id must be passed to ToolContext)对应的测试用例 tests/test_tool_context.py 逐一验证了缺任一字段都会触发ValueError。此外tool_namespace允许显式传入字符串若未显式提供但tool_call带有namespace属性构造器会通过get_tool_call_namespace(tool_call)自动推断见 tests/test_tool_context.py 中test_tool_context_constructor_infers_namespace_from_tool_call。在工具函数中使用 ToolContext实战示例ToolContext与RunContextWrapper的使用方式几乎一致把它作为工具函数的第一个参数类型标注即可。官方示例源自 docs/context.md展示了如何读取工具级元数据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(descriptionThe city name) temperature_range: str Field(descriptionThe temperature range in Celsius) conditions: str Field(descriptionThe 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(citycity, temperature_range14-20C, conditionsSunny with wind.) agent Agent( nameWeather Agent, instructionsYou are a helpful agent that can tell the weather of a given city., tools[get_weather], )这个例子同时体现了ToolContext的两大能力继续访问应用上下文ctx.context这里类型为WeatherContext与RunContextWrapper完全一致工具实现仍然可以读写你的自定义状态与依赖。获取调用元数据ctx.tool_name、ctx.tool_call_id、ctx.tool_arguments提供了本次调用的精确信息可用于日志、审计、追踪或按调用粒度做分支逻辑。在FunctionTool层面on_invoke_tool回调同样接收ToolContext作为第一个参数见 docs/tools.md 对on_invoke_tool的说明。运行期上下文类型的自动分发由invoke_function_tool完成若回调标注接收ToolContext则原样传递若标注接收普通RunContextWrapper则剥离工具元数据只传运行级包装器tests/test_tool_context.py 中的test_invoke_function_tool_passes_plain_run_context_when_requested与test_invoke_function_tool_preserves_tool_context_when_requested分别验证了这两种路径。命名空间与 qualified_tool_name当工具通过tool_namespace()或其他命名空间化接口加载时tool_namespace字段会记录其 Responses 命名空间。此时你可能需要带命名空间的完整限定名ToolContext提供了只读属性property def qualified_tool_name(self) - str: Return the tool name qualified by namespace when available. return tool_trace_name(self.tool_name, self.tool_namespace) or self.tool_name其行为规则由测试覆盖正常情况namespace 为billing、tool_name 为lookup_account时qualified_tool_name返回billing.lookup_accounttests/test_tool_context.py合成命名空间折叠当命名空间与工具名相同如 tool_name 与 namespace 均为get_weather时qualified_tool_name折叠为get_weather避免出现冗余的get_weather.get_weathertests/test_tool_context.py。这个属性在追踪tracing、审批键解析等需要稳定工具标识的场景中很有价值。构造方式构造函数与 from_agent_contextToolContext提供两条构造路径1. 直接构造构造函数保留了 v0.7 的定位参数风格同时接受新增的关键字字段ToolContext( context..., # 应用上下文对象 usage..., # 可选缺省时自动创建 Usage() tool_namemy_tool, # 必填 tool_call_idcall-2, # 必填 tool_arguments{}, # 必填 tool_callNone, # 可选 *, tool_namespaceNone, # 可选缺省时从 tool_call 推断 agentNone, # 可选 run_configNone, # 可选接受 RunConfig 或 dict turn_inputNone, tool_inputNone, )其中run_config参数既可以是RunConfig实例也可以是普通字典——构造函数内部通过_coerce_run_config将其规范化tests/test_tool_context.py 验证了字典形式的run_config会被正确转换且未知字段会抛出TypeError。2. from_agent_context 类方法这是框架内部最常用的创建方式从已有的RunContextWrapper派生出一个ToolContextclassmethod def from_agent_context( cls, context: RunContextWrapper[TContext], tool_call_id: str, tool_call: ResponseFunctionToolCall | None None, agent: AgentBase[Any] | None None, *, tool_name: str | None None, tool_arguments: str | None None, tool_namespace: str | None None, run_config: RunConfig | dict[str, Any] | None None, ) - ToolContext:其关键行为src/agents/tool_context.py字段继承复制源RunContextWrapper所有initTrue的字段context、usage、turn_input等参数回退未显式传tool_name/tool_arguments时从tool_call.name/tool_call.arguments解析两者都不可用时才触发必填断言agent 与 run_config 继承若源对象本身就是ToolContext则继承其agent与run_config测试test_tool_context_from_tool_context_inherits_agent、test_tool_context_from_tool_context_inherits_run_config覆盖显式传入的agent/run_config优先级更高test_tool_context_from_agent_context_prefers_explicit_run_config状态共享通过_share_tool_state_with共享审批状态与调用记录并通过set_agent_tool_state_scope保持工具状态作用域一致。审批路由approve_tool / reject_tool 与嵌套场景ToolContext覆写了父类的approve_tool与reject_tool方法。普通情况下它直接调用RunContextWrapper的实现记录审批决策always_approve/always_reject支持对同一工具的后续调用做持久化决策。特殊之处在于嵌套 Agent 工具场景当Agent.as_tool()作为工具被调用时内部会产生一个新的运行上下文。如果审批项实际上归属于嵌套的 Agent 工具运行直接在当前上下文上审批会错误。为此ToolContext._find_nested_approval_target会通过peek_agent_tool_run_result检查挂起的嵌套运行结果找到真正拥有该审批项的嵌套上下文然后把审批/拒绝请求路由过去若嵌套上下文中存在与审批项完全匹配或唯一规范匹配的候选则路由到该嵌套上下文执行审批若同一个工具调用身份同时属于当前运行与嵌套运行抛出UserError提示使用不同的 call ID若存在多个包含相同工具调用身份的嵌套调用抛出UserError提示使用不同的 call ID 消除歧义。这套路由逻辑src/agents/tool_context.py保证了在多层as_tool嵌套时人类审批human-in-the-loop依然能精确落到正确的执行者上。相关配套机制可参考 docs/human_in_the_loop.md 与 docs/guardrails.md。源码中的实际使用Agent 作为工具时的嵌套上下文ToolContext在框架内部最重要的使用点是Agent被当作工具调用Agent.as_tool()时。在 src/agents/agent.py 中可以看到if isinstance(context, ToolContext): # Use a fresh ToolContext to avoid sharing approval state with parent runs. nested_context ToolContext( contextcontext.context, usagecontext.usage, tool_namecontext.tool_name, tool_call_idcontext.tool_call_id, tool_argumentscontext.tool_arguments, tool_callcontext.tool_call, tool_namespacecontext.tool_namespace, agentcontext.agent, run_configresolved_run_config, ) set_agent_tool_state_scope(nested_context, tool_state_scope_id) if should_capture_tool_input: nested_context.tool_input params_data这里的注释点明了设计意图创建全新的ToolContext避免与父运行共享审批状态——父 Agent 的审批决策不应自动污染子 Agent 工具运行。同时工具状态作用域tool_state_scope_id会被显式设置保持与父上下文一致若配置了input_builder/ 结构化输入捕获模型解析后的参数会写入nested_context.tool_input供子 Agent 运行内通过wrapper.tool_input读取当should_capture_tool_input为 False 且源对象是普通RunContextWrapper时甚至可以直接复用context.context避免多余的对象包装开销。这也印证了 docs/context.md 中的说明嵌套Agent.as_tool()运行可能附加不同的tool_input但默认不会获得隔离的应用状态副本——同一运行内派生的包装器共享底层应用上下文、审批状态与用量统计。测试佐证与行为契约tests/test_tool_context.py 从多个维度锁定了ToolContext的行为契约可以作为理解其语义的权威参考测试用例验证的行为test_tool_context_is_hashable_like_run_context_wrapperdataclass(eqFalse)保持按身份哈希可用于 dict 键test_tool_context_requires_fields/test_tool_context_missing_defaults_raisetool_name、tool_call_id、tool_arguments缺一不可test_tool_context_from_agent_context_populates_fields未显式传参时从tool_call解析名称与参数test_tool_context_constructor_infers_namespace_from_tool_callnamespace 从tool_call自动推断qualified_tool_name拼接命名空间test_tool_context_constructor_normalizes_dictionary_run_configdict 形式run_config被规范化为RunConfig未知字段报错test_tool_context_from_tool_context_inherits_agent/run_config从源ToolContext继承 agent 与 run_configtest_invoke_function_tool_passes_plain_run_context_when_requested回调标注RunContextWrapper时剥离工具元数据test_invoke_function_tool_preserves_tool_context_when_requested回调标注ToolContext时原样传递此外tests/test_agent_hooks.py、tests/test_run_hooks.py、tests/test_agent_runner.py等测试也在运行级场景中验证了ToolContext在钩子与工具执行链路中的传递行为。使用建议与注意事项结合 docs/context.md 与源码实现实践中应遵循以下要点按需选用仅当工具实现确实需要tool_name、tool_call_id、tool_arguments等调用级元数据时才使用ToolContext单纯的跨工具状态共享使用RunContextWrapper即可因为ToolContext继承自它任何能接收RunContextWrapper的地方都兼容ToolContext。类型一致性一次 Agent 运行中的所有工具、钩子必须使用相同类型的上下文对象用Agent[TContext]与ToolContext[TContext]的泛型标注可以让类型检查器在编译期拦截上下文类型不匹配的错误。不要依赖必填字段的默认值三个必填字段的default_factory是校验兜底而非默认值直接构造时必须显式传入框架内部统一走from_agent_context或显式构造避免误用。上下文不进入 LLMToolContext.context与应用状态一样纯粹是本地对象不会被发送给模型需要让模型感知的信息应放入instructions、input或通过FunctionTool按需暴露详见 docs/context.md 的 Agent/LLM context 一节。审批状态的作用域嵌套as_tool场景下子运行使用全新的ToolContext且审批状态与父运行隔离若你在自定义审批流程中操作approve_tool/reject_tool请留意审批项是否属于嵌套上下文框架会自动路由但要求嵌套调用使用不同的 call ID 以避免歧义。ToolContext是连接运行级上下文与单次工具调用的桥梁向上继承RunContextWrapper的全部能力向下提供精确到调用的元数据与审批路由。掌握它你就能在工具实现中做出更精细的日志、审计与分支决策也能在复杂的多 Agent 嵌套场景中正确驾驭审批与状态作用域。【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考