首页 / Codex 教程 / 非交互模式与脚本化

Codex 教程

非交互模式与脚本化

本教程共 32 篇 · 第 28 篇 · 更新于 2026-07-26 · 约 11 分钟阅读

CodexCodex 教程非交互模式codex execCI/CD脚本化自动化JSON

28. 非交互模式与脚本化

本节目标:搞懂 codex exec 非交互模式—一句话丢进去、跑完吐结果就退出,怎么接进脚本和 CI/CD,怎么拿干净结果,怎么安全地无人值守运行。

前面讲的交互式 TUI 模式有个死穴:它假设有个活人坐在屏幕前盯着。可一旦你想「每天早上自动审一遍昨天的提交」「CI 跑挂了自动提修复 PR」「把报错日志管道喂给它要根因」—这些场景里压根没活人。codex exec 就是为「没活人」这件事生的。

exec 是什么

codex exec 是 Codex 的非交互模式。不开那个全屏 TUI 界面,一句提示词丢进去、跑完吐结果就退出,专为脚本和 CI 设计。

打个比方,交互模式像打电话—你来我往、随时插话。codex exec 像发邮件—你把要办的事在一封邮件里写清楚,发出去,对方关起门把事办完,回你一封信。你没法在它办事中途插嘴改需求,所以提示词得一次性写明白。

它专治三类场景:

  • 重复到让人烦的活:每天给昨天的提交生成发布说明,写进脚本一键出
  • 没有图形界面的环境:CI 流水线、云服务器、Docker 容器里起不了 TUI
  • 要把输出喂给下一个程序:结果直接写进文件,或管道接给 gh pr comment 贴 PR

最基础的用法

最简单的样子就一句话:

codex exec "总结这个仓库的结构,列出最该警惕的 5 个地方"

它会读当前工作目录、定计划、把过程往屏幕上滚,最后打出一句话总结,然后退出。不进 TUI、不停在界面等你按键。这就是「非交互」最直白的含义:跑完就走。

几个变体:

# 短别名 codex e,跟 codex exec 完全等价
codex e "解释这个项目是干什么的"

# 换个模型跑这一次
codex exec -m gpt-5.6 "审查当前改动,列出潜在 bug"

# 不把这次会话记录落盘
codex exec --ephemeral "快速过一遍这个仓库"
Warning

Codex 硬性要求命令在一个 Git 仓库里跑。这是为了防止它在没有版本管理的地方造成不可逆破坏。非 Git 目录里跑会被拦下来,临时目录加 --skip-git-repo-check 跳过检查—但只在确信环境安全时才这么干。

关键设计:进度走 stderr,结果走 stdout

这是 exec 能优雅接进管道的命门。

Codex 跑的过程会输出一大堆东西—它在想啥、跑了哪条命令、改了哪个文件。可你真正想要的往往只是最后那条总结。要是全混在一起,想「把结果写进文件」时,文件塞满过程噪音,没法用。

Codex 的解法:过程噪音走 stderr(标准错误),最终结果走 stdout(标准输出)。这俩是 Unix 的两条独立输出流,平时终端看它们混在一起,但管道 | 和重定向 > 默认只接 stdout。

# 结果既打到屏幕、又存进文件,过程噪音不会进文件
codex exec "给最近 10 个提交生成发布说明" | tee release-notes.md

常见操作对照表:

你想干的事怎么写原理
只把结果存文件codex exec "..." > out.md> 只接 stdout
结果既存又看codex exec "..." | tee out.mdtee 一份存盘、一份打屏
结果喂给下个程序codex exec "..." | pbcopy下游只收到干净结果
过程噪音也留存codex exec "..." > out.md 2> log.txt2> 单独收 stderr
Warning

别手贱用 2>&1 把 stderr 并回 stdout。我第一次写脚本就这么干,文件里全是「正在思考」「正在读取文件」的过程行,最终总结被淹在里头。把 2>&1 去掉,文件立刻就干净了。

沙箱权限:默认是只读

这是最容易想当然写错的一点:codex exec 默认跑在只读沙箱里。它能读、能分析、能给建议,但默认不改你的文件、不执行有副作用的命令。要让它真动手,得显式放开。

为什么默认这么保守?因为它是为无人值守设计的,没人在旁边喊停。要是默认就放开写权限又没人盯,一个理解偏差就可能在你仓库里乱改一通。

三档对照:

沙箱档位能干啥什么时候用
read-only(exec 默认)只读、只分析,不改文件代码审查、生成报告
workspace-write工作区目录内读写要它真改代码、修 bug
danger-full-access几乎不设限只在隔离环境用
# 默认只读:审查改动,碰不到你的文件
codex exec "审查当前改动,列出潜在 bug"

# 放开工作区写权限:让它真去修
codex exec --sandbox workspace-write "修好失败的测试用例"

# 仅限隔离环境:几乎不设限
codex exec --sandbox danger-full-access "<隔离 runner 里的任务>"
Tip

老脚本里的 --full-auto 已弃用,新脚本直接用 --sandbox workspace-write 替代,意图更明确。danger-full-access 只在隔离的 CI runner 或容器里用,在你日常开发机上对着重要项目甩这一档,等于交出万能钥匙。

还有两个给受控自动化用的开关:

  • --ignore-user-config:不加载 $CODEX_HOME/config.toml,保证每台机器行为一致
  • --ignore-rules:跳过用户级和项目级的 execpolicy .rules 文件
Warning

--ignore-user-config 会让写在 config.toml 里的认证配置也不生效。如果你的 API key 在那个文件里,加了它这次跑就报 401。它适合认证走环境变量或 CI Secret 的受控环境,本机练习别加。

机器可读输出:—json 和 -o

纯文本输出对脚本不友好。Codex 给了两个互补的工具。

—json:过程变事件流

--json,stdout 从一段自然语言变成 JSON Lines(JSONL)—每行一个独立 JSON 对象,Codex 每发生一个状态变化就吐一行。

codex exec --json "总结这个仓库的结构" | jq

事件类型包括 thread.started(线程开始)、turn.started / turn.completed / turn.failed(一轮的开始/完成/失败)、item.*(具体动作,如执行命令、改文件)、以及 error

{"type":"thread.started","thread_id":"0199a213-81c0-7800-8aa1-bbab2a035a53"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_3","type":"agent_message","text":"仓库包含 docs、sdk、examples 三个目录。"}}
{"type":"turn.completed","usage":{"input_tokens":24763,"output_tokens":122}}

有了这个,脚本里判断成功失败、提取它干了哪些事、统计 token 用量,全都有结构化字段可抓。

-o:捞最终总结到文件

很多时侯你只想要最后那段总结,落进一个文件给下一步用。加 -o 就行:

codex exec "提炼项目元信息" -o ./summary.md
Note

-o 会把最终消息写进文件,同时还照常打到 stdout。它不抢 stdout,你想再管道接给别人也不耽误。

CI 里的黄金搭配是两个一起上:

# 既拿机器可读的进度,又拿一份最终的自然语言总结
codex exec --json -o summary.md "审查这个 PR 的改动"
你想要的用哪个
程序逐步读它的动作、判断成败--json
只要最终总结落一个文件-o <path>
CI 里两样都要--json + -o 一起上

还有个进阶的 --output-schema:给它一个 JSON Schema,让最终结果严格按你定义的字段结构产出,下游程序拿到的字段稳定可靠。

stdin 管道:把上游输出喂给它

太多场景是「先有一坨数据,再让 Codex 处理它」。构建挂了有一段报错日志、CI 跑完有一段输出,你不想手动复制粘贴进交互界面。

这里有两种姿势,分清楚就不会用错:

姿势一:提示词 + 管道

当你心里清楚要它干啥,只是想把某个命令的输出当材料喂进去时用这个。提示词是你写的指令,管道内容是上下文材料。

# 构建挂了,把报错管道喂给它要根因
npm test 2>&1 \
  | codex exec "总结失败的测试,提出最小改动的修复方案" \
  | tee test-summary.md
# 把长日志喂给它做根因分析
tail -n 200 app.log \
  | codex exec "找出最可能的根因,给出接下来三步排查建议" \
  > log-triage.md

姿势二:codex exec -

当整个提示词都是上一个命令动态拼出来的时用这个。省略提示词参数,Codex 就从 stdin 读;想强制这个行为用 - 当哨兵。

# 把文件里的内容当成整个提示词
cat prompt.txt | codex exec -
# 用脚本现拼一个完整提示词
printf "用 3 条要点总结这段错误日志:\n\n%s\n" "$(tail -n 200 app.log)" \
  | codex exec -
Tip

维护几个提示词模板文件(比如标准的 PR 审查提示词),需要时 cat 模板.txt | codex exec - 直接跑。改提示词只改文件、不用动脚本,指令和脚本解耦,在团队里特别值。

resume:接着上次跑

非交互不代表只能一锤子买卖。一个流程可以分两段跑,靠 resume 把前后串起来。

# 第一阶段:先让它找问题
codex exec "审查这处改动有没有竞态条件"

# 第二阶段:接着上次,让它修掉刚找到的问题
codex exec resume --last "把你发现的竞态条件修掉"

--last 接当前工作目录下最近的那次会话。想精确接某次特定会话,把会话 ID 跟在后面:

codex exec resume <SESSION_ID> "继续上次的任务"
Note

加了 --ephemeral 不落盘的跑法没法 resume—都没存,接什么。要用两阶段流水线,就别加 --ephemeral。跨目录找最近的会话加 --all

CI/CD 集成

GitHub Action

官方提供 openai/codex-action@v1,在 CI 任务里替你装好 Codex CLI、起好代理、跑一条 codex exec。你不用自己在 runner 上折腾安装和登录。

# .github/workflows/codex-review.yml
name: Codex pull request review
on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  codex:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v5
        with:
          ref: refs/pull/${{ github.event.pull_request.number }}/merge
          persist-credentials: false

      - name: Run Codex
        uses: openai/codex-action@v1
        with:
          openai-api-key: ${{ secrets.OPENAI_API_KEY }}
          prompt-file: .github/codex/prompts/review.md
          output-file: codex-output.md

  post_feedback:
    runs-on: ubuntu-latest
    needs: codex
    if: needs.codex.outputs.final_message != ''
    permissions:
      issues: write
      pull-requests: write
    steps:
      - name: Post Codex feedback
        uses: actions/github-script@v7
        with:
          github-token: ${{ github.token }}
          script: |
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.payload.pull_request.number,
              body: process.env.CODEX_FINAL_MESSAGE,
            });
        env:
          CODEX_FINAL_MESSAGE: ${{ needs.codex.outputs.final_message }}

action 的几个关键输入:

输入干什么备注
prompt / prompt-file任务描述二选一,都给会报错
sandbox沙箱模式默认只读,要改文件得显式写 workspace-write
model / effort模型和推理强度留空用默认,别写死型号
output-file最终消息存到磁盘方便后续步骤上传
codex-args额外 CLI 参数JSON 数组如 ["--ephemeral"]
safety-strategy安全策略默认 drop-sudo,Windows 必须设 unsafe

密钥安全三条红线

第一条:key 存进 GitHub Secrets,绝不硬编码。 公开仓库全世界都能看,你把真实 key 写进 YAML 提交,等于把家门钥匙照片发到朋友圈。扫密钥的爬虫一刻不停。

第二条:别把 key 设成 job 级环境变量。 就算你存了 Secrets,如果往 job 顶上 env: 一挂,这个 job 里跑的任何东西—你的测试、npm install 触发的脚本、甚至被投毒的第三方 action—都能顺手偷走你的 key。正确做法是只传给 codex-action 那一步。

第三条:管住触发者。 默认只有对仓库有写权限的人能触发。公开仓库尤其要管住,别让任意陌生人都能让 Codex 在你仓库上跑。

其他 CI 平台

# GitLab CI
codex_review:
  image: node:20
  before_script:
    - npm install -g @openai/codex
  script:
    - codex exec "Review changes in this merge request" -o review.md
  artifacts:
    paths:
      - review.md
// Jenkins Pipeline
pipeline {
    agent any
    stages {
        stage('Code Review') {
            steps {
                sh 'codex exec "Review changed files" -o review.md'
            }
        }
    }
    post {
        always {
            archiveArtifacts 'review.md'
        }
    }
}

无人值守的安全要点

非交互模式跑在无人值守环境,几个安全要点得记住:

  • 沙箱选能跑通的最窄那档:纯审查用 read-only,要改文件才给 workspace-write
  • 清洗外部输入:把 PR 描述、commit 信息喂给 Codex 前,先审有没有 HTML 注释或隐藏文本(提示注入风险)
  • Codex 放 job 最后一步跑:免得后面的步骤继承它可能造成的意外状态改动
  • 设合理超时:避免卡死流水线
  • 验证成功后再继续:检查返回码,失败要有回滚
Warning

read-only 只是不让它改文件、不让它联网,但它仍以高权限运行。别只靠 read-only 来保护密钥,要护密钥靠的是 drop-sudo(默认开,别在多租户 runner 关)或低权限用户那套。

小结

  • 是什么codex exec 是非交互模式,一句话丢进去、跑完吐结果就退出,专为「没活人盯着」的场景生
  • 关键设计:进度走 stderr、结果走 stdout,> / tee / 管道默认只接到干净结果,别用 2>&1 把噪音并回去
  • 沙箱默认只读:要改文件得显式 --sandbox workspace-writedanger-full-access 只在隔离环境用,老的 --full-auto 已弃用
  • 机器可读--json 把 stdout 变 JSONL 事件流,-o 把最终总结落文件且仍打 stdout,CI 里两个一起上
  • stdin 管道:指令你现写用「提示词 + 管道」,整个提示词由上游生成用 codex exec -
  • resumecodex exec resume --last 接着上次跑,--ephemeral 没法 resume
  • CI 集成:GitHub Action 用 openai/codex-action@v1,key 存 Secrets 不硬编码、别设 job 级 env、drop-sudo 别关

下一章讲 Git 工作树与 GitHub 集成—Worktrees、PR Chat、GitHub Action、@codex 评论委派。