用 codex exec 把广告诊断跑成不用人盯的流程
非交互跑的关键不是命令怎么写,而是先把边界和失败行为定死。推荐用只读沙箱加不弹审批,让它把结果打到标准输出、由外层脚本写文件,这样 Codex 全程碰不到写权限。必须提前写死的一条是数据缺失时明确失败——否则你可能连着一周拿旧数据算出看起来完全正常的报告。日报要分成今天要处理的、本周积累观察的、明细三段,并且敢说今天没事。
先讲结论:先定失败行为,再谈命令
交互式用 Codex,你坐在旁边,它做错了你当场能看见。非交互跑不一样——命令发出去,你不在,结果第二天才看。
所以非交互的关键不是命令怎么写,而是两件事先定死:
- 边界:它能碰到什么,出界了怎么办
- 失败行为:数据不全、文件不存在、算不出来的时候,它该干什么
第二件比第一件更容易被忽略,也更容易出事。一个配错的边界你很快会发现,一个「悄悄凑了个结果」的流程可以错很久没人知道。
这篇用广告诊断做例子,因为它是跨境运营里最典型的定时活:数据每天都有、判断标准写得出来、结果一眼能验证。
codex exec 是什么
codex exec 是 Codex 的非交互模式,跑完就退出,不进对话界面。
codex exec "把 data/ads/ 下最新的广告报表按 AGENTS.md 的口径分析,输出诊断结论"
和交互式的区别只有一个:没有人可以回答它的问题。
这带来的连锁反应是:审批没人批,歧义没人澄清,缺数据没人补。所以所有这些情况的处理方式,都要提前写在规则里。
权限怎么配:两个方案
这里有个看起来矛盾的地方:你想让它输出报告,但又不想给写权限。
方案一:只读加输出重定向(推荐)
codex exec \
--sandbox read-only \
--ask-for-approval never \
"分析 data/ads/ 下最新的广告报表,按 AGENTS.md 的口径输出诊断报告" \
> reports/ads-$(date +%F).md
Codex 全程只读,报告由 shell 重定向写出去。写文件这件事由你自己的脚本做,不经过它的手。
权限最小,出问题的可能性也最小。
方案二:给工作区写权限
codex exec \
--sandbox workspace-write \
--ask-for-approval never \
"分析并把报告写到 reports/"
好处是它可以自己组织输出,比如一次生成多个文件。代价是这个目录里的东西它都能改。
**选哪个看目录里放了什么。**如果工作区里只有报表和报告,方案二可以接受;如果里面还有别的资料,用方案一。
三条非交互的硬规则
第一,绝不用全权限
无人值守的时候出问题,等你发现可能已经跑了很多轮。全权限在有人盯着的时候都要谨慎,非交互场景直接排除。
第二,不弹审批就必须收紧沙箱
--ask-for-approval never 和收紧的沙箱是一对。逻辑很简单:不弹审批,就得靠沙箱兜底。
如果既不审批、沙箱又开得大,等于完全没有边界。这是非交互配置里最危险的组合。
第三,数据缺失时必须明确失败
这条要写进 AGENTS.md:
今天的报表文件不存在时,直接输出「今日数据缺失,未生成报告」并停止。
不要用昨天的数据代替,不要估算,不要跳过缺失的列继续算。
没有这一条,最坏的情况是这样的:某天导出脚本挂了,目录里还是昨天的文件,Codex 照常分析、照常生成报告,报告看起来完全正常。你连着一周看的都是同一份数据算出来的结论。
这类错误最难发现,因为它不报错。
定时怎么跑
codex exec 就是一条普通命令,用系统自带的定时机制跑就行——macOS 和 Linux 用 cron 或 launchd,Windows 用任务计划程序。
外层脚本建议长这样:
#!/usr/bin/env bash
set -euo pipefail
cd ~/ops/ads-report
DATE=$(date +%F)
# 数据没到就直接退出,不要让 Codex 跑
if [ ! -f "data/ads/${DATE}.csv" ]; then
echo "[$(date)] 报表未就绪,跳过" >> run.log
exit 0
fi
codex exec \
--sandbox read-only \
--ask-for-approval never \
"分析 data/ads/${DATE}.csv,按 AGENTS.md 的口径输出诊断报告" \
> "reports/ads-${DATE}.md" 2>> run.log
echo "[$(date)] 完成 reports/ads-${DATE}.md" >> run.log
有几个地方是故意这么写的:
set -euo pipefail:任何一步失败就整体退出,不要带着错误往下跑- 数据检查放在外层:文件不在就根本不启动 Codex,省钱也省得它去猜
- 日志单独记:跑没跑、成没成,要能查
- 报告文件名带日期:不覆盖历史,才能做环比
--json 输出
如果你要把结果喂给别的程序,codex exec 支持 --json 输出,方便脚本解析。
典型用法是让它输出结构化的异常清单,外层脚本判断:有紧急项就发通知,没有就只存档不打扰。
这一步不是必须的,但它能让「日报」变成「只在需要时找你」,实际使用率会高很多。
日报该分成三段
这是让这套东西能长期活下去的关键设计。
日报最大的风险不是写得不准,是变成一封没人看的邮件。而对抗这件事的办法,是让它敢说「今天没事」。
建议固定成三段,重要的在最上面:
- 需要今天处理的:只放触发了止损阈值的项。没有就明确写「今日无需处理」
- 本周积累观察:连续异常若干天的项,攒着周会一起看
- 今日明细:完整数据,供需要时查阅
写进 AGENTS.md:
## 报告分层
报告分成三段,顺序固定:
1. 需要今天处理的:只放触发止损阈值的项。为空时写「今日无需处理」
2. 本周积累观察:连续异常 3 天及以上的项
3. 今日明细:完整数据
不要把明细项提到第一段。第一段每多一条,读的人就少一分。
最后那句是写给模型看的,也是写给你自己看的。
加上时间维度
只看当天数据的日报,看两周就麻木了——每天都是那几个高花费词。
要让它有用,得加时间对比。三样东西:
环比:和昨天比,也和上周同一天比。后者更重要,广告数据有明显的周内规律,只跟昨天比会误判。
连续异常天数:同一个关键词连续几天被标异常,这个数要累计。日报真正的重点不是「今天有哪些异常」,而是「哪些已经连续异常 5 天了」——前者是噪声,后者是信号。
广告活动的贡献变化:整体 ACoS 变了,要能定位到是哪个活动导致的。
实现方式是让它读 reports/ 下最近两周的历史报告,自己算这些对比。所以前面强调报告文件名要带日期、不能覆盖。
跑起来之后怎么盯
自动化最怕的不是出错,是悄悄不跑了。
三个简单的监控手段:
- 日志里记每次的开始和结束,隔几天扫一眼
- 报告文件按日期命名,缺哪天一眼能看出来
- 给自己设一个反向提醒:如果连着两天没收到报告,说明流程断了
第三条最实用。人对「收到了什么」很敏感,对「没收到什么」很迟钝,需要一个机制来兜。
诊断可以每天,调整不要每天
最后一条判断,比技术配置更重要。
能每天诊断,不等于该每天调整。
广告数据天生有噪声。一个词昨天花了 40 块没出单、今天出了 3 单,这中间大概率什么都没发生。如果你看到日报标红就去动竞价,会同时踩三个坑:
- 打断广告系统的学习期,数据反而更不稳
- 调整太频繁,事后分不清效果变化是哪次操作带来的
- 把噪声当信号,越调越乱
所以日频诊断的正确用途是积累证据,不是触发动作。连续几天都异常的项,才值得动。
实际节奏建议:
- 每天一分钟:只看报告第一段,有没有要今天处理的。没有就关掉
- 每周一次:看第二段的连续异常清单,集中做一次调整
- 每月一次:回看这个月的调整记录,检查阈值定得对不对
第三条最容易跳过,但它决定了你的阈值准不准。如果回看发现按清单否定掉的词大部分是误伤,那要改的是阈值,不是关键词。
常见问题
| 症状 | 优先检查 |
|---|---|
| 报告是空的 | 数据文件在不在、列名对不对 |
| 报告生成了但数字明显不对 | 列对应错了。第一次一定要手工抽查两三行 |
| 命令挂在那里不返回 | 是不是在等审批。非交互场景要确认审批策略配了 never |
| 它想联网但失败 | 沙箱默认关网络。分析类任务本来就不该联网,先确认它为什么要联 |
| 定时任务没跑 | 定时机制里的环境变量和 PATH 和你的终端不一样,先在脚本里写绝对路径 |
| 连着几天报告一样 | 数据没更新,而它没有明确失败。回去补「数据缺失时怎么办」那条规则 |
倒数第二条是配定时任务最常见的坑:手动跑得好好的,一进 cron 就找不到命令。
下一步
这篇的数据都是你手动导出来放进目录的。下一篇讲 MCP:怎么让 Codex 直接连上外部系统取数据,以及哪些数据源值得接、哪些不该接。




