ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

openai-agents-python 上下文管理完全指南:本地应用上下文与 LLM 可见上下文的正确用法

openai-agents-python 上下文管理完全指南:本地应用上下文与 LLM 可见上下文的正确用法 openai-agents-python 上下文管理完全指南本地应用上下文与 LLM 可见上下文的正确用法【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python导读在 openai-agents-python 中context上下文是一个被严重过载的术语——它既指代你的 Python 代码工具函数、回调、生命周期钩子运行时可以访问的本地数据与依赖也指代 LLM 在生成回复时真正看得见的对话历史数据。本指南以 docs/context.md 为主线结合仓库源码系统讲解这两类上下文的区别、RunContextWrapper与ToolContext的完整用法、能力可见性capability visibility模式以及如何安全地把上下文与RunState序列化、人工审批、Agent 嵌套等机制协同使用。读完本文你将能正确设计跨工具、跨 Agent 共享的应用状态同时避免把不该暴露的本地数据泄漏给模型。一、两类上下文先分清给代码的与给 LLM 的上下文管理的第一步是理解两种截然不同的上下文类别本地上下文Local context你的代码在运行时需要的数据和依赖——工具函数执行时、on_handoff回调中、生命周期钩子里等。它以RunContextWrapper为载体永远不会被发送给 LLM。LLM 上下文Agent/LLM contextLLM 在生成回复时能看到的数据。LLM 唯一能看到的就是对话历史conversation history因此要让新数据对模型可见必须通过某种方式把它注入到历史中。这两类上下文分别解决代码需要什么和模型需要什么两个问题混淆它们会导致两种典型错误把数据库连接池放进 system prompt既浪费 token 又无意义或者把用户信息放在本地上下文里却期望模型猜到。二、本地上下文RunContextWrapper的核心机制2.1 工作方式三步走本地上下文由RunContextWrapper类及其内部的context属性承载使用方式非常直接创建任意 Python 对象。最常见的模式是使用 dataclass 或 Pydantic 模型但本质上任何类型都可以。把该对象传给各种 run 方法例如Runner.run(..., contextwhatever)。所有工具调用、生命周期钩子等都会收到一个包装对象RunContextWrapper[T]其中T是你的上下文对象的类型原始对象本身通过wrapper.context访问。在源码中RunContextWrapper是一个泛型 dataclass其定义src/agents/run_context.py清晰展示了它的组成dataclass(eqFalse) class RunContextWrapper(Generic[TContext]): context: TContext # 你传给 Runner.run() 的应用上下文对象 usage: Usage field(default_factoryUsage) # 本次运行累计的请求与 token 用量 turn_input: list[TResponseInputItem] field(default_factorylist) tool_input: Any | None None # 嵌套 Agent.as_tool() 运行时注入的结构化输入 _approvals: ... field(default_factorydict) # 内部审批状态SDK 管理 _tool_invocations: ... field(default_factorydict, initFalse, reprFalse)类文档字符串明确标注了关键约定src/agents/run_context.pyNOTE: Contexts are not passed to the LLM. Theyre a way to pass dependencies and data to code you implement, like tool functions, callbacks, hooks, etc.2.2 最重要的约束所有参与者必须使用同一类型的上下文同一 Agent 运行中的所有 Agent、工具函数、生命周期钩子等必须使用相同类型的上下文。这是本地上下文使用中最重要的规则。原因在于 SDK 用泛型T做静态类型检查。例如在Agent中Agent[UserInfo]的泛型标注会让类型检查器在给一个接收不同类型上下文的工具时直接报错。仓库的测试与示例也遵循这一约定——工具函数的第一个参数必须是RunContextWrapper[T]或ToolContext[T]且T与 Agent 的泛型参数一致。2.3 本地上下文可以放什么官方推荐的用途包括三类运行的上下文数据例如用户名/uid 或其他用户信息依赖例如 logger 对象、数据抓取器data fetchers、数据库客户端等辅助函数封装好的可复用逻辑。dataclass class UserInfo: name: str uid: int # 还可以放 logger、db_client、helper 函数等2.4 危险提示上下文对象不会发送给 LLM⚠️ 重点提醒context 对象不会被发送给 LLM。它纯粹是本地对象你可以读取、写入、甚至调用其方法但模型永远看不到它的内容。这意味着不要指望把数据塞进context后模型自动知道想让模型感知数据必须走本文第四节的LLM 上下文通道。2.5 派生 wrapper 的共享语义在一次运行内部由 SDK 派生的 wrapper 共享同一个底层应用上下文、审批状态approval state和用量统计usage tracking。这一点在源码中有直接实现_share_tool_state_with()src/agents/run_context.py会让派生 wrapper 共享_approvals与_tool_invocations_fork_with_tool_input()/_fork_without_tool_input()src/agents/run_context.py创建子上下文时usage和审批状态是共享引用而tool_input可以不同。特别地嵌套的Agent.as_tool()运行可能附加不同的tool_input但默认不会得到应用状态的隔离副本。也就是说内层 Agent 的工具仍然能读到外层传入的同一个context对象。在设计多 Agent 系统时要注意这一点对状态隔离的影响。三、使用本地上下文实现能力可见性capability visibility当函数工具function tools、MCP 工具和 handoff 依赖同一个请求策略比如按用户身份决定哪些能力可用时官方推荐的模式是把策略输入或辅助函数放在应用上下文上而不是维护多份独立的能力清单。SDK 的每个表面surface都通过各自的回调暴露当前运行上下文SDK 表面回调接收的上下文函数工具FunctionTool.is_enabledRunContextWrapperHandoffHandoff.is_enabledRunContextWrapperMCP 工具MCPtool_filterToolFilterContext其run_context属性包含当前RunContextWrapper例如Handoff.is_enabled的源码签名src/agents/handoffs/init.py明确支持bool 或接收 run context 与 agent 的可调用对象is_enabled: bool | Callable[[RunContextWrapper[Any], AgentBase[Any]], MaybeAwaitable[bool]] True Whether the handoff is enabled. Either a bool or a callable that takes the run context and agent and returns whether the handoff is enabled...而 MCP 侧的ToolFilterContextsrc/agents/mcp/util.py是一个 dataclass包含三个字段dataclass class ToolFilterContext: run_context: RunContextWrapper[Any] # 当前运行上下文 agent: AgentBase # 请求工具列表的 Agent server_name: str # MCP 服务器名称3.1 关键边界能力可见性 ≠ 授权authorization文档明确划出了安全边界这些回调只控制SDK 在当前运行中暴露哪些能力即工具是否对模型可见它们不能授权模型生成的参数或资源选择。也就是说函数工具参数层面的安全决策必须在工具实现内部强制执行或借助工具输入 guardrails与人工审批完成MCP 工具MCP 服务器必须自行授权其受保护的操作SDK 侧过滤不构成服务端授权Handoff带input_type在on_handoff开头、应用副作用发生之前检查解析后的输入授权失败时应该raise而不是return。注意工具输入 guardrails 不会对 handoff 运行。完整的回调生命周期见 handoff inputs。四、RunContextWrapper暴露了什么实际开发中你绝大多数时候只用以下成员成员说明wrapper.context你自己的可变应用状态与依赖唯一由你定义的对象wrapper.usage当前运行累计的请求数与 token 用量wrapper.tool_input当前运行位于Agent.as_tool()内部时的结构化输入wrapper.approve_tool(...)编程式更新审批状态批准工具调用wrapper.reject_tool(...)编程式更新审批状态拒绝工具调用除了wrapper.context其余字段都是SDK 管理的运行时元数据。4.1wrapper.usage跨运行累计的用量统计usage字段的类型是Usage它聚合了本次运行所有模型请求的用量。关键字段包括requests发往 LLM API 的总请求数input_tokens/output_tokens/total_tokens累计输入、输出、总 token 数input_tokens_details/output_tokens_details与 Responses API 对齐的 token 明细cached_tokens、cache_write_tokens、reasoning_tokens等request_usage_entries逐请求的用量明细RequestUsage列表用于精确成本计算——例如一次运行发起 3 次 API 调用、输入 token 分别为 100K/150K/80K则input_tokens聚合为 330K而request_usage_entries保留[100K, 150K, 80K]的分解。Usage.add()src/agents/usage.py实现增量聚合每次调用自动在request_usage_entries中追加条目只要新增用量代表一次新请求且有非零 token。因为usage是通过Usage.add原地累计的序列化/恢复RunState时 SDK 会深拷贝usage见_copy_for_run_statesrc/agents/run_context.py避免一个可恢复检查点checkpoint的 token 落到其他检查点或最终结果上。4.2approve_tool/reject_tool编程式审批当你的应用需要以编程方式响应工具审批时可以调用这两个方法src/agents/run_context.pydef approve_tool(self, approval_item: ToolApprovalItem, always_approve: bool False) - None: Approve a tool call, optionally for all future calls. def reject_tool(self, approval_item: ToolApprovalItem, always_reject: bool False, rejection_message: str | None None) - None: Reject a tool call, optionally for all future calls.always_approveTrue/always_rejectTrue会把决策做成粘性sticky影响同 scope 下该工具的后续调用reject_tool支持携带rejection_message该消息会在模型再次尝试时回传给 LLM可通过get_rejection_message查询src/agents/run_context.py。另外还有is_tool_approved(tool_name, call_id)返回True/False/None和get_approval_status(...)可用于查询审批状态。审批/拒绝决策会被记录在_approvals字典中_ApprovalRecord支持布尔粘性决策与按 call ID 列表决策两种粒度见 src/agents/run_context.py。4.3 序列化与安全把RunState持久化时的注意事项如果你随后将RunState序列化用于 human-in-the-loop 或持久化任务工作流这些运行时元数据usage、approvals、tool invocations会随状态一并保存。因此避免在RunContextWrapper.context中存放密钥secrets如果你打算持久化或传输序列化后的状态。这一警告在源码层面有呼应_approvals、_tool_invocations、usage都提供对应的序列化/反序列化路径如serialize_usage/deserialize_usage、_rebuild_approvals、_rebuild_tool_invocations见 src/agents/run_context.py说明这些 SDK 元数据是状态恢复协议的一部分。4.4 对话状态是另一回事对话轮次conversation turns的延续不属于本地上下文的职责。请根据你想要的前进方式在以下机制中选择result.to_input_list()见 resultssession/conversation_id/previous_response_id见 running agents 与 sessions。五、完整示例把 dataclass 上下文传给工具下面是一个可直接运行的最小完整示例源自文档并补充了类型标注说明import asyncio from dataclasses import dataclass from agents import Agent, RunContextWrapper, Runner from agents.decorators import tool dataclass class UserInfo: # (1) 上下文对象dataclass也可以用任意类型 name: str uid: int tool async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) - str: # (2) 工具接收 RunContextWrapper[UserInfo] Fetch the age of the user. Call this function to get users age information. return fThe user {wrapper.context.name} is 47 years old async def main(): user_info UserInfo(nameJohn, uid123) agent AgentUserInfo Agent 用泛型 UserInfo 标注类型检查器可捕获错误 nameAssistant, tools[fetch_user_age], ) result await Runner.run( # (4) context 传给 run 函数 starting_agentagent, inputWhat is the age of the user?, contextuser_info, ) print(result.final_output) # (5) Agent 正确调用工具并拿到年龄 # The user John is 47 years old. if __name__ __main__: asyncio.run(main())要点回顾UserInfo是应用自定义的上下文对象dataclass、Pydantic 模型或任意类型皆可工具函数第一个参数声明为RunContextWrapper[UserInfo]实现中通过wrapper.context读取数据Agent[UserInfo]的泛型标注让类型检查器能在编译期拦截上下文类型不一致的错误通过Runner.run(contextuser_info)注入工具正常执行并返回基于上下文的结果。六、进阶ToolContext——工具级元数据大多数场景下RunContextWrapper已足够。但如果你需要访问正在执行的工具的额外元数据——工具名称、调用 ID、原始参数字符串——请使用ToolContext它是RunContextWrapper的子类。6.1 示例带结构化返回值的天气工具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], )6.2ToolContext的完整字段ToolContext提供与RunContextWrapper相同的.context属性外加与当前工具调用相关的字段src/agents/tool_context.py字段说明tool_name正在调用的工具名称tool_call_id本次工具调用的唯一标识tool_arguments传给工具的原始参数字符串tool_namespace工具调用的 Responses 命名空间当工具通过tool_namespace()或其他带命名空间的表面加载时存在qualified_tool_name有命名空间时用命名空间限定后的工具名只读属性见 src/agents/tool_context.py此外ToolContext还携带tool_call原始的ResponseFunctionToolCall、agent当前 Agent和run_config等字段。因为它继承自RunContextWrapper所以在嵌套的Agent.as_tool()运行提供了结构化输入时它同样可以暴露.tool_input。6.3 何时用哪个需要工具级元数据名称/ID/原始参数用于日志、调试、审计或条件逻辑 → 用ToolContext只需要在 Agent 与工具之间共享应用状态→RunContextWrapper已经足够。一个值得注意的实现细节ToolContext覆写了approve_tool/reject_toolsrc/agents/tool_context.py当审批项属于嵌套 Agent-as-tool 运行时会自动路由到拥有该审批项的嵌套上下文而不是错误地应用到当前上下文——这体现了 SDK 在多层嵌套下对审批状态归属的精细管理。七、LLM 上下文让模型看见数据的四种途径当 LLM 被调用时它唯一能看到的只有对话历史。因此要让新数据对模型可用必须让它进入历史。官方提供了四种途径按注入时机/方式区分7.1 途径一Agentinstructions系统提示/开发者消息把数据加进 Agent 的instructions即system prompt或developer message。instructions可以是静态字符串也可以是接收 context 并输出字符串的动态函数。这是始终有用的信息例如用户名、当前日期的常用做法——每次对话开始时都会带上成本可控。7.2 途径二Runner.run的input参数把数据加进调用Runner.run时的input。这与instructions策略类似区别在于这些消息在命令链chain of command中处于较低层级因此优先级低于系统提示——当指令冲突时更高层级的系统提示拥有更高权威。7.3 途径三通过FunctionTool按需暴露把数据通过FunctionTool实例暴露给模型。这对按需on-demand上下文最合适模型自己判断何时需要某些数据然后调用工具去获取。例如fetch_user_age这类工具模型只在需要年龄时才调用而不是每条消息都携带。7.4 途径四检索retrieval与网页搜索web search使用检索或网页搜索这类特殊工具它们能从文件/数据库retrieval或网页web search拉取相关数据。这适合把回答grounding锚定在相关上下文数据上减少幻觉。仓库中提供了对应的真实示例examples/tools/web_search.py、examples/tools/file_search.py可配合 docs/tools.md 阅读。7.5 小结四途径的选择矩阵途径数据特性成本特征适用场景instructions动态函数始终有用每条消息都占用 token用户名、当前日期、全局规则input参数单次运行相关每条消息都占用 token低于系统提示优先级的临时注入FunctionTool按需获取模型调用时才产生数据库查询、API 数据拉取retrieval / web search外部 grounding按检索调用计费文件检索、网页事实核查八、最佳实践与易错点汇总统一类型一次运行内所有 Agent、工具、钩子必须使用相同类型的 context善用Agent[T]泛型让类型检查器替你把关。本地数据不自动可见context不会发给 LLM要让模型感知数据必须走第七节的四种途径之一。能力可见性不等于授权is_enabled/tool_filter只控制能力暴露参数级、资源级授权必须在工具实现、guardrails、审批或 MCP 服务端完成。带input_type的 handoff 要在on_handoff开头、副作用发生前校验输入并raiseguardrails 不覆盖 handoff。嵌套共享语义Agent.as_tool()嵌套运行共享应用上下文、审批状态与用量默认不会隔离应用状态设计多 Agent 时要明确这一点。持久化防泄密RunState序列化会包含 usage、审批等运行时元数据不要在context中放 secrets。对话状态另走通道多轮对话的前进用to_input_list()、session、conversation_id或previous_response_id不要塞进本地 context。九、延伸阅读run_context.py 源码RunContextWrapper、AgentHookContext的完整定义与审批状态管理tool_context.py 源码ToolContext的字段、from_agent_context工厂方法与嵌套审批路由usage.py 源码Usage聚合逻辑与逐请求用量明细生命周期钩子各钩子如何接收RunContextWrapper/AgentHookContext相关文档handoffs、guardrails、human_in_the_loop、mcp 动态工具过滤、results、running agents、sessions。【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表