Claude Code 工具调用机制
Claude Code 中模型
tool_use从产生、调度、授权、执行、结果回传到继续下一轮推理的完整运行时链路,是 Agent harness 可控性的核心基础。
简介
Claude Code 工具调用机制指的是 Claude Code 在一次 agentic turn 中如何把模型生成的 tool_use 内容块转化为真实工具执行,并把结果安全、可控地回传给模型继续推理的完整系统。它不是一个单点 API,而是一条由 queryLoop、工具调度器、权限系统、Hook、工具执行器、结果存储、后台任务队列和 Abort Controller 共同组成的运行时链路。
这套机制的核心价值在于把“模型想调用工具”变成“系统可审计地执行工具”。模型只负责在消息中产生结构化的工具调用意图;Claude Code 运行时负责判断能否并发、是否有权限、是否需要用户确认、Hook 是否要改写输入或阻止继续、工具结果是否过大需要持久化、后台任务完成后如何通知、以及中途取消时如何在控制器之间传播。这些工程层让 Claude Code 不只是一个聊天模型外壳,而是一个能长期执行文件读写、命令运行、MCP 调用、子 agent 调度等真实副作用操作的 Agent harness。
从 AI Agent 智能体 视角看,工具调用机制对应 ReAct 循环中的“Act/Observe”部分:模型推理出下一步行动,运行时执行行动并把观察结果写回对话。从 Function Calling 视角看,它是函数调用机制在 Claude Code 这类本地 Agent 工具中的具象实现。从 MCP 模型上下文协议 视角看,它又是 MCP 工具和本地内置工具被统一纳入同一执行生命周期的承载层。
关键信息
- 类型:Agent harness 运行时机制 / 工具调用执行链路
- 核心入口:
queryLoop检查本轮 assistant 消息是否出现tool_use - 执行核心:
runToolUse串联 Hook、权限、工具执行、结果映射和收尾信号 - 并发原则:只有连续且显式声明并发安全的工具调用才合并批次;失败时保守串行
- 权限原则:Hook allow 不等于越权放行;deny/ask 规则和工具自身安全检查优先
- 结果原则:大结果持久化到文件并返回预览,不简单截断
- 后台原则:后台任务通过通知队列异步回到对话,而不是阻塞等待
- 相关概念:Claude Code、Deferred Tools、Function Calling、MCP 模型上下文协议、AI Agent 智能体、Loop Engineering
核心特性
queryLoop:用 tool_use 驱动 agentic turn
Claude Code 的对话入口 query() 会把执行权交给 queryLoop。queryLoop 本质上是一个不断产出消息的异步生成器,每轮做四件事:调用模型、检查输出、执行工具、决定继续或结束。关键变量是 needsFollowUp:如果 assistant 消息里出现了 tool_use 内容块,就说明模型要求执行工具,本轮必须把工具结果作为新的用户消息/工具结果回传,进入下一轮;如果没有工具调用,则进入收尾判断。
这使 Claude Code 的 Agent 循环保持清晰边界:模型不能自己执行副作用操作,只能提出工具调用;运行时执行后把 observation 重新喂给模型。needsFollowUp 的存在让工具调用成为循环的结构性开关,而不是散落在对话逻辑里的临时分支。
并发调度:先分批,再在批次内并发或串行
当一轮 assistant 消息里包含多个 tool_use,Claude Code 不会直接 Promise.all 全部执行,而是先用 partitionToolCalls 分批。每个工具调用会先用工具自己的 input schema 解析参数,再调用 isConcurrencySafe(parsedInput.data) 判断“这一次具体调用”能否并发。只有连续出现、并且都声明并发安全的调用会合并进同一个并发批次;只要遇到不可并发调用,或 input 解析失败,或并发安全判断抛错,就单独成批。
这个设计体现了 Agent harness 的保守性:并发不是默认权利,而是工具显式声明的能力。文件写入、Git 操作、状态修改等工具如果随意并发,容易造成竞态和半写入状态;读取类、搜索类工具则更容易声明并发安全。批次之间严格串行,保证前一批的上下文修改和结果已经稳定,再进入下一批。
runToolUse:单工具调用生命周期
runToolUse 是单个 tool_use 最终落地的函数。它按顺序处理:PreToolUse hooks、权限决策、工具执行、结果映射、PostToolUse hooks 和收尾消息。PreToolUse hooks 可以给出权限建议、改写输入、标记 preventContinuation,甚至直接 stop;权限系统会结合 hook 决策、规则、工具自身检查和当前模式决定是否执行;工具本体执行后,结果会被映射为 tool_result;PostToolUse hooks 可以追加附件消息,MCP 工具的输出甚至可能在此阶段被改写;最后如果前面标记过 preventContinuation,就追加 hook_stopped_continuation 信号,让 queryLoop 在本轮工具跑完后不要继续自动推进。
这种生命周期让每次工具调用都可插拔、可审计、可控。Hook 能在执行前后介入,但不能直接绕过硬权限规则;工具可以返回主结果、额外消息、上下文修改和 MCP 元数据;运行时统一负责把这些差异折叠进下一轮模型可理解的对话历史。
权限边界:Hook allow 不能绕过硬规则
Claude Code 的权限模型不是“某个 allow 一票通过”。PreToolUse hook 如果返回 deny,会直接阻止执行;如果返回 ask 或没有决策,会走正常权限流程;如果返回 allow,也只是跳过交互确认,仍要跑规则检查和工具自身的 checkPermissions。只要命中 deny 规则、内容级 ask、工具安全检查,执行仍会被阻止。
这点很关键,因为 Claude Code 的工具往往有真实副作用:写文件、执行命令、调用外部服务、发送请求。系统必须允许自动化提高效率,但不能让自动化绕过用户设置的 deny/ask 边界。换句话说,Hook allow 是“少问一次”的优化,不是“突破护栏”的特权。
结果映射与持久化:大输出写文件,模型拿预览
工具执行后返回的结果会被映射为 API 对话里的 tool_result。如果结果过大,Claude Code 不是粗暴截断,而是把完整结果写入会话目录下的 tool-results/{id}.json|txt,再返回一个包含预览片段和文件路径的 <persisted-output>。当一条消息里多个工具结果合计过大时,也会挑最大的新鲜结果进行持久化。
这解决了两个问题:一是避免工具结果撑爆模型上下文;二是保留完整可追溯数据,模型后续如果确实需要完整内容,可以再用普通 Read 工具读取路径。这比“截断到看不见关键部分”更适合工程任务,尤其是日志、测试输出、搜索结果和长文件读取。
后台任务:通过通知队列回到对话
Bash、PowerShell 和 AgentTool 具备后台执行语义。模型显式传 run_in_background: true 时,工具可以快速返回 backgroundTaskId,真实命令或子 agent 继续在后台运行。部分长时间命令或 coordinator 场景还会自动转入后台。
后台任务完成后不会让原来的 queryLoop 阻塞等待,因为那一轮可能早就结束。Claude Code 采用旁路通知:后台任务完成时把消息塞进进程级队列;如果当前仍有 query 在跑,本轮可注入附件消息;如果系统已经空闲,REPL 层的队列处理器会在“没有 query 正在运行且队列非空”时主动发起新一轮对话。用户如果确实希望等待某个后台任务,需要显式使用 Sleep 这类等待工具。
取消机制:Abort Controller 分层与反向冒泡
工具执行涉及三层 Abort Controller:query 级根 controller、同批并发工具共享的 sibling controller、每个工具自己的 tool controller。父级 abort 会传导到子级;子级 abort 在非 sibling_error 等横向错误场景下,也会反向冒泡到根 controller,让 queryLoop 感知整轮应该终止。
这套设计解决了“局部工具被拒绝或取消,但主循环继续推进”的风险。例如某个权限确认被拒绝,如果只取消单工具 controller,主循环可能把拒绝当普通结果继续执行;反向冒泡能让根 controller 同步中止,从而保持用户意图。与此同时,普通工具默认不可中途取消,避免写文件、跑命令等副作用操作被砍在半路留下不一致状态。
不同素材中的观点
- 2026-07-05-juejin-claude-code-tool-calling:这篇素材对 Claude Code 工具调用机制做源码级拆解,最大贡献是把“模型调用工具”拆成可工程化理解的多层运行时:
queryLoop用tool_use判断是否继续,partitionToolCalls按并发安全分批,runToolUse串联 Hook/权限/执行/结果/收尾,后台任务通过通知队列异步回到对话,Deferred Tools 通过客户端过滤和搜索结果解析降低 MCP 工具上下文占用。核心判断:Claude Code 的可靠性不只来自模型,而来自这条工具执行链路对副作用、权限和上下文成本的控制。
实用信息
设计 Agent 工具调用系统时可复用的原则
- 并发必须显式声明:不要默认所有工具都能并发,尤其是写入类、状态修改类和外部发布类工具。
- 权限要多层兜底:Hook、配置规则、工具自身安全检查和运行模式应共同决策;自动放行不能覆盖硬 deny。
- 工具结果要可追溯:大结果应持久化并给模型预览路径,避免截断导致关键信息丢失。
- 后台任务要事件化:不要阻塞主循环等待长任务;用任务 ID 和通知队列把完成事件重新注入对话。
- 取消要区分副作用:可安全取消的工具和不可中断的副作用工具应有不同 interrupt behavior。
- 工具过多要延迟加载:MCP 工具数量增长后,应使用类似 Deferred Tools 的发现机制,避免初始 prompt 被 schema 撑爆。
与 Function Calling 的区别
Function Calling 更偏模型/API 层:模型如何生成函数名和参数,应用如何执行并回传结果。Claude Code 工具调用机制更偏 Agent harness 层:在函数调用前后加上并发调度、Hook、权限、安全边界、后台任务、结果持久化和工具规模化加载。可以说 Function Calling 解决“会不会调用”,Claude Code 工具调用机制解决“能否安全、可控、长期地调用”。