# 14. Function Calling / Tool Use 工具调用
# 14.1 原理
Function Calling 是模型生成结构化动作的接口,Tool Use 是从定义、选择、审批、执行到结果反馈的完整生命周期。
安全执行顺序:
lookup → schema parse → semantic validate → hooks/policy // 解析工具并依次做类型、语义、Hook 与策略校验
→ approval → sandbox → execute → normalize result // 获批后在沙箱执行,并统一成功或错误结果结构
→ output bound → persist → feed back to model // 控制输出大小、持久化事实,再反馈给下一次采样
Schema 校验只能保证类型正确,不能保证语义安全。例如 {command: "rm -rf ..."} 完全符合字符串 schema,仍需命令解析、权限和沙箱。
并发策略也不能只由“模型一次返回多个 ToolCall”决定:只读工具通常可并发;写文件、改变工作目录、修改共享 Context 或依赖前一步结果的调用必须串行。把并发结果加入下一轮 Context 时,应保持原 ToolCall 顺序,或通过明确的 call id 与调用对应。
# 14.2 Claude Code 源码
partitionToolCalls 根据 Tool 的 isConcurrencySafe 划分批次;并发工具产生的 Context Modifier 先按 ToolUse id 缓存,之后按原调用顺序应用,避免完成时序导致状态不确定:toolOrchestration.ts:91、toolOrchestration.ts:36。
执行链对 input schema、Tool 自定义验证、Hook 和 Permission 分层处理;执行成功后统一映射 ToolResult、记录大小与 OTel 事件,再跑 PostToolUse Hook:toolExecution.ts:650、toolExecution.ts:798、toolExecution.ts:1271。
# 14.3 Codex 源码
Codex 的 ToolRouter、Registry 与 Parallel Runtime 将协议 ToolCall 路由到具体 Handler;MCP 是 Tool Handler 的一种,Shell/ApplyPatch/协作工具也共享生命周期和事件模型:router.rs、parallel.rs。
# 14.4 伪代码
function partition(calls): // 将模型同时产生的 ToolCall 划分为安全执行批次
batches = [] // 初始化保持原始调用顺序的批次列表
for call in calls: // 逐个检查每个调用的并发安全属性
spec = registry[call.name] // 从工具注册表读取只读性和状态影响元数据
safe = spec.readOnly && spec.noSharedContextMutation // 只有只读且不修改共享上下文时才视为可并行
if safe and batches.last?.parallel: // 当前调用可加入紧邻的并行安全批次
batches.last.add(call) // 合并调用以降低独立执行等待时间
else: // 写操作或批次边界必须新建有序批次
batches.add(Batch(parallel=safe, calls=[call])) // 创建串行或并行属性明确的新批次
return batches // 返回供 Runtime 按顺序执行的批次序列
# 14.5 面试题
问:工具结果为什么也要 schema?
结果是下一步模型与 UI 的输入。结构化结果便于裁剪、展示、评测和错误分类;纯字符串会把业务数据、日志和错误混在一起,也更容易被 Prompt Injection 影响。
# 14.6 原理深化:Tool Use 必须经过统一、不可绕过的受控执行流程
一次工具调用应先被规范化成 call_id + tool_identity + validated_args + principal + step_snapshot,后续 Hook、权限、沙箱、审计和持久化都引用这份对象。Schema Parse 解决结构合法性,Tool Validator 解决领域约束,PreToolUse Hook 提供扩展拦截,Permission 决定主体是否可请求,Sandbox 强制资源边界;它们不是功能重复,而是处在不同信任层。
Claude Code executeToolCall 的顺序把输入校验、Hook、权限和执行分层,partitionToolCalls 又只并行声明为安全的调用,并按原 ToolUse 顺序应用 Context Modifier。这说明并发完成顺序不能决定共享状态顺序,否则相同模型输出会得到不同上下文:toolExecution.ts:614、toolExecution.ts:798、toolOrchestration.ts:36。Codex 也在 Step 内固定 Tool Registry/Context,再由 Router 与 Handler 处理具体协议,避免工具清单在解析与执行之间漂移。
失败必须被规范化为可决策结果:validation_error 不应重试,denied 等待用户或改变方案,sandbox_violation 表示策略与动作冲突,transient_execution_error 才可能按幂等性退避重试,unknown_after_interrupt 则要求查询外部事实或人工确认。PostToolUse Hook 只能观察或在受限 Schema 内改写结果,不能把失败伪装成成功。最终 ToolResult 必须先获得稳定 call id 并写入可恢复记录,再提供给下一次模型采样。