在桌面应用里手工派发 Agent,适合探索任务和需要人工判断的流程;但当一个工作流需要每天运行、批量处理几十个模块,或者要接入 CI 时,单靠聊天窗口就不够了。你需要把输入、输出、等待、重试和失败处理写成可以重复执行的脚本。

Codex CLI 的价值不在于把聊天界面搬到终端,而在于把一次 Agent 运行变成自动化流程中的一个步骤。一个 CLI 进程可以负责分析一个模块,多个进程可以并行运行,脚本再把结果交给汇总 Agent 或验证器。本文用这种思路组织一个可维护的命令行编排框架。

文中的命令用于说明工作流形状。Codex CLI 的子命令、参数和实验能力会随版本变化,实际运行前应先执行当前版本的 codex --help、具体子命令的 --helpcodex doctor,确认本机是否支持对应入口。不要把示例中的模型名、沙箱选项或远程参数直接当作所有版本的固定配置。


一、CLI 编排和 App 编排分别适合什么

桌面 App 更适合:

  • 需要浏览器、截图或人工观察的验证;
  • 需要持续跟踪多个 Task 的进展;
  • 需要在不同主机和 Worktree 之间交接;
  • 任务中途经常需要人判断和纠偏。

CLI 更适合:

  • 非交互地跑完一个明确任务;
  • 从脚本或 CI 批量启动多个独立 Worker;
  • 把结果保存为 Markdown、JSON 或测试报告;
  • 使用退出码、超时和重试接入已有自动化系统。

二者不是互相替代关系。复杂流程通常由脚本负责调度,由 Agent负责需要理解和推理的部分,再由确定性工具负责验证。能用 shell、测试框架或 JSON 校验完成的事情,不要全部交给模型。


二、先检查运行环境和命令入口

在项目根目录执行以下检查。不同安装方式的输出会不同,重点是确认命令存在、当前版本、认证状态和能力开关:

codex --help
codex doctor
codex features list

如果当前版本提供具体命令,可以继续查看:

codex exec --help
codex review --help
codex fork --help
codex resume --help

前置条件至少包括:

  1. CLI 已安装并能从当前 shell 找到;
  2. 当前用户已完成产品要求的认证;
  3. 工作目录是预期仓库,且 Git 状态已记录;
  4. 脚本运行账户具备所需文件权限;
  5. 模型、MCP、沙箱和网络设置经过单独验证;
  6. 任务输出目录不会与源码和临时缓存混在一起。

不要在 CI 里第一次运行就启动批量任务。先用一个小模块验证认证、提示词、输出格式和退出码,再逐步增加并发量。


三、最小单任务:先让输出可消费

脚本化的第一步不是并行,而是定义一个稳定的 Worker 接口。以只读代码分析为例,Worker 接收模块路径,输出带文件位置的 Markdown 报告:

codex exec "只读分析模块 auth 的错误处理。不要修改文件。检查真实调用路径,输出问题、风险等级、file:line 证据、复现条件和未覆盖范围。" > out_auth.md

这个示例中的 codex exec 只表达“非交互执行一轮 Agent 工作”的意图。若当前版本使用不同的非交互入口,应按本机帮助替换。重定向输出前,要确认命令的标准输出确实是最终文本;有些版本可能将进度日志写到标准错误,脚本需要分别保存。

完成一次运行后检查:

Test-Path out_auth.md
Get-Content out_auth.md
git status --short

在 Linux 或 macOS 中可以使用 test -s out_auth.mdgit status --short。预期结果是报告文件非空,源码没有未授权改动,报告包含任务要求的证据字段。


四、Fan-out:批量并行分析多个模块

当多个模块互不依赖时,可以让每个模块启动一个独立 CLI 进程。下面是 PowerShell 形式的示意脚本:

$modules = @("auth", "billing", "search")
$jobs = foreach ($module in $modules) {
    Start-Job -ScriptBlock {
        param($name)
        codex exec "只读分析模块 $name 的错误处理。输出 file:line 证据和未覆盖范围。" |
            Set-Content -Encoding UTF8 "out_$name.md"
    } -ArgumentList $module
}

$jobs | Wait-Job | Out-Null
$jobs | Receive-Job
$jobs | Remove-Job

这是为了说明“一个模块一个独立进程、最后等待和收集”的形状,实际脚本应补充工作目录、超时、错误输出和退出码处理。Windows PowerShell 的作业环境和当前 shell 的环境变量可能不同,运行前要确认 codex 在作业进程中可见。

在类 Unix shell 中可以使用后台进程和 wait

for module in auth billing search; do
  codex exec "只读分析模块 $module 的错误处理,输出 file:line 证据" \
    > "out_$module.md" 2> "err_$module.log" &
done
wait

不要把并行数量直接设置成模块数量。先设置一个小的并发上限,例如同时处理 2 到 4 个模块,再根据额度、CPU、内存和上游服务限制调整。批量任务还要避免多个 Worker 修改同一工作树;只读分析可以共享目录,写入任务应使用独立 Worktree 或串行执行。


五、Reduce:让第二个 Agent 汇总第一批结果

并行 Worker 的结果不应该直接拼成一条超长提示词。先把每份结果存档,再由汇总步骤读取目录中的报告。简单场景可以使用 shell 拼接,但必须控制文件大小、编码和敏感信息:

codex exec "综合 reports/ 目录中的模块分析,按严重程度排序,去掉重复问题。每条结论保留来源文件和 file:line 证据;无法确认的内容列入待验证。" \
  > report.md

如果 CLI 不能直接安全读取目录,就在脚本中先生成一份明确的输入清单,或把报告摘要转换为 JSON。汇总 Agent 不应默认相信所有 Worker 的结论,它需要检查:范围是否重叠、证据是否真实存在、多个报告是否描述同一问题、是否遗漏失败的 Worker。

一个适合自动处理的结果格式是:

{
  "module": "auth",
  "status": "completed",
  "findings": [
    {
      "severity": "high",
      "file": "src/auth/session.ts",
      "line": 42,
      "claim": "过期会话被当作有效会话继续使用",
      "evidence": "测试 test_expired_session 未覆盖该分支"
    }
  ],
  "uncovered": ["真实 Redis 过期行为"]
}

Markdown 适合人读,JSON 更适合脚本判断。可以让 Worker 同时输出两者,或者先输出 JSON,再由另一个步骤生成可读报告。关键是不要让后续阶段依赖自然语言中的固定句式。


六、Pipeline:让每个项目独立进入下一阶段

批量升级依赖、生成文档或做格式迁移时,每个 item 通常都要经过多阶段。脚本不应等所有 item 完成第一阶段后再启动第二阶段,而应在单个 item 完成后立即推进。

item A:扫描 -> 修改 -> 测试 -> 评审
item B:扫描 -> 修改 -> 测试 -> 评审
item C:扫描 -> 修改 -> 测试 -> 评审

每个阶段都写入状态文件,例如:

{
  "item": "repo-a",
  "stage": "test",
  "attempt": 1,
  "input": "artifacts/repo-a/implementation.diff",
  "output": "artifacts/repo-a/test.json",
  "status": "running"
}

脚本每次启动前先读取状态,把已完成阶段移出队列;失败时只重跑失败的 item,而不是整批重来。状态文件要原子写入,避免进程中断后留下半截 JSON。简单方案可以先写临时文件,再替换正式文件。


七、失败、超时与重试必须由脚本控制

模型任务可能出现网络错误、认证错误、上下文过长、工具失败和输出格式错误。不要把所有失败都交给 Agent 自己无限重试,脚本应按类型分类:

退出码非零且日志显示网络暂时失败 -> 有上限的重试
认证或权限失败 -> 立即停止并提醒人工
输出不是合法 JSON -> 重新提示一次,仍失败则转人工
测试失败 -> 保存测试输出,交给修复步骤
超时 -> 标记 timeout,不自动认为任务成功

重试要记录原始日志、尝试次数和最终状态。常见的指数退避可以减少瞬时拥塞,但不能修复错误的提示词、错误的目录或缺少权限。重试次数建议由任务类型决定,并设置全局上限。

验证脚本时至少测试四种情况:正常完成、命令不存在、Agent 返回非零退出码、Agent 输出空文件。没有这些测试,批量脚本很可能在失败时仍然生成一个看似成功的报告。


八、把 CLI 接入 CI 时的安全边界

CI 运行 Agent 与本地交互有几个明显区别:没有人实时确认、日志可能被长期保存、凭据由平台注入、并行任务可能共享缓存。因此要提前设定限制:

  • 使用专门的低权限账户和最小工作目录;
  • 不把 API Key、访问令牌或内部日志写进提示词和输出;
  • 对写入任务使用临时分支或 Worktree;
  • 对删除、发布、合并和外部消息保留人工审批;
  • 限制单个任务的最大时间、最大重试次数和最大并发数;
  • 将 Agent 输出与测试输出分开保存,便于判断失败来源;
  • CI 日志脱敏,避免把命令行参数中的秘密打印出来。

模型可以帮助判断代码和文本,但脚本仍要负责权限、超时、退出码、产物路径和审批节点。把安全控制写在提示词里是不够的,因为提示词不是权限边界。


九、什么时候需要 App Server 或外部控制器

纯 CLI 脚本适合线性流程、简单 fan-out 和有限重试。如果出现以下需求,就应考虑更明确的状态控制器:

  • 任务跨多个项目和主机;
  • 节点有复杂依赖,需要动态计算 ready 状态;
  • 需要持续订阅事件,而不是等待进程退出;
  • 结果需要按 schema 校验并驱动下一节点;
  • 任务运行时间很长,需要暂停、恢复和人工接管;
  • 需要在 Web 面板或数据库中查看全局状态。

这时可以把 CLI 作为 Worker,把 Registry、队列和调度器放在外部系统中。边界要清楚:Thread 或进程保存 Agent 会话,Registry 保存业务 DAG,Artifacts 保存交付物。不要依赖翻聊天记录恢复整个流程状态。


十、脚本化工作流的验证清单

在接入真实项目之前,先用一个小仓库完成一次演练:

[ ] 单个 codex exec 能成功完成并产生非空产物
[ ] 命令失败时脚本能返回非零状态
[ ] 多个 Worker 的输出文件互不覆盖
[ ] 汇总步骤能识别失败或缺失的 Worker
[ ] JSON 结果能通过结构校验
[ ] 超时任务不会被误标记为成功
[ ] 重试次数和日志都能追溯
[ ] 工作树没有出现未授权改动
[ ] 秘密没有出现在提示词、日志和产物中
[ ] 中断后重新运行不会重复破坏已完成结果

测试通过后,再逐步增加模块数量、并发数和阶段。每次只改变一个变量,才能知道问题来自任务拆分、模型输出、环境资源还是脚本调度。


结语

CLI 编排的核心是把 Agent 当作一个可以观察、限制和验证的执行步骤:输入要明确,输出要落盘,退出状态要处理,失败要分类,下一阶段要读取真实产物。

从“一个模块、一个报告、一个验证命令”开始,逐步扩展到 Fan-out、Reduce、Pipeline 和 CI。需要复杂依赖时再引入 Registry 或 App Server。脚本负责确定性控制,Agent 负责理解和推理,二者分工清楚,自动化流程才不会因为一次异常回复而失去可控性。


附注:API 接入层作为脚本化工作流的基础设施

在上述 CLI 编排中,每个 Agent 任务最终都需要调用大模型 API。当并行 Worker 数量增多时,多个进程同时发起 API 请求,密钥管理、用量统计和成本追踪的复杂度会随之上升。

一种工程上的可选做法是引入统一的 API 接入层(例如兼容 OpenAI 接口的中转服务),让所有 Worker 通过同一个网关调用模型。这样,脚本只需在环境变量中配置一个统一的 BASE_URLAPI_KEY,而不必为每个 Worker 单独管理不同厂商的凭证。同时,用量统计和费用汇总可以在网关层集中完成,便于在脚本化工作流中加入预算检查和成本告警。

目前市面上已有 4SAPI 等提供此类能力的服务,它们不改变 Codex CLI 的任务编排逻辑,仅作为基础设施层简化多 Worker 场景下的 API 治理。是否采用取决于你对密钥管理复杂度、模型切换灵活性和统一计费的实际需求。无论直连还是通过网关接入,核心原则不变:脚本负责确定性控制,Agent 负责理解和推理,而 API 接入层负责统一凭证、用量和成本的可观测性。

Logo

葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。

更多推荐