Claude Code 如何判断工具调用该并发还是串行?
在 Claude Code 里,模型经常会调用工具,比如读文件、搜索代码、执行 Bash 命令、编辑文件等。
一个很自然的问题是:
Claude Code 是怎么知道多个工具应该并发执行,还是一个一个串行执行的?
答案可以概括为一句话:
模型负责决定“这一轮要不要发多个工具调用”,客户端负责判断“这些工具能不能安全并发执行”。
一、整体流程
Claude Code 并不是靠模型返回一个明确的 parallel: true 或 serial: true 字段来判断。
它的逻辑分成两层:
- 提示词层:告诉模型什么时候应该一次性发多个工具调用
- 执行层:客户端根据工具的安全属性决定是否真的并发执行
流程大概如下:
flowchart TD
A[用户提出任务] --> B[系统提示词指导模型]
B --> C{任务之间是否独立?}
C -->|独立| D[模型在同一轮返回多个 tool_use]
C -->|依赖| E[模型先返回一个 tool_use]
D --> F[客户端收到多个工具调用]
E --> F
F --> G{工具是否并发安全?}
G -->|是| H[并发执行]
G -->|否| I[串行执行]
H --> J[返回 tool_result]
I --> J
J --> K[模型继续下一轮推理]
二、模型侧:通过提示词引导并发
Claude Code 的系统提示词里有类似这样的规则:
|
|
翻译一下就是:
- 互不依赖的任务:尽量放在同一轮里一起调用
- 有依赖关系的任务:等前一个工具结果回来后,再调用下一个
例如:
可以并发
用户问:
帮我看看 src/query.ts 和 src/Tool.ts 里工具调用相关逻辑。
模型可以在同一轮返回两个读文件工具:
|
|
因为这两个文件的读取互不依赖。
应该串行
用户问:
先运行测试,看看失败原因,然后帮我修复。
这时模型应该先调用:
|
|
等测试结果回来后,再决定读哪个文件、改哪里。
因为后续操作依赖测试输出。
三、客户端侧:通过 isConcurrencySafe 判断是否安全
即使模型在同一轮返回了多个工具调用,Claude Code 也不会无脑并发执行。
真正执行前,它会检查每个工具是否“并发安全”。
核心字段是:
|
|
每个工具都可以声明自己是否适合并发执行。
默认情况下,工具是 不并发安全 的:
|
|
这是一种保守策略:
如果不确定工具是否安全,就按串行处理。
四、哪些工具可以并发?
读文件工具:可以并发
读文件不会修改状态,所以通常是并发安全的。
|
|
例如:
|
|
这些可以一起执行。
Bash 工具:看命令是否只读
Bash 比较特殊,因为它既可能是安全的:
|
|
也可能是危险的:
|
|
所以 Bash 工具的逻辑大致是:
|
|
也就是说:
- 只读命令:可以并发
- 会修改系统状态的命令:串行执行
五、工具调用会被分批执行
客户端会把模型返回的一组工具调用拆成多个 batch。
规则是:
- 连续的并发安全工具会合并成一个并发 batch
- 非并发安全工具单独成为一个 batch
- batch 之间串行执行
例如模型返回了下面这些工具:
|
|
可能会被拆成:
flowchart TD
A[模型返回多个 tool_use] --> B[Batch 1: Read A + Read B]
B --> C[并发执行]
C --> D[Batch 2: Edit C]
D --> E[串行执行]
E --> F[Batch 3: Read D + Bash git status]
F --> G[并发执行]
G --> H[Batch 4: Bash npm install]
H --> I[串行执行]
原因是:
- Read(A) 和 Read(B) 都安全,可以并发
- Edit(C) 会修改文件,必须单独执行
- Read(D) 和 git status 都是只读,可以并发
- npm install 会修改依赖和文件系统,必须串行
六、最大并发数限制
即使有很多工具都可以并发,Claude Code 也有并发上限。
默认最大并发数是:
|
|
也可以通过环境变量调整:
|
|
这样可以避免一次性启动太多任务,导致系统资源压力过大。
七、Streaming 模式下也一样
Claude Code 还有一种 Streaming Tool Execution 模式。
也就是说,模型还在流式输出时,只要某个 tool_use 已经出现,客户端就可以提前开始执行工具,而不用等整条 assistant 消息结束。
但它仍然遵守同样的并发安全规则:
|
|
可以理解为:
flowchart TD
A[流式收到一个 tool_use] --> B{当前有工具在执行吗?}
B -->|没有| C[立即执行]
B -->|有| D{当前工具和新工具都并发安全吗?}
D -->|是| E[一起并发执行]
D -->|否| F[进入队列等待]
C --> G[返回结果]
E --> G
F --> G
八、总结
Claude Code 的工具并发机制设计得比较稳健:
| 层级 | 作用 |
|---|---|
| 模型提示词 | 引导模型把独立任务放在同一轮调用 |
| tool_use 数量 | 同一轮多个 tool_use 表示可以尝试批量执行 |
| isConcurrencySafe | 工具自己声明是否并发安全 |
| 执行器 | 按安全性分批,并发或串行执行 |
| 并发上限 | 防止资源被打爆 |
最终逻辑可以总结为:
模型决定“哪些事情可以一起请求”,客户端决定“哪些工具真的可以一起跑”。
这也是一个很值得借鉴的设计:
- 模型负责语义判断
- 程序负责安全约束
- 默认保守
- 显式声明并发安全
- 对读操作开放并发,对写操作保持串行
这种设计既能提高性能,又能避免并发修改带来的风险。