乐于分享
好东西不私藏

Codex App windows版高级教程(三):AGENTS.md + config.toml,把 Codex 调成你想要的样子

Codex App windows版高级教程(三):AGENTS.md + config.toml,把 Codex 调成你想要的样子

前面已经讲了codex app 两大高级应用,分别是worktree和Subagent,有读者已经催更了,非常感谢大家的支持,今天呢我们接着继续讲两个更基础但更实用的东西:AGENTS.md 和 config.toml。

一个是告诉 Codex”我的项目是什么样的”,一个是告诉 Codex”你应该怎么工作”。

这两个文件配好了,Codex 才算真正认识你的项目。不然每次开新线程都像换了个实习生,什么都要从头交代。


AGENTS.md:给 Codex 写一份入职手册

两层加载

Codex 加载指令文件分两层:全局和项目。

第一层是全局 AGENTS.md,放在 ~/.codex/AGENTS.md。我这边因为重新设置过codex_home,

我的在这这个目录下

但其实你不用去找这个文件——在 App 里打开 Settings > Personalization,下面有个 Custom instructions 输入框,

一开始我什么都没填写,就提问:

发现它会执行内部的指令也就是openai给它设置的指令,

其中有一条可以解释大家为什么会任务gpt5.4比opus4.6更像一个愣头小子

就是gpt5.4会自己发挥想象力,不会事事都来问你,而opus4.6就明显的好的多,他会来多跟你确认下你的需求才帮你做,就像一个很多年的高级程序员,他不会自己去猜需求,而是把需求都跟你说明好后再去动代码,我觉得是跟他的内部指令是有关系的,所有模型的强弱有的时候内部指令很重要,所以我们现在讲解的agent.md的自定义指令,我觉得在某些时间也是起到很重要的作用。

回归正题,你在这里写的内容,本质就是在编辑全局 AGENTS.md。改这里 = 改那个文件,效果完全一样。

比如我随便加了一个指令:所有回复的开头必须加上”[AI一手]”这个前缀

保存后,在文件里面也同步修改了

第二层是项目级 AGENTS.md,放在你项目的根目录,和 .git 同级。比如项目在 D:\Projects\my-app\,那就是 D:\Projects\my-app\AGENTS.md

两层怎么合并?

Codex 会把全局的和项目级的按顺序拼成一个大文本,全局在前,项目级在后,一起喂给模型。

如果两层写了矛盾的指令——比如全局说”测试命令用 npm test”,项目级说”测试命令用 mvn test”——模型会优先听后面的。这不是 Codex 做了什么特殊处理,就是大语言模型的特性:后出现的指令权重更高。

如果两层写的是不同方面的事,一个说测试命令,一个说代码风格,那两条都会生效,互不干扰。

对大多数人来说,只需要关注这两层就够了。

进阶机制:知道有这回事就行

嵌套目录多层合并。这主要是 CLI 场景用的。如果你用 CLI 在项目的某个子目录下运行 codex,它会从 Git 根目录逐级走到你当前目录,沿途每一层都检查有没有指令文件,找到的全部按顺序拼在一起。每层目录里先找 AGENTS.override.md,有就读它,同目录的普通 AGENTS.md 直接跳过;没有 override 才读普通版本。每个目录最多只读一个文件。

在 App 里你是按项目添加的,工作目录就是项目根目录,不会触发嵌套扫描。这个功能是给 CLI + 大型 monorepo 准备的。

AGENTS.override.md 临时覆盖。任何目录下,如果同时存在 AGENTS.override.md 和 AGENTS.md,Codex 只读 override,完全忽略普通版。用途很简单:不想删原来的 AGENTS.md,但想临时换一套规则,放个 override 文件就行,不用了删掉,原来的自动恢复。

fallback 文件名。默认只认 AGENTS.override.md 和 AGENTS.md,但如果你项目历史上用的是别的名字,比如 TEAM_GUIDE.md,可以在 config.toml 里配:

# 项目根目录/.codex/config.tomlproject_doc_fallback_filenames = ["TEAM_GUIDE.md", ".agents.md"]

配了之后查找顺序变成:AGENTS.override.md → AGENTS.md → TEAM_GUIDE.md → .agents.md,找到就停。从零开始建项目直接用 AGENTS.md,不用管这个。

其他细节

改了 AGENTS.md 不用清缓存。Codex 每次启动新线程都会重新扫描、重新读取,开个新线程就自动生效。这个一定要注意改了内容就要新开线程才能生效。后面测试会有。

合并后的总内容有大小上限,默认 32KB,大约 3 万字。超过会被截断。绝大多数情况碰不到,如果你真的写了特别多,可以在 config.toml 里调:

project_doc_max_bytes = 65536    # 改成 64KB

案例 1:全局自定义指令生效验证

打开 App 设置 > Personalization > 自定义指令,写一条非常容易验证的规则,比如:所有回复的开头必须加上"[AI一手]"这个前缀

一开始我没开新的线程,给我的回复是没有使用到自定义指令的

然后我了开了一个新线程,看 Codex 的回复是不是真的以”[AI一手]”开头。

果然出现了

案例 2:项目级 AGENTS.md 生效验证和合并生效验证

在项目根目录创建 AGENTS.md,写上项目专属规则

然后开新线程,问 Codex”这个项目的测试命令是什么?”看它是不是回答 mvn test。

完全遵守你的指令办事情,非常靠谱;

并且你会发现一个特点:

就是遵守了两个指令的合并,一个是全局指令,一个是项目指令;也就是同时生效了。

案例 3:AGENTS.override.md 临时覆盖

在项目根目录创建 AGENTS.override.md,写一条和 AGENTS.md 矛盾的规则

开新线程问”测试命令是什么?”

回答 gradle test,说明 override 生效了。

这个文件当你不需要的时候你就可以删除,它的存在可以让你不用去动原来的AGENT.md文件。

交付模板:项目级 AGENTS.md

测试案例一句话就能验证,但实际使用时你的 AGENTS.md 应该写得更完整。把项目的技术栈、目录结构、测试命令、注意事项都告诉 Codex,它每次干活之前就已经知道你的项目长什么样,不用每次重复交代。

下面这个模板按 Java 后端项目写的,换成你自己项目的实际情况就行:

# AGENTS.md## 项目信息这是一个 Spring Boot + MyBatis 项目Java 17,Maven 构建测试命令:`mvn test`代码风格:阿里巴巴 Java 开发手册## 项目结构Controller 层在 src/main/java/com/example/controller/Service 层在 src/main/java/com/example/service/MyBatis XML 映射在 src/main/resources/mapper/## 数据库MySQL 8.0Redis 用于缓存(Spring Data Redis)数据库改动需要同步更新 MyBatis XML## 注意事项不要改 application-prod.yml,只改 application-dev.ymlPR 描述模板在 .github/PULL_REQUEST_TEMPLATE.md

写的时候几个原则:写 Codex 不可能自己知道的信息(比如”不要改 prod 配置”这种团队约定),不用写常识(”Java 用分号结尾”,因为codex 的知识库很强大,常识的东西就不用浪费上下文了)。越具体越好,”测试命令是 mvn test“比”请运行测试”有用得多。不用写太长,这些内容会占上下文空间,把最重要的信息放前面。


config.toml:告诉 Codex 怎么工作

AGENTS.md 管的是”项目是什么”,config.toml 管的是”Codex 怎么干活”——用哪个模型、改文件前要不要先问你、能不能访问网络、搜索用实时的还是缓存的。

文件在哪?

全局配置在 ~/.codex/config.toml,所有项目共享。项目级配置在项目根目录下 .codex/config.toml,只对当前项目生效。

最简单的找法:打开 App 设置页,点右上角”Open config.toml”,它打开的就是你的全局配置文件。

覆盖规则

项目级只覆盖它写了的那几项,没写的继续用全局的值。完整优先级从高到低:App 界面的运行时选择(比如模型和推理,沙箱等) > 命令行参数 > 项目级 config.toml > 全局 config.toml > 内置默认值。

但项目级 config.toml 能生效有一个前提——项目必须是 trusted 状态。

Codex 第一次打开一个项目时会问你”是否信任这个项目”。为什么要有这个机制?因为项目级的配置是跟着代码仓库走的,任何人都能往仓库里提交一个 sandbox_mode = "danger-full-access" 加 approval_policy = "never"。如果不加区分地直接读取,你 clone 一个陌生仓库打开就可能被恶意配置坑了。

自己的项目直接信任就行。如果之前选了不信任想改回来:

[projects."D:\\Projects\\my-app"]trust_level = "trusted"

场景 1:Codex 每次改文件都要我确认,太烦了

这是 sandbox 和审批策略的问题。默认是 read-only(只读),改文件会被拦住。你需要调两个配置:

sandbox_mode = "workspace-write"approval_policy = "on-request"

sandbox_mode 三个级别:read-only 最严格,只能看不能改,默认值。workspace-write 能改项目目录下的文件,超出范围弹窗问你,日常开发推荐。danger-full-access 完全不限制,名字里带 danger 是有原因的。

这三个级别对应 App 界面底部的下拉菜单。

不过app选择没有config.toml里面多,这也是config.toml的作用所在

你在 App 界面选的优先级比 config.toml 高,但不会写回 config.toml 文件。所以你可能看到 config.toml 里根本没写 sandbox_mode,但 App 显示的是”完全访问权限”——App 把你的选择记在了自己的内部状态里。

在 config.toml 里写 sandbox_mode 的意义是设一个底线默认值,以及配 App 界面上配不了的细项(比如 network_access)。

approval_policy 三个级别:untrusted 最谨慎,只有完全安全的只读命令才自动跑。on-request 是默认值,Codex 自己判断什么时候该问你。never 永远不问,适合自动化,日常不建议。

注意别搞混两个 sandbox 配置:Windows 上 config.toml 里可能有 [windows] sandbox = "elevated",这和 sandbox_mode 不是一回事。前者是 Windows 平台用什么技术手段隔离进程,后者是 Codex 的行为权限。

场景 2:搜索结果不够新

Codex 的网页搜索有三种模式:

web_search = "cached"     # 从缓存索引找结果,默认web_search = "live"       # 实时搜索web_search = "disabled"   # 关掉

这里有个很多人不知道的细节:如果你用的是”完全访问权限”(danger-full-access),web search 默认就是 live,不是 cached。

我平常喜欢用完全访问权限,我比较懒得去点击确定,所以我这边搜索的时候自己就可以搜索最新的,不需要额外配。只有在”沙箱”或”只读”模式下默认才是 cached,想搜最新内容才需要手动改成 live。但是有一点我提一下,就是OpenAI 的缓存索引更新频率很高,实际体验上和 live 差别不大。说大白话一点就是你就算选了默认权限,用了默认 cached,你让它搜索最新的东西也能搜出来。

场景 3:不同场景快速切换模型和参数

这是 Profiles 的用途。在 config.toml 里定义几套命名配置,一个参数切换:

[profiles.fast]model = "gpt-5.4-mini"model_reasoning_effort = "low"[profiles.deep-review]model = "gpt-5.4"model_reasoning_effort = "high"

CLI 里这样用:

codex --profile fast "这个函数是干什么的?"codex --profile deep-review "审查这次改动有没有安全风险"

注意:Profiles 目前是实验性功能,只有 CLI 和 IDE Extension 支持,App 暂时不支持。想设默认 Profile 的话在 config.toml 顶部加 profile = "fast"

场景 4:用本地模型或第三方 API

大多数人用不上这个。你用 ChatGPT Pro 订阅,直接在 App 里选模型就够了。这个主要给两类人:想用本地模型省钱的,或者公司要求不能直连 OpenAI 的。

[model_providers.ollama]name = "Ollama"base_url = "http://localhost:11434/v1"wire_api = "chat"[model_providers.deepseek]name = "DeepSeek"base_url = "https://api.deepseek.com/v1"env_key = "DEEPSEEK_API_KEY"wire_api = "chat"

配好之后在 Profiles 里指定用哪个 provider:

[profiles.local]model = "qwen2.5-coder"model_provider = "ollama"

注意:配了其他 provider 之后,App 里的模型下拉菜单选的还是 OpenAI 的模型。只有通过 config.toml 指定了 model_provider 的 Profile 才会走第三方接口,两边互不影响。

场景 5:跑完任务没注意到

配个外部通知脚本:

notify = ["python3", "C:/scripts/notify.py"]

目前只支持 agent-turn-complete 事件。可以接桌面通知、企业微信 webhook、钉钉,或者任何你想要的提醒方式。

场景 6:怕环境变量泄露

shell_environment_policy 控制 Codex 给子进程传哪些环境变量:

[shell_environment_policy]inherit = "all"exclude = ["*SECRET*", "*TOKEN*", "*API_KEY*", "*PASSWORD*"]

所有名字包含这些关键词的环境变量都不会传给 Codex 启动的子进程。

交付模板:config.toml

# ~/.codex/config.toml# ═══════════════════════════════════════════════# 模型设置# ═══════════════════════════════════════════════model = "gpt-5.2-codex"model_reasoning_effort = "medium"    # minimal / low / medium / high / xhigh# ═══════════════════════════════════════════════# 沙箱与审批(日常开发推荐 workspace-write + on-request)# ═══════════════════════════════════════════════approval_policy = "on-request"sandbox_mode = "workspace-write"# ═══════════════════════════════════════════════# 网页搜索# ═══════════════════════════════════════════════# web_search = "cached"    # cached(默认)/ live(实时)/ disabled(关闭)# ═══════════════════════════════════════════════# Profiles —— 快速切换不同工作模式(实验性,CLI/IDE 可用)# ═══════════════════════════════════════════════# profile = "fast"    # 取消注释可设默认 Profile[profiles.fast]model = "gpt-5.4-mini"model_reasoning_effort = "low"[profiles.deep-review]model = "gpt-5.4"model_reasoning_effort = "high"# ═══════════════════════════════════════════════# 环境变量安全(排除敏感变量)# ═══════════════════════════════════════════════[shell_environment_policy]inherit = "all"exclude = ["*SECRET*", "*TOKEN*", "*API_KEY*", "*PASSWORD*"]# ═══════════════════════════════════════════════# Subagent 设置(上一篇文章讲过)# ═══════════════════════════════════════════════[agents]max_threads = 6max_depth = 1# ═══════════════════════════════════════════════# MCP Servers(后面 MCP 部分会讲)# ═══════════════════════════════════════════════[mcp_servers]# 在这里配 MCP servers# ═══════════════════════════════════════════════# 外部通知(可选)# ═══════════════════════════════════════════════# notify = ["python3", "C:/scripts/notify.py"]

这个模板和上一篇 Subagent 文章里的配置不冲突,[agents] 部分直接放在同一个 config.toml 里就行,不用建两个文件。

以上觉得有用的话,关注下、点个赞或收藏、转发给你需要的朋友,如果想第一时间收到推送,也可以给我个星标⭐ 谢谢你看我的文章,下次见。