在终端里直接让 AI 读代码、改代码、跑测试,这种体验试过一次就很难回去。但真正落地时,多数人卡在同一处:客户端装好了,却不知道怎么把模型换成自己能负担、能掌控的那一个;网上教程要么只讲一半,要么把配置写成“照抄就行”,一旦报错就无从下手。
本文围绕 Codex CLI 展开,从安装、配置、模型接入一路讲到实战和排错。目标读者是已经会写代码、想在本地或服务器上跑一个命令行编码助手的开发者,包括后端、前端、测试和运维同学。读完之后你应该能独立完成三件事:把 Codex CLI 装好并验证;用自备 API Key 接入兼容 OpenAI 协议的平台(本文以 DeepSeek 为例)或本地模型;在真实项目里用它完成一次小需求的开发,并且知道怎么控制成本、怎么守住安全边界。
需要先说明一点:本文所有操作都基于官方客户端加自己申请的 API Key,不涉及任何共享账号、来源不明的第三方中转服务,也不涉及绕过平台计费的手段。想省钱有正当路子,下面会具体讲。
1. 概念先行:Codex 到底是个什么东西
1.1 Codex CLI 的定位
Codex CLI 是运行在终端里的编码代理(Coding Agent)。它和你在网页上聊天问代码最大的区别在于:它拥有对你当前工作目录的读写能力,可以自己决定去读哪个文件、执行哪条命令、改哪几行,然后根据命令输出决定下一步。所以它更像是一个坐到你旁边、能动手的结对伙伴,而不是一个只会给建议的顾问。
一个典型的交互循环是这样的:你描述需求,它先列出要查看的文件,读完给你一段方案,等你确认后开始改代码,改完再自己跑一遍测试,把失败信息读回来继续修。整个过程里每一步操作是否放行,取决于你设置的审批策略和沙箱模式,这两块后面会单独拆开讲。
1.2 它和 IDE 补全、聊天问答的区别
IDE 里的智能补全解决的是“这一行怎么写”,上下文通常只有当前文件附近几百行;网页聊天解决的是“这段逻辑对不对”,但你得手动把代码复制粘贴过去。Codex CLI 解决的是“这个功能怎么落地”,它的上下文来自整个仓库,动作是文件级的修改和命令级的执行。
这也是为什么它对模型能力的要求更高。补全模型只需要续写,代理模型需要稳定地输出结构化的工具调用(读文件、写文件、执行命令),并且在多轮里不跑偏。选模型时,工具调用能力比单纯的代码生成分数更值得关注。
1.3 关于“免费算力”的几点实话
网络上有不少“免费连接器”“共享中转”之类的说法,这里必须把风险讲清楚。第一是密钥风险,来源不明的 Key 随时可能被回收,你的工作流会在某天毫无征兆地断掉;第二是数据风险,你的代码和提示词会经过对方的服务器,商业项目里这等于把源码交出去;第三是稳定性风险,这类服务没有 SLA,限流、超时、返回格式错乱都很常见,而代理型工具对返回格式极其敏感,一个字段不对整轮就白跑。
正当的降本方式其实有三种。一是用本地模型,Ollama 这类工具可以在本机跑开源代码模型,没有 token 费用,代价是能力和速度受硬件限制。二是用按量计费的 API,先把用量摸清楚再决定投入,DeepSeek 这类平台的单价相对友好,个人开发者一个月几十块能跑不少活。三是把上下文和提示词管好,代理工具的开销大头往往不是模型单价,而是无效的重复读取和超长上下文,这一块优化空间最大,第七节会详细展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与版本说明
2.1 系统与运行时要求
Codex CLI 跨平台,macOS、Linux 和 Windows(建议在 WSL 里用)都可以跑。它的分发方式之一是 npm 包,因此需要 Node.js 运行时。建议使用当前 LTS 版本,Node 18 是下限,实际使用时更新的 LTS 体验更好,尤其是在依赖安装和 ESM 支持上。
具体版本请以官方 README 为准,因为这类工具迭代很快,本文示例以常见环境演示配置思路,版本细节请按你的实际环境调整。先确认一下基础环境:
bash复制# 检查 Node.js 与 npm 版本
node -v
npm -v
# 检查是否已安装过 codex
which codex || echo "not installed"
2.2 三种安装方式
最通用的是 npm 全局安装:
bash复制# 全局安装 Codex CLI
npm install -g @openai/codex
# 验证安装
codex --version
macOS 用户如果习惯 Homebrew,可以查一下对应的 formula 名称再用 brew install 安装,命令以官方文档为准。第三种方式是直接下载对应平台的二进制包,适合内网环境或没有 Node 运行时的机器,下载后解压、加执行权限、放进 PATH 即可。
如果你在公司内网,npm 可能需要配置镜像源。这里要注意别把镜像源和 API 地址搞混:镜像源只影响安装包下载,不影响运行时调用的模型接口地址。
bash复制# 仅示意:按公司规范配置 npm 源
npm config set registry https://registry.npmmirror.com
npm config get registry
2.3 配置目录与文件位置
Codex CLI 的用户级配置默认放在家目录下的 .codex 目录里,核心文件是 config.toml。这个目录不跟着项目走,属于“当前用户”的全局设置;项目级的约定则通过仓库根目录的 AGENTS.md 来表达。理解这个分层很关键:模型、密钥、沙箱这些机器相关的配置放全局,项目规则、编码规范、测试命令放仓库。
bash复制# 查看配置目录
ls -la ~/.codex
# 如果没有配置文件,手动创建
mkdir -p ~/.codex
touch ~/.codex/config.toml
团队协作时,~/.codex/config.toml 一定要加进全局 gitignore 的思路里——它不该被提交,因为里面会引用你的密钥环境变量名,甚至可能直接写了不该外传的地址。仓库里只提交 AGENTS.md。
2.4 升级与卸载
升级直接用 npm 的全局更新命令,升级后务必重新跑一次 codex --version 和一次最小会话,确认配置项没有被新版本废弃。这类工具在版本迭代中调整配置键名是常事,升级后突然报“未知配置项”,八成就是这个原因。
bash复制# 升级
npm update -g @openai/codex
# 卸载
npm uninstall -g @openai/codex
3. 核心机制:模型提供方、认证与沙箱
3.1 model_provider 与 base_url 的作用
Codex CLI 把“用哪个模型”拆成了两层:model 指定模型名,model_provider 指定去哪调用。提供方(provider)本质是一组连接参数,包含 base_url(接口根地址)、env_key(从哪个环境变量读密钥)、wire_api(走哪种请求协议)。
这个设计是整篇文章的关键,因为它意味着只要对方兼容 OpenAI 的接口协议,你就能把 Codex 指向它。DeepSeek 提供了 OpenAI 兼容的接口,所以接入是可行的;本地 Ollama 也提供了兼容层,同样可以接。wire_api 通常有 chat(Chat Completions)和 responses(Responses API)两种取值,第三方平台一般用 chat。
3.2 两条认证路径:账号登录与环境变量
Codex CLI 支持用账号登录的方式认证,也支持通过 env_key 指定的环境变量读取 API Key。前者适合已经在用官方订阅的人,后者适合本文这种“自备 Key、按量计费”的场景。
选环境变量这条路,有一个细节值得强调:配置文件里写的是变量名,不是密钥本身。这样做的好处是配置可以分享、可以进版本库(脱敏后),密钥则留在 shell 环境或密钥管理工具里。很多人图省事把 key 直接写进 toml,结果一次截图就把密钥泄了。
3.3 沙箱与审批:安全边界在哪里
代理型工具最危险的地方在于它能执行命令。Codex CLI 用两层机制约束:沙箱(sandbox)限制它能写哪些目录、能不能联网;审批策略(approval policy)决定哪些动作需要你点头。
常见的组合是“工作区可写 + 按需审批”:它可以在当前仓库里自由读写,但涉及执行命令、访问工作区之外的路径时需要确认。第一次使用时不要一上来就放开全部权限,尤其是在有生产配置、有数据库连接串的仓库里。正确顺序是先在小仓库里试,摸清它的行为模式,再逐步放宽。
3.4 AGENTS.md:把项目规则写进仓库
AGENTS.md 是给代理看的项目说明书。它和 README 的区别是:README 写给人看,讲项目是什么;AGENTS.md 写给模型看,讲在这个仓库里该怎么干活。内容包括目录结构约定、代码风格、测试命令、禁止改动的目录、提交信息格式等。
写好这个文件,收益非常直接:模型不需要每次都靠猜来定位测试入口,也不会把生成文件写进 src。它是降低 token 消耗最划算的一次性投入,因为省下的是每一轮对话的重复探索。
4. 接入 DeepSeek:完整配置实战
4.1 获取并安全存放 API Key
先在 DeepSeek 开放平台注册账号并创建 API Key,具体入口和是否有新用户赠送额度以官网为准,不建议把工作方案建立在这类活动上。拿到 Key 之后,把它放进环境变量,不要直接写进配置文件。
bash复制# 临时生效(当前终端会话)
export DEEPSEEK_API_KEY="sk-你的真实密钥"
# 永久生效:写入 shell 配置文件
echo 'export DEEPSEEK_API_KEY="sk-你的真实密钥"' >> ~/.zshrc
source ~/.zshrc
# 验证是否读到了(只打印前几位,避免泄露)
echo "${DEEPSEEK_API_KEY:0:6}..."
需要提醒的是,把密钥写进 ~/.zshrc 只是最省事的做法,安全性一般。如果你在共享机器或公司电脑上工作,更推荐用系统钥匙串、direnv 配合被 gitignore 的 .env,或者密钥管理命令行工具注入。个人机器上,至少确保 .zshrc 的权限是 600。
bash复制chmod 600 ~/.zshrc
4.2 编写 config.toml
下面是接入 DeepSeek 的最小可用配置。注意缩进和段落名,TOML 对层级敏感,[model_providers.deepseek] 这种带点的表名表示嵌套结构,写错了会变成两个不相干的对象。
toml复制# 文件路径:~/.codex/config.toml
# 默认使用的模型名,需与提供方支持的模型列表一致
model = "deepseek-chat"
# 默认使用的提供方,对应下面 [model_providers.deepseek] 这一段
model_provider = "deepseek"
# 审批策略:on-request 表示由模型主动请求时才需要用户确认
approval_policy = "on-request"
# 沙箱模式:workspace-write 允许在工作区内读写,工作区之外仍受限制
sandbox_mode = "workspace-write"
# 自定义模型提供方:DeepSeek 的 OpenAI 兼容接口
[model_providers.deepseek]
name = "DeepSeek"
base_url = "https://api.deepseek.com/v1"
env_key = "DEEPSEEK_API_KEY"
wire_api = "chat"
逐项解释一下。base_url 是接口根地址,多数 OpenAI 兼容平台都要带 /v1 后缀,少写会 404。env_key 填的是环境变量名而不是密钥值,Codex 会在启动时去读这个变量,读不到会直接报错提示。wire_api = "chat" 表示走 Chat Completions 协议,这是第三方平台兼容性最好的选择;如果你的提供方明确支持 Responses API 且你希望用它的特性,可以改成 responses,但第三方平台通常不支持。
approval_policy 和 sandbox_mode 的取值在不同版本里略有差异,建议用 codex --help 或官方文档核对当前版本支持的值。写了一个不认识的枚举值,表现通常是启动即报错,而不是静默忽略。
4.3 用 profile 管理多套配置
真实使用中你很少只用一个模型:日常改代码想要快,复杂重构想要强,离线时只能用本地。Codex CLI 支持 profile,把多套组合命名保存,用 --profile 切换。
toml复制# 追加到 ~/.codex/config.toml
# 场景一:复杂重构,用推理型模型,慢但想得更清楚
[profiles.reason]
model = "deepseek-reasoner"
model_provider = "deepseek"
# 场景二:日常小改动,用通用对话模型,响应更快
[profiles.fast]
model = "deepseek-chat"
model_provider = "deepseek"
# 本地模型提供方:Ollama 默认监听 11434
[model_providers.ollama]
name = "Ollama"
base_url = "http://localhost:11434/v1"
env_key = "OLLAMA_API_KEY"
wire_api = "chat"
# 场景三:断网或敏感代码,全部在本机跑
[profiles.local]
model = "qwen2.5-coder:14b"
model_provider = "ollama"
本地模型这一段需要注意:Ollama 本身不校验密钥,但 Codex 会按 env_key 去读变量,读不到可能报错,所以随便设一个占位值即可。
bash复制# 给本地提供方一个占位值,避免启动时读取失败
export OLLAMA_API_KEY="ollama"
# 确认本地模型已经拉好
ollama list
模型名要和你本地实际拉取的模型标签一致,写错了会返回“model not found”。另外要清楚一点:本地小参数模型的工具调用能力普遍弱于云端大模型,做纯问答没问题,让它自主改多文件项目容易反复横跳,适合当离线兜底而不是主力。
4.4 启动与验证
配置写好后先做一次最小验证,不要直接扔进大项目。
bash复制# 使用默认配置启动交互式会话
codex
# 指定 profile 启动
codex --profile fast
# 只做一次非交互式调用,适合快速验证连通性
codex exec "用一句话说明当前目录是什么项目"
进入交互界面后,可以先用一句低风险的话测试:
code复制请列出当前目录的结构,只输出说明,不要修改任何文件。
如果模型能正确列出目录并给出说明,说明模型、密钥、网络三条链路都通了。如果它开始尝试改文件,说明审批策略配得太松,回去把 approval_policy 调严。
4.5 常见配置错误对照
第一次配 config.toml 几乎都会踩坑。401 基本是密钥没读到,先确认 env_key 里的变量名和实际 export 的名字完全一致,注意大小写;404 多半是 base_url 少了 /v1 或者多写了一段路径;返回格式解析失败则通常是 wire_api 选错了,第三方平台改成 chat 一般能解决。还有一个隐蔽的坑:某些 shell 里 export 写在 if 分支内,非交互式会话读不到,用 codex exec 就会失败,而交互式终端里却正常。
5. 实战:用 Codex 完成一次真实的小需求
5.1 准备一个最小项目
为了让示例可复现,这里用一个极小的 Python 项目。目录结构如下:
code复制demo-calc/
├── AGENTS.md
├── src/
│ └── calc.py
└── tests/
└── test_calc.py
python复制# 文件路径:src/calc.py
def divide(a, b):
return a / b
def average(numbers):
total = 0
for n in numbers:
total += n
return divide(total, len(numbers))
python复制# 文件路径:tests/test_calc.py
from src.calc import average, divide
def test_divide_normal():
assert divide(6, 3) == 2
def test_average_normal():
assert average([1, 2, 3]) == 2
这段代码有两个明显的健壮性问题:除零没有处理,空列表会抛 ZeroDivisionError。我们让 Codex 来找并修掉。
5.2 写一份 AGENTS.md
markdown复制# 项目约定
## 技术栈
- Python 3.10+
- 测试框架:pytest
## 目录结构
- `src/` 放业务代码,不要在此目录生成临时文件
- `tests/` 放测试,测试文件命名 `test_*.py`
## 编码规范
- 函数必须有类型注解
- 异常信息使用中文,并说明参数含义
- 不允许吞掉异常后返回 None
## 常用命令
- 运行测试:`pytest -q`
- 单文件测试:`pytest tests/test_calc.py -q`
这份文件的价值在于,后面你不需要每次重复“用 pytest”“别在 src 里建文件”。一次写好,长期省 token。
5.3 让它先出方案,再动手
启动会话后,第一句不要直接说“帮我改好”,而是要求它先分析。这个顺序很重要:先看方案,你可以在它还便宜的时候纠正方向,避免它改了十个文件你才发现思路错了。
code复制先不要改代码。阅读 src/calc.py 和 tests/test_calc.py,
列出这个模块在边界情况下的问题,每条给出一个会触发它的输入。
预期它会指出:b 为 0 时 divide 抛 ZeroDivisionError;numbers 为空时 average 也会抛 ZeroDivisionError,并且 total 初始值写死为 0 无法处理其他数值类型。确认方案没问题后,再让它动手:
code复制按上面的分析修改 src/calc.py,要求:
1. divide 在除数为 0 时抛 ValueError,信息里带两个入参
2. average 在入参为空时抛 ValueError
3. 补充对应单元测试
4. 修改完成后运行 pytest -q
5.4 审查 diff 与测试结果
改完后它会给出变更摘要,并跑一次测试。这一步不要跳过人工审查,重点看三件事:异常类型选得对不对(ValueError 比 ZeroDivisionError 更贴合“参数非法”的语义)、测试是否真的覆盖了边界(有些模型会写一个永远为真的断言)、有没有顺手改动无关文件。
python复制# 文件路径:src/calc.py(修改后)
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError(f"除数不能为 0,当前入参:a={a}, b={b}")
return a / b
def average(numbers: list[float]) -> float:
if not numbers:
raise ValueError("numbers 不能为空列表,至少需要一个数值")
return divide(sum(numbers), len(numbers))
对应测试:
python复制# 文件路径:tests/test_calc.py(追加)
import pytest
from src.calc import average, divide
def test_divide_by_zero_raises():
with pytest.raises(ValueError, match="除数不能为 0"):
divide(1, 0)
def test_average_empty_raises():
with pytest.raises(ValueError, match="不能为空列表"):
average([])
验证命令与预期输出:
bash复制pytest -q
# 预期输出类似:
# ....
# 4 passed in 0.03s
5.5 用非交互模式接入脚本
确认交互模式跑顺之后,可以把一些固定任务写成非交互调用,比如提交前的静态检查。注意 codex exec 的可用性和参数在不同版本里可能有差异,用 codex exec --help 确认后再用。
bash复制# 只做审查,要求不改文件,把结果输出到文件里
codex exec --profile fast \
"审查 src/ 下所有函数的边界处理,只输出问题清单,禁止修改任何文件" \
> review.md
这种用法适合放进 CI 的提示环节,但不要让它自动改代码后直接部署,代理的输出需要人来兜底。
6. 常见问题与排查思路
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 启动报 401 未授权 | 环境变量没读到,或变量名与 env_key 不一致 |
echo $变量名 验证;确认大小写;非交互式 shell 也要能读到 |
| 请求返回 404 | base_url 少了 /v1 或路径多写 |
按平台文档核对根地址,不要自己拼接完整 URL |
| 返回内容解析失败、频繁重试 | wire_api 选错 |
第三方平台改为 chat |
| 提示未知模型 | 模型名与提供方列表不一致 | 用平台文档里的准确模型标识 |
交互式能用,codex exec 报错 |
变量只在交互式 shell 中导出 | 把 export 移到 rc 文件顶层 |
| 改完代码跑不了测试 | 上下文里缺测试命令 | 在 AGENTS.md 里写明测试命令 |
| 频繁被审批打断 | 审批策略过严 | 在可信小仓库里逐步放宽,不要一步全开 |
| 本地模型答非所问 | 模型规模太小,工具调用不稳定 | 换更大的代码专用模型,或仅用于问答 |
排查顺序建议固定下来:先验证密钥(最小请求能不能通),再验证地址(状态码是不是 404),再看协议(能不能正常解析),最后看模型能力(是不是答得不对)。按这个顺序走,八成问题在前两步就能定位,不用反复怀疑模型。
7. 最佳实践与工程建议
7.1 上下文与成本控制
代理型工具的费用大头在上下文重复。一个仓库几万行代码,如果每轮都全量读一遍,费用会很快上去。有效的做法是:把任务拆小,一轮只处理一个模块;用 AGENTS.md 提前告知目录结构,减少盲目探索;让它先输出计划再由你裁剪范围;避免在同一会话里连续做五个不相关的需求,及时开新会话清空上下文。
另一个容易忽略的点是缓存。很多平台对命中缓存的输入 token 打折,而缓存生效的前提是前缀稳定。也就是说,不要把变化的随机内容放在消息最前面,保持系统提示和项目说明在前、具体需求在后,命中率会明显更好。这些细节对长期使用的影响,比换一个更便宜的模型更实际。
7.2 密钥与配置管理
三条底线:密钥不进仓库、不进截图、不进日志。配置文件里只写环境变量名;项目里需要声明依赖的环境变量时,用 .env.example 写占位符而不是真实值;CI 里用平台提供的密钥注入机制,不要硬编码。
轮换也要有预案。给 Key 起一个有辨识度的名字(比如带上机器和用途),出问题时能快速定位是哪台机器在用;定期在平台后台检查用量,异常飙升通常是某次会话陷入了循环读取。真出问题,第一动作是吊销再重建,不要试图“先观察一下”。
7.3 沙箱与权限边界
把 Codex 当作一个有执行权限的助手来对待。默认让它只在工作区写,需要访问工作区之外时必须经过审批;有生产凭据的仓库,先把连接串换成占位值再让它读,或者干脆不在那台机器上跑代理;涉及数据库、部署脚本、批量删除这类操作,一定要在测试环境验证,并且坚持人工确认后再执行。代理再聪明也不会为你的生产事故负责。
7.4 团队协作规范
如果准备在团队里推广,建议先做三件事:统一 AGENTS.md 模板,让所有人的代理行为一致;统一模型和 profile 命名,减少“我这能跑你那不能跑”的争论;约定变更审查规则,代理产出的代码同样要走 Code Review,不能因为“是 AI 写的”就放松标准。还可以在 CI 里加一条轻量的格式检查,把代理常见的格式偏差挡在门外。
8. 学习路线与下一步
到这里,安装、配置、接入、实战、排错这几块已经串起来了。接下来值得深入的方向有三个:一是把 AGENTS.md 写得更精细,针对不同子模块给出更具体的规则,这是投入产出比最高的优化;二是尝试把工具用在测试补全和代码审查这类低风险高频场景,先积累信任再扩大范围;三是了解你所用平台的计费与缓存规则,把用量曲线看明白,比盲目换模型更省钱。
如果是个人练习,建议先用本地模型把流程跑通,确认交互习惯合适了再换成云端模型;如果是团队落地,建议先用一个小仓库试点两周,把审批策略和 AGENTS.md 调到顺手,再推广到主仓库。开始阶段最容易犯的错是一上来就把权限全开、把大仓库丢进去,然后被一堆莫名其妙的改动吓退。
最后留一个实操建议:第一次真正用它干活时,选一个你已经很熟的小需求,比如给一个函数补边界处理和单测。你熟悉这块代码,就能一眼看出它哪里改错了,这个反馈循环建立起来之后,再交给它更陌生的任务,判断力才靠得住。
