把 `codex exec --json` 接进自己的流水线:命令怎么写、产出怎么接、怎么验收

2026-08-09

把 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 也都放在 doctorexec 之前。照这个位置写,不会有歧义。

--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.persistencesave-allnone。以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。

二、产出物长什么样

先说清楚边界:本文不会给你事件的字段名清单。 我们只跑过只读命令,一次模型对话请求都没发过,任何字段名都得靠猜,而猜出来的字段名会让你的解析脚本在第一次真跑时就崩。有依据的部分只有这些:

  • --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.inheritall / 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 listEnv 列里环境变量值会被打成 *****,只显示键名。

相关阅读


本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Configuration Reference》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。