我一直觉得,Codex 这类本地 CLI Agent 工具,真正拉开差距的不是提示词写得有多花哨,而是配置链路吃得有多透。最近帮几个朋友排查问题,发现十有八九都卡在同一个地方:TOML 改了 model 没生效、AGENTS.md 写了但 Agent 不照做、环境变量和切换工具互相打架。如果你也在用 Codex 本地跑 Agent 任务,或者想把默认模型换成自己常用的模型服务,那这篇东西应该能帮你把这些链路彻底理顺。文章不会讲太多抽象概念,更多是我自己在实际配置过程中反复试出来的经验,尤其是那层最容易被忽略的配置优先级,我会放在最核心的位置来拆。
1. 本地自定义 Agent 与模型配置的整体思路
1.1 为什么本地要自己接管 Agent 和模型
默认安装完 Codex,你拿到的是一个开箱即用的命令行助手:选定模型、发起任务、看它在沙盒里帮你改代码。但问题在于,默认配置是面向官方服务的,一旦你有多套模型服务、多套工作目录,或者团队里要统一 Agent 的行为规范,默认配置就不够用了。我自己喜欢的做法是把它变成一个“带规则的本地工人”:既要知道模型从哪个接入端点来,也要知道项目里哪些代码不许动、哪些目录先读、哪些命令禁止执行。这套能力在 Codex 里是由两个文件共同承担的:config.toml 负责模型和运行环境,AGENTS.md 负责 Agent 的行为约束。
可能有人会问,为什么不用命令行参数一次性搞定?参数确实能覆盖很多东西,但每次敲一长串 --model 和 --config 不仅容易错,而且没法固化到团队协作里。配置文件的价值在于:它是默认值,是可以提交进仓库的规范,是新人拉下代码后 Agent 行为保持一致的基础。所以我的建议是:把自定义模型接入和项目级指令都落到文件层面,命令行参数只用于临时切换。这个思路听起来简单,实际执行时坑非常多,后面我会把每个文件的作用和边界讲清楚。
1.2 TOML 和 AGENTS.md 的分工
这两个文件的定位完全不同。config.toml 定义的是“用什么模型、调什么接口、开什么开关”,它决定了 Codex 进程启动时加载哪些接入信息,比如模型名称、模型提供方名称、API 服务地址、环境变量名。AGENTS.md 定义的是“用这套模型去执行任务时必须遵守什么规则”,它不决定调用哪个接口,但决定 Agent 在面临选择时倾向哪种处理方式。举个例子:你可以在 TOML 里指定一个擅长重构的模型,然后在 AGENTS.md 里规定“任何修改必须保留原有导出接口”,前者管能力,后者管行为。
| 维度 | config.toml | AGENTS.md |
|---|---|---|
| 核心职责 | 模型、Provider、运行参数 | 项目规则、角色设定、执行约束 |
| 作用阶段 | 进程启动、请求发起前 | 提示词组装后、任务执行前 |
| 谁能覆盖 | 环境变量、命令行参数 | 项目内更具体层级的 AGENTS.md |
| 是否入库 | 通常不入库,保留本机 | 建议入库,作为团队规范 |
从表格能看出,AGENTS.md 在项目里是可以分层的:全局目录放一份,具体项目再放一份,Codex 会把它们按顺序加载。这时候就有个常见误解——以为 AGENTS.md 写满了就能覆盖所有配置。它不是配置覆盖层,它影响的是模型的上下文;真要改模型、改接口,还是得回到 TOML 和环境变量这条链。所以我在配置前都会先问一句:这个问题是模型接入问题,还是行为约束问题?想清楚这个,基本就成功了一半。
2. TOML 配置拆解:模型、Provider 与常用参数
2.1 配置文件在哪、长什么样
Codex 的全局配置默认放在用户目录下的 ~/.codex/config.toml,如果你在 Windows 上跑桌面版,路径通常是 %USERPROFILE%.codex\config.toml。启动后它会按这个路径读取,如果文件不存在,Codex 会生成一份带默认值的文件。很多人喜欢把官方模板直接粘贴进去,结果发现自定义 Provider 没生效,原因往往是[model_providers]区块写错、少了闭合括号,或者api_key_env_var指向了一个根本没导出过的环境变量。我先给你看一份能正常工作的最小配置结构。
model = "deepseek-chat" model_provider = "deepseek" [model_providers.deepseek] name = "DeepSeek" base_url = "https://api.deepseek.com/v1" api_key_env_var = "DEEPSEEK_API_KEY" [model_providers.openai] name = "OpenAI" base_url = "https://api.openai.com/v1" api_key_env_var = "OPENAI_API_KEY"这份配置的核心逻辑是:顶层model指定默认模型名,model_provider指定用哪一个提供方,然后在[model_providers.*]下面定义每个提供方的服务地址和密钥来源。Codex 发起请求时,会去api_key_env_var对应的环境变量里取密钥,再往base_url拼接出请求地址。所以如果你只改了 model 却忘了改 model_provider,系统还是会用原来的提供方去请求原来的模型,报错也就顺理成章了。
2.2 接入第三方模型服务:以 DeepSeek 为例
先明确一个事实:Codex 使用的是 OpenAI 兼容的接口协议,所以凡是提供兼容接口的第三方模型服务,例如 DeepSeek、通义千问的开放平台、智谱开放平台、Kimi 开放平台等,理论上都能通过自定义 Provider 接进来。接入的关键就三件事:拿到服务地址、拿到密钥、知道模型名。拿 DeepSeek 举例,开放平台的服务地址是 https://api.deepseek.com/v1,常见模型名是 deepseek-chat 和 deepseek-reasoner,密钥在控制台申请。
配置步骤可以拆成这样:第一步,添加[model_providers.deepseek]区块,把上面那三个信息填进去;第二步,在终端里导出DEEPSEEK_API_KEY环境变量,或者在启动 Codex 前把它写进 shell 的启动脚本;第三步,把顶层model和model_provider改成 deepseek 对应的值。改完别急着跑,先执行codex --version或者启动一个最小任务确认进程能正常加载,我通常在配置完第一件事是让 Agent 跑一句whoami,用最小代价验证整条链路通没通。
这里有个实操细节值得记下来:第三方服务通常有自己的限流策略和上下文长度限制,而 Codex 默认的并发和重试策略是按官方服务设计的。接入之后如果频繁报超时,不要盲目调大并发,先去看服务的速率限制文档,再在 TOML 里适当降低相关参数。我见过有人把并发参数调到 20 去跑长任务,结果被服务端直接限流,反而比默认配置慢得多。
2.3 常用参数的调优经验
| 参数 | 作用 | 我的建议 |
|---|---|---|
| model | 默认模型名 | 按任务类型分别配,别在一个模型上死磕 |
| model_provider | 默认提供方 | 有多套服务时务必和 model 成对修改 |
| temperature | 采样温度 | 代码生成 0.1~0.3,脑暴类 0.7 |
| top_p | 核采样 | 一般保持默认,改了不一定更好 |
| sandbox_mode | 沙盒运行模式 | 默认先禁写,验证后在指定目录放开 |
温度这个参数我要多讲一句。很多人把它理解成“越高温越聪明”,其实它控制的是随机性。代码任务里你希望输出稳定、可复现,温度太高会出现同一段需求每次改法都不一样的情况;反过来,如果你想用它做架构方案或者写测试场景,稍微高一点反而能带来更多尝试。我自己的习惯是项目常规开发用 0.2,需要探索性方案时临时用命令参数覆盖到 0.7,而不是在全局配置里写死一个值。
还有一个容易忽略的参数是沙盒模式。Codex 默认会在沙盒里执行命令,这对本地文件是保护,但也经常导致 Agent 说“修改了文件”实际却没写进去,因为写操作被沙盒拦住了。如果你是在自己的开发机上跑个人项目,并且已经通过 AGENTS.md 约束了操作范围,可以在目标目录的配置里开放写权限。要注意的是:开放写权限意味着 Agent 拥有这个目录的实际操作能力,务必搭配自动确认限制使用,别在完全信任的目录之外随便开。
3. AGENTS.md:本地项目最该花时间写的文件
3.1 全局与项目级的生效规则
AGENTS.md 的加载规则是我在调试时花了最多时间的部分。Codex 会优先读取当前工作目录里的 AGENTS.md,同时也会读取用户全局目录下的那份,两边的内容会合并进提示词上下文。官方对这个文件的定位是“给 Agent 的项目使用说明”,你可以把它想成新员工入职第一天拿到的手册:告诉他团队代码规范、常用命令、禁忌操作和验收标准。
全局 AGENTS.md 适合放那些跨项目都不变的东西,比如“不允许修改 lock 文件”“提交前必须运行测试”“所有生成代码必须带类型注解”;项目级 AGENTS.md 适合放本项目特有的约定,比如“本目录 src/models 下的文件不要动”“后端启动命令是 npm run dev:api”。要注意的是,项目级文件不能替代全局文件,它是追加关系,不是替换关系,我在早期就踩过“项目里写了规则但全局规则仍然生效”的困惑,其实是加载机制本来如此。
3.2 一份可直接抄的 AGENTS.md 模板
# 项目 Agent 使用说明 ## 角色 - 你是本项目的后端开发助手,目标是帮助完成 API 层开发与调试。 ## 项目结构速览 - src/handlers:请求处理入口 - src/services:业务逻辑层 - src/tests:测试目录,新增逻辑必须补测试 ## 常用命令 - 启动开发服务:npm run dev - 单测:npm test - 类型检查:npx tsc --noEmit ## 硬性约束 - 不得修改 src/handlers 下的既有文件,除非任务明确要求改动。 - 不得删除 lock 文件。 - 禁止在代码中写入硬编码密钥。 - 每次修改后必须运行类型检查和相关单测。 ## 工作流 1. 先阅读 README 和相关模块源码,理解现状。 2. 给出改动方案,等用户确认后再写代码。 3. 修改范围尽量小,不重构无关代码。 4. 完成后总结改动清单和验证结果。这份模板的关键不是格式,而是它把“Agent 该做什么、不该做什么、做完要交什么”都约束住了。我实际使用中发现,规则写得越具体,Agent 的听话程度越高。比如“不得修改既有文件”比“请谨慎修改”有效得多,因为后者给模型留了太多解释空间。另一个经验是:把验收动作写进 AGENTS.md,例如“每次修改后必须运行单测”,这比在对话里反复叮嘱要稳定得多,因为它是每次任务都会被加载的固定上下文。
3.3 写 AGENTS.md 的三个坑
第一个坑:把 AGENTS.md 写成需求文档。有人会写“希望 Agent 帮我实现一个订单系统,要求高可用、高性能”,这种描述等于没写,因为 Agent 不知道你对高可用的定义。正确写法是给动作、给边界、给验收标准。
第二个坑:规则太多导致上下文膨胀。我见过有人往 AGENTS.md 里塞了几千行团队规范,模型每次任务都要处理大量冗余内容,反而把真正的项目指令稀释了。我自己的阈值是:全局文件控制在 50 行内,项目文件控制在 100 行内,超出部分拆成单独的能力描述文件或放到文档目录,需要时再由任务主动指引读取。
第三个坑:只写文件不验证。写完 AGENTS.md 后,至少要跑三个不同类型的最小任务验证规则是否被遵守——比如一个常规改代码任务、一个禁止操作任务、一个需要多命令协作的任务。验证不一定一次过,但能让你尽早发现文件路径写错、规则表达模糊这类问题。我会在第一次验证时故意给一个违反规则的请求,看 Agent 会不会拒绝,这个测试非常能暴露问题。如果它乖乖照做,说明规则表达还不够严格,需要进一步收敛动作边界,而不是继续在文件里堆更多条目。
4. 配置优先级:为什么你改了 TOML 没反应
4.1 从命令行到模型推理的完整链路
配置优先级是我认为全篇最重要的一节。Codex 在决定一次请求用什么模型、什么提供方时,遵循的链路可以概括成:命令行参数高于环境变量,环境变量高于 TOML 默认值,AGENTS.md 则是在提示词层面影响行为规则,它和前面三层不在同一个维度。也就是说,你 TOML 里写 model = 'deepseek-chat' 看起来很合理,但如果系统环境变量里存在一个覆盖项,或者启动命令带了相关模型参数,那 TOML 里的值就不会起作用。
我举个实际场景:你在 config.toml 里配好了默认模型,结果每次启动 Codex 都提示用回了默认模型,查来查去发现是 shell 启动脚本里导出了相关的模型环境变量。环境变量这种东西特别隐蔽,它不会显示在你打开的 TOML 文件里,却实实在在地在进程启动时把它覆盖掉了。排查的第一步永远是:在终端打印 Codex 相关的环境变量,看看有没有历史配置残留。
4.2 实战排查:模型名对不上是谁的问题
我自己的排查顺序是这样的:先看启动命令有没有带模型参数;再看项目目录的 .env 或用户级 shell 配置里有没有导出相关环境变量;最后才回头检查 TOML 文件本身。曾经遇到一个问题:同一份 TOML 在 A 机器上跑得好好的,在 B 机器上却走了完全不同的模型,最后定位到是 B 机器安装了一个配置切换工具,它在启动前自动重写了 TOML 文件里的 provider。这个案例非常典型,它说明本地配置并不是静态的,任何“管理工具”都可能成为隐藏的覆盖层。
所以我会建议你在排查时把链路拆成四步:确认命令参数、确认环境变量、确认哪些工具在改配置文件、确认配置文件内容。每一步都可以用最小命令验证,比如先查环境变量,再看 Codex 当前支持的启动参数。这套排查思路不仅适用于模型对不上,也适用于 API 服务地址被改掉、密钥失效这类问题,因为它们的根因往往是同一层。
4.3 cc switch 这类切换工具怎么影响 TOML
说到配置切换工具,就不得不提 cc switch。这类工具的定位是帮你快速在不同的模型 Provider 配置之间切换,免去每次手工改 TOML 的麻烦。但很多人在用它之后遇到了一个典型的烦恼:cc switch 会覆盖 TOML。你在 TOML 里精心配好的 provider,被工具一刷新就改回去了。这个问题的根源在于工具的刷新机制:它读取的是自己维护的一份配置清单,切换时直接覆盖写入 config.toml 的相关区块,你在文件里的手工修改自然就没了。
我自己现在的做法是:把 cc switch 这类工具里维护的配置作为唯一的事实来源,手工改 TOML 只用于临时调试;如果要在多个 Provider 之间跳跃,我优先用工具切换,而不是频繁手改文件。如果出现切换后请求报错,比如终端提示 failed while handling codex endpoint /responses,通常说明切换结果和 Codex 当前加载的配置不一致。这时候不要急着重装,先确认工具写入的 base_url 和 api_key_env_var 是否完整、环境变量是否已导出,往往就是某个区块被覆盖成了空值。我见过有人来回改文件改了半小时,最后只是工具没有正常刷新环境变量导致的,白折腾。
5. 从零实操:完整接入一个自定义模型
5.1 安装、登录与初始化
平台不同安装方式略有差异,macOS 和 Linux 上通常可以直接通过官方脚本或包管理器装,Windows 则要用桌面版安装包。装完之后不要急着把一堆配置塞进去,我建议按“最小闭环”走:先启动一次默认配置,确认客户端能正常发起任务,再考虑改模型。登录环节要注意凭据存放路径,Codex 默认会把登录信息放在用户配置目录里,如果后面你改了 TOML 却不生效,偶尔也和登录凭据里的组织选择有关系,这种问题一般重置登录即可。
5.2 自定义 Provider 的配置过程
- 在模型开放平台完成服务申请,记录服务地址、密钥和模型名。
- 备份原始 config.toml。
- 添加 provider 区块。
- 导出密钥到环境变量。
- 修改 model 与 model_provider。
- 启动 Codex 执行最小任务验证。
我拿接入一个通用的 OpenAI 兼容服务作为例子,配置写入前先看一眼原始文件的格式。备份之后,把下面这份配置粘贴进去,改成自己的接入信息。这里我特意保留了一个指向本地测试服务的 provider,虽然名称是虚构的,但配置逻辑和真实第三方完全一致。
model = "custom-model-v1" model_provider = "local-test" [model_providers.local-test] name = "Local Test Service" base_url = "http://localhost:8000/v1" api_key_env_var = "LOCAL_API_KEY"验证时可以通过codex exec 'print("hello")'这类最小命令确认模型是否被正确调用;如果日志显示请求打到了另一个 base_url,说明 model_provider 或环境变量优先级有问题,接着按第 4 节排查思路走。符号链接、大小写、末尾斜杠这类细节也值得注意,base_url 的末尾/v1写不写,不同版本的客户端对路径拼接的处理会有差异,报 404 时先检查这一项。
5.3 验证配置与真实跑一次任务
验证不是跑通一次就算完。我会分三步:第一步跑一个只读任务,比如让 Agent 解释当前文件结构,确认请求能到指定模型;第二步跑一个写操作任务,在沙盒禁止的环境下故意让它创建一个文件,确认沙盒规则生效;第三步跑带项目规则的任务,看 AGENTS.md 的指令是否被遵守。三步都过,我才认为这套本地 Agent 配置是真正可用的,而不是“碰巧能启动”。
5.4 实操中遇到的报错和处理
实操中最常见的报错有几类:一类是模型名不被支持,例如在客户端请求中指定了一个当前 Codex 版本不认识的模型,出现的提示会直接告诉你某个模型不受支持。解决办法是回到模型服务方确认准确的模型名,并检查 model_provider 是否指向了正确的服务。另一类是密钥相关错误,表现为请求鉴权失败,通常是 api_key_env_var 指向的环境变量没导出,或者切换工具切换后密钥被改掉。还有一类是网络链路错误,表现为请求发出后迟迟没有响应或连接被拒,优先检查 base_url 是否可达,以及本机是否存在全局网络配置覆盖了服务地址。定位这些问题时,Codex 的调试日志是你最该看的东西,它会明确告诉你请求发去了哪个地址、用的哪个模型、失败在哪一步。
6. 常见问题速查与避坑总结
6.1 报错信息排查速查表
| 现象 | 最可能原因 | 处理动作 |
|---|---|---|
| 改了 TOML model 不生效 | 环境变量或命令参数覆盖 | 检查相关环境变量,去掉覆盖项 |
| 切换工具后报 failed while handling codex endpoint /responses | 切换写入的配置和加载配置不一致 | 检查工具清单、重新切换、确认环境变量 |
| 提示某模型 not supported | 模型名写错或当前版本不支持 | 核对模型服务方文档、修改 model 名 |
| 请求鉴权失败 | api_key_env_var 未导出或密钥失效 | 重新导出密钥、确认变量名拼写 |
| 请求 404 | base_url 路径拼接不正确 | 检查末尾 /v1、大小写,对照服务文档 |
| Agent 说改了文件实际没改 | 沙盒写权限未开放 | 按需配置沙盒模式,开放指定目录写权限 |
| AGENTS.md 规则不生效 | 文件路径错误或规则表达模糊 | 确认放在工作目录,测试更具体的约束语句 |
这张表是我的速查工具,不是完整知识体系。记住核心思路:先确认配置在哪个层面生效,再动手改。绝大多数配置问题都出在“你以为它在这个层面生效,其实它在另一个层面被覆盖了”,顺着这个思路排查,基本都能快速定位。
6.2 几个值得记住的底层原则
最后整理几条我从踩坑中总结的原则。第一条,配置文件是默认值不是绝对命令,环境变量和命令行参数永远可能覆盖它,所以任何“不生效”都要先看更高优先级。第二条,AGENTS.md 是行为规则不是参数配置,它没法帮你改模型,也没法直接改变 Provider,不要混淆这两个文件的职责。
第三条,切换工具会自动改写 TOML,这是特性不是 bug,理解它的工作方式之后就能避免在它和手工配置之间反复拉扯。第四条,本地配置讲究可复现,我会把关键配置和验证步骤记录成一份简单的操作手册,放在项目 docs 目录里,下次换机器、换人接手时按手册走一遍就能恢复环境,而不是靠记忆。
我个人的体会是,Codex 这类本地 Agent 工具的上手门槛不在安装,而在于把配置链路当成一个系统去理解——模型从哪里来、规则从哪里来、参数在哪一层被覆盖。把这些理顺之后,你再从零配一个自定义模型,可能五分钟就能完成。最后再分享一个小技巧:每次改动配置后,别急着跑大任务,先用最小命令验证链路通不通;这个习惯帮我避免了无数次“改了一大堆才发现是环境变量写错”的返工。希望这篇实战记录对你也有用。