我让 Codex CLI 替我生图,还得防着它拿旧图糊弄我

大家好,我是若风。

最近在生图这件事上,卡在一个挺别扭的处境里。我手上有 Codex 订阅,Codex CLI 内置的 image_gen 工具能直接生图,质量不差。但我日常写作、跑自动化都泡在 Claude Code 里,这俩是两套运行时,我在 Claude Code 里没法直接喊 Codex 的 image_gen,除非自己再去开一张 OPENAI_API_KEY,单独为生图付费。

更要命的是,就算把 Codex 驱动起来了,agent 也未必老实。baoyu-skills 仓库有个 issue #185,记录过 agent 干的一件挺丢人的事,它没去生图,直接从历史目录里复制了一张无关旧图交差。

所以这篇拆一个工具,baoyu-codex-imagegen。它做的事一句话讲清,把 Codex CLI 当成一个免 API Key 的生图 agent 来驱动,在非 Codex 运行时里 spawn 一个 codex exec 子进程,委托它内置的 image_gen 生图,走 Codex 订阅计费,全程不用 OPENAI_API_KEY。

但真正值钱的不是怎么驱动,是它怎么防住 agent 作弊。三层,层层堵死。我读完的第一反应是,这套东西对任何「让一个 agent 驱动另一个 agent」的场景都有参考价值。

不是生图,是把 Codex 当 agent 驱动

先把话说在前头,这个工具本身不做任何图像生成,连一行生图代码都没有。源码在 baoyu-skills 仓库的 packages/baoyu-codex-imagegen,TypeScript 加 Bun,没有构建步骤。它在 baoyu-skills 插件里对应 preferred_image_backend 里的 codex-imagegen 配置项,也是 baoyu-image-gen 带上 --provider codex-cli 背后的引擎。

它的工作方式是 spawn 一个无头 codex exec 子进程,用自然语言指令驱动 Codex agent 去调它自己的 image_gen 工具。整条链路是这样。

text 复制代码
调用方 → bun src/main.ts → codex exec --json --sandbox danger-full-access
        (stdin 写入指令)        ↓
                            Codex agent 调 image_gen
                                ↓ 写入
                            $CODEX_HOME/generated_images/{thread_id}/
                                ↓ agent 自己 cp/mv
                            用户指定的 --image 路径

这里有几个 spawn 参数值得点一下。--json 让输出变成 JSONL 事件流,有 thread.started、item.*、turn.completed 这些,方便后面解析验证。--sandbox danger-full-access 给的是全权限,因为 agent 得有权把 PNG 从 generated_images 目录搬到目标路径。--skip-git-repo-check 允许在非 git 目录里跑,比如 /tmp 或者插件安装目录。指令从 stdin 用 - 读入,绕开转义问题。参考图用 --image 传,可以重复多张。

这套东西最精妙的地方不在驱动,在「验证」。

三层防作弊,堵住 agent 的捷径

写这个工具的人,显然是被 agent 坑过。issue #185 就是那个惨案,agent 从历史目录里抄了一张旧图交差,你还以为它真去生图了。于是整个设计都围绕一个前提,假设 agent 会走捷径,然后一层一层堵死它。

第一层在指令里,叫 HARD CONSTRAINTS。buildInstruction 会把一堆禁令写进发给 agent 的 prompt,不许在调 image_gen 之前用 ls、find、rg 去扫 generated_images 目录,不许用 curl 或 Python 调外部 API,不许用 bash 伪造图片。这是对着 #185 打补丁,从源头告诉 agent 别想着抄旧图。

第二层最妙,我管它叫物证层。image_gen 有个挺特别的行为,它不会作为事件流里的一个 item 出现。也就是说,你没法直接从 stream 上确认它到底被调过没有。那真正的证据是什么,是 $CODEX_HOME/generated_images/{thread_id}/ 目录下出现了 PNG。这个目录按 thread_id 隔离,每次调用一个独立目录。从别的 thread 复制旧图,在这里不会留痕,正好防住 #185 那种抄旧图的操作。stream 里的 image_gen item 检查,只是个前瞻兼容的兜底。

第三层在产物上。validator 会做 PNG magic byte 校验,文件头得是 89 50 4E 47 那串,再加一个最小体积 1000 字节的卡点,防止你用 echo 伪造个空文件充数。

三层放一起看,其实是一套完整的验证链。指令层压住行为,物证层定位真相,产物层核验格式。我读到这里想到一句更抽象的话,这是「用 prompt 操纵另一个 agent 帮你干活,再用文件系统物证验证它没骗你」的完整范式。

除了防作弊,还得让它别撞车

防作弊之外,还有一堆现实问题要处理,任何一个都能让生图链路说崩就崩。

重试加指数退避是标配。默认重试 2 次,但错误被拆成 10 种 error_kind,只有可重试的才会真的重试,timeout 会重试,invalid_png 这种就不浪费一次。

文件锁解决的是并发。并行跑 codex exec 会撞车,所以用一个 codex-exec.lock 把并发强行压成 1,超过 10 分钟自动判 stale 释放。

幂等缓存是省钱的。用 SHA256(prompt|aspect|refs) 做 key,命中就直接 copy 缓存产物,不到 0.3 秒,跳过一次完整的生图。

路径安全是防注入的。输出路径和参考图路径会被裸拼进发给 agent 的指令里,所以先拒绝任何含 shell 元字符的路径,避免指令注入。

这四件事听起来琐碎,但缺一个,这个工具就从「能用」掉回「偶尔能跑」。

上手,就几条命令

前置条件很轻。全局装好 Codex CLI,用 OpenAI 账号登录订阅,版本大于等于 0.130,再装个 bun 当运行时。

bash 复制代码
npm install -g @openai/codex
codex login            # 用 OpenAI 账号登录(订阅)
codex --version        # 确认 >= 0.130

brew install oven-sh/bun/bun   # 运行时需要 bun

用法上,最简单是内联 prompt 直接跑。

bash 复制代码
./src/main.ts \
  --image /tmp/cat.png \
  --prompt "A friendly orange cat, watercolor"

prompt 太长就从文件读,顺便带上宽高比和缓存目录。

bash 复制代码
bun src/main.ts \
  --image cover.png \
  --prompt-file prompts/01-cover.md \
  --aspect 16:9 \
  --cache-dir ~/.cache/baoyu-codex-imagegen

要风格或构图引导,就用 --ref 传参考图,可以重复多张,指令里会告诉 agent「N 张参考图已附上」。

bash 复制代码
bun src/main.ts \
  --image new-cover.png \
  --prompt-file prompts/01-cover.md \
  --aspect 16:9 \
  --ref style-ref.png \
  --ref layout-ref.png

没装 bun 也有兜底,npx -y bun 一条命令搞定。常用参数我整理成一张表。

Flag 说明
--image 输出 PNG 路径,必填
--prompt 内联 prompt 文本
--prompt-file 从文件读 prompt,与 --prompt 互斥
--aspect 1:1、16:9、9:16、4:3、2.35:1,默认 1:1
--ref 参考图,可重复多张
--timeout codex exec 超时,默认 300000
--retries 可重试错误的重试次数,默认 2
--cache-dir 开启幂等缓存,默认关闭
--log-file 追加结构化 JSONL 日志
-v, --verbose 详细 stderr 日志

实测,80 秒一张水彩猫

光看文档不算数,我跑了一次真实生图,就是上面那张水彩橘猫。stdout 输出单行 JSON,成功时长这样。

json 复制代码
{"status":"ok","path":"/tmp/cat.png","bytes":2904047,"elapsed_seconds":80,"thread_id":"01a03808-9279-7733-a75f-2775a78531ac","attempts":1,"cached":false,"usage":{"input":68237,"cached_input":53504,"output":243,"reasoning":36},"tool_calls":[{"tool":"error","status":"completed"},{"tool":"error","status":"completed"},{"tool":"error","status":"completed"},{"tool":"shell","status":"completed"},{"tool":"agent_message","status":"completed"}]}

几个数字。耗时 80 秒,产物 2.9MB,跟文档标称的 50 到 90 秒对得上。token 消耗 68k input,其中 53k 命中 prompt 缓存,output 只有 243。

这里有个细节特别容易吓到人。tool_calls 里出现三个 "tool":"error",看着像翻车了,其实是正常现象。parser 对不认识的流 item 会兜底显示它的 type,这三个 item 不影响最终结果。成败判定从来不看这个字段,看的是 thread 目录物证加 PNG 校验。

失败的时候输出也简单,status 变 error,带上 error_kind。

json 复制代码
{"status":"error","path":"...","bytes":0,"error":"...","error_kind":"timeout"}

error_kind 一共 10 种,codex_not_installed、invalid_args、prompt_file_missing、spawn_failed、timeout、no_image_gen_tool_use、output_missing、invalid_png、agent_refused、lock_busy。看一眼就知道问题出在哪一层。

代价,慢 5 到 10 倍

说了这么多好处,该交底了。这套方案的代价很实在。

比直接调 OpenAI API 慢 5 到 10 倍,缓存命中除外。你多绕了一个 agent 的思考过程,多等一次 image_gen 的内部调用,这 80 秒里有相当一部分是 agent 在「想」。

它消耗的是 Codex 订阅额度。codex exec 的编程式使用和交互式使用,适用的是同一条款。你要是拿它批量生图,订阅额度会掉得比你想象得快。

还得依赖本机 codex CLI 且保持登录状态。换台机器、登录过期、版本不对,整条链路就直接断了。

所以它适合的场景很清晰,手上已经有 Codex 订阅,不想再单独开生图 API,生图量不大、对速度不敏感。反过来,要高频、要低延迟、要精确控制,还是老老实实走原生 API。

写在最后

这篇文章最想留下的,不是这个工具本身,是它示范的那套思路。

让一个 agent 驱动另一个 agent 干活,这个模式会越来越常见。但 agent 会偷懒,会走捷径,会把上一次的结果糊弄给你。光靠 prompt 里写「别作弊」是不够的,你得有一层东西去验证它真的干了。

这个工具给出的答案是三层,指令约束压行为,thread 隔离目录取证,文件格式校验核产物。三样加起来,才敢放心把活交给一个你控制不了的 agent。

我倒是觉得,这比「怎么把 prompt 写得更漂亮」重要得多。等哪天你的工作流里也开始让 agent 驱动 agent,记得先把验证这一层搭起来。

希望这篇能帮你省点时间。尤其是当你发现 agent 交上来的东西「看着挺对」的时候,别急着信,去看一眼文件系统。

相关推荐
倔强的石头_1 小时前
一条“60%预付款”风险,是怎样穿过蓝耘 MaaS 变成一份报告的
aigc
Csvn2 小时前
第 1 章 AI Agent 是什么
人工智能·aigc
后端小肥肠2 小时前
历经两个月,我跑通了 Codex 自动剪辑,涨粉破千
人工智能·aigc·agent
xierui1231233 小时前
AI创作工作流状态管理:为什么保存 Prompt仍然无法复现作品
人工智能·自动化·prompt·aigc·软件工程
threerocks3 小时前
《The AI-Native SDLC Playbook》万字拆解
算法·aigc·ai编程
Behavior4 小时前
我用 Claude Code + grill-me 做了个「牛来」跑酷:写代码前先被烤问 10 遍,零返工
aigc·claude
ClouGence4 小时前
不用编程,物理老师也能用AI一键生成交互式课件
人工智能·html·aigc
全栈弄潮儿4 小时前
用 AI 做代码优化:哪些建议值得采纳
aigc·openai·ai编程