Claude Code 调用 Codex:一次性子进程与常驻 App Server
type
status
date
slug
summary
tags
category
icon
password
wechat_gate
“Claude Code 调用 Codex”听起来像一个功能,实际至少包含两种完全不同的进程模型。
第一种通过
codex exec 启动一次性子进程,投喂一段明确指令,等待文件或结构化结果生成后退出。第二种通过 codex app-server 建立常驻运行时,使用 JSON-RPC 管理 thread、turn、review 和 interrupt,适合需要状态延续的协作任务。两者都能让 Claude Code 借用 Codex,但它们在启动成本、通信协议、权限控制、错误恢复和适用场景上差异很大。理解这些差异,才能避免在简单任务上过度设计,也能避免在复杂任务里使用无法续跑的一次性调用。
先给结论:不是两种命令,而是两种架构
维度 | codex exec 一次性子进程 | codex app-server 常驻服务 |
代表实现 | baoyu codex-imagegen 后端 | OpenAI Codex Plugin for Claude Code |
进程形态 | 每次启动,任务结束后退出 | 服务进程常驻,会话内复用 |
通信方式 | 启动参数、stdin、JSONL 事件流 | JSON-RPC 请求与通知 |
状态模型 | 单次运行,默认不依赖前一轮 | thread 包含多个 turn,可 resume |
权限姿态 | 示例实现使用 danger-full-access | review 固定只读,task 可切换 workspace-write |
典型任务 | 出图、生成文件、单次确定性操作 | 代码审查、长任务委派、多轮续跑 |
主要风险 | 全权限子进程、每次冷启动 | 协议和生命周期更复杂 |
一句话判断:
- 只需要“跑一次,拿到一个确定产物”,优先考虑
codex exec。
- 需要“持续协作、保留上下文、随时取消或恢复”,优先考虑
codex app-server。
核验边界:版本信息必须分清
这次整理最先暴露的问题不是架构,而是版本口径。我原本沿用了原稿中的“本机当前版本”表述,继续检查安装记录后,才确认 marketplace 源码与当前激活插件不是同一快照。
本机命令与插件记录显示:
- Codex CLI 为
0.140.0。
- OpenAI Codex Plugin for Claude Code 为
1.0.4,commit 为807e03a。
- baoyu-skills 的 marketplace 源码快照为
2.5.1,commit 为441ca30。
- 但 Claude Code 的已安装插件记录中,baoyu-skills 仍指向较早的
1.111.1快照。
因此,本文关于
baoyu-codex-imagegen 的代码分析,准确表述应当是:基于本机 marketplace 中的 baoyu-skills v2.5.1 源码快照,而不是声称当前激活插件已经升级到 v2.5.1。
这是一个很容易忽略的细节。插件市场源码、缓存快照和当前激活版本可能不是同一个提交。只看目录名或 changelog,很容易把“我读到的源码版本”误写成“系统正在运行的版本”。

路径一:codex exec,把 Codex 当一次性算子
它解决什么问题
baoyu-skills 的
baoyu-codex-imagegen 用途很明确:让 Claude Code 等非 Codex 宿主调用 Codex CLI 内置的 image_gen 工具,并把图片保存到指定路径。这类任务有几个共同点:
- 输入边界清晰,通常是一段 prompt、图片比例和输出路径。
- 结果边界清晰,通常是一个文件和一行结构化状态。
- 不需要多轮讨论,也不需要恢复上一轮上下文。
因此,它没有引入常驻服务,而是直接启动:
如果存在参考图,还会追加一个或多个
--image 参数。每个参数为什么存在
exec用于非交互、脚本化运行。OpenAI 的 CLI 文档将它定位为适合自动化和 CI 的执行方式,运行完成后返回结果。
-json
把过程输出改为逐行 JSON 事件,也就是 JSONL。上游程序不需要解析终端展示文本,可以直接读取 thread、tool call、usage 和最终消息等结构化事件。
-sandbox danger-full-access
这个实现要求 Codex 从默认生成目录复制图片到调用方指定的任意目标路径,因此选择了完全文件权限。
这不是一个通用最佳实践。OpenAI 文档明确建议,自动化场景优先使用
workspace-write,除非运行环境本身已经隔离,否则应避免不必要的全权限。-skip-git-repo-check
图片任务可能从临时目录或插件目录启动,而不是可信 Git 仓库。这个参数允许 Codex 在非 Git 目录运行。
末尾的
-表示从 stdin 读取指令。封装层通过
child.stdin.write(instruction) 写入任务契约,再关闭 stdin。真正重要的是任务契约
这条链路没有直接透传用户 prompt,而是包了一层严格指令,大意如下:
这是一种典型的“把子 Agent 当算子”设计:
- 输入结构固定。
- 允许使用的工具固定。
- 文件副作用固定。
- 最终输出格式固定。
- 禁止项明确。
对于自动化链路,约束比语言表达更重要。上游需要的是可验证结果,不是一次开放式对话。
不相信自报结果:三级验证
这套实现最值得保留的工程细节,是它不会因为 Codex 回复“成功”就判定成功。
它会依次检查:
- JSONL 事件里是否存在 thread ID。
$CODEX_HOME/generated_images/{threadId}/是否真的出现图片。
- 如果目录检查失败,tool call 中是否存在从生成目录复制到目标路径的
cp或mv。
- 目标文件是否真实存在,并且字节数大于零。
失败会转换为结构化错误,例如:
agent_refused
no_image_gen_tool_use
timeout
codex_not_installed
spawn_failed
这里的核心不是图片,而是一个通用原则:
Agent 的自然语言回复只能算声明,文件、事件和可重复检查才算证据。
适用场景与局限
适合:
- 单次出图或文件生成。
- 边界清晰的代码转换。
- 一次性分析并返回结构化 JSON。
- 不要求继承上下文的自动化任务。
局限:
- 每次运行都有进程和模型冷启动成本。
- 默认没有跨运行状态。
- 如果使用
danger-full-access,信任边界非常宽。
- 超时、取消和恢复通常由封装层自行实现。
路径二:codex app-server,把 Codex 当有状态服务
OpenAI Codex Plugin for Claude Code 没有为每个命令重新运行
codex exec。它启动 codex app-server,通过 JSON-RPC 管理持续会话。OpenAI 官方文档把 App Server 的核心抽象定义为三层:
- Thread:一段可持续的对话。
- Turn:Thread 中的一轮用户输入与 Agent 执行。
- Item:Turn 中的消息、推理、命令、文件修改等事件。

直连和 Broker
插件实现了两种连接方式。
第一种是直连:
客户端直接启动
codex app-server,通过标准输入输出发送逐行 JSON-RPC 消息。第二种是 Broker:
插件通过
CODEX_COMPANION_APP_SERVER_ENDPOINT 保存 broker endpoint,让同一个 Claude Code 会话里的 review、rescue 和状态命令共享 Codex 运行时。如果 broker 返回忙错误
-32001,或者连接出现 ENOENT、ECONNREFUSED,插件会放弃 broker,并直接启动一个 App Server 重试。这比一次性子进程多了一层复杂度,但换来了:
- 会话内运行时复用。
- thread 持久化。
- 后台任务管理。
- 取消和恢复能力。
- review 与 task 的权限隔离。
连接建立:先 initialize
App Server 连接建立后,客户端必须先发送
initialize,然后发送 initialized 通知。插件传入的客户端身份是:
它还通过
optOutNotificationMethods 退订部分逐字增量事件,只保留对上游更有价值的结构化通知,减少噪音。会话模型:thread 与 turn
插件使用的关键 RPC 方法包括:
方法 | 作用 |
thread/start | 创建新线程 |
thread/name/set | 设置线程名称 |
thread/resume | 恢复已有线程 |
thread/list | 查询历史线程 |
turn/start | 在线程中启动一轮任务 |
review/start | 启动代码审查 |
turn/interrupt | 中断正在执行的 turn |
这意味着 App Server 不是“把 prompt 送进去等结果”的单轮封装,而是一个可管理的会话运行时。
review 与 task 的权限不同
插件把两个动作分得很清楚。
review:
- 固定使用
read-only。
- 使用临时 thread。
- 通过
review/start执行。
- 只返回审查结论,不修改代码。
task:
- 默认可以只读。
- 传入
-write后切换到workspace-write。
- 可保存 thread。
- 可通过
-resume或-resume-last延续上一次任务。
这比“所有任务都全权限运行”更接近工程系统应有的默认姿态:先按任务性质设定最小权限,再决定是否扩大写入范围。
Hooks 把 Codex 接入 Claude Code 生命周期
插件注册了三类 Claude Code Hook:
SessionStart:准备共享运行时。
SessionEnd:清理 broker 和会话资源。
Stop:可选的停止门审查。
启用 review gate 后,Claude Code 每次准备停止时,都可以让 Codex 检查上一轮是否存在需要阻断的问题。
这类机制的价值不是“多一个模型”,而是把第二模型放进交付流程:
但它也有成本。官方插件 README 明确提醒,review gate 可能形成长时间 Claude/Codex 循环,并快速消耗使用额度,因此不应无条件开启。
怎么选择
选择 codex exec 的条件
同时满足以下大部分条件时,使用一次性子进程更简单:
- 任务只有一轮。
- 结果可以用文件或 JSON 验证。
- 不需要恢复历史上下文。
- 冷启动成本可以接受。
- 调用方能够独立处理超时和重试。
典型例子:
- 生成一张图片。
- 把一份输入转换成固定格式。
- 对一个文件执行单次分析。
- 在 CI 中运行一次检查。
选择 codex app-server 的条件
出现以下需求时,常驻服务更合适:
- 需要多轮会话。
- 需要 thread 恢复。
- 需要后台运行和状态查询。
- 需要中断正在运行的任务。
- 需要区分 review 与 write 权限。
- 需要接入 Claude Code 的会话生命周期。
典型例子:
- 对一个分支持续审查。
- 委派长时间排查任务。
- 让 Codex 修改代码后继续补测。
- 在停止前自动进行第二模型门禁检查。
真实核验过程
这篇发布版没有只依赖原稿描述,而是重新做了一次最小核验。
我实际执行的步骤包括:
- 读取原稿,列出所有涉及版本、命令、RPC 方法和权限的事实性陈述。
- 运行
codex --version、codex exec --help和codex app-server --help,确认当前 CLI 的命令与参数。
- 检查 OpenAI 插件 manifest、安装记录、
app-server.mjs、codex.mjs和 Hook 配置。
- 检查 baoyu marketplace 源码中的
spawn.ts、main.ts、版本文件和 Git commit。
- 对照 OpenAI Codex CLI、App Server、Codex Plugin 和 Claude Code Hooks 官方文档。
- 将“当前激活版本”和“实际阅读的源码快照”分开记录。
这次遇到的错误与教训
我最初把原稿中的 baoyu-skills v2.5.1 当成“本机当前版本”。继续检查后发现,本机确实存在 v2.5.1 marketplace 源码,但 Claude Code 的已安装插件记录仍指向较早的快照。
如果不检查安装记录,这个表述看起来合理,却不够准确。
这次修订得到的教训是:
分析本地插件时,至少要同时记录 marketplace HEAD、安装缓存路径、插件 manifest 和 commit。任何一个都不能单独代表“当前正在运行的版本”。
使用建议
一次性任务:把输出契约写死
不要只写“帮我生成图片”或“帮我检查代码”。自动化 prompt 至少应包含:
这样做可以降低 Agent 自由发挥带来的不确定性,也方便上游判断成功或失败。
长任务:续跑只发送增量
恢复 thread 时,只发送新的变化:
没有必要重新复制整段背景。重复上下文既增加噪音,也可能让模型误判任务边界。
审查任务:结论必须绑定证据
无论使用标准 review 还是对抗式 review,都应要求每个问题对应:
- 实际查看过的文件或 diff。
- 可复现的失败路径。
- 明确的风险级别。
- 事实、推断和待确认项的区分。
没有证据的“可能有问题”,很难进入工程决策。
权限:默认从最小范围开始
选择顺序应当是:
只有任务确实需要更大文件范围,并且运行环境可信时,才扩大权限。
总结
Claude Code 调用 Codex,不是一个统一的调用方式。
codex exec 的本质是一次性、无状态、容易封装的子进程。它适合边界清晰、结果可验证的单次任务。codex app-server 的本质是有状态、可恢复、可管理的 Agent 服务。它适合代码审查、任务委派和需要持续协作的复杂工作。真正的选型标准不是“哪个更高级”,而是:
- 任务是否需要状态。
- 结果是否能一次性验证。
- 是否需要中断、恢复和后台管理。
- 权限是否可以按动作分级。
- 额外协议复杂度是否值得。
简单任务用简单进程,持续协作用有状态服务。把这条边界划清,系统会更容易理解,也更容易维护。
参考资料
相关阅读
以下作为可延伸阅读方向:
- 如何为 AI Agent 设计可验证的输出契约
- Claude Code Hook 的工程化使用方式
- 多 Agent 代码审查中的权限与责任边界
2026.06.18 20:34
沪 · 赵巷
📌 声明:本文由 AI 辅助完成