跳到主要内容

内容阅读

并行工具调用:让 Codex 同时收集证据,而不是同时制造混乱

AI 编程阿新聊ai

并行工具调用:让 Codex 同时收集证据,而不是同时制造混乱

TL;DR

并行工具调用的价值不在“更快跑命令”,而在缩短独立信息收集的等待时间。适合并行的工作通常是只读、彼此没有写入依赖、输出可控的动作,例如同时读取多个文件、同时查询不同目录、同时查看配置和测试入口。不适合并行的动作包括写同一批文件、安装依赖、迁移数据库、修改 git 状态、启动多个会抢端口的服务。技术负责人应该把并行看成调度策略:先拆出互不依赖的证据收集,再串行执行会改变环境的步骤。

读者定位

本文面向用 Codex 处理中大型代码库的开发者、平台工程师和技术负责人。你可能已经看到 Codex 在一次回复里触发多个工具,也可能在提示词里要求它“并行检查”。本文关心的不是模型底层如何调度,而是工程上如何定义安全的并行边界,怎样让并行工具调用提升诊断质量,怎样避免并行写入、并行审批和并行日志把任务推向不可复现状态。

问题:速度提升和状态复杂度同时到来

软件任务里有大量等待时间。读 8 个文件、查 5 个目录、看 3 个配置、统计 2 个日志,如果逐步执行,模型会在每一步后重新规划,整体变慢。并行工具调用能把这些独立读取合并成一轮,尤其适合刚进入陌生仓库时建立地图。

问题在于,工具调用不是纯函数。shell 可能写文件,包管理器会改变 lockfile,测试会生成缓存,开发服务器会占端口,git 命令会改变索引,云端和连接器工具可能修改 issue、PR、邮件或部署状态。并行执行这些动作,失败不是简单的“某一步报错”,而是环境被多个动作同时改变,模型难以给出可靠因果链。

Codex 官方文档强调沙箱、审批、permission profile、rules 和 hooks,原因就在这里。agent 工作不是脚本批处理,它会根据工具输出继续决策。如果并行阶段收集到的是互相污染的输出,后续推理会建立在错误证据上。并行策略应该首先回答一个问题:这些调用是否可以交换顺序,并且无论谁先结束都不改变另一个调用的语义。

心智模型:并行只用于“无共享写状态”的证据收集

可以用三类任务判断是否适合并行:

只读观察:适合并行
独立验证:谨慎并行
改变状态:默认串行

只读观察包括读取文件、列目录、搜索关键词、查看 git status --short、查看配置片段、统计字符数。这些动作不应该改变仓库,输出之间也没有顺序依赖。它们是并行工具调用的主战场。

独立验证看似只读,但常有副作用。例如测试会写缓存,构建会生成产物,lint 可能修复文件,开发服务器会占端口,npm install 会改 lockfile。只有当你确认命令不会写共享路径、不会抢同一资源、输出可分离时,才考虑并行。多数团队应该先串行跑一个验证命令,把失败收敛后再做全量并行验证。

改变状态的动作默认串行。编辑文件、移动文件、删除文件、写数据库、发布包、推送分支、创建 PR、触发部署、修改 issue 状态,都需要清晰的前后关系。并行写入让审查难度上升,也会破坏“看到失败就回滚某一步”的能力。

详细机制:并行调用会放大四个工程约束

输出预算会被多个结果共同消耗

并行不是免费上下文。一次并行读取 10 个长文件,返回结果会挤占同一轮对话预算。模型可能只看到每个文件的开头,反而错过关键函数。并行前应限制每个调用的输出:用 Get-Content -TotalCountsed -nrg -ngit diff --statSelect-Object -First 这类方式拿概要,必要时二次读取具体行号。

并行读取的好单位不是“文件越多越好”,而是“每个结果都能改变下一步判断”。例如重构一个 API 时,同时读取 router、handler、schema、测试和类型定义是有意义的;同时读取整个 src/ 下所有文件没有意义。前者形成因果图,后者只是把检索负担交给模型。

沙箱和审批会在并行里暴露边界

官方 CLI 文档说明,--ask-for-approval 控制 Codex 何时为命令请求人工批准,--sandbox 控制生成命令的文件系统和网络边界。并行阶段如果混入越界命令,可能出现一部分只读命令已经完成,另一部分命令等待审批或被拒绝。模型需要能把“已获得证据”和“未执行动作”分开,不应在审批未通过时继续假设动作已经发生。

团队在提示词或 AGENTS.md 中可以明确:并行阶段只能执行只读命令;任何写入、联网、安装和 destructive 操作必须单独说明理由并等待审批。这样能减少“一个并行批次里夹带高风险命令”的情况,也让审批弹窗更容易审查。

并行会放大工作目录和路径错误

多个工具调用同时运行时,如果每个命令的工作目录不同、路径是相对的、输出没有标明来源,模型很容易混淆结果。尤其在 monorepo 里,package.jsontsconfig.jsonREADME.md 可能出现多份。并行命令应尽量使用明确路径,并在结果解释里保留路径。

一个实用规则是:并行读取文件时使用完整仓库相对路径;并行搜索时把路径范围写进命令;并行统计时输出文件名和数值,不输出无标签数字。工具结果的“可归因性”比速度更重要。

并行不会替代计划

有些人让 Codex “并行做完所有步骤”,这会把任务拆解、执行、验证和修复混在一起。更稳的做法是分两段:第一段并行收集上下文,第二段基于上下文串行修改和验证。并行适合问“当前系统是什么样”,不适合问“我要同时把系统改成什么样”。

这和人类团队协作类似。五个人可以同时阅读不同模块并汇报发现,但不能在没有接口约定时同时改同一个核心类型。Codex 的工具调用也是如此。并行调度要服务于更清楚的依赖图,而不是掩盖依赖图。

真实工作流案例:审查一个认证改动

假设你让 Codex 审查一个 PR,本次改动涉及登录、session、API middleware 和测试。低质量提示是:

检查这个认证改动有没有问题,能并行就并行。

模型可能同时跑测试、看 diff、读配置、查日志,输出混乱。更好的任务拆解是:

第一轮只做只读并行收集:
1. 查看 git diff --stat 和认证相关 diff。
2. 读取 auth middleware、session store、login handler、相关测试。
3. 搜索新增环境变量和 cookie 配置。
不要运行会写缓存或安装依赖的命令。收集完再给出风险列表和下一步验证计划。

在 Windows PowerShell 中,并行读取的单个命令可以保持短输出:

Get-Content -LiteralPath src\middleware\auth.ts -TotalCount 220
Get-Content -LiteralPath src\session\store.ts -TotalCount 220
Select-String -Path src\**\*.ts -Pattern "SESSION_COOKIE|sameSite|httpOnly" | Select-Object -First 80
git diff --stat

在 Unix shell 中可用同样思路:

sed -n '1,220p' src/middleware/auth.ts
sed -n '1,220p' src/session/store.ts
rg -n "SESSION_COOKIE|sameSite|httpOnly" src tests | head -80
git diff --stat

第一轮结束后,模型应该给出证据表:哪个文件定义 cookie,哪个测试覆盖过期 session,哪个 diff 改了 middleware 顺序。只有这时才进入串行验证:先跑认证相关测试,再跑更大范围测试。如果测试失败,按失败摘要继续收缩。这个流程里并行减少了等待,但没有让多个写入动作同时发生。

操作清单:如何写可并行的 Codex 任务

  • 明确第一轮是“只读上下文收集”,还是“执行修改”。只读可以并行,修改默认串行。
  • 给并行调用列出具体目标:文件路径、搜索关键词、配置名、测试入口。不要说“把相关文件都看一下”。
  • 限制每个调用输出长度。读取文件用行数窗口,搜索用 -nFirst/Head,diff 先看 --stat 或聚焦路径。
  • 并行结果必须带来源路径。无路径的片段不能作为修改依据。
  • 不要并行运行会写共享目录的命令,例如多个测试同时写 coverage、多个构建同时写 dist。
  • 不要并行启动多个会抢端口的服务。需要服务时先串行启动,再单独验证健康检查。
  • 并行批次里不要混入 npm installpip installgit push、数据库迁移、删除和移动。
  • 如果某个并行调用需要审批,等待审批结果后重新规划,不要假设它已经执行。
  • 在最终说明里区分“已并行收集的证据”和“尚未验证的假设”。

可以写入 AGENTS.md 的团队规则:

## Parallel Tool Calls

- Parallelize read-only file reads, code search, and metadata inspection.
- Do not parallelize edits, dependency installation, database changes, git index changes, pushes, deployments, or server startup.
- Keep every parallel result attributable to a path or command.
- If a command may write shared output, run it serially and explain why it is needed.

权衡与风险

并行会减少等待,却增加调度复杂度。对小任务,串行读两个文件可能比设计并行批次更省心。对大任务,并行上下文收集能明显减少来回,但前提是任务边界清楚。技术负责人不应把并行作为 KPI,而应看它是否减少了无效命令、是否提高了证据质量。

并行还会影响审计。串行命令天然有时间顺序,并行命令则需要额外记录每个调用的目的和结果。出现失败时,要能回答:哪个命令先失败,哪个命令被跳过,哪个命令可能改变了环境。对受监管项目,工具日志和审批记录需要保留这种区分。

另一个风险是模型在并行结果之间做过度关联。两个文件同时出现同名函数,不代表它们属于同一调用链;两个测试同时失败,不代表根因相同。并行收集后仍要回到依赖图:调用关系、配置加载顺序、运行时路径、数据流。速度不能替代因果分析。

常见误区

误区一:并行越多越好。并行数量超过模型能消化的范围,结果会变成噪声。每轮并行最好围绕一个问题,例如“认证链路地图”或“构建配置来源”。

误区二:只读命令一定安全。git statusrgsed通常安全,但读取敏感文件仍可能把秘密带入对话。只读也要遵守权限和脱敏规则。

误区三:测试可以随便并行。测试框架常写缓存、快照、覆盖率和临时数据库。没有隔离目录时,并行测试可能互相污染。

误区四:并行可以替代代码理解。并行只能更快拿到材料,真正的判断仍要看接口边界、数据流和失败复现。

误区五:多个 agent 等于并行工具调用。多个 agent 可能有不同工作区、不同上下文和不同审批边界;单个 Codex 会话里的并行工具调用则共享同一任务上下文。治理方式不同,不能混为一谈。

团队落地:并行前先画依赖图

并行工具调用最容易被滥用的原因,是它看起来像免费的提速按钮。实际上,成熟团队在并行前会先画一个很小的依赖图:哪些信息彼此独立,哪些信息必须等前一步结果,哪些动作会改变共享状态。这个图不需要正式建模,几行文字就够。比如“先同时读取配置、入口文件、测试和 diff;等确认调用链后再改代码;改完后串行跑相关测试”。这样并行服务于任务理解,而不是替代任务理解。

对中大型仓库,第一轮并行最有价值。陌生代码库的主要成本是定位入口。Codex 可以同时看目录结构、包配置、路由入口、测试索引和最近 diff。人类开发者也会这样做:打开多个文件,快速建立地图。只要这些动作都是只读,并且每个输出有上限,并行就能显著减少等待时间。问题从第二轮开始出现。一旦模型开始形成假设,后续读取就应该围绕假设收缩,而不是继续扩大并行面。

团队可以规定并行批次的最大语义宽度。一个批次只回答一个问题。例如“认证链路在哪里”“构建配置来自哪里”“失败测试涉及哪些文件”。不要把“读代码、跑测试、查文档、编辑文件”放进同一批次。语义宽度过大时,模型会在同一轮拿到互不相干的信息,容易把不同问题串成错误因果。

并行结果的命名也很重要。很多工具输出只显示内容,不显示命令来源。模型在总结时可能把 A 文件的配置当成 B 文件的行为。建议并行读取时明确路径,输出摘要时用“来源文件:结论”的格式。对 shell 命令,保留命令本身;对连接器工具,保留对象 ID、仓库名、issue 编号或 URL。没有来源标签的信息,不应该作为修改依据。

并行写入为什么危险

并行写入的风险不只是文件冲突。更常见的是语义冲突。两个并行动作可能都能成功写文件,但它们基于不同假设修改同一接口。比如一个调用把函数参数改成可选,另一个调用按旧签名补测试;一个调用更新 schema,另一个调用根据旧 schema 生成类型。git 最后可能没有冲突,但系统已经进入不一致状态。

并行运行验证也有类似问题。两个测试命令同时写 coverage,结果文件互相覆盖;两个构建命令同时写 dist/,失败原因变得不可复现;两个开发服务器同时抢同一个端口,后启动的报错;两个数据库测试同时使用同一个本地 schema,数据互相污染。表面上这些是“测试不稳定”,根因是并行破坏了隔离。

对 agent 来说,并行写入还会破坏叙事。模型需要知道“我改了什么,所以哪个测试结果变化”。如果两个修改并行发生,测试失败时无法判断是哪一个引起。人类开发者通常也不会在没有接口协议时同时改同一个模块。Codex 也应遵守这个原则:收集证据可以并行,产生状态变化要串行,除非已经有明确隔离边界。

多 worker 协作中的并行纪律

用户提到多个 worker 并行时,还要区分“工具并行”和“人机任务并行”。多个 Codex worker 同时在同一个仓库工作时,每个 worker 都应该有文件范围和责任边界。一个 worker 不应格式化全仓库,不应更新公共目录索引,不应删除临时文件,不应重跑会改共享缓存的命令。否则并行工具调用的风险会叠加到多 agent 协作上。

实用做法是给每个 worker 写清楚“只编辑这些文件”,并要求它在最终报告中列出触碰路径。执行命令时,也尽量按负责范围过滤。比如只统计自己负责的八篇 Markdown 字符数,不统计全仓库;只运行能验证文档格式的脚本,不运行会重写目录或生成图片的脚本。如果必须运行全仓库检查,要说明它可能受其它 worker 未完成改动影响。

在多 worker 场景里,并行读取仍然可以用,但要避免把其它 worker 的未完成改动当成稳定事实。git status 看到的大量变化可能来自别人,不是当前任务上下文。Codex 应报告“工作树已有其它改动,本任务只处理指定文件”,然后忽略无关路径。这个纪律比速度更重要,因为它保护了协作边界。

并行调用的验收标准

可以用四个标准判断一次并行调用是否合格。第一,独立性:任意两个调用交换执行顺序,结果语义不变。第二,可归因性:每个结果都能追溯到命令、路径或对象。第三,可压缩性:每个结果都有上限,模型能在一轮内读完。第四,可串接性:并行结果能自然形成下一步串行计划。

不合格的并行调用通常也很容易识别。它们包含写入命令、安装命令、发布命令、服务启动命令或数据库命令;结果里没有路径标签;输出很长;批次问题不清楚;某个调用失败后模型仍然假设它成功。团队可以在 AGENTS.md 中写明:并行只用于只读证据收集,任何改变状态的命令必须单独执行并说明目的。

一个成熟的 Codex 工作流应该像这样:并行读取建立地图,串行修改控制因果,聚焦验证定位问题,最后全量检查确认边界。并行只占第一段,不占全部流程。这样既能利用工具调度能力,又不会牺牲可复现性和审计性。

评审问题:并行是否真的降低了不确定性

并行工具调用的评审可以从一个问题开始:这轮并行结束后,不确定性是否下降?如果并行只是拿到了更多文件片段,却没有缩小问题范围,它就是噪声。合格的并行结果应该让下一步变窄:从全仓库变成三个文件,从多个可能配置源变成一个加载顺序,从模糊失败变成一个测试入口。

第二个问题是:失败结果有没有被正确处理?并行批次中某个调用失败很常见,例如路径不存在、权限不足、命令超时。模型不能把失败调用当作成功,也不能忽略它继续推理。它应该明确说哪个证据缺失,缺失是否影响结论,是否需要补一次串行读取。并行结果不是投票,缺失的关键证据可能比成功读取的十个片段更重要。

第三个问题是:并行是否跨过了任务边界。一个 worker 负责文档,不应并行读取和修改源码;一个安全审计任务,不应并行安装依赖;一个只读解释任务,不应并行生成代码。并行会让越界动作更隐蔽,因为它们夹在多个看似正常的读取动作中。评审时要逐条看调用类型,而不是只看最终结果。

第四个问题是:并行结果是否能被人类快速复查。每个结果都应该有路径、命令和简短结论。最终摘要不要写“我查看了相关文件”,而要写“读取了 A、B、C;A 定义入口,B 定义 schema,C 的测试覆盖失败路径”。这种写法能让人类 reviewer 发现遗漏,例如模型没有看迁移文件或没有看旧测试。

并行的质量不取决于数量,而取决于它是否让任务从开放搜索变成可执行计划。这个标准能过滤掉很多表面高效的工具批次。

最低通过标准

最低通过标准很简单:并行批次结束后,Codex 必须能说清每个调用为什么独立、每个结果来自哪里、下一步为什么要串行。如果这三句话说不清,就说明并行只是把等待时间换成了理解成本。合格的并行会让任务范围变小,不合格的并行会让上下文变吵。团队评审时不必问“用了几个并行调用”,应问“这些调用让哪个判断更确定”。

延伸阅读


GitHub 原文:17-parallel-tool-calls.md

评论

0
登录后可以参与评论和讨论。

还没有评论

欢迎留下第一条评论,帮助这篇内容更快形成讨论。