你把 Codex Agent 指向一个 50 万行的 monorepo,让它”加一个 API endpoint、串通 service + DB、再加个测试”。两分钟后它读了 40 个随机文件、plan 列表被自动 summarize 了两次、现在它在改错的 package——它忘了本来该动的是哪个 app 的路由层。输出看着像能跑又像跑不通,你也说不清它在哪儿走偏的。小仓库 Codex 像魔法;大仓库它就迷路。
根因很少是”模型对大代码就笨”。是 agent 构建的 working set(文件读 + plan + AGENTS 规则 + 推理)超出了上下文窗口,自动 summarizer 把结构锚点丢了。修法是 agent 开跑前先把 working set 界定好:scope 到子树、预喂结构摘要、把相关 AGENTS.md 作为钉死的段落强制加载。
常见原因
按大仓库失忆的命中率排序。
1. 没 scope —— agent 扫整个仓库
你说”找找认证在哪儿接的”。Agent 全 monorepo 跑 ripgrep,4000 个 match,读 50 个随机文件,原任务直接滚出上下文。
如何判断:transcript 里出现了无关 package 的读(编辑 api-server/ 时读 marketing-site/)。
2. Plan 列表被自动 summarize 掉
上下文用过 70% 后 runner 会压缩早期 turn。原本 12 步 plan 变成”用户想加个 endpoint”,agent 忘了 6-12 步。
如何判断:重 prompt”列出剩余 plan”——答案比你写的少,或者比原文更模糊。
3. AGENTS.md / 约定掉出窗口
AGENTS.md 是第 1 turn 加载的,到第 30 turn 已被 summarize 成”follow conventions”。Agent 在没有具体约定的状态下生成。
如何判断:输出违反了 AGENTS.md 明确写过的约定。重 prompt”引用相关 AGENTS.md 规则”——agent 用同义改写或编造,就说明规则不在上下文里了。
4. 冗长 tool 输出灌满上下文
pnpm tsc --noEmit 4000 个类型错误的 dump、含完整文件内容的 30 文件目录列表、1 万行测试日志——每一个一次 turn 就能烧光窗口。
如何判断:某一次 tool 调用占了总 token 的 > 30%。事后 wc -l 跑可疑 tool 的 stdout。
5. Agent 反复读同一个文件
在没有”我已经读过哪些”上下文缓存时,agent 会反复 re-issue 已经读过文件的 read。每次重读都耗窗口、零新信息。
如何判断:transcript 搜重复的 Read <path> 调用。同一个 path 读 3+ 次 = 严重浪费。
6. 跨 package 编辑但没有依赖图
任务动 3 个 package,agent 不知道依赖图。它反复读 package.json、tsconfig.json、*.lock 自己摸索——每次摸索都吃窗口。
如何判断:transcript 里有大量来自不同目录的 Read package.json / Read tsconfig.json。
动手前先确认
- 大致摸一下仓库规模:
tokei或cloc给基线(代码行数、文件数)。 - 确认任务实际需要的子树——用一句话写下来。
- 把当前 AGENTS.md 内容存一份;可能需要瘦身才能 in-context 加载。
需要收集的信息
- 仓库总文件数和 LoC(
find . -type f -name "*.ts" | wc -l、cloc .)。 - 任务触及的具体子树。
- 当前模型的上下文窗口(gpt-5.5 long:1M;gpt-5.4:200k;gpt-5.5 标准:400k)。
- AGENTS.md、CLAUDE.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%。
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 直接跳过结构发现阶段进入正题。
Step 3:把 AGENTS.md 钉成 task 的 header
不要指望自动 summarize 能保留它,把相关切片嵌进来:
[AGENTS.md 摘录 —— 适用于 packages/api-server]
约定:
- 路由通过 routes/index.ts 里的 registerRoute() 注册
- Services 通过 barrel 文件导出
- 所有 handler 返回 { ok: boolean, data?: T, error?: AppError }
[摘录结束]
任务:...
这条能撑过 summarize,因为它在 user message 里而不是 tool 输出里。
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。
Step 5:限制冗长 tool 输出
把吵的命令包一层:
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 的子任务、各自全新上下文
长任务跑一串 agent 调用:
调用 1:在 packages/api-types 加新类型。Commit。
调用 2:在 packages/api-server 加 route + handler。Commit。
调用 3:加集成测试。Commit。
每次调用一个全新上下文,步骤之间的 commit 是持久状态。
Step 7:必要时切长上下文模型变体
如果 scope 完还是要 30 万 + token 上下文:
codex agent run --model gpt-5.5-long task.md
但要明白:长上下文是原因 #1 和 #5 的创可贴,解决不了原因 #2(plan summarize)和原因 #4(冗长输出淹没注意力)。
怎么确认已经修好
- 把同一任务重跑,看 transcript:只在 scope 内读、没有跨 package 乱跑。
- 中途重 prompt”你在第几步”和”为这步引用 AGENTS.md 规则”——两个都应该精确而不是同义改写。
- 本地的话开
top/ nvidia-smi——token 吞吐应该稳定、不该卡在巨量 tool 输出上。
长期预防
- monorepo 上每个 agent 任务开头都有显式
Working scope:段。 - 根目录维护一份
repo-map.md:目录树摘要 + 关键文件指针;让 agent 先读它。 - Monorepo 每个 package 一份 AGENTS.md——最近的那份生效,单任务的 doc 量小很多。
- 探索阶段先
ls + head -1摘要,再做完整文件读。 - Shell tool 输出用 wrapper 限到 100 行,溢出导到
/tmp/*.log。 - 多 package 工作拆成 commit checkpoint 的子任务;3-package 改动绝不一个超长 prompt。
常见坑
- 把 AGENTS.md 贴一次就以为 200 turn 后还在——它不会在,summarize 会把它吃掉。
- 相信
--max-tokens 1M万能——注意力质量在硬上限之前很早就掉了。 - 不 scope 就用长上下文模型——花 10 倍钱拿迷路的输出。
tsc --noEmit不加 head / tail 帽——一条坏命令能炸掉 40% 窗口。- 重 prompt”你读过哪些文件”——答案是不完整的,因为 read list 本身已经被 summarize 了。
常见 FAQ
Q:我的仓库 20 万行,真的需要 scope 吗?
要。20 万行全读也是 ~500 万 token。你永远只能覆盖整个 codebase 的一小部分,早点 scope 让”小部分”是有意为之而不是随机的。
Q:怎么快速做结构摘要?
tree -L 3 -I 'node_modules|dist|.next|coverage' > repo-map.txt
两条命令就有一份 1-2k token 的图,胜过 50 次随机文件读。
Q:Agent 无视了我”不要碰其他 package”的规则,怎么办?
把那条规则搬到 AGENTS.md(自动加载)、钉在任务消息顶端,再加 verifier:“完成前跑 git diff --name-only,确认所有文件都在 scope 内”。Verifier 把静默违反变成可见错误。
Q:开 Codex 的 caching 有用吗?
有——prompt cache 让 scoping 更便宜,但防不住上下文溢出。Cache 加速重复读同内容,但内容依然占窗口。