你把 Codex 指向一个 50 万行的 monorepo,让它”加一个 API endpoint、串通 service 和 DB、再加个测试”。两分钟后它读了 40 个随机文件、会话已经 compact 了两次、现在它在改错的 package——它忘了本来该动的是哪个 app 的路由层。输出看着像能跑又像跑不通,你也说不清它在哪儿走偏的。小仓库里 Codex 几乎不费劲;大仓库它就迷路。
最快的修法: 在 agent 开跑之前先把 working set 界定好。明确告诉它该改哪个子树、把那个子树的 tree 摘要贴给它让它跳过探索阶段、再把它必须遵守的约定放进 AGENTS.md(Codex 每个 turn 都会重新读它,而且它能扛过 compaction),而不是放进一条只发一次、会被 compaction 摘要掉的消息里。这三步能在你动用更大模型之前,就解决掉绝大多数”大仓库里迷路”的会话。
先纠正一个会影响整套策略的点:截至 2026 年 6 月,Codex CLI 跑的是 gpt-5.5(ChatGPT 登录时的默认模型),而 OpenAI 刻意把 Codex 这一层的上下文窗口封在 40 万 token,尽管 GPT-5.5 的 API 本身能到 1M。你是在 40 万的空间里干活,不是一百万。所以在大仓库上 scope 不是可选项——你会远在”整个仓库都进上下文”之前就把 40 万撑爆。
Codex 怎么管理上下文(理解了这些,修法才说得通)
两个机制最关键:
- Compaction(压缩)。 会话接近窗口上限时,Codex 会把较早的 turn 摘要掉来腾空间(你也可以用
/compact手动触发,或用/clear直接清空会话)。Compaction 会保留它判断重要的内容、丢掉其余的——这正是 12 步 plan 或某个结构锚点悄悄消失的原因。 - AGENTS.md 每个 turn 都会被重新读取。 跟你只贴过一次的聊天消息不同,
AGENTS.md链在每个 turn 都会被重新注入,而且能完整扛过 compaction。这是你手上最有用的杠杆:任何你丢不起的东西都该写进AGENTS.md,而不是放进一条性的 prompt 里。
查找链(每个会话构建一次)从 ~/.codex/AGENTS.md 往下走,沿着从 git 根目录到当前目录路径上的每一份 AGENTS.md。文件按根在前、叶在后拼接,所以更近的 AGENTS.md 在冲突时会覆盖根目录的。只有你路径上的文件会加载——你在 apps/web/ 干活时,apps/api/AGENTS.md 不会加载。整条链默认封顶 32 KiB(project_doc_max_bytes),所以每份文件都要精简,否则叶子层的规则会被臃肿的根文件挤掉。
常见原因
按大仓库失忆的命中率排序。
1. 没 scope —— agent 扫整个仓库
你说”找找认证在哪儿接的”。Agent 全 monorepo 跑 ripgrep,4000 个 match,读 50 个随机文件,原任务直接朝着 compaction 线滚过去。
如何判断: transcript 里出现了无关 package 的读(编辑 api-server/ 时却读 marketing-site/)。
2. Plan 列表被 compact 掉
会话一接近窗口上限,Codex 就会压缩早期 turn。原本 12 步 plan 变成”用户想加个 endpoint”,agent 忘了 6-12 步。
如何判断: 重 prompt”列出剩余 plan”——答案比你写的少,或者比原文更模糊。
3. 约定写在性消息里,而不是 AGENTS.md
你把项目约定贴进了开场消息里。到第 30 turn 会话已经 compact 过,只剩”follow conventions”幸存下来。Agent 现在在没有具体规则的状态下生成。(如果约定放在 AGENTS.md 里,它每个 turn 都会被重读、本来能扛过这一关。)
如何判断: 输出违反了你明确说过的约定。重 prompt”为这步引用相关规则”——agent 用同义改写或编造,就说明规则当初在一条被 compact 掉的消息里,而不在 AGENTS.md 里。
4. 冗长 tool 输出灌满上下文
pnpm tsc --noEmit 4000 个类型错误的 dump、含完整文件内容的 30 文件目录列表、1 万行测试日志——每一个一次 turn 就能烧光窗口。
如何判断: 某一次 tool 调用占了总 token 的 > 30%。事后用 wc -l 跑可疑命令的 stdout。
5. Agent 反复读同一个文件
在没有”我已经读过哪些”上下文缓存时,agent 会反复 re-issue 已经读过文件的 read。每次重读都耗窗口、零新信息,而且重读会在 compaction 之后陡增(读取历史被摘要掉了)。
如何判断: transcript 搜重复的 Read <path> 调用。同一个 path 读 3 次以上 = 严重浪费。
6. 跨 package 编辑但没有依赖图
任务动 3 个 package,agent 不知道依赖图。它反复读 package.json、tsconfig.json 和 lockfile 自己摸索——每次摸索都吃窗口。
如何判断: transcript 里有大量来自不同目录的 Read package.json / Read tsconfig.json。
对症诊断:你属于哪一类?
| transcript 里的症状 | 最可能的原因 | 跳到 |
|---|---|---|
| 读了任务从没提过的 package | 没 scope(#1) | Step 1 |
| Agent 跳过 / 忘了你后面的 plan 步骤 | Plan 被 compact(#2) | Step 6 |
| 输出违反了你明确说过的规则 | 约定没放进 AGENTS.md(#3) | Step 3 |
| 某一条命令占了 token 大头 | 冗长 tool 输出(#4) | Step 5 |
| 同一个文件读了 3 次以上 | 没有读取缓存(#5) | Step 4 |
反复读 package.json / tsconfig.json | 没有依赖图(#6) | Step 2 |
动手前先确认
- 大致摸一下仓库规模:
tokei或cloc给基线(代码行数、文件数)。一个 10 万行的仓库按语言不同 tokenize 出来大概 30-50 万 token——已经超过 40 万的 Codex 窗口了,这就是为什么”全读”从来不是方案。 - 确认任务实际需要的子树——用一句话写下来。
- 检查你的
AGENTS.md链长度;整条链封顶 32 KiB,所以如果根文件很大,你 package 级的规则可能正在被截断。
需要收集的信息
- 仓库总文件数和 LoC(
find . -type f -name "*.ts" | wc -l、cloc .)。 - 任务触及的具体子树。
- 你模型的 Codex 上下文窗口:截至 2026 年 6 月,Codex 这一层的
gpt-5.5是 40 万(1M 的 GPT-5.5 窗口只在 API 上有)。其他可选模型:gpt-5.4(20 万)、gpt-5.4-mini、gpt-5.3-codex、gpt-5.3-codex-spark(仅 Pro)。 AGENTS.md和根README.md的 token 长度(wc -w× 1.3 是粗略的 token 估算)。- 任务相关的内部术语 glossary(项目代号、包简称)。
最短修复路径
按收益从高到低。
Step 1:prompt 里界定 working scope
任何 plan 之前:
Working scope:
- 只编辑:packages/api-server/、packages/api-types/
- 只读参考:packages/db-client/(只读,不改)
- 不要碰:monorepo 里其他任何东西
如果某个问题需要在 scope 外做改动,停下来先问。
这条能把 agent 的搜索空间在大多数 monorepo 上砍 80-90%。想在工具层面强制它,就用 -C(工作目录)把 Codex 指向那个子树,再配 --sandbox workspace-write,让 workspace 外的写入都需要审批:
codex --cd packages/api-server --sandbox workspace-write
Step 2:预喂结构摘要
Agent 开始扫之前,先给它你生成好的目录树摘要:
tree -L 3 packages/api-server -I 'node_modules|dist|.next' > /tmp/tree.txt
然后 prompt 里:
仓库结构(读这个,不要再列):
[贴 tree 输出]
关键文件:
- packages/api-server/src/routes/index.ts — 路由注册
- packages/api-server/src/services/ — 业务逻辑
- packages/api-types/src/index.ts — 共享类型
Agent 直接跳过结构发现阶段进入正题。这同时也提前堵住了原因 #6:把依赖关系直接喂给它,它就不会为了重建这些关系而从五个目录反复读 package.json。
Step 3:把约定放进 AGENTS.md,而不是 prompt
这是杠杆最大、也最多人搞错的一步。别把约定贴进开场消息然后指望它一直在——compaction 会把它摘要掉。把约定放进离子树最近的那份 AGENTS.md(Codex 每个 turn 都会重读,而且它扛得过 compaction):
# packages/api-server/AGENTS.md
约定:
- 路由通过 routes/index.ts 里的 registerRoute() 注册
- Services 通过 barrel 文件导出
- 所有 handler 返回 { ok: boolean, data?: T, error?: AppError }
- 完成前跑 `git diff --name-only`,确认每个文件都在 packages/api-server 或 packages/api-types 下
因为这条链是根在前、叶在后拼接,这份 package 级文件会覆盖仓库根 AGENTS.md 里更宽松的规则。保持精简:整条链封顶 32 KiB,臃肿的根文件会把这些规则挤掉。
Step 4:用目录级摘要代替文件读
探索阶段优先用摘要而不是完整内容:
跑:ls packages/api-server/src/services/
跑:head -1 packages/api-server/src/services/*.ts (每个文件的第一行 / docstring)
锁定目标 service 之前不要读完整内容。
知道 30 个 service 名 + 各自一行 doc 大概 500 token,全读 30 个要 5 万 token。这也顺手干掉了原因 #5 的重读浪费——agent 挑出它唯一需要的那个文件,而不是一路乱啃。
Step 5:限制冗长 tool 输出
把吵的命令包一层,让单次 dump 炸不掉你的窗口:
pnpm tsc --noEmit 2>&1 | tee /tmp/tsc.log | head -100
echo "(完整输出在 /tmp/tsc.log)"
或者让 agent 只 grep 它要的:
grep -E "error TS|src/api-server/" /tmp/tsc.log | head -50
100 行而不是 4000 行。
Step 6:拆成 commit checkpoint 的子任务、各自全新上下文
长任务跑一串非交互调用,每次都是全新上下文。中间 commit,让 commit 过的代码——而不是对话——成为持久状态:
codex exec - < step1-add-types.md # 在 packages/api-types 加新类型,然后 commit
codex exec - < step2-add-route.md # 在 packages/api-server 加 route + handler,然后 commit
codex exec - < step3-add-test.md # 加集成测试,然后 commit
codex exec(别名 codex e)是非交互命令;codex exec - < file 从文件读取 prompt。在交互会话里你也能拿到同样的效果:在干净的 checkpoint 上跑 /compact,或者每个子任务 commit 完之后用 /clear 开一段真正全新的对话。
Step 7:先调 compaction 阈值,再考虑动更大的模型
你没法挑一个”长上下文”的 Codex 模型——没有 gpt-5.5-long,而且 Codex 这一层无论如何都把 gpt-5.5 封在 40 万。所以杠杆不是更大的窗口,而是给 compaction 留更多余量。对那些必备上下文很大的仓库,把自动 compaction 触发点调低到容量的 80-85% 左右,让 compaction 之后的重读循环(系统 prompt 加 AGENTS.md 链)有地方落脚:
# ~/.codex/config.toml
model = "gpt-5.5"
要明白这里的取舍:更早 compact 能防硬溢出(原因 #1 和 #5),但对已经被压缩的 plan(原因 #2)或巨型 tool dump(原因 #4)毫无帮助。Scope 和 AGENTS.md 才是真正的修法。
怎么确认已经修好
- 把同一任务重跑,看 transcript:只在 scope 内读、没有跨 package 乱跑。
- 中途重 prompt”你在第几步”和”从 AGENTS.md 里为这步引用规则”——两个都应该精确而不是同义改写。如果规则被一字不差地引回来,说明它确实在上下文里。
- 最后跑
git diff --name-only,确认每个改动文件都在你声明的 scope 内。出现 scope 外的路径,说明 Step 1 没守住,该把这条 diff 检查规则加进AGENTS.md。
长期预防
- monorepo 上每个 agent 任务开头都有显式
Working scope:段,启动时用-C指向子树。 - 根目录维护一份
repo-map.md:目录树摘要 + 关键文件指针;让 agent 先读它。 - Monorepo 每个 package 一份
AGENTS.md——冲突时最近的那份生效,单任务的 doc 量小很多。保持根文件精简,别超过 32 KiB 的链上限。 - 探索阶段先用
ls加head -1摘要,再做完整文件读。 - Shell tool 输出用 wrapper 限到 100 行,溢出导到
/tmp/*.log。 - 多 package 工作拆成 commit checkpoint 的子任务;3-package 改动绝不一个超长 prompt。
常见坑
- 把约定贴一次就以为 200 turn 后还在——它不会在;放进
AGENTS.md,它每个 turn 都重读、扛得过 compaction。 - 指望某个”长上下文”Codex 模型救你——没有这种东西;Codex 上的
gpt-5.5是 40 万,而注意力质量在这个上限之前很早就掉了。 - 把所有规则都塞进仓库根
AGENTS.md,直到它撑爆 32 KiB 的链上限、悄悄截断了你叶子 package 的规则。 tsc --noEmit不加head/tail帽——一条坏命令能炸掉 40% 窗口。- 重 prompt”你读过哪些文件”——答案是不完整的,因为读取历史本身已经被 compact 了。
常见 FAQ
Q:我的仓库 20 万行,真的需要 scope 吗?
要。20 万行全读大概是 500 万 token 量级,对着 40 万的 Codex 窗口。你永远只能把整个 codebase 的一小部分放进上下文,早点 scope 让这”一小部分”是有意为之而不是随机的。
Q:怎么快速做结构摘要?
tree -L 3 -I 'node_modules|dist|.next|coverage' > repo-map.txt
两条命令就有一份 1-2k token 的图,胜过 50 次随机文件读。
Q:Agent 无视了我”不要碰其他 package”的规则,怎么办?
把那条规则从聊天消息搬进最近的 AGENTS.md(每个 turn 都重读),再加一行 verifier:“完成前跑 git diff --name-only,确认所有文件都在 scope 内”。Verifier 把静默违反变成可见错误。启动时用 -C 加 --sandbox workspace-write 还能在上面再加一道硬护栏。
Q:GPT-5.5 不是有 1M 上下文窗口吗?我怎么还溢出?
那 1M 是 GPT-5.5 的 API 上限。截至 2026 年 6 月,Codex 出于吞吐和成本考虑,刻意把它这一层封在 40 万。在 Codex 里你只有 40 万,所以要按 40 万来规划,不是一百万。
Q:prompt caching 有用吗?
它让 scope 更便宜,但防不住溢出。Cache 加速重复使用相同内容(也降成本),但那部分内容依然占窗口。真正减少窗口占用的是 scope。