跳到主要内容

工具系统与 agent-as-tool

这一章讲「手脚」:模型只会说话,工具让它能动手。先讲 tool() 怎么把一个普通函数变成模型可调用的工具,再讲四类工具的分工,最后讲最妙的一招——Agent.asTool,把一整个 agent 当成一个工具。

1. 它要解决的小问题

模型说「调用 getWeather({city:'Tokyo'})」时,它给的是一段 JSON 字符串。你需要:校验这段 JSON 合不合 schema、转成对象、调你的真实函数、把返回值转回字符串喂给模型;还要处理超时、出错、要不要先问用户同意。tool() 工厂把这一整套包好。

2. 四类工具,各管一摊

SDK 把「工具」分成几类,模型看到的接口一致,但执行方不同:

工具类型干什么谁执行工厂
function调你的本地 JS 函数SDK(本地)tool() tool.ts:1860
computer操作电脑 GUI(截图/点击/打字)你提供的 ComputercomputerTool() tool.ts:510
shell跑 shell 命令本地或托管容器shellTool() tool.ts:869
apply_patch对文件应用补丁(改代码)你提供的 EditorapplyPatchTool() tool.ts:952
hosted_tool托管工具(如远程 MCP 服务器、web search)OpenAI 服务端hostedMcpTool() tool.ts:1000

本章聚焦最常用的 function 工具,其余在第 5 章和人审章里会再碰到。

3. tool():从函数到工具

3.1 思路

tool() 返回一个 FunctionTool 对象,关键字段是 invoke(runContext, input, details)——一个接收 JSON 字符串 的执行器。它在内部:解析 → 校验 → 调你的 execute → 把结果字符串化。

3.2 原理演示

// 示意,非源码 —— tool() 的精神
function tool({ name, parameters, execute, errorFunction }) {
const { parser } = schemaAndParser(parameters); // Zod / JSON schema → 校验器
async function invoke(runContext, inputJson, details) {
const parsed = parser(inputJson); // ① 校验+解析 JSON
const result = await execute(parsed, runContext, details); // ② 调你的函数
return toStringIfNeeded(result); // ③ 字符串化回喂模型
}
return { type: 'function', name, parameters, invoke, needsApproval, ... };
}

3.3 真实实现

tool()tool.ts:1860)做的事,逐条对得上:

  • 名字归一化tool.ts:1867 toFunctionToolName(options.name ?? options.execute.name)——没给名字就用函数名。
  • strict 模式默认开tool.ts:1880 const strictMode = options.strict ?? true;用 Zod 参数时强制 strict(tool.ts:1881-1883,否则抛 UserError)。
  • 校验器tool.ts:1892 getSchemaAndParserFromInputType(...) 产出 parser 与序列化 parameters
  • 核心 invoke:内部 _invoketool.ts:1898)先 parser(input),解析失败抛 InvalidToolInputErrortool.ts:1913),成功则 await options.execute(parsed, runContext, details)tool.ts:1928),再 toSmartString(result)
  • 错误兜底invokeWithoutTimeouttool.ts:1940)catch 住执行错误,若配了 errorFunction 就把错误转成给模型看的文本tool.ts:1962),否则重抛。默认有个 defaultToolErrorFunction。这让「工具报错」不会炸掉整个 run,而是变成模型能读到的反馈。
  • 超时invoketool.ts:1969)外面套了 invokeFunctionToolWithTimeouttool.ts:1725)——用 Promise.race 把执行和一个 setTimeout 比赛,超时则按 timeoutBehavior 决定抛异常还是返回一段超时文本(tool.ts:1796-1807)。注意它用 AbortController 把超时信号也传进 executedetails.signal,让你的函数能感知取消。

3.4 巧妙之处:错误是「反馈」不是「崩溃」

默认把工具异常包成文本回喂模型(tool.ts:1953-1963),是 agent 框架的关键设计——模型看到「工具失败了:xxx」可以自我纠正重试,而不是整个流程挂掉。要改这行为,传 errorFunction: null 就会重抛。

4. Agent.asTool:把一个 agent 当工具

4.1 它要解决的小问题

你有个「翻译 agent」,想让「主 agent」在需要时调它,但不想把对话交给它(交接会换掉当前 agent)。你要的是「调一个子程序、拿回结果、自己继续」。这就是 agent-as-tool。

4.2 和 handoff 的区别(重要)

agent.ts:653-663 的 docstring 把区别讲得很清楚:

handoff(交接)asTool(agent 当工具)
谁拿到对话历史新 agent 接收完整历史子 agent 只收到生成的 input
谁接管对话新 agent 接管,原 agent 退出子 agent 当工具被调,原 agent 继续
控制流标签next_step_handoff就是一次普通 function_call

4.3 真实实现

Agent.asToolagent.ts:665 起)本质是:tool() 包一个 execute,这个 execute 内部 new Runner(...).run(this, input, ...) 跑一次嵌套的完整 run。

  • execute 里启动嵌套 runagent.ts:796 const runner = new Runner(nestedRunConfig),然后 agent.ts:846-853 runner.run(this, runInput, {...})
  • 输出提取:默认取「最后一条 assistant 消息的文本」当工具结果(agent.ts:907-927),可用 customOutputExtractor 覆盖。
  • 可选流式:只有当你注册了 onStream.on(...) 处理器时,才切到 streaming 模式去逐事件透传(agent.ts:829-872),否则走非流式省开销。
  • 可恢复:如果父 run 是从序列化状态恢复的,嵌套 run 也能从 details.resumeState 重建 RunStateagent.ts:802-826)——这让「agent-as-tool + 人审」也能跨进程续跑。

4.4 一个坑:stopAtToolNames 在 asTool 下不直观

如果子 agent 配了 toolUseBehavior.stopAtToolNames,又被当工具调,SDK 会打 debug 日志提醒「这可能不是你想要的」(agent.ts:888-900),建议你用 customOutputExtractor 或包一层函数工具来稳定返回最终输出。

5. 关键细节

  • 工具的开关Agent.getAllToolsagent.ts:1038)在每次 run 时按 isEnabled 过滤函数工具,并把 MCP 服务器的工具拼进来。所以「这次 run 模型能看到哪些工具」是动态算的。
  • 审批挂在工具上tool() 接受 needsApproval(布尔或函数,tool.ts:1993),它决定这工具调用要不要先暂停等人点同意——细节见人审章。
  • 工具级护栏tool() 还能挂 inputGuardrails / outputGuardrailstool.ts:2024-2025),在工具执行前后跑——见护栏章。

6. 代码地图

主题文件符号
函数工具工厂tool.tstoolinvokeFunctionToolinvokeFunctionToolWithTimeout
其它工具工厂tool.tscomputerToolshellToolapplyPatchToolhostedMcpTool
agent 当工具agent.tsAgent.asTool
工具执行(含并行/串行)runner/turnResolution.tsresolveTurnAfterModelResponseorderShellAndApplyPatchActions
函数工具执行runner/toolExecution.tsexecuteFunctionToolCallsparseToolArguments
动态工具集agent.tsAgent.getAllToolsAgent.getMcpTools