Codex 只读工具开始并行
Codex 0.163.0 允许独立只读工具并行执行,复杂会话的读取阶段不必再被单个查询串行拖住。
正文
这次变化是调度器放开读取锁
Codex 0.163.0 把一批不会改变状态的查询从独占调度锁中释放出来,技能、记忆、会话历史、插件、Agent 列表、当前时间和剩余上下文等读取可以并行执行。对开发者来说,关键变化不是“所有工具都变快了”,而是读取阶段的并发边界变得更细:独立的查找可以同时开始,而创建目标、更新目标等仍保持串行。升级后,依赖多个上下文来源的代理可以先批量读取,再把结果合并进同一轮决策,减少因为一个慢查询而让其他无依赖查询排队的情况。
这项能力来自 Codex 仓库的 0.163.0 发布记录和对应的合并请求。合并请求明确列出了可并行的读取范围,同时说明目标创建与更新仍然串行。这个边界很重要:它是 Codex 内部工具调度策略的变化,不是把你的业务 API 自动变成并发,也不是承诺所有 MCP 工具都可以安全并行。
如果你在 平台目录 中核对不同模型入口,或通过 API 网关连接 Codex,仍然需要单独检查服务端是否支持并发请求、是否有共享状态,以及是否会把并行读取误判成重复操作。Codex 的调度器只知道工具被声明为读取用途,不能替你证明外部服务没有副作用。
为什么读取也会被串行拖住
在旧的执行路径里,没有选择并行执行的工具会取得独占调度锁。一个读取工具只要占住这把锁,其他本来互不依赖的读取就要等它结束。例如,一个任务需要同时读取技能清单、检索历史会话和检查当前上下文,如果这些调用都走串行路径,那么最慢的读取会把后续查询全部挡住。这里的浪费并不来自模型推理,而来自工具调度器把“读取”当作了需要互斥的通用操作。
0.163.0 的改动把这类只读调用标记为可并行,让调度器可以同时运行独立查询。官方变更涉及技能列表和读取、记忆列表与读取和搜索、会话历史搜索与读取、插件列表、Agent 列表、当前时间以及 get_goal。这些名称说明它面向的是 Codex 自己的工作上下文,不等于任意 shell 命令、文件写入、网络请求都自动获得并行资格。
调度并行还需要一个前提:调用之间没有先后依赖。如果第二个查询必须使用第一个查询返回的 ID、游标或权限结果,就不能仅因为它们都是读取而同时发出。并行优化解决的是“没有依赖却排队”的问题,不会把有依赖的流程变成无依赖流程。工程上应先画出调用图,再决定哪些节点可以成组执行。
另一个容易混淆的点是“模型并行生成”和“运行时并行执行”不是一回事。模型可以在一轮中提出多个工具调用,宿主也可以选择按顺序执行它们;或者模型只提出一个调用,宿主内部仍可能为这个调用展开额外读取。Codex 0.163.0 的发布说明谈的是工具执行调度,不是修改模型的输出协议,也不是改变服务端对 parallel_tool_calls 的解释。
适合并行的读取与不该并行的动作
可以并行的第一类是互相独立的目录查询。比如代理需要知道可用技能、插件和 Agent 列表,它们各自返回自己的集合,结果之间没有引用关系。把它们同时读取后,再在本地合并,可以把等待时间集中到最长的那个查询,而不是把每个耗时相加。
第二类是不同来源的上下文检索。会话历史搜索、记忆搜索和当前上下文读取常常服务于同一个判断,但检索本身不一定互相依赖。先并行拿到候选结果,再在排序、去重和权限过滤阶段串行合并,通常比先搜历史、再搜记忆、最后读上下文更容易观察,也更容易为每个来源记录耗时和错误。
第三类是纯观测操作,例如读取当前时间或剩余上下文。它们不应该写入状态,也不应该改变会话权限。把这些结果作为同一轮决策的输入时,可以与其他只读查询一起发出,但要在合并层明确时间戳和采样时刻,避免把不同时间点的状态误当成一个原子快照。
不应直接并行的是创建和更新目标的操作。目标创建会产生新的状态,目标更新会改变已有状态;如果两个更新同时提交,最终状态可能取决于到达顺序。即便 API 本身返回成功,也不能据此推断两个写入形成了事务。发布、删除、授权、提交文件、修改配置、发送消息等动作都应继续沿用串行或显式锁,并为重试设计幂等键。
还要谨慎看待“名义只读、实际有副作用”的工具。某些查询会刷新缓存、延长租约、写审计日志、消耗一次性游标或触发后台任务。它们在名称上像读取,实际却改变了外部状态。只有确认工具契约没有这些行为,才适合把它放进并行读取组。不能只根据函数名中的 get、list 或 search 做判断。
迁移时先看版本和调用边界
第一步是确认实际运行的 Codex 版本,而不是只看安装脚本或容器标签。官方发布记录对应的是 rust-v0.163.0,发布日期为 2026 年 10 月 15 日。团队如果通过锁定的二进制、镜像或内部包装脚本分发 Codex,应在运行时输出版本,并把并行读取能力作为版本门槛记录下来。否则,部分机器升级、部分机器未升级时,问题会表现成偶发的等待差异。
第二步是把读取任务分成“可同时开始”和“必须等待前置结果”两组。可以先列一张简单表:工具名称、是否改变状态、是否依赖其他工具输出、是否访问共享游标、失败后能否安全重试。只有同时满足不改变状态、无前置依赖、不会消耗一次性资源、失败可安全重试的读取,才放进同一并行批次。剩余调用保持原顺序,优先保证语义正确。
第三步是为并行批次增加观测。至少记录批次 ID、工具名、开始时间、结束时间、输入摘要、结果大小和错误类型。并行执行后,日志顺序不再等于业务顺序,因此不能用日志行的先后直接重建因果链。需要用调用 ID 和依赖关系把事件串起来;如果结果要合并,还应记录合并阶段使用的规则。
第四步是测试失败与取消。一个只读调用失败时,其他独立读取是否仍应返回,取决于任务是否要求“全部上下文齐全”才能决策。可以把错误分为可忽略、可降级和必须终止三类,不要让并行 Promise 的任意一个异常默认决定全部任务的语义。用户中断时,还要确认尚未开始的调用不会继续启动,已经开始的调用能否收到取消信号,以及外部请求是否真正释放连接。
如果你通过网关接入多个模型,建议把 Codex 客户端的并行读取与网关层的限流分开配置。网关可以对同一会话设置读取并发上限,对写操作设置更严格的互斥策略,并保留每个工具的权限判断。模型路由、API 认证和工具调度是三个不同层次:路由决定请求送到哪里,认证决定能不能调用,调度决定哪些调用可以同时进行。把这三者混成一个“并发开关”,出了问题很难定位。
如何验证并行真的发生了
不要只用“总耗时变短”作为证据,因为网络波动、缓存命中和服务端排队都会影响总耗时。可以准备两个没有副作用的测试工具:它们各自等待一个可控的延迟,然后返回工具名和时间戳。测试时让同一轮任务同时请求两个工具,记录两个工具的启动事件。如果第二个工具在第一个工具结束前已经启动,才能确认调度器允许并行;如果只是最终结果更快,却没有事件级记录,结论仍不完整。
然后测试一个有先后依赖的读取流程。第一个工具返回资源版本号,第二个工具必须拿到这个版本号才能继续。即使两者都属于读取,也不能把第二个调用提前到版本号产生之前。这个测试能防止团队把“只读”误解成“无依赖”,也能发现合并层是否把过期结果混进最终上下文。
最后测试取消和重试。并行批次启动后立刻取消,检查未开始的调用是否保持未启动;让其中一个调用返回可重试错误,检查是否会重复执行另一个已经成功的调用;让一个查询产生很大的结果,检查合并层是否仍能限制上下文大小。只读不代表没有资源消耗,历史检索、记忆读取和插件列表都可能把大量内容送入后续提示。
这套验证也适用于把其他 Agent 工具接入 Codex 的场景。你可以先在 AI SDK 工具调用并发文章 中区分模型生成并发与 SDK 执行并发,再回到 Codex 的具体工具边界做验收。若问题出现在流式传输或取消后状态不一致,还应参考 流式输出排查,把网络流生命周期和工具生命周期分开记录。
结论:并行读取是局部优化,不是全局放行
Codex 0.163.0 的价值在于把“独立只读查询可以同时进行”落实到工具调度器,而不是给所有工具加一个粗粒度的并发开关。它适合减少上下文收集阶段的无谓排队,也让工具契约中关于只读、写入和依赖关系的边界更值得被明确记录。开发者升级时,最稳妥的做法不是把所有调用都改成并行,而是从无副作用、无依赖、可安全重试的读取开始,逐批验证取消、错误和结果合并语义,再把写操作继续留在显式串行边界内。
参考来源
以下来源均于 2026 年 10 月 16 日打开并核对。版本号、并行范围和串行边界以官方仓库发布记录与合并请求为准。
- Codex 0.163.0 发布记录 —— 确认版本、发布日期、并行只读工具和保留的行为边界。
- Codex 合并请求:Enable parallel execution for read-only tools —— 确认调度器从独占锁中放开哪些读取,以及目标创建与更新仍需串行。
- Codex 0.163.0 完整变更记录 —— 核对该版本的完整提交范围与版本上下文。
- OpenAI Codex 提示编写指南 —— 说明 Codex 中并行工具调用与读取操作的工作方式,帮助区分模型调用和工具执行。
- OpenAI 工具调用文档 —— 作为 OpenAI 工具配置的背景资料,不将 API 层并行选项等同于 Codex 内部调度。