让Agent只读该读的:大型项目文档路由三层原则

大型项目文档要让 Agent 从当前任务走到该读的规范,靠的是路由而非引用连通。
文章延续 L0–L3 分层模型,聚焦"接到任务的 Agent 该读哪些文档"这一问题。作者先区分"被埋没"与"不可达"两类失败:前者靠排版修,后者只能修路。路由失败有三种形态——没有路、路太宽、路断在中途,根因是文档按写作者分类组织,而 Agent 只带着当前任务进门。据此提出三条原则:入口只路由不承载内容;按工作类型而非目录路由;每一跳都声明下一跳和父级。落地上给出"操作 → 必读"路由表、AGENTS.md 单一入口、共享层与项目层两层继承、逐跳路由链与 handoff 一次性路由等机制,并总结三个典型断点:路由表膨胀、指向已被取代的文档、项目层不声明继承。结论是引用连通不等于有路,修路胜过加粗。
让Agent只读该读的:大型项目文档路由三层原则
大型项目里,Agent 的失败往往不是"文档写得太差",而是"从手上的任务出发,根本走不到那份文档"。即便引用链在形式上连通,只要路径上没有一块路牌写着当前任务的名字,对每场会话都失忆的 Agent 而言,那份文档就等于不存在。
这是"面向 Agent 的大型项目文档分层"系列第二篇。上一篇给出了 L0–L3 四层模型:L0 是每次会话自动加载的入口文件,L1 是按工作类型拆分的规范,L2 是模块与变更文档,L3 是执行证据。本篇只回答一个问题:接到任务的 Agent,该读哪些文档。
问题不是"没有规范",而是"没有路"
原文作者在复盘企业支付审批系统时,先精确区分了两种失败——它们的修复方式完全不同:
- 被埋没(Buried):路是通的,只是被几百行叙述盖住。修法是缩短、加粗、搬到显眼处。
- 不可达(Unreachable):从手上的任务出发,入口没有任何指向它的路牌。唯一的修法是——修一条路。
他们的项目属于第二种。严格说,引用图里其实有一条两跳路径:入口的开发流程小节指向开发流程文档,该文档的相关文档表里有一行指向项目测试规范,而"六套全量"的定义就在那份规范第 154 行。路径是连通的。但对于一个接到"发布封板"任务的 Agent,沿途没有任何一块路牌提到"全量"或"发布封板"。另一条路径提到了发布封板,却停在了错误的位置——它指向的是某一次历史封板的需求,而不是当前定义。
对没有跨会话记忆的读者来说,一份无法从当前任务抵达的文档,写得再完整也等于不存在。

什么是"面向 Agent 的文档"与"失忆的读者"
传统文档的隐含假设是:读者是一个持续存在的人,会记得仓库结构、读过哪些文档、上次项目里"全量"指什么。大型语言模型驱动的 Agent 不具备这种跨会话记忆——每次任务开始时,它的上下文里只有系统提示、用户任务和它被要求读取的文件。因此文档设计必须把"读者是新人"当作默认前提,而不是例外情况。这与"新人友好文档"有本质区别:新人会主动提问、会建立心智模型、会把一次学到的东西带到下一次;Agent 不会,它只沿着显式给出的路径走。路由概念正是从这一差异中生长出来的——把文档当作节点、把引用当作有向边之后,真正决定 Agent 能否到达某份文档的,不是这份文档是否存在,而是从当前任务节点出发是否有一条显式标记的路。
路由失败的三种形态
把视角从单一封板放大到整个文档系统,路由失败有三种形态,项目里三种都出现过:
- 没有路。规范存在,但从入口走不到。
- 路太宽。入口文件加载了每种工作的详细规则,每次会话都得整吞,真正关键的那一行被稀释——这是 L0 膨胀的根源。
- 路断在中途。路由表把 Agent 送到项目层规范,项目层却从未声明自己继承自哪份共享规范。Agent 读完项目层,以为读完了全部。
三种形态同一个根因:文档是按写作者分类的方式组织的,而 Agent 走进来时只带着一个问题——我现在该做什么。 写作者按目录、按文档类型、按时间组织,这对熟悉仓库的人很自然;而 Agent 每场会话都是新人,它只知道用户让它封板、改表或审 diff。路由的键,必须是 Agent 进门时就已经拿在手里的东西。
Token 预算与"路太宽"的量化代价
Agent 每次会话的上下文窗口是有限的,L0 入口文件被整体加载,占用的 token 会挤占后续推理的空间。假设入口文件 800 行、每行平均 15 个 token,一次会话还没开始工作就烧掉约 12000 token;如果其中 80% 的内容与当前任务无关,相当于把绝大多数注意力预算花在了不会用到的规则上。这就是"路太宽"不只是可读性问题、更是工程问题的原因:它直接降低了 Agent 在真正相关规则上的有效注意力。把定义从入口下沉到 L1 文档、入口只留一行路由,本质是把固定成本转化为按需成本——只有真正执行"发布封板"的会话才为"六套全量"的定义付出 token。
三条原则
原则一:入口文件只做路由,不承载内容。 入口每次会话都会整体加载,写进去的每一行都由每一次会话买单。把"六套全量"的定义抄进入口,那么每次与封板无关的会话都要读一遍;把它做成一条路,只有封板会话才付这个成本。
原则二:按工作类型路由,而不是按目录。 目录树回答的是"东西在哪",前提是读者知道东西叫什么、属于哪一类——这个前提本身就是记忆。Agent 走进来时没有这份记忆,只有用户交给它的任务。所以路由表第一列是一个动作:封板发布、改表结构、审代码,而不是一个目录名。
原则三:每一跳都声明下一跳和自己的父级。 人读到"项目数据库规范",会自然想到"大概还有一份共享的"。Agent 不会。文档如果不写"我是那份的落地",Agent 就把它当作全部规则;如果不写"读完去哪儿",Agent 就停住。路由不只是入口的事,沿路每一份文档都得回答这两个问题。
机制一:"操作 → 必读"路由表
路由表的形态是这样的(源码为项目入口文件中的实际表格,已脱敏节选):
| 操作 | 必读规范 | 一句话要点 |
|---|---|---|
| 发布封板 / 出包 / 打 tag / 说"全量绿" | handbook/engineering/release-process.md | 只有六套按既定标准全过,才允许说"全量绿" |
| DB schema / 跨环境数据变更 | handbook/database/project-database-changes.md | schema 变更必须有增量脚本,且脚本要在生产执行器上跑过 |
| 编写 / 修改 / 审阅任何测试 | testing/guides/project-testing.md | 先从"工作类型 → 测试层"表选层,不要把 UI 契约升级到端到端 |
| 在浏览器里验证前端行为 | 入口文件"不要猜选择器"小节 | 浏览器自动化用于定位,不作为验收证据 |
| 代码审查 / 复审 | 变更的 handoff.md + test-cases.md | 结论必须带"path:line"证据 |
什么操作才配有一行:高风险、低频、凭记忆容易错。 高风险指做错的代价昂贵或不可逆;低频指一个月做一两次的事,相关规范不会恰好待在近期上下文里;凭记忆容易错指 Agent 的常识会给出一个看起来合理但错误的答案——"全量"就是典型案例,常识里全量等于"跑所有测试",而在项目里它是固定的六套。普通编码不该有一行,它们属于开发规范,入口一个链接就够了。
"一句话要点"这一列的价值在于:即使那一跳没走,最重要的约束也已经送达。 路由表在 L0,每次都会读;目标文档在 L1,还差一跳。Agent 可能因为上下文紧张、或判断"这次改动很小"而跳过这一跳。要点列是地板:即便没跳,"schema 变更必须有增量脚本"也已经摆在它面前。还有一个副作用——如果一份规范的要点写不成一句话,往往说明这份规范自己没有核心规则,应该先清理目标文档。
"一句话要点"作为降级容错
路由表的设计隐含一个工程事实:路由不是百分之百会走通的。Agent 可能因为上下文紧张而跳过一跳,也可能主观判断"这次改动很小,不需要读规范"。"一句话要点"列的价值在于提供一条降级路径——即便跳读发生,最关键的约束也已经随 L0 一起加载。要做到这一点,要点必须满足三个条件:可判定(能明确判断当前操作是否符合)、不可推导(不能从常识直接得出,否则没有信息量)、单句(长于一句就会在入口文件里退化为正文)。反过来,如果某份规范写不出这样一句话,通常意味着它缺乏一个可以压缩的核心规则,应该先回去清理目标文档,而不是强行给它编一句要点。
机制二到五:一套完整的路由链
单一入口,其余转发。 项目入口一度叫 CLAUDE.md(Claude Code 期望的文件名),但随着 Codex、Kimi 也加入协作,按某一种工具约定命名的入口,在工具切换时未必会被加载。整理后所有规则集中到 AGENTS.md,CLAUDE.md 缩成 5 行转发器——转发器里不能有一条规则,一旦有,两个入口就会各自生长,不同 Agent 看到不同的项目规则。
个人记忆是单一入口的另一半。判断一条经验该放哪,问一个问题:如果换 Agent、换机器,这条还应该生效吗? 如果应该,就进入仓库;只保留协作偏好(比如用中文回复、提交前给人类看 diff)在个人记忆里。
共享层 → 项目层,两层继承。 共享层写跨项目成立的规则并留下占位符,项目层只写四样东西:填好的占位符、项目当前状态、对共享规则的例外、项目踩过的坑。两层靠一句继承声明连接,且声明必须放在项目层顶部——因为路由表指向的是项目层,Agent 落在项目层,如果项目层不写明父级,共享层就从它这里不可达,这正是开头的问题在 L1 内部重演。继承声明还要写清楚"冲突时以谁为准",否则只会照字面执行的 Agent 会随手挑一句相信。
逐跳路由链: 入口 → 文档首页(按职责分)→ 顶层目录 README(二级路由 + 放置规则)→ 文档顶部的 AI Quick Path → 正文。每一层只携带下一跳,所以每一步只花少量 token;新增一份文档只改它所在目录的 README,入口文件和路由表都不动。
handoff 文档是一次性路由。 路由表服务每场会话,handoff 文档只服务一次——一个变更从当前 Agent 交到下一个。它同时指两个方向:向内说明先读什么、按什么顺序;向外规定"每次交接必须附带哪些证据"。变更完成后它降级为 L2 历史,不再承担路由职责。
AGENTS.md 作为跨工具约定入口
CLAUDE.md、.cursorrules、GEMINI.md 等文件是各家 Agent 工具自定义的入口约定。当项目同时被多种工具协作时,按某一种工具命名入口会导致其他工具"看不到"规则——不是规则不存在,而是那一个工具根本不加载这个文件名。AGENTS.md 的意义在于把这层工具差异收拢到一个中立文件名上,让入口成为一次约定而非一次绑定。CLAUDE.md 保留为转发器(通常只有几行,内容形如"规则见 AGENTS.md"),是因为部分工具仍会优先读取约定文件名;如果转发器里混入了实际规则,就会形成"双入口",两边各自演化、各自被不同的 Agent 看到,最终导致跨工具行为不一致。转发器里不能有一条规则的纪律,正是为了维持单一真相源。
路由最容易断在三处
断点一:路由表自己长成了入口文件。 规则写入时 7 行中有 3 行指回入口文件自身,到后来 12 行里有 5 行指回自己。修正办法:某行的目标若是"入口文件某一节",先问它在不在 L0 硬规则清单里,不在就把那一节下沉到 L1 文档;要点列只允许一句话;新增行前先过三根标尺。
断点二:指向一份已被取代的文档。 坏链至少会暴露自己,真正的危险是链接活着、目标却不再是当前事实。"已被取代"有两种形式:被新版本正式替换,以及本来就只是某一次历史实例却被当成当前定义。原文的发布封板路径就栽在第二种——规范的最后一跳落在一份历史交付文档上,等于把"照上次那样做"写进了规则,导致交接文档一代代漂移、连续多个版本缺失规范明确要求的 SHA-256 校验。修正办法:路由只指向"当前定义"类文档,或写成随任务变化的指针(如"对应变更的 handoff"),不硬编码某个实例。
断点三:项目层不声明自己继承了什么。 这是项目里最完整的反例:清理前项目测试规范和项目数据库规范各引用共享规范 0 次,而共享层其实早就在顶部写了 AI Quick Path——父级指向了子级,路却只向下走。修正办法:项目层顶部的继承声明是必需的,也是最容易用机器检查的,一条 grep 就能发现。清理时两份项目层规范都补上了继承声明,共享数据库规范也从草稿转为定稿,缺失继承声明成为 doc-lint 的错误。
写在最后
这份经验的落点不在"怎么让 Agent 记住规范",而在于:如何保证从入口到每一份该读的规范都有路,且 Agent 只走它该走的路。 能否从手上的任务抵达,决定了一份文档对 Agent 是否存在;引用连通不等于有路,修路胜过加粗。
相关推荐

Workbuddy零基础入门:桌面AI智能体如何落地工作流
Workbuddy桌面AI智能体零基础教程解读:从安装、积分领取到文件管理与自动化工作流搭建,对比CodeX的适用场景与成本差异,帮助职场人完成从AI提问者到AI管理者的思维转变。

OpenAI智能体新栈:计算机操作、决策API与超速工作流
OpenAI Dev Day发布智能体新栈:Computer Use操作速度超越普通人,Decisions API基于Luna实现极低延迟分类,Agents API开放同源计算机操作能力,GPT-6.1 Sol成本仅为Astra七分之一。本文梳理核心更新、实测数据与开发者落地要点。

梁文峰开源推理框架DeepInfo,硬刚美国禁令
DeepSeek在开源模型后再开源推理框架DeepInfo,包含GPU/CPU交叉流水线、小模型预判大模型复核、华为昇腾与英伟达芯片全面适配等技术,直面美国联邦禁令。本文解析其对AI产业落地的意义与开源策略。