Codex 这次的价格调整,是近期编码类模型里最值得关注的一次变动。它本质上不是单一产品,而是 OpenAI 面向代码场景的一整套能力组合:一端是 Codex 系列模型,负责理解仓库结构、生成补丁、解释报错、写测试;另一端是跑在本地的 Codex CLI 编码代理,把模型能力接进终端,让它直接读文件、改文件、执行命令。两者配合起来,就形成了一个可以在真实项目里干活的命令行助手。
围绕这次降价,讨论最多的是一句话:价格已经压到比 DeepSeek 还低。这句话需要拆开看。不同厂商的计费口径并不统一,输入价、输出价、缓存命中价、批处理价、长上下文加价,任何一项不同都会让"谁更便宜"的结论反转。更稳妥的判断是:Codex 在这一轮调整后,单位 token 的成本明显下探,部分档位已经进入和国内主流模型同一量级甚至更低的区间,但具体数字必须以官方定价页为准,本文不写死任何单价。
从工程角度看,降价带来的实际影响比"便宜"两个字更具体。编码代理这类工具天生费 token:一次任务可能要读十几个文件,来回几轮工具调用,最后才输出一个补丁。单价降一半,意味着你可以更放心地让它跑长任务、跑批量重构、跑整仓代码审查,而不是每次都小心翼翼地控制上下文。这才是降价真正的价值。
这篇文章会带你完整走一遍:Codex CLI 的安装与启动、配置文件怎么写、项目规则文件怎么放、几种典型编码任务怎么验证、非交互模式和批量脚本怎么搭、token 消耗怎么观察、常见报错怎么排查。适合已经在用命令行开发、想把手上的重复编码工作交出去的后端、前端和运维同学阅读,也适合正在做模型选型和成本核算的技术负责人参考。
1. Codex 核心能力速览
先把关键信息压缩成一张表,方便快速判断要不要继续往下看。表里没有写具体价格和具体显存数字,因为这两项都属于"必须按你实际的账号档位和机器环境核对"的内容,写死了反而误导。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 编码代理 CLI + 代码专用模型服务 |
| 提供方 | OpenAI |
| 主要功能 | 代码生成、仓库级重构、Bug 定位、测试编写、命令执行、代码审查 |
| 交互方式 | 终端交互模式、非交互单次执行模式、脚本调用 |
| 启动方式 | 全局安装后直接运行 codex 命令 |
| 认证方式 | 账号登录或环境变量注入 API Key |
| 配置文件 | 用户级 ~/.codex/config.toml |
| 项目规则 | 仓库根目录 AGENTS.md |
| 沙箱机制 | 支持只读、工作区可写、完全放行等多档权限 |
| 是否支持 API | 支持,模型能力也可通过标准接口调用 |
| 是否支持批量任务 | 支持,可通过脚本循环 + 非交互模式实现 |
| 本地算力要求 | 无 GPU 要求,推理在服务端完成,本地只跑 CLI |
| 磁盘占用 | CLI 体积很小,主要开销在 Node 运行时和依赖 |
| 适合场景 | 日常编码辅助、遗留代码重构、批量脚本化处理、CI 集成 |
这张表里最容易被忽略的一行是"本地算力要求"。Codex CLI 本身不跑模型,所以不存在显存门槛,一台 8G 内存的轻薄本也能用。真正决定体验的是网络连通性和账号额度,而不是显卡。这一点和本地部署的开源模型路线完全不同,选型时要先想清楚自己是要"离线可控"还是"效果优先"。
另一行值得单独说的是沙箱机制。编码代理和普通聊天机器人的最大区别,是它真的会动你的文件。默认情况下它会在受限权限里操作,需要写文件或执行命令时会请求确认。这个设计不是多余的谨慎,而是必需的安全边界,后面第 2 节会展开讲。
1.1 模型侧与工具侧的分工
很多人把 Codex 理解成"一个模型",实际上它是两层。模型层负责推理,工具层负责执行。当你让它"把 utils 目录下的时间处理函数统一换成 dayjs"时,模型先规划步骤,然后工具层去列目录、读文件、逐个改写、最后跑一遍测试。整个过程里模型只看到工具返回的文本,真正落地的是工具层的文件操作。
这个分工带来两个直接后果。第一,效果好坏不只取决于模型,还取决于工具层给的上下文够不够,比如它有没有读到正确的文件、有没有拿到完整的报错栈。第二,权限控制点在工具层,所以你能精确控制它能碰哪些目录、能不能执行命令,这在企业环境里很关键。
1.2 价格调整带来的实际变化
价格下探之后,最明显的变化不是单次对话便宜了几毛钱,而是"敢不敢让它跑长任务"。以前一个跨十个文件的改造任务,token 消耗可能让人心疼,于是大家习惯把任务拆得很碎,结果模型缺少全局视角,改出来的东西前后不一致。现在成本压力小了,可以把完整需求一次性交给它,让它自己规划、自己读文件、自己验证,成功率反而更高。
第二变化是批量场景变得可行。比如给一百个接口补单元测试、给整个仓库的日志调用统一加上 trace id,这类任务以前属于"人工做太烦、自动化做不稳"的灰色地带,现在可以用脚本驱动编码代理跑批,成本可控。
第三变化是试错成本降低。编码代理的输出有一定随机性,同一个需求跑两次结果可能不一样。成本高的时候你只敢跑一次,将就着用;成本低的时候可以跑三次挑最好的,或者让它先出方案再出实现。这个使用习惯的转变,往往比单价本身更能提升产出质量。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Codex 适用场景与使用边界
工具再好也有边界。这一节把"该用"和"不该用"分清楚,能省掉很多无效尝试。
2.1 适合交给 Codex 的任务
第一类是结构清晰的机械改造。比如把所有 var 换成 const/let、把回调风格改成 async/await、把散落的字符串常量抽成枚举。这类任务规则明确、边界清楚,模型不容易理解偏,出错也容易通过 diff 发现。
第二类是需要阅读上下文的解释型任务。"这个函数为什么在并发下会出问题""这段配置的优先级是怎么算的""这个报错栈对应哪一行",这类问题人工排查要翻好几个文件,交给代理能快速定位,你再复核结论。
第三类是重复度高的补充型任务。补测试、补类型注解、补 JSDoc、补错误处理分支,这些工作技术含量不高但量大,非常适合批量处理。
第四类是跨文件的局部重构。比如把某个工具函数从一个模块挪到另一个模块,同时更新所有引用。这类改动涉及多文件但逻辑简单,代理的强项就在这里。
2.2 不适合交给 Codex 的任务
第一类是涉及核心业务逻辑的设计决策。让它帮你选分库分表方案、选消息队列、定幂等策略,它能给出一堆看起来合理的选项,但它不了解你的流量特征、团队能力和历史包袱,最后的判断还得自己做。
第二类是安全敏感的代码。认证、鉴权、加密、支付回调这些模块,即便代理写出来的代码能跑,也需要逐行审查。让它生成可以,但绝不能直接合入。
第三类是依赖大量隐式约定的老项目。有些仓库里有很多"只有老员工知道"的规则,比如某类文件不能直接改、某个目录由构建脚本生成。这些约定如果没有写进 AGENTS.md,代理大概率会踩坑。
第四类是需要真实环境验证的改动。涉及数据库迁移、配置变更、线上依赖升级的改动,代理只能改代码,验证还得靠你自己在测试环境跑一遍。
2.3 合规与安全边界
编码代理会读取你的源码,这一点必须先想清楚。如果项目里有不能外传的代码、密钥、客户数据,需要提前做隔离:把敏感文件加进忽略列表,把密钥从仓库里挪到环境变量,必要时只在不含敏感信息的子目录里启用代理。
代码生成物的版权归属也要注意。代理生成的代码在多数场景下可以正常商用,但如果生成内容明显复刻了某个开源项目的实现,仍需要遵循原项目的许可证。合入前做一次依赖和许可证扫描,是成本很低的好习惯。
最后是权限边界。不要为了省事长期开启"完全放行"模式。默认的确认机制虽然打断流程,但它拦住的往往是误删文件、误执行危险命令这类不可逆操作。真要提高效率,更稳妥的做法是在容器或独立工作目录里跑,而不是把权限开到最大。
3. Codex 本地部署环境准备
这一节讲清楚装之前要准备什么。Codex CLI 是 Node 生态的工具,门槛不高,但有几个前置条件必须先确认。
3.1 系统与运行时要求
操作系统方面,macOS、Linux 和 Windows 都可以用。Windows 上有两种跑法:原生 PowerShell,或者 WSL。从实际体验看,WSL 更稳一些,因为编码代理会执行 shell 命令,Linux 环境下的命令兼容性更好,遇到 grep、sed、find 这类工具时不会因为平台差异出问题。
Node.js 版本建议用当前的 LTS。装之前先确认一下版本:
bash复制node -v
npm -v
如果版本太老,先升级。用 nvm 管理多版本是最省事的方式:
bash复制# 安装并切换到 LTS 版本,具体版本号以官方 LTS 为准
nvm install --lts
nvm use --lts
node -v
Windows 原生环境下如果遇到 npm 全局安装的脚本无法执行,通常是执行策略限制,可以在 PowerShell 里检查当前策略,按官方文档调整,不建议直接无脑放开所有限制。
3.2 网络与账号准备
Codex CLI 的推理在服务端完成,所以必须能正常访问模型服务。这一条没有变通空间,装之前先确认网络环境可用,否则后面每一步都会卡在认证或请求超时上。
账号方面需要准备两样东西之一:一是可以正常登录的账号,用交互式登录流程完成认证;二是 API Key,通过环境变量注入。两种方式各有适用场景,个人开发用登录更省事,CI 或服务器环境用 Key 更合适。
如果走环境变量路线,注意不要把 Key 写进代码或提交到仓库:
bash复制# 写入当前 shell 会话,注意不要把真实 Key 提交到仓库
export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxx"
# 验证变量是否生效(只看前缀,避免泄露完整 Key)
echo "${OPENAI_API_KEY:0:8}"
Windows PowerShell 的写法不同:
powershell复制# PowerShell 中设置环境变量
$env:OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxx"
3.3 依赖与磁盘检查
装之前顺手确认几件事,能避免后面大量莫名其妙的报错。
第一是磁盘空间。CLI 本身体积不大,但 npm 的缓存和依赖树会占一些空间,留出几百 MB 比较稳妥。
第二是 shell 环境。如果你的默认 shell 是 zsh 或 fish,确认 npm 全局 bin 目录在 PATH 里,否则装完会出现"命令找不到"的情况:
bash复制# 查看 npm 全局 bin 目录
npm bin -g 2>/dev/null || npm config get prefix
# 确认该目录在 PATH 中
echo $PATH | tr ':' '\n' | grep -i npm
第三是项目目录的准备。建议第一次测试专门建一个空目录或者克隆一个小型示例仓库,不要直接在生产仓库里试水。编码代理会读写文件,第一次用最好在可以随时 git reset 的环境里。
bash复制# 准备一个干净的测试目录
mkdir -p ~/codex-lab && cd ~/codex-lab
git init
printf 'console.log("hello");\n' > index.js
git add -A && git commit -m "init"
到这里环境就绪了。整个过程不需要显卡、不需要 CUDA、不需要下载模型权重,这是它和本地部署路线最大的区别。
4. Codex CLI 安装部署与启动方式
安装本身只有一条命令,麻烦的是后面的认证和配置。按顺序来。
4.1 安装
用 npm 全局安装是最通用的方式:
bash复制# 全局安装 Codex CLI,包名以官方文档为准
npm install -g @openai/codex
# 验证是否安装成功
codex --version
macOS 用户如果习惯用 Homebrew,也可以走 brew 渠道,具体 formula 名称以官方文档为准。两种方式装出来的功能一致,只是升级路径不同:
bash复制# Homebrew 渠道示例,实际名称请核对官方文档
brew install codex
如果安装过程中出现权限错误,不要直接用 sudo npm install -g,那样会把全局目录的属主改乱,后续升级更麻烦。更稳的做法是配置一个用户级的全局目录:
bash复制# 把 npm 全局目录指到用户目录,避免权限问题
mkdir -p ~/.npm-global
npm config set prefix ~/.npm-global
export PATH="$HOME/.npm-global/bin:$PATH"
# 重新安装
npm install -g @openai/codex
记得把 export PATH 那一行写进 ~/.bashrc 或 ~/.zshrc,否则新开终端又会找不到命令。
4.2 认证登录
安装完成后第一次运行,会引导你完成认证:
bash复制# 启动交互式登录流程
codex login
按提示完成授权即可。如果是在没有浏览器的服务器上,用 API Key 方式更合适,先设置环境变量再启动,CLI 会自动读取。
认证完成后可以跑一个最小验证,确认链路通了:
bash复制# 最小验证:让代理解释当前目录结构
codex exec "用一段话说明当前目录里有哪些文件,各自大概做什么"
如果这条命令能返回合理的描述,说明安装、认证、网络三件事都没问题。
4.3 两种启动模式
Codex CLI 有两种使用方式,用途完全不同。
第一种是交互模式,直接运行 codex 进入对话界面。适合边聊边改、需要多轮澄清的任务:
bash复制# 进入交互模式
cd ~/codex-lab
codex
进去之后可以连续提问、连续修改,代理会保留上下文。适合探索性任务,比如"帮我看看这个模块为什么会内存泄漏"。
第二种是非交互模式,用 codex exec 执行单次任务,跑完就退出。适合脚本化、批量化、CI 集成:
bash复制# 非交互执行单次任务
codex exec "给 index.js 补充一个最简单的单元测试文件"
# 以 JSON 格式输出,方便脚本解析
codex exec --json "列出当前仓库中所有 TODO 注释所在文件和行号"
--json 这个参数在批量场景里非常关键,后面第 7 节会详细讲怎么用它做任务队列。
4.4 权限与沙箱参数
启动时可以显式指定权限档位,这一点在生产环境里必须做:
bash复制# 只读模式,代理只能读文件不能改,适合代码审查
codex exec --sandbox read-only "审查当前改动,列出潜在风险点"
# 工作区可写,可以改文件但执行命令需要确认
codex exec --sandbox workspace-write "重构 utils 目录下的工具函数"
# 完全放行,仅在隔离环境使用,不建议在真实仓库长期开启
codex exec --dangerously-bypass-approvals-and-sandbox "在容器内跑完整测试并修复失败用例"
最后一档参数名本身就带了警告意味,用之前先确认当前目录是可以随时丢弃的。在容器、临时克隆、一次性工作目录里用没问题,在主力仓库里长期开着就是给自己埋雷。
5. 配置详解与项目级规则
装完之后默认配置能用,但想用得顺手,得改两个地方:用户级配置和项目级规则。
5.1 用户级配置文件
配置文件默认在 ~/.codex/config.toml,TOML 格式。这个文件控制模型选择、审批策略、沙箱默认值等。下面是一份可以直接参考的模板,具体字段名和可选取值需要按你当前安装的版本核对官方文档:
toml复制# ~/.codex/config.toml
# 默认模型,具体名称以官方文档为准
model = "gpt-5-codex"
# 默认审批策略:on-request 表示代理主动请求时才确认
approval_policy = "on-request"
# 默认沙箱模式:workspace-write 允许写工作区
sandbox_mode = "workspace-write"
# 允许代理写入的额外目录,按需添加
[sandbox_workspace_write]
writable_roots = ["./src", "./tests"]
# 模型提供方配置,使用官方服务时一般不需要改
[model_providers.openai]
name = "OpenAI"
base_url = "https://api.openai.com/v1"
env_key = "OPENAI_API_KEY"
几个字段值得单独说明。approval_policy 决定了代理会不会频繁打断你,设成 on-request 是折中方案,既不会每步都问,也不会完全失控。sandbox_mode 决定它能写哪些目录,把它限制在 src 和 tests 里,可以避免误改构建脚本或部署配置。
5.2 项目级规则文件
比配置文件更重要的是 AGENTS.md。这个文件放在仓库根目录,代理每次启动都会读,相当于给它的项目说明书。写得好不好,直接决定输出质量。
一份实用的 AGENTS.md 至少应该包含四块内容:
markdown复制# 项目说明
## 技术栈
- 后端:Node.js 20 + TypeScript 5
- 框架:Fastify
- 测试:Vitest
- 包管理:pnpm(不要用 npm 或 yarn)
## 目录约定
- `src/api`:路由层,只做参数校验和响应封装
- `src/service`:业务逻辑,所有数据库访问必须走这里
- `src/repo`:数据访问层,禁止在 service 里直接写 SQL
- `tests`:测试文件,命名规则 `*.test.ts`
## 编码规范
- 禁止使用 `any`,不确定的类型用 `unknown` 收窄
- 所有对外函数必须写 JSDoc
- 错误统一抛出自定义的 AppError,不要直接 throw new Error
- 时间处理统一用 dayjs,禁止 moment
## 禁止操作
- 不要修改 `src/generated` 下的任何文件,这些是代码生成的
- 不要改动 `.github/workflows` 下的 CI 配置
- 不要提交任何包含密钥的文件
这份文件的价值在于把"隐式知识"变成"显式约束"。没有它的时候,代理会按通用最佳实践写代码,结果可能是用 npm 装了依赖、在 service 里直接写了 SQL、用了 moment,改完还得你手工回滚。有了它之后,这些坑基本能避开。
维护 AGENTS.md 有个技巧:不要一开始就写全,而是等代理犯错之后再补一条。比如它用了 yarn,你就加一行"包管理用 pnpm";它改坏了生成文件,你就加一条禁止规则。用一两周时间迭代下来,这份文件会变成非常有价值的项目资产。
5.3 忽略文件的作用
除了规则文件,还要注意什么不该被读到。在大型仓库里,如果代理一次性扫到几千个文件,token 会迅速烧完,而且大部分是噪音。
比较稳妥的做法是在项目根目录维护一个忽略清单,把构建产物、依赖目录、日志、测试快照这些排除掉。具体文件名各家实现不同,有的读 .gitignore,有的读专门的忽略配置,用之前查一下当前版本的规则,别想当然。
一个实用的判断标准是:如果这个文件你 review PR 时不会看,那代理也不需要看。按这个标准筛一遍,通常能把可读文件量压掉一大半。
6. Codex 功能测试与效果验证
配置好之后要验证能力。这一节给五个测试用例,从简单到复杂,每个都说明测试目的、操作步骤、预期结果和失败判断。建议按顺序跑,前面的过了再跑后面的,出问题容易定位。
6.1 基础代码生成测试
测试目的:验证最基本的代码生成链路是否通畅,同时观察输出风格是否符合项目规范。
操作步骤:在一个干净的测试仓库里,让代理生成一个具体的小函数。
bash复制cd ~/codex-lab
codex exec "在 index.js 里实现一个函数 debounce(fn, wait),要求:
1. 支持取消
2. 保留 this 上下文和参数
3. 补充 JSDoc
4. 顺便给出两个使用示例"
预期结果:生成一个包含 debounce 实现、JSDoc 注释和调用示例的文件,代码能直接运行。
判断成功的标准:函数逻辑正确,取消逻辑完整,this 和参数透传没丢,注释说明了参数含义。
常见失败原因:如果生成的代码只实现了基础版本没有取消功能,说明提示词里的约束没被完整采纳,可以把要求拆成多轮,先让它出方案再让它写实现。如果直接报网络错误,回到第 4 节检查认证。
6.2 仓库级理解与解释测试
测试目的:验证代理读取多文件、串联上下文的能力。这是编码代理相对普通聊天机器人的核心优势。
操作步骤:找一个有实际逻辑的小仓库,让它解释调用链。
bash复制codex exec "阅读 src 目录,说明一次 HTTP 请求从进入路由到返回响应,
中间经过了哪些函数,每个函数负责什么,用列表形式输出"
预期结果:给出完整的调用链,函数名、文件路径、职责描述都对得上。
判断成功的标准:能准确指出中间件顺序、参数在哪里被校验、错误在哪里被捕获。
常见失败原因:如果回答笼统、没有具体文件名,通常是它没真正读文件就凭猜测作答。可以在提示词里明确要求"每个结论都要标注文件名和行号",强制它去读。如果读到的文件明显不全,检查第 5.3 节的忽略配置。
6.3 多文件重构测试
测试目的:验证跨文件改动的完整性和一致性。这是实际工作里用得最多的能力。
操作步骤:先准备一个有多处重复代码的仓库,然后发起重构。
bash复制codex exec "把 src 下所有文件里的字符串 'user_id' 统一改成 'userId',
要求:
1. 只改代码里的变量名和属性名,不要改数据库字段和 API 请求体
2. 改完列出所有被修改的文件
3. 不要动 src/generated 目录"
预期结果:代理先搜索所有出现位置,逐个判断上下文,只改该改的地方,最后给出修改清单。
判断成功的标准:用 git diff 检查,改动范围准确,没有误伤数据库映射和接口契约。
常见失败原因:最常见的是过度替换,把不该改的也改了。解决办法是在 AGENTS.md 里写清楚哪些目录和文件禁止改动,同时在提示词里给出反例。另一个常见问题是漏改,尤其是有动态拼接的场景,改完最好再让它自查一遍。
6.4 Bug 定位测试
测试目的:验证代理根据报错信息反查代码的能力。
操作步骤:准备一个能稳定复现的报错,把完整堆栈给它。
bash复制codex exec "运行 npm test 后出现下面的报错,请定位原因并给出修复方案:
TypeError: Cannot read properties of undefined (reading 'id')
at formatUser (src/utils/user.ts:23:18)
at src/service/order.ts:56:20
要求:先说明根因,再给最小改动方案,不要重写整个函数"
预期结果:定位到具体行,说明为什么是 undefined,给出针对性修复。
判断成功的标准:根因分析正确,修复方案是改一行能解决的,不是把整个函数推翻重写。
常见失败原因:如果它直接开始改代码而没有先分析,说明提示词约束不够,明确要求"先输出分析再改"。如果定位偏了,把相关文件内容一起贴进提示词,减少它的搜索范围。
6.5 测试补全测试
测试目的:验证批量补测试的可行性和质量。这类任务最适合脚本化。
操作步骤:挑一个没有测试的模块,让它补齐。
bash复制codex exec "给 src/utils/date.ts 补充单元测试,要求:
1. 使用项目现有的 Vitest
2. 覆盖正常输入、边界输入、非法输入三类
3. 每个用例的断言要具体,不要只写 expect(result).toBeTruthy()
4. 跑一遍测试确认全部通过"
预期结果:生成测试文件,覆盖三类场景,运行后全部通过。
判断成功的标准:测试能跑通,断言有实际意义,边界用例不是凑数。
常见失败原因:如果生成的断言过于宽松,在提示词里直接禁止 toBeTruthy 这类模糊断言。如果测试跑不通,可能是它没读测试配置,把配置文件路径写进提示词。如果用例数量明显偏少,说明它对边界情况理解不足,可以给出具体的边界示例让它参照。
7. Codex 接口 API 与批量任务
单次手动调用解决不了规模化问题。真正提效的是把编码代理接进脚本,让它在无人值守的情况下跑批。
7.1 非交互模式的脚本化用法
codex exec 本身就是为脚本设计的。最基本的用法是循环处理一批任务:
bash复制#!/usr/bin/env bash
# batch-refactor.sh
# 用途:对一批文件逐个执行相同的改造任务
set -euo pipefail
TARGET_DIR="./src/legacy"
LOG_DIR="./logs"
mkdir -p "$LOG_DIR"
for file in "$TARGET_DIR"/*.ts; do
name=$(basename "$file" .ts)
echo ">>> 处理 $file"
# 每个文件单独一次非交互调用,失败不中断整体流程
if codex exec "把 $file 中的回调风格改写为 async/await,保持对外行为不变,改完输出变更摘要" \
> "$LOG_DIR/$name.log" 2>&1; then
echo " 成功"
else
echo " 失败,见 $LOG_DIR/$name.log"
fi
done
这段脚本有三个细节值得注意。set -euo pipefail 让脚本在异常时及时暴露问题,避免静默失败。日志按文件拆分,出问题能精确定位。循环里的失败不会中断整体,这是因为批量任务里个别失败很正常,全量重跑的成本更高。
7.2 结构化输出与结果解析
要让脚本能判断任务成败,就不能只靠看文字。用 --json 拿到结构化输出,解析更可靠:
bash复制codex exec --json "检查 src 目录下所有文件是否存在未处理的 Promise 拒绝,
以 JSON 数组输出,每项包含 file 和 line 字段" > result.json
然后用 Python 处理结果:
python复制import json
import subprocess
def scan_promise_issues(workdir: str) -> list[dict]:
"""调用编码代理扫描未处理的 Promise 拒绝,返回结构化结果"""
prompt = (
"检查 src 目录下所有文件是否存在未处理的 Promise 拒绝,"
"以 JSON 数组输出,每项包含 file 和 line 字段,不要输出其他内容"
)
proc = subprocess.run(
["codex", "exec", "--json", prompt],
cwd=workdir,
capture_output=True,
text=True,
timeout=600,
)
if proc.returncode != 0:
raise RuntimeError(f"代理执行失败: {proc.stderr[:500]}")
try:
return json.loads(proc.stdout)
except json.JSONDecodeError:
# 模型偶尔会包一层说明文字,这里做容错
start = proc.stdout.find("[")
end = proc.stdout.rfind("]") + 1
return json.loads(proc.stdout[start:end]) if start >= 0 else []
if __name__ == "__main__":
issues = scan_promise_issues("./my-project")
for item in issues:
print(f"{item['file']}:{item['line']}")
这里的关键是要求模型只输出 JSON。提示词里加上"不要输出其他内容"能大幅提升解析成功率。即便如此也要做容错,因为模型有时会加一句前缀说明,直接 json.loads 会失败。
7.3 调用模型接口的通用模板
如果你想在自己的服务里直接调用模型能力,而不是通过 CLI,可以用标准接口。下面是通用模板,具体的模型名、端点路径和参数需要按官方文档调整:
python复制import os
import requests
def ask_codex(prompt: str, model: str = "gpt-5-codex") -> str:
"""调用模型接口的通用封装,端点与模型名以官方文档为准"""
url = "https://api.openai.com/v1/responses"
headers = {
"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
"Content-Type": "application/json",
}
payload = {
"model": model,
"input": prompt,
"max_output_tokens": 2048,
}
resp = requests.post(url, headers=headers, json=payload, timeout=180)
resp.raise_for_status()
data = resp.json()
# 不同接口版本的返回结构不同,取文本时要做兼容
if "output_text" in data:
return data["output_text"]
for item in data.get("output", []):
for content in item.get("content", []):
if content.get("type") == "output_text":
return content.get("text", "")
return ""
封装里有两点经验。第一是超时时间要给足,代码类任务输出长,180 秒不算多。第二是返回结构不要写死,接口版本迭代时字段可能变,做一层兼容能省掉不少维护工作。
7.4 批量任务的设计要点
真要在团队里用批量任务,有四件事必须提前想清楚。
任务粒度。一个任务处理一个文件、一个函数还是一整个目录?粒度太细,调用次数多、成本高;粒度太粗,单次失败影响面大、重试成本高。经验值是"一个任务对应一次可回滚的改动",通常是单个文件级别。
幂等性。批量脚本必须能重复跑而不产生副作用。最简单的做法是每次跑之前先 git status 检查工作区干净,跑完让代理自己提交或者由脚本打 patch 保存,失败时直接 git checkout . 回滚。
失败重试。模型输出有随机性,同一个任务第一次失败第二次可能成功。建议设置最多重试两到三次,每次重试时把上次的失败原因作为额外上下文传进去,成功率会明显提升。
进度与日志。批量任务动辄跑几十分钟,没有日志就是黑盒。至少记录每个任务的开始时间、结束时间、退出码和 token 用量,出问题能回溯,也方便做成本核算。
json复制{
"batch_name": "legacy-refactor",
"input_dir": "./src/legacy",
"output_dir": "./logs",
"max_retries": 2,
"timeout_seconds": 600,
"concurrency": 1,
"dry_run": true,
"record_token_usage": true
}
上面这份配置是通用的队列描述模板,字段含义按你的调度脚本自行实现。concurrency 建议先设成 1 串行跑,确认稳定之后再考虑并发。编码代理会改文件,并发跑同一目录很容易互相覆盖。
8. 成本观察与性能调优
价格降了不代表可以随便烧。编码任务天生费 token,建立一套观察习惯比单纯关注单价更有价值。
8.1 消耗从哪里来
一次编码任务的 token 消耗大致分三块。输入上下文占大头,代理读的每个文件都要进上下文,一个中型任务读十几个文件很常见。工具调用往返是第二块,每次执行命令、读文件、写文件都会产生一轮请求和响应。最终输出反而占比最小,一个补丁通常只有几十行。
这个分布决定了两件事。第一,控制上下文比控制输出更有效,少读无用文件能直接降本。第二,任务拆得太碎反而更贵,因为每次都要重新建立上下文,碎片化任务的固定开销很高。
8.2 观察方式
CLI 一般会在输出里带上用量信息,或者在非交互模式下通过 --json 拿到结构化数据。如果你自己封装了接口调用,直接在返回体里读 usage 字段最准确:
python复制import requests
import os
def ask_with_usage(prompt: str) -> tuple[str, dict]:
"""调用接口并同时返回用量信息,便于成本核算"""
resp = requests.post(
"https://api.openai.com/v1/responses",
headers={
"Authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",
"Content-Type": "application/json",
},
json={"model": "gpt-5-codex", "input": prompt},
timeout=180,
)
resp.raise_for_status()
data = resp.json()
usage = data.get("usage", {})
text = data.get("output_text", "")
return text, usage
text, usage = ask_with_usage("给 utils/date.ts 补三个边界测试")
print(f"输入 token: {usage.get('input_tokens', 'N/A')}")
print(f"输出 token: {usage.get('output_tokens', 'N/A')}")
print(f"缓存命中: {usage.get('cached_tokens', 0)}")
把每次调用的用量落库或者写日志,跑一周就能看出规律:哪类任务最费,哪个目录最容易被反复读取,哪个提示词写法效率最低。这些数据比任何优化建议都实用。
8.3 降低消耗的实用手段
缩小可读范围。在第 5.3 节讲过,忽略配置能把噪音文件挡在外面。一个几千文件的仓库,如果每次任务都扫一遍,成本会高得离谱。
复用上下文。交互模式下连续提问比反复启动新会话便宜得多,因为上下文可以复用。如果有一系列相关问题,就在一个会话里问完。
利用缓存。多数服务对相同前缀的请求有缓存优惠,价格明显低于普通输入。把稳定的系统提示、项目规则、常用文档放在前面,把变化的部分放在后面,能吃到这部分折扣。这也是为什么 AGENTS.md 值得精心维护——它每次都一样,正好命中缓存。
控制输出长度。在提示词里明确"只输出修改后的代码,不要解释",能显著减少输出 token。需要解释时单独问一轮,比让它一边改一边讲更划算。
先跑 dry run。批量任务第一次先只让代理输出"打算改哪些文件",确认范围正确再真正执行。这一轮成本很低,能避免大面积误改后的返工。
8.4 价格对比的正确方法
回到标题里的那句话。要比价,先统一口径。按下面这几个维度逐项核对,才不会被单点数字误导:
| 对比维度 | 说明 | 注意点 |
|---|---|---|
| 输入单价 | 每百万 token 的价格 | 不同上下文长度档位价格可能不同 |
| 输出单价 | 每百万 token 的价格 | 通常显著高于输入价 |
| 缓存命中价 | 重复前缀的优惠价 | 编码任务里占比很高,必须看 |
| 批处理价 | 异步批量的折扣 | 适合离线任务,不适合交互 |
| 长上下文加价 | 超长输入的额外费用 | 大仓库场景要特别注意 |
| 免费额度 | 新账号或特定档位的赠送 | 短期能省,长期不可依赖 |
真正的成本 = 输入量 × 输入单价 + 输出量 × 输出单价 + 缓存未命中部分 × 普通价。只看单价不看用量结构,很容易得出相反结论。举个常见情况:A 模型单价低但读文件多,B 模型单价高但上下文压缩得好,实际任务是 B 更便宜。所以更靠谱的做法是拿同一批真实任务跑一遍,统计实际消耗再对比。
具体数字这里不写,因为定价会调整,写死就是误导。建议直接看官方定价页,把上面这张表的六行逐项填上,再结合自己的用量结构算一遍。
9. Codex 常见问题与排查方法
下面这张表覆盖了使用过程中最常遇到的问题,按现象、原因、排查、解决四列整理。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
提示 command not found: codex |
全局 bin 目录不在 PATH | npm config get prefix 看路径 |
把该路径下的 bin 加进 PATH |
| 安装时报权限错误 | 全局目录属主混乱 | 检查 npm config get prefix 指向 |
改成用户级 prefix,不要用 sudo |
| 启动后提示未认证 | 未登录或 Key 未生效 | echo $OPENAI_API_KEY 看是否为空 |
重新 codex login 或设置环境变量 |
| 请求超时 | 网络不通或服务端限流 | 用最小任务测试 | 检查网络,降低并发,间隔重试 |
| 代理不读文件就回答 | 提示词没要求引用来源 | 看回答里有没有文件名 | 明确要求"每个结论标注文件与行号" |
| 改错文件或过度替换 | 缺少项目规则约束 | git diff 检查改动范围 |
完善 AGENTS.md,写明禁止目录 |
| 改完项目跑不起来 | 未执行验证步骤 | 手动跑构建和测试 | 提示词里加"改完必须跑测试" |
| 上下文太长导致中断 | 仓库太大,读入文件过多 | 观察单次任务读取的文件数 | 配忽略规则,缩小任务范围 |
| 批量任务中途卡住 | 某个任务超时无退出 | 看日志里最后一个处理项 | 加超时控制和失败跳过 |
| 输出格式不是 JSON | 提示词约束不够强 | 看首尾是否有额外说明 | 强调"只输出 JSON",加解析容错 |
| 会话越用越慢 | 上下文累积过多 | 观察多轮后的响应时间 | 定期新开会话,任务的归任务 |
| 同一任务结果不稳定 | 模型输出有随机性 | 重复跑对比差异 | 多跑几次择优,或先出方案再实现 |
表里有几条值得展开。
关于"改完项目跑不起来"。这是最高频的翻车方式。代理改完代码自己觉得没问题,但实际构建会挂。稳妥的做法是把验证写进提示词:"改完之后执行 pnpm build 和 pnpm test,如果失败就自己修,直到通过为止。"让它自己闭环,比你在外面反复催效率高得多。
关于"批量任务中途卡住"。编码代理执行命令时如果遇到交互式提示(比如某个命令在等输入),会一直挂着。解决办法是给每个任务设超时,超时直接 kill 并记录失败,不要让它无限等待。同时尽量在提示词里要求使用非交互参数,比如 --yes、--no-input、CI=true。
关于"会话越用越慢"。上下文不是免费的,累积到一定程度响应会明显变慢,而且早期信息会被稀释。养成习惯:一个任务一个会话,任务完成就退出。需要延续时把关键结论摘出来带过去,比直接把整个历史拖过去更高效。
关于"输出格式不稳定"。要求 JSON 时,模型大概率会给你 JSON,但偶尔会加一句"好的,结果如下"。所以解析必须做容错,用 find('[') 和 rfind(']') 定位边界是最简单有效的方法。更严谨的做法是用 schema 约束输出,具体支持情况看接口文档。
10. 最佳实践与落地建议
把前面几节的经验收敛成几条可以直接执行的原则。
从最小任务开始。第一次用不要上来就让它重构整个仓库。先让它解释一个文件、改一个函数、补一个测试,确认行为符合预期之后再放大任务范围。这个顺序能帮你快速建立对它能力的准确预期。
把规则写进仓库。AGENTS.md 是这个工具最有价值的配套文件。每次它犯错,就补一条规则。两周之后你会发现,同样类型的错误不再重复出现,这才是真正的提效。规则文件也要进版本控制,团队共享。
保留可回滚的现场。每次批量任务之前确认工作区干净,任务之后用 git diff 检查,不满意直接 git checkout .。这条习惯能让你在试错时没有心理负担,反而更愿意尝试更复杂的任务。
目录分离。源码、脚本、日志、输出结果分开放。批量任务的日志按任务名和日期分目录,跑一个月之后你会庆幸当初分了。
权限按最小化给。默认用 workspace-write,只在容器或一次性目录里用完全放行模式。生产仓库里永远不要长期开启绕过审批的参数,这个成本省不得。
验证写进流程。让代理自己跑构建和测试,在提示词里明确要求。如果它跑不通就自己修,形成闭环。这一步能挡掉大部分低级错误。
关注真实成本而非单价。建立用量记录,按任务类型统计消耗,找出最费的那几类任务针对性优化。单价只是其中一环,上下文管理的影响往往更大。
合成内容要复核。代理生成的代码需要人来看,尤其是涉及权限、加密、数据库操作的部分。它的作用是减少打字量,不是替代代码审查。
合规底线不能松。上传到服务的代码要确认不含密钥、客户数据、未公开的商业信息。涉及第三方代码时要确认许可证兼容。生成的代码在合入前做一次依赖和许可证扫描,成本很低但能避免大麻烦。
团队内先小范围试点。挑一两个愿意折腾的同学先跑两周,积累出适合自己项目的 AGENTS.md 和提示词模板,再推广。直接全员铺开,容易因为个别人的糟糕体验导致整体被否定。
Codex 这次降价最直接的影响,是把"要不要用它"这个问题从成本考量变成了习惯考量。当单次任务的开销低到可以忽略时,真正决定效率的就不再是价格,而是你有没有把项目规则写清楚、有没有把验证流程搭起来、有没有养成小步试错的习惯。这三件事做到位,一个命令行编码代理能省下的时间,远超它本身的费用。
如果只挑一件事先做,建议是把 AGENTS.md 写起来。它不需要额外成本,却能让后面每一次调用都更准确。等这份文件积累到几十条规则的时候,你会发现自己已经不太需要反复纠正它了——那时候工具才算真正接进了你的工作流。
