把 `codex exec --json` 接进自己的流水线:命令怎么写、产出怎么接、怎么验收
把 Codex(OpenAI Codex)接进流水线,和坐在终端里跟它对话,是两件事。交互模式下你能看着它一步步来,随时按住不放;一旦塞进脚本,你只剩两样东西:它往 stdout 吐了什么、退出后留下了什么文件。这篇就只讲这两样。
Codex CLI 里负责非交互执行的是 codex exec(别名 e),官方对它的一句话说明是 “Run Codex non-interactively”。它有两个专门为「被程序调用」准备的选项:
--json:事件以 JSONL 打到 stdout-o, --output-last-message <FILE>:把 agent 的最后一条消息写到文件
一个负责过程,一个负责结论。流水线接哪个,取决于你下一步是要做统计还是要拿答案。
一、命令怎么写
先给一条可以直接复制的完整命令。Git Bash / macOS / Linux 下:
git diff --cached | codex -s read-only -a never exec \
--json \
--color never \
-o last-message.md \
"只阅读这段 diff,指出可能引入回归的地方,不要修改任何文件" \
> events.jsonl 2> codex.err
逐个说为什么它们在这。
-s read-only:沙箱模式。--sandbox 的三个合法取值是 read-only / workspace-write / danger-full-access。流水线里第一版一律先用 read-only——让它只读不写,跑通了再考虑放开。
-a never:审批策略。never 的官方释义是「从不询问,执行失败直接回传给模型」。这个名字非常容易理解反:never 不等于把权限放开,它只是不再向人弹窗;沙箱该拦的照样拦,被拦下来的失败会回传给模型,让它自己换个办法。脚本里不设它的后果是可能卡在一个没人回答的审批上。如果你需要的是「有人审批但不必是我」,官方另有 --approve-for-me(审批请求走自动复核,使用 workspace-write 沙箱)。
-s 和 -a 是顶层选项,写在子命令 exec 前面。用法行本身就是 codex [OPTIONS] <COMMAND> [ARGS];在 codex-cli 0.147.0(Windows 11)上本机实测的两条命令,-c 和 --strict-config 也都放在 doctor、exec 之前。照这个位置写,不会有歧义。
--json:事件以 JSONL 打到 stdout,这是整条流水线的输入源。
--color never:--color 的取值是 always / never / auto,默认 auto。默认值在 CI 上的行为取决于是否检测到终端,与其赌它,不如显式写死 never,免得 ANSI 转义序列混进你要解析的内容里。
-o last-message.md:把最后一条消息单独落一个文件。哪怕你要解析 JSONL,也建议同时开着它——出问题时这个文件是最省事的排查入口。
管道那一段是 codex exec 的一个很实用的行为:位置参数 [PROMPT] 不给参数或给 - 时从 stdin 读;stdin 是管道且同时给了 prompt,则 stdin 会作为 <stdin> 块追加。也就是说上面那条命令里,diff 和你的指令都能进去,不用自己拼字符串。
Windows PowerShell 下续行符不是 \ 而是反引号:
git diff --cached | codex -s read-only -a never exec `
--json `
--color never `
-o last-message.md `
"只阅读这段 diff,指出可能引入回归的地方,不要修改任何文件" `
> events.jsonl 2> codex.err
另外几个按场景加的选项:
--skip-git-repo-check:允许在非 Git 仓库里运行。构建机上在临时目录处理一堆散文件时需要它,仓库内不必加。--ephemeral:不把会话文件落盘。构建机跑完即弃时值得加,理由见下面第三节的磁盘那段。--ignore-user-config:不加载$CODEX_HOME/config.toml。想让流水线结果不受某台机器上个人配置的影响,这是最干净的办法。注意它有个边界:auth 仍然使用CODEX_HOME,所以它隔离的是配置不是登录。--output-schema <FILE>:一个 JSON Schema 文件路径,描述模型最终回复的结构。下一步是程序读而不是人读时,用它来约束结构,比在 prompt 里恳求「请只输出 JSON」靠谱。
如果你不想用 --ignore-user-config 把配置全掐掉,也可以用 -p, --profile <CONFIG_PROFILE_V2>,把 $CODEX_HOME/<name>.config.toml 叠加到基础配置之上,给流水线单独留一层。配置侧和非交互执行直接相关的键有这么两个:
hide_agent_reasoning = true
history.persistence = "none"
hide_agent_reasoning 官方说明是「在 TUI 与 codex exec 输出里抑制推理内容」;history.persistence 取 save-all 或 none。以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。
二、产出物长什么样
先说清楚边界:本文不会给你事件的字段名清单。 我们只跑过只读命令,一次模型对话请求都没发过,任何字段名都得靠猜,而猜出来的字段名会让你的解析脚本在第一次真跑时就崩。有依据的部分只有这些:
--json的产出是 JSONL 打在 stdout:一行一个 JSON 对象,行与行之间不是一个大数组,所以别拿整文件去JSON.parse,要逐行解析。JSONL 的好处是可以边流边处理,也可以直接tail -f看着。-o的产出是 agent 的最后一条消息,一个文件、一段文本。- 上面命令里我把 stderr 单独重定向到了
codex.err。这不是官方规定的分工,是个防御性写法:万一有非 JSON 的内容出现在错误流上,它不会污染你要逐行解析的那个文件。
字段名怎么办?自己采一份样本,别抄别人的。在你自己的机器和版本上跑一次最小任务,把 events.jsonl 留下来,先统计一遍出现过哪些事件类型和键,再照着写解析器,并且对没见过的行跳过而不是报错。理由在第三节最后一段:版本会自己往前走,字段没理由保证永远不变。
接下来一步通常是三选一:把 JSONL 存档做审计、从中提取需要的行喂给下一个程序、或者干脆只用 -o 那个文件当结论。第三种最省事,也最不容易被格式变化绊倒。
三、怎么验收
上线前按顺序检查这几处,顺序是有讲究的——排在前面的坏了,后面的检查全是白费。
第一处:配置到底加载成功了没有。 这是本机实测得出的一手结论。在 codex-cli 0.147.0(Windows 11)上,故意用 -c 传一段语法不合法的 TOML(codex -c 'features=[unclosed' doctor --summary),命令没有崩溃退出,doctor 照常跑完,只是在结果里多出一行:
✗ config config could not be loaded - Fix the reported config error, then rerun codex doctor.
对流水线的意义很直接:配置坏了不一定会让你的构建变红,它可能只是悄悄按默认值跑了。所以「改完配置没生效」的第一步永远是跑 codex doctor 看这一行,而不是去怀疑模型。
第二处:别指望 --strict-config 兜住所有拼写错误。 --strict-config 的作用是 config.toml 里出现本版本不认识的字段时直接报错退出。但本机实测 codex -c model_reasoning_effortt=high --strict-config exec --help(键名故意多打一个 t)正常打印了 help,没有报未知字段错误。说明校验发生在真正加载配置去跑会话的时候,--help 这类不进入会话的路径不触发。所以拿 --help 当冒烟测试是测不出配置拼写错的。
第三处:-c 的值有没有被 shell 吃掉。 -c, --config <key=value> 用点号路径表示嵌套,value 按 TOML 解析,解析失败则按字面字符串处理——注意是「按字面字符串处理」而不是报错。官方给的三个例子是 -c model="o3"、-c 'sandbox_permissions=["disk-full-read-access"]'、-c shell_environment_policy.inherit=all。带引号和方括号的那种,在 PowerShell 和 Git Bash 里的引号剥离规则不一样,很容易变成一个你没预期的字符串却毫无提示。把整个 key=value 用单引号裹住是比较稳的写法,改完再用第一处的 doctor 复核一遍。
第四处:枚举值拼错会不会被拦。 这个会。本机执行 codex -s bogus-mode 的报错是:
error: invalid value 'bogus-mode' for '--sandbox <SANDBOX_MODE>'
[possible values: read-only, workspace-write, danger-full-access]
参数级的取值校验是硬拦的,这类错在第一次跑就会暴露,不用担心它悄悄溜过去。
第五处:环境变量会不会被带进去。 这条特别容易翻车。shell_environment_policy.ignore_default_excludes 官方默认 true,含义是保留(不是排除)含 KEY、SECRET、TOKEN 的变量——名字读起来正好是反的。构建机的环境里往往塞满了各种密钥,配 shell_environment_policy.inherit(all / core / none)和这一项之前,先想清楚你希望哪些变量进得去。
第六处:磁盘。 在 codex-cli 0.147.0(Windows 11)本机上,~/.codex/ 下的 SQLite 日志库单个文件就有 763 MB,codex doctor 的 Notes 区也提示 rollouts 占 3.07 GB。这是天天用积累出来的,但长期跑流水线的构建机同样会积。跑完即弃的场景加 --ephemeral(不把会话文件落盘),长期存活的构建机就把 ~/.codex/ 纳入磁盘监控。
第七处:版本。 同一台机器上本次采集开头执行 codex --version 得到 codex-cli 0.131.0,十几分钟后再执行同一命令得到 codex-cli 0.147.0,全程只有一个可执行文件。Codex 具备自更新能力,所以「我上周验过没问题」在这里不成立。流水线里把 codex --version 的输出打进日志,出问题时第一件事是看当次的实时版本号,而不是凭记忆。
最容易出错的一步,实测下来是第一处和第三处的组合:-c 被 shell 吃掉 → 配置没加载 → 一切照默认值跑 → 表面上什么都没坏。
四、什么情况不适用
要它改文件、而你没打算人工看 diff 的时候。 上面那条命令是 -s read-only 的。一旦你为了让它真的动手改成 workspace-write,就必须有人看改动。Codex CLI 提供了 apply(别名 a)把最新的 diff 以 git apply 应用到工作树,这个”生成”和”落地”分离的设计,本身就是留给人做检查的。
没有回归判据的任务。 流水线的价值在于失败能被自动识别。如果这个任务的对错只有人读了才知道,那它接进 CI 也只是多产出一堆没人看的日志。
依赖字段名做硬解析、又不做容错的场景。 前面说过,我们没有任何真实事件流的字段依据;你自己采到的样本也只对你当次的版本负责。解析器必须对陌生行宽容。
追求”绝对不会出问题”的场景。 沙箱、审批策略这些机制是降低风险的,不是消除风险的。-a never 只是不问你,-s read-only 也不该被理解成保险箱。真的怕出事,就把它放进你已有的、与 Codex 无关的隔离环境里跑。
最后补一句和流水线关系不大但值得知道的:codex doctor --json 的官方说明是 “Emit a redacted machine-readable report”,是脱敏的。构建机上诊断环境出了问题要贴给别人看时,这个比手抄配置安全。同理,codex mcp list 的 Env 列里环境变量值会被打成 *****,只显示键名。
相关阅读
- 把回复变成脚本能解析的 JSON:
codex exec --output-schema实战 - 在 CI 里跑
codex exec:非仓库、免配置与超时这三件事 - 给 Codex CLI 装上 shell 补全:一份不靠猜的落地清单
- Codex 的六个使用面:一张图看懂该用哪个
本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Configuration Reference》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。