Claude Code Agent Loop 研究系列(03)—— 从读文件到并行调度
内容提要
本文探讨Claude Code中多个工具调用(tool_use)的执行调度机制。核心是混合策略:每个工具自我声明能否并行,harness据此分批——连续可并行的工具组成一批并行执行,不可并行的单独串行,批次间严格顺序。工具崩溃时转为is_error的tool_result反馈给LLM,而非中断循环,确保tool_use必有对应tool_result的不变量。流式解析JSON时可提前启动工具以节省延迟。
延伸解读
并行决策下放给工具自身
Claude Code 没有在 harness 层维护一张“哪些工具可并行”的全局表,而是让每个工具自己声明“我这次能否安全并行”。这种设计把副作用知识内聚到工具内部,新增命令或副作用时只需更新工具自身,无需改动调度器。判断出错时系统会保守降级为串行,宁可慢也不冒险,体现了“不确定时走安全路径”的工程取向。
分批规则与顺序保证
harness 按声明顺序扫描工具调用,将连续可并行的工具合并为一批,遇到不可并行的则切断,批次之间严格串行。这样既保证并行效率,又避免并发修改同一文件等冲突。例如,两个 Read 可并行,但 Edit 前后的 Read 会被分隔,确保 Edit 执行时前后状态一致。并行度上限默认 10,防止资源耗尽。
流式预启动的延迟优化
在流式输出中,工具参数 JSON 是分片到达的。Claude Code 不等完整 JSON,而是边流边解析,一旦关键参数(如文件路径)完整就立即启动工具,使工具执行与 LLM 继续输出文本并行。一次调用可节省约 100ms,累积数十次调用可省 5-10 秒。代价是需要一个能容忍不完整 JSON 的解析器。
错误转为反馈而非异常
工具执行失败(如文件不存在、命令退出码非零)不会中断循环,而是被捕获并转换为 is_error: true 的 tool_result 反馈给 LLM。这样既维护了“每个 tool_use 必有对应 tool_result”的不变量,又让 LLM 自主决定下一步(重试、换工具或告知用户)。用户中断、未知工具名等异常情况也统一走此路径,确保不留下孤儿 tool_use。
Q&A
Claude Code 中多个工具调用是如何决定并行还是串行执行的?
Claude Code 采用混合策略:每个工具自己声明能否安全并行,harness 根据声明顺序将连续可并行的工具打成一个批次并行执行,遇到不可并行的工具则切断,批次之间严格串行。
在 Claude Code 中,哪些工具可以并行执行?
只读工具如 Read、Grep、Glob 通常可以并行;修改文件的工具如 Edit、Write 不能并行;Bash 工具则根据具体命令判断,如 git status 可并行,rm 必须串行。
Claude Code 如何保证每个 tool_use 都有对应的 tool_result?
即使工具执行失败或用户中断,harness 也会将异常转换为 is_error 的 tool_result 返回给 LLM,确保每个 tool_use 都有对应的 tool_result,维护配对不变量。
Claude Code 在流式解析 JSON 时如何优化工具启动?
Claude Code 在流式解析 JSON 时,只要关键参数(如 file_path)完整,就立即启动工具,而不必等待整个 JSON 解析完成,从而让工具执行与 LLM 输出并行,节省延迟。
如果工具判断能否并行的逻辑本身出错,Claude Code 会怎么处理?
如果工具判断能否并行的逻辑抛出异常,Claude Code 会捕获异常并保守地认为该工具不能并行,从而降级为串行执行,确保安全。
Claude Code 中工具执行失败时,错误信息是如何反馈给 LLM 的?
工具执行失败时,harness 会将异常捕获并转换为 is_error: true 的 tool_result,内容为 <tool_use_error> 描述,然后作为 user 消息返回给 LLM,由 LLM 决定下一步操作。