OpenAI Agents SDK 工程笔记:as_tool 嵌套调用与生产禁区
2026/9/24 11:28:57 网站建设 项目流程

千笔-AIWritePaper · https://www.aiwritepaper.com

把专科 agent 做成工具(agent.as_tool)时,最容易踩的坑不是「调用语法」,而是控制权与状态边界:manager 仍掌握回合,但嵌套 run不会自动继承父对话状态;若把session乱共享、把needs_approval绕开、或把嵌套final_output当成已审计真相,账单与合规会一起炸。官方 Tools · Agents as tools 写清:相对 handoff,as_tool 让中心 agent 编排专科网络而不交出控制;as_tool支持max_turns/run_config/session/needs_approval/parameters/custom_output_extractor/is_enabled/on_stream等。本文按工程笔记钉死对照表、可跑 smoke 与生产禁区。示例模型名写作gpt-4o以你账号可用快照与官方文档为准

图:上方 as_tool vs handoff;中部嵌套选项与状态不继承;下方生产禁区与 fail smoke。

目标说明

读完你应能独立完成五件事:

  1. 用一句话说清:as_tool 时manager 保持控制;handoff 会交出对话控制权。
  2. 列出as_tool常用选项:max_turnsrun_configsessionneeds_approvalparameterscustom_output_extractoris_enabledon_stream
  3. 写出可跑片段:orchestrator + specialist.as_tool,并说明嵌套 run不继承父 conversation state。
  4. 钉死生产禁区:静默嵌套花费、错误共享 session、审批旁路、把嵌套 final_output 当审计真理。
  5. 留下 fail smoke 笔记:无 key 至少能构造工具;有 key 时验证嵌套输出与审批中断路径可观测。

规格钉死(对照官方 Agents as tools):

  • 控制权:as_tool = 中心 agent 编排;handoff = 移交控制。
  • 状态:嵌套 agent 的 state 选项配置该次嵌套 run;父 run 会话状态不自动继承
  • 共享历史:要共享 client-managed 历史,须显式把同一session传给父与嵌套;并与previous_response_id/conversation_id三选一策略,勿混用。
  • 审批needs_approval与 function_tool 同流;中断进result.interruptions,经to_state()+approve/reject再 resume。
  • 输出:默认把嵌套 final 回给 manager;可用custom_output_extractor抽取/校验。
  • 条件启用is_enabled可为 bool 或(ctx, agent)->bool;禁用则对 LLM 完全隐藏。

适用边界

适合用 as_tool

  • 需要中心编排多个专科(翻译、检索、计费问答),且回合后仍要由 manager 汇总
  • 希望专科失败时 manager 仍可换工具或降级,而不是整段对话被 handoff 带走。
  • 需要在专科入口挂needs_approval或结构化parameters

更适合 handoff(或单独 Runner.run)

  • 专科必须接管后续多轮用户对话(客服转专家坐席)。
  • 你其实要「换一个 agent 成为当前对话主体」,而不是「调一次工具拿结果」。

不该指望 as_tool 单独搞定

  • 嵌套自动继承父消息历史:默认不继承;要共享须显式 session/continuation。
  • 嵌套 final_output = 已审计业务事实:只是模型产物,除非你另有核验与审批。
  • is_enabled=False 等于鉴权:它管可见性/调度,不替代工具内授权与资源级检查。
  • on_stream 开了 = 已记账清晰:流式事件便于观察,不自动产生成本审计表。

风险提示

嵌套 run 会单独消耗 turns 与 tokens;manager 若在循环里反复调贵专科,成本呈乘法。把同一可变 session 在无隔离场景乱传,可能串话。审批工具若在测试里永远approve,等于没测拒绝路径。把 extractor 写成「缺失则编造默认 JSON」会把空结果洗成成功。

步骤与机制

1. as_tool vs handoff 对照

维度as_toolhandoff
控制权manager 保持交给目标 agent
典型用途编排专科、汇总答案转交会话主体
状态继承嵌套不自动继承父会话按 handoff/会话策略走
审批挂载needs_approval可挂工具入口另见 HITL/中断模式
失败后manager 可继续选工具对话已在专科侧

2. as_tool 选项速查

选项做什么不做什么
tool_name/tool_description暴露给模型的工具面不改专科内部 instructions
max_turns限制嵌套回合不限制父 run
run_config嵌套 RunConfig不自动合并父 config 全部字段(以官方为准实测)
session嵌套 client-managed 历史不隐式等于父 session
needs_approval调用前可暂停不替代工具内鉴权
parameters结构化入参 schema默认仍是{"input": str}
custom_output_extractor改写回传内容不自动校验业务真伪
is_enabled运行时显隐不是授权系统
on_stream监听嵌套流事件不代替成本账本

3. 可跑 smoke:orchestrator + specialist.as_tool

pip install openai-agents,并导出OPENAI_API_KEY(无 key 时至少应能 import 与构造as_tool)。

importasynciofromagentsimportAgent,Runner# 专科:短指令,便于观察嵌套输出specialist=Agent(name="CitationCheck",instructions=("你只做一件事:根据输入指出引用核验要点。""不要编造 DOI;不确定就写「需人工打开核验」。"),model="gpt-4o",# 占位:以账号可用快照为准)orchestrator=Agent(name="PaperOrchestrator",instructions=("你是论文助手编排者。需要引用核验时必须调用 citation_check 工具;""工具失败或不确定时,向用户披露,禁止把猜测写成已核验。"),model="gpt-4o",tools=[specialist.as_tool(tool_name="citation_check",tool_description="对给定引用片段做核验要点检查",max_turns=4,# 嵌套默认不继承父会话;此处故意不传 session,便于对照)],)asyncdefmain()->None:result=awaitRunner.run(orchestrator,"请核验:Smith 2021 JournalX 卷3 页12-20,DOI 未提供。",)print("final:",result.final_output)# 观察嵌套痕迹:new_items / raw_responses(以 SDK 版本为准)print("items:",len(getattr(result,"new_items",[])or[]))if__name__=="__main__":asyncio.run(main())

验收:进程可构造工具;有 key 时最终输出应承认「需人工打开」类不确定性,而不是伪造「DOI 已验证通过」。把一次成功日志路径写入_w/as-tool-smoke-checklist.md

4. Fail smoke:误共享 session / 审批旁路笔记

Fail A — 误以为嵌套继承父历史

# 反例说明(不要当最佳实践):# 父 run 已有多轮上下文,但 as_tool 未传 session / continuation。# 期望:专科「记得」刚才用户纠正过的主张编号。# 实际:嵌套从自己的输入起步,可能丢掉纠正 → 输出漂移。# 记 fail:nested_state_inherit=fail

Fail B — 审批永远自动通过

# 若 as_tool(..., needs_approval=True) 却在测试夹具里无条件 approve,# 则从未验证 reject / 超时未审批路径。# 门禁:至少 1 次 reject 样本落盘,否则 approval_path=fail

Fail C — extractor 把空结果洗成成功

asyncdefunsafe_extract(run_result):# 反例:缺失时返回伪造 JSONreturn'{"status":"ok","verified":true}'# 正确方向:缺失则返回明确失败标记,让 manager 降级asyncdefsafe_extract(run_result):text=str(getattr(run_result,"final_output","")or"").strip()ifnottext:return'{"status":"empty","verified":false}'returntext

把 A/B/C 三行 fail 记入 checklist;零 fail 样本不得宣称生产禁区已测

5. 结构化 parameters 与输出抽取(可选加强)

frompydanticimportBaseModel,FieldclassCheckInput(BaseModel):cite_key:str=Field(description="正文引用键,如 [12]")snippet:str=Field(description="待核验片段")tool=specialist.as_tool(tool_name="citation_check_struct",tool_description="结构化引用核验",parameters=CheckInput,include_input_schema=True,custom_output_extractor=safe_extract,)

门禁:结构化入参仍要在专科 instructions 里禁止编造;schema 合法 ≠ 事实正确。

生产禁区(对标 90/91)

禁区为什么危险替代
静默嵌套花费专科×循环=账单乘法max_turns + 成本审计 + 调用次数上限
以为嵌套继承父会话纠正丢失、串话显式 session 或声明无共享
同一 session 无隔离乱传用户/租户串数据按请求隔离;写清策略
审批夹具永远 approve拒绝路径未测强制 reject smoke
把嵌套 final_output 当真理幻觉进入业务抽检/二次核验/人工闸
extractor 缺失即「成功 JSON」空失败被洗白空=失败标记
is_enabled 当鉴权仍可能被错误装配工具内授权 + guardrail
与 handoff 混用却无 runbookoncall 无法判断控制权对照表进仓库

可验证清单

  • 能用一句话区分 as_tool 与 handoff 的控制权
  • orchestrator + specialist.as_tool smoke 可跑或可构造
  • 文档写明:嵌套不自动继承父 conversation state
  • 至少 1 条 fail:无共享却期望继承 / 或错误共享
  • 若启用 needs_approval:approve 与 reject 各 ≥1 样本
  • 成本:记录嵌套调用次数与 max_turns
  • 禁止把嵌套 final_output 直接写入「已核验」业务字段

踩坑

  • 把 handoff 示例代码改两个字就当 as_tool,导致控制权误解。
  • 父 instructions 写「你记得全部历史」,却给嵌套空 input。
  • on_stream只打印不落盘,排障时只剩 final 字符串。
  • 生产用贵模型做专科,却在 smoke 用极短 prompt 掩盖超时与花费。
  • 多专科并行时假设「一个失败全体停」——须对照 runner 取消与工具错误策略实测。

与 sessions / RunConfig / HITL 的分工

管什么不管什么
as_tool嵌套调用面与选项自动继承父历史
sessionclient-managed 历史策略替代审批
run_config嵌套追踪/模型等配置自动合并父全部语义
needs_approval人机闸工具内业务鉴权
custom_output_extractor回传整形事实核验

常见误配:只开 tracing 观察嵌套很忙,却不设max_turns;或只设审批,却在 resume 时无条件放行。

当天最小实验(30–40 分钟)

  1. 复制 orchestrator smoke,无 key 验证可构造;有 key 跑通一次。
  2. 造 Fail A:期望继承却不传 session,记 nested_state_inherit=fail。
  3. 若启用审批:造一次 reject。
  4. 给 extractor 空结果,确认不会洗成 verified=true。
  5. 把四行结果写入_w/as-tool-smoke-checklist.md

没有第 5 步落盘,不得自称「as_tool 生产禁区已钉死」。

多服务落地建议

  • 只读专科(格式检查、提纲):可 as_tool + 短 max_turns。
  • 写操作/发信/扣费:默认 needs_approval 或外层事务;嵌套成功 ≠ 已提交。
  • 长对话专家坐席:优先 handoff,而不是假装 as_tool 能接管多轮。
  • 多租户:session 与日志按租户隔离;禁止全局单例 session。

失败含义速查

现象含义下一步
专科「忘了」用户纠正状态未共享或 input 未带纠正显式传入或改 handoff
账单陡增嵌套循环/无 max_turns限次 + 审计
final 很顺但业务未核验把模型输出当真理加抽检闸
审批日志全是 approve拒绝未测补 reject smoke
is_enabled 关了仍被调用装配/缓存旧 tools查构建时 tools 列表

Runbook 片段(可直接贴进仓库)

[as-tool-policy] control = manager_keeps_control nested_inherits_parent_conversation = false session_strategy = explicit_only max_turns_default = 4 approval_write_tools = required extractor_empty = fail_not_ok smoke_required = construct_or_run, nested_no_inherit_fail, reject_once

发版检查官只问三句:嵌套是否声明了状态策略?写操作有没有拒绝样本?嵌套输出进业务前有没有第二道核验?

对照:编排观感 vs 生产应有行为

场景编排观感生产应有行为
翻译专科「调一下工具」max_turns + 输出语言抽检
引用核验专科「已经 check 过了」空 DOI→需人工;禁 verified 洗白
计费问答「答案很完整」金额字段二次校验
多轮专家「再 as_tool 一次」评估是否该 handoff

嵌套花费审计字段(建议落盘)

嵌套 run 至少记录:

  1. parent_run_id/nested_tool_name/nested_max_turns
  2. nested_turns_used(若可从结果推断)
  3. session_strategynone|shared_explicit|server_continuation
  4. approval_decisionn/a|approve|reject|timeout
  5. extractor_statuspassthrough|empty_fail|custom_ok

把五行打进_w/as-tool-smoke-checklist.md。没有花费与审批列,只写「调用成功」,等于没做生产验收。

条件启用 is_enabled 的正确用法

is_enabled适合:环境开关(预发关计费专科)、租户能力包、A/B 工具面。不适合:把「用户是否有权扣款」塞进可见性函数却不在工具内校验参数。

用法可接受危险
预发隐藏写操作专科生产仍靠隐藏当鉴权
按语言偏好显隐翻译专科把偏好当安全边界
异步函数查配额再显隐配额通过后不再校验参数

门禁:工具实现内仍要做资源级授权;is_enabled只减暴露面。

与多 agent 模式文档的对齐提示

官方多 agent 模式常并列 handoff、agents-as-tools、LLM-as-router。工程验收上要求写清:本服务默认哪一种控制权模型;切换时变更记录在哪。本文不复述全部模式图,但 runbook 必须能回答「失败时谁还握着对话」。

流式 on_stream 最小约定

若传入on_stream

  • 事件类型对齐raw_response_event/run_item_stream_event/agent_updated_stream_event(以 SDK 为准)。
  • 处理器同步或异步均可,但应有序处理。
  • 生产建议:采样写入日志,避免把全量 token 明文塞进无加密存储。
  • 打开on_stream会走嵌套 streaming 并在返回 final 前排空流——延迟与费用预期要写入容量计划。

对照:handoff 误用 as_tool 的症状

症状可能误用纠正
用户后续问题「专科不接话」本该 handoff 却 as_tool改 handoff 或显式多轮 session
专科答完 manager 又改口as_tool 正常用 extractor/指令约束改口
账单暴涨循环 as_toolmax_turns + 断路器
审批从未出现未设 needs_approval写操作强制挂闸

当天加强:结构化失败注入

在预发造三条:

  1. 专科 instructions 故意要求编造 DOI → 看 manager 是否照抄进 final。
  2. max_turns=1逼出截断 → 看是否被叙述成「已完整核验」。
  3. needs_approval=True后 reject → 看业务是否仍写入「已通过」。

三条日志路径进 checklist;缺一则 as-tool 闸门未完整。

安全笔记(短)

  • 嵌套 agent 的 tools 列表应最小权限;不要把生产写库工具挂到「只读核验」专科。
  • custom_output_extractor里不要执行未校验代码路径。
  • 日志中的tool_call参数可能含 PII:与 tracing 敏感开关一并治理。

总结

as_tool的工程核心不是语法糖,而是控制权仍在 manager,状态与花费在嵌套边界重新结算。相对 handoff,它适合编排;相对「嵌套自动记得一切」,它默认不继承。可跑 smoke + fail 样本 + 审批拒绝路径 + 禁止把嵌套 final 当审计真理,才对得起 tool 超时/guardrails 91、RunConfig/sessions 90 那一档强度。

参考

  • Tools(Agents as tools):https://openai.github.io/openai-agents-python/tools/
  • 质量要点:/workspace/csdn-posts/quality/LATEST.md

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询