8  载体:规则什么时候到达

这一章讲一件容易被忽略的事:同一条规则,放错载体就等于不存在。

它不依赖任何基建,所以对小团队最有用,也因此它是最长的一章。

8.1 问题不是”这条规则重不重要”

一条必须无条件生效的规则,如果藏在需要 Agent 主动去查的地方,它就形同虚设。 反过来,一条只有跨会话才有意义的约定,塞进单次会话的上下文里也留不住。 两种错配的表现完全不同,但根子是同一个。

所以真正该问的问题不是”这条规则重不重要”,而是:

它什么时候需要到达 Agent?

这个问题有答案,而且答案是可判定的 —— 这就是这一章的全部内容。

大部分团队没问这个问题,于是所有规则都被塞进同一个地方, 通常是一份不断变长的常驻文件。那份文件变长的每一步, 都在稀释它里面每一条规则的到达概率。这是一个很隐蔽的失败: 你加的每一条都是对的,而它们的总和让系统变差了。

8.2 按到达时机分五种载体

五种载体,按”什么时候到达”排:

flowchart TB
    T1["永远在场"] --> C1["常驻文件<br/>常驻不变量,无条件触发"]
    T2["被调用时"] --> C2["skill(动词)<br/>可调用的过程,做完一件事"]
    T3["主动查阅时"] --> C3["guide(名词)<br/>机制说明,不抢占上下文"]
    T4["碰到路径时"] --> C4["路径清单<br/>改到哪,哪的背景自己浮上来"]
    T5["跨会话"] --> C5["迭代契约<br/>一次会话装不下的长任务"]
    C1 -.->|"成本:每次会话"| X1(("稀缺"))
    C4 -.->|"零主动动作"| X2(("唯一不靠<br/>「想起来」的"))
    style C1 fill:#f8d7da
    style C4 fill:#e8f4e8

图里标出来的两格是这五种里最特别的。常驻文件的成本乘上了会话数 —— 它是唯一一种每次都在场的,所以它是稀缺资源。路径清单是唯一一种 零主动动作的 —— 其余四种都需要某个主体做一个决定(读、调用、查阅、继续), 只有它的触发条件是一个客观事实:这次改动碰了哪些文件。

8.2.1 比这张表更重要的一条约束

配套的文档里写着这样一句:必须无条件触发的规则要留在常驻文件本体, 不能挪进按需发现的 skill 或 guide。 理由很简单,也很硬:一条要等人(或 Agent)想起来才生效的规则, 和没有这条规则的区别不大。

这句话看起来像常识,但它在实践中被违反得非常频繁,而且违反的动机总是好的 —— 常驻文件太长了得瘦身;这条规则很详细,写成一份专门的说明文档更清楚。 这两个动机都是对的,而按它们行动通常是错的。

正确的做法不是把规则挪走,是把规则压缩到能留在常驻文件里的长度, 然后把展开的部分放进 guide —— 规则本身留下,细节移出去。

8.3 载体一:永远在场

先看一个可核验的数字:一个 310 万行的仓库,常驻上下文 153 行, 比例约 1:20,000。

大部分团队的常驻文件会长到一两千行然后变成噪音。原因不是没人管, 而是”加一条”的成本看起来是零 —— 它不需要评审、不需要测试、 不占用任何人的时间。它真实的成本是:稀释了其余每一条的到达概率。

一份 1,500 行的常驻文件和一份 150 行的,对 Agent 来说不是”信息多十倍” 的关系。超过某个长度之后,它更像是一份背景噪音 —— Agent 会读它, 但不会让每一条都影响自己的动作。

8.3.1 判断一条规则能不能进常驻文件

三个判据,按顺序问。

第一,它是否必须在 Agent 落笔之前就在场?如果这条规则只在特定任务里 才相关(比如”怎么加一个新的埋点指标”),它属于 skill 或 guide, 不属于常驻文件。

第二,不遵守它的后果是否不可逆或静默? 可逆且会报错的问题,可以让检查去拦 —— Agent 撞一次就学会了,成本是一轮流水线。而不可逆的(改坏了一个已经应用过的迁移) 或静默的(限流悄悄塌成一个桶),必须在动手前就在场。

第三,它能否被写成结构,而不是文字? 这一条最容易被忽略,也最重要。 如果一条规则可以被表达成目录结构、类型定义、依赖方向,或者一条自动检查, 那它就不该占用常驻文件的位置。常驻文件应该是”实在没法写进结构”的那些东西 的最后归宿,不是第一选择。

按这三条筛一遍,大部分团队的常驻文件会掉下去一半以上。

(那份 153 行文件的逐行注解在 附录 C, 包括一条禁令的六个组成部分,以及它没有包含什么。)

8.3.2 每条规则都要带着它的失败形态

这是常驻文件质量的分界线,而差别巨大。对比同一条规则的两种写法。

大多数人写的是:“不要用 sed -i 做批量替换。”

实际那份里写的是:绝不用原地批量替换修改文件 —— 一个为你推理过的调用点写的模式,也会重写你没有推理过的, 而损坏是静默的,构建照样通过。接着是五类会被误伤的文件、 一句”被重写的迁移在跑过它的数据库里无法撤销”、 一个替代做法,以及对”但我这次量很大”这个例外的回答。 (全文和它六个部分的拆解在 小节 C.1。)

区别不在字数。写法一只能挡住字面匹配的那一种(用了 sed -i), 而写法二让 Agent 能够泛化到没被列举的情况 —— 一个读了写法二的 Agent, 在考虑用某个新工具做批量替换时,会自己识别出这是同一类风险。 读了写法一的不会,因为它得到的是一条禁令,不是一个机制。

这条纪律可以直接抄走:常驻文件里的每一条规则,都要回答”不遵守会怎样”, 而且答案要具体到失败的形态。

8.3.3 常驻文件的其它特征

那份 153 行的文件还有两个特征值得学。

规则被写成决策程序,而不是口号。 比如目录层级那条(小节 6.2), 它不只是说”每层必须被挣得”,而是紧接着给了三个真实路径作为判例 —— 双平台的产品长什么样、多进程的产品长什么样、单一的产品长什么样。 一个 Agent 读完能够”判断”,而不只是”遵守”。

它预判了 Agent 特有的失败方式。 文件里有这样几句:把评审和追问 当成提高抽象层级的信号,而不是打点修复的请求;不要在一个重复的机制之上 打磨业务 bug;有”页面”这个词、或者能被展示,都不足以让它成为一个页面。 这三句都不是在描述架构,是在描述 Agent 会怎么想错 —— 写这三句的人观察过 Agent 在这些地方失败,然后把观察写了进去。

8.4 载体二:被调用时 —— skill

这里的 skill 不是提示词模板,而是把产品的整个生命周期做成了一批可以调用的过程。 实测 21 个,从建产品到上架,中间每一段都有入口:建产品建页面建组件、 生成素材做图标做上架截图、调关键词接订阅、加埋点查日志、 代码审查架构检查界面打磨、合并迭代本地化。

关键区分是:skill 是动词(做完一件事),guide 是名词(解释一个机制)。 这个区分决定了它们各自的发现方式和上下文成本 —— 一个动词需要被”调用”, 所以它必须出现在客户端的发现位置;一个名词只需要被”查阅”, 所以它按路径读就行,不占常驻上下文。

8.4.1 载体的发现机制是代码,不是约定

这一节是这一章最硬的证据。

skill 的规范形态存放在一个中立的目录里,然后被投射到各个客户端的 原生发现位置。校验脚本里的三行常量把这个结构说得很清楚:

CANONICAL_ROOT = REPO_ROOT / "agents"  / "skills"   # 唯一事实源
CODEX_ROOT     = REPO_ROOT / ".agents" / "skills"   # 投射一
CLAUDE_ROOT    = REPO_ROOT / ".claude" / "skills"   # 投射二

一份规范源,两个投射位置 —— 这是”每份可变状态收敛到单一 writer” 用在了 Agent 指令上。

更值得学的是它对规范源施加的约束。校验脚本里有一张可移植性模式表, 这些东西不许出现在规范源里:某个客户端的目录路径、某个客户端特有的 参数替换语法、某个客户端特有的环境变量、某个客户端特有的交互工具名、 序列化的 MCP 工具名、个人 macOS 路径、供应商署名行、 以及只在特定编辑器里可解析的双括号链接。

这张表的意义远超”代码整洁”:它是一条施加在载体本身上的守卫。 规范源必须是供应商中立的,而客户端特有的部分(那份包装里只允许四个键) 只能出现在投射层 —— 机制与策略分离,用在了 Agent 指令上: skill 是机制,投射是策略。

它带来的实际好处是可以验证的:同一批 skill 同时喂给两个不同的 Agent 客户端。 常驻文件那边用了一个更省事的办法 —— 一个符号链接, 一份内容两个文件名,因为两个客户端读的是不同的文件名。

这一节想说的是:载体不是一个约定,是一套有校验的机制。 如果你的”skill 目录”只是一个大家说好放在那儿的文件夹, 那它会在第三个人加入的那天开始漂移。

8.5 载体三:主动查阅时 —— guide

guide 是机制说明,边做边查,无投射,按路径读,不抢占上下文 —— 而最后半句是它存在的全部理由。

一份讲”模拟器上有哪些坑”的说明可能有两千字。它必须存在, 否则 Agent 会花两小时去修一个不存在的 bug(小节 9.3); 但它不能常驻,因为绝大多数任务跟模拟器无关。guide 解决的就是这个: 内容可以很长,因为它只在需要时被读。

判断一件事该写成 guide 还是写进常驻文件,用 小节 8.3.1 那三条判据的第一条就够 —— 它是否必须在落笔之前就在场? “改共享协议后要 grep 所有实现”必须在场,进常驻文件; “模拟器上的调试浮层恒在最上层”只在做模拟器相关任务时相关,进 guide。

8.5.1 guide 该多长

常驻文件要短,而 guide 可以长 —— 但也不是无限。判据是:Agent 会不会读完它?

这个问题可以被拆成两个更实际的。它有没有一个明确的入口问题? 一份好的 guide 开头就说清楚”你在遇到什么问题时该读我”, 没有这个,Agent 读了前几段发现不相关就不会继续。它的结论在不在前面? 这是和给人看的文档最大的区别 —— 给人的文档可以循序渐进, 给 Agent 的应该结论先行,因为它可能只读前三分之一。

那份关于模拟器的说明就是这么写的:它没有从”模拟器是什么”讲起, 它直接说”改取窗逻辑一律无效”,然后才解释为什么。

8.6 载体四:碰到路径时

这是最巧妙的一种,也是最容易被忽略的一种。

前三种载体都需要一个主动动作才能到达:常驻文件靠”永远读”, skill 靠”决定调用”,guide 靠”想起来查”。第四种不需要 —— Agent 改到哪个路径,那个路径的背景自己浮上来。

路径清单里有一个 read 字段,列出”动手之前应该先读什么”。它不依赖 Agent 记得去查,因为触发条件是文件路径,而文件路径是 Agent 必然会碰到的东西。

这解决了一个根本问题:一个 owner 脑子里的不变量, 怎么在他不在场的时候仍然生效。 章节 14 会详细讲这一层, 这里只需要记住它在载体谱系里的位置 —— 它是唯一一种”零主动动作”的载体。

8.7 载体五:跨会话

任务的形态决定了它需要什么。一次性的任务开一个会话就够了,前面四种载体 足以支撑它把事情做完。但周期性的、要跑很多轮的任务不一样 —— 它必须有契约,否则每一轮结束之后什么都留不住,下一轮等于从头开始。

这类任务又分两种,而退出条件完全不同。收敛型的目标是固定的, 跑到完成或预算耗尽就退出,典型的是重构、迁移、修 bug; 演进型的目标每轮被改写,棘轮式前进 —— 锁定进展、重新瞄准 —— 而退出靠人判”够好了”,典型的是研究、写作、产品打磨。

区分这两种很重要,因为给演进型任务设一个”成功就停”的条件是有害的 —— 它会让任务在第一个看起来达标的地方停下,而演进型任务的价值恰恰在于 它每一轮都在重新定义什么叫达标。(顺带一提:写这些东西本身就是一个演进型任务。)

8.7.1 把「记忆」拆开,别糊成一个 blob

这个机制里最值得借鉴的部分,是它没有把记忆做成一个笼统的状态文件, 而是拆成六类,每一类各守各的读写纪律。

不变意图记录这个任务到底想干什么,纪律是永不被迭代改 —— 改它等于人做了一个决定。当前任务态是覆盖写的,单一 owner。 决策叙事记录这轮为什么改瞄,只追加、不可变、一轮一条。 副作用回执记录对外做了什么以及怎么回滚,只追加、一动作一档。 蒸馏的教训是编译过的廉价验证器,去重、冲突时和解。 状态检查产结构化读数,用退出码当通过/失败。

这六条纪律和仓库里代码那套是同一套思想,只不过用在了记忆上: 不变意图只有人能改对应路径不变量,状态单一 writer 对应 owner-first, 日志只追加不可变对应证据可追溯,副作用带回滚对应发布的预演模式, 检查用退出码对应判定的三态。

第一条是最要紧的。 一个能修改自己目标的控制器,永远会报告成功 —— 它会在遇到困难时悄悄把目标改成自己已经做到的那个。把不变意图设成 “只有人能改”,堵死的正是这条路。章节 20 会讲, 这条原则在整个系统层面同样成立,而且那一层上它还没有被贯彻。

8.7.2 节奏由外部反馈延迟决定

周期任务的节奏不是拍脑袋定的,而是由外部系统的反馈延迟决定 —— 改动多久能看到效果,观察周期就得多长。

仓库里现有的两个周期任务正好是两种节奏。一个是日级:投放报表按日结算、 出价改动约一天见效,所以日级是最小有效观察粒度。另一个是周级: 元数据要走应用商店审核(一到两天)加上重新索引(数天)才见效。

这一条在控制论里叫采样周期匹配对象时间常数(见 小节 17.4)。 采样快于对象响应,采到的是噪声 —— 而且更糟的是,你会依据噪声动作。 大部分人会为了”响应快”把这类回路设成小时级,然后 100% 在追噪声, 把一个本来会自己稳定下来的系统搅成振荡。

8.8 五种载体的选择流程

把上面的内容压成一个可以照着走的流程:

flowchart TB
    Q1{"必须在落笔前就在场吗?"}
    Q1 -->|否| Q4{"是一个要做完的动作吗?"}
    Q1 -->|是| Q2{"能写进结构吗?<br/>目录 / 类型 / 依赖 / 检查"}
    Q2 -->|能| ST["写进结构<br/>不进任何载体 ← 首选"]
    Q2 -->|不能| Q3{"只在特定路径下相关吗?"}
    Q3 -->|是| PATH["路径清单"]
    Q3 -->|否| ALW["常驻文件<br/>压缩到一两句"]
    Q4 -->|是| SK["skill"]
    Q4 -->|否| GD["guide"]
    style ST fill:#e8f4e8
    style ALW fill:#fdf3e3

外加一条正交的:如果这个任务跨会话,无论上面走到哪一支, 都还需要一份迭代契约。

这个流程的第一个分叉最重要:首选永远是”写进结构”。 每一条留在文档里的规则都是一次失败 —— 它意味着这条规则没能被表达成 一个 Agent 一读代码就接收到的事实。

8.9 常驻文件是怎么变长的

知道判据还不够,因为常驻文件变长从来不是一次决定, 而是几十次各自合理的小决定。逐个看它们长什么样。

“这条很重要,得加进去。” 重要不是判据,判据是”它必须在落笔前在场”。 一条极其重要但只在特定任务里相关的规则,放进常驻文件会稀释其余每一条, 而它本可以放在那个任务的 skill 里。

“上次出了事,加一条免得再发生。” 这是最有说服力也最危险的一种。 事故之后加一条规则的冲动很强,但正确的问题是那三条判据的第三条: 这次事故能不能被写进结构?小节 15.1 里那次就是反例 —— 不变量在事故前一天写进了常驻文件,第二天照样出事。

“写清楚一点,免得它理解错。” 于是一条三行的规则变成十五行。 超过某个长度之后,详细程度和到达概率是反向关系 —— 一条十五行的规则,Agent 读到的是它的前两行。正确的做法是压缩规则本身, 把展开部分放进 guide。

“这条和那条差不多,一起放着吧。” 相似的规则应该被合并成一条更一般的, 而不是并列摆着。两条相似的规则并列,意味着它们背后那条真正的原则 没有被找出来

四种冲动的共同点是:它们都在回答”该不该有这条规则”, 而正确的问题是”它该放在哪个载体上”。 面对”文件已经太长了”这个真实的问题,正确的动作在 小节 8.17.1

8.10 载体错配的四种典型

反过来看,把规则放错载体会发生什么。

无条件规则放进 guide,结果是形同虚设 —— 只有想起来查的人才受约束。 特定任务的规则放进常驻文件,结果是稀释其余每一条的到达概率。 路径相关的规则放进常驻文件,同样是稀释,而且它本可以零成本地精确触发。 跨会话的状态塞进单次上下文,结果是每一轮从头开始,前面的进展全部丢失。

第三种值得单独说,因为它是最容易被忽略的一种浪费。一条”改数据迁移时 要注意 X”的规则,如果放进常驻文件,它会在每一个会话里占用位置 —— 而其中 99% 的会话根本不碰迁移。把它挪进路径清单之后, 它在那 1% 的会话里更醒目(因为它是被专门触发的), 在其余 99% 里不占任何成本

这是一次纯粹的帕累托改进,而它只需要换一个载体。

8.11 载体分类为什么有效

这一章到这里为止讲的都是”怎么做”。这一节讲”为什么”, 因为这个解释会告诉你在什么情况下它会失效。

值得先说一句:编解码器那个模型解释不了这一节小节 21.3)。 载体分类之所以有效,跟解码的确定性没有任何关系,它依赖的是三个更具体的机制。

第一,上下文里的东西不是等价的。 一段文字出现在上下文里, 不等于它会等权重地影响输出 —— 位置、重复、与当前任务的相关性, 都会改变它的实际权重。一份两千行的常驻文件里,中间那一千行的实际影响力 接近于零。153 行这个数字的分量就在这里:它不是审美, 是把每一条都保持在”能实际起作用”的范围内。

第二,模型有它自己的先验,而你的规则在和先验竞争。Agent 见过海量代码, 它对”日志该怎么写”“目录该怎么分”有很强的先验。你的一条规则要压过这个先验, 需要的不只是”出现在上下文里”,而是足够具体、足够有理由。这解释了 小节 8.3.2 那条纪律为什么有效:带失败形态的规则之所以 比裸禁令强,不是因为它更长,是因为它给出了一个先验里没有的事实 (“这个操作是不可逆的”)—— 而事实比禁令更能改变输出分布。

第三,触发时机决定了它是否参与那次决策。一条在决策发生之后才到达的规则, 无论多正确,都不参与那次决策,它只能触发一次返工。这是路径清单 (小节 8.6)的全部价值:它把到达时机绑定在了一个 Agent 必然会经过的事件上。

8.11.1 这个解释预测了什么时候会失效

有了机制解释,就能预测失效。常驻文件很好但规则还是没被遵守, 说明它在和一个很强的先验竞争,需要更具体的理由。规则在简单任务里有效、 复杂任务里失效,说明上下文被任务本身占满了,规则的相对权重下降。 同一条规则对不同 Agent 效果不同,说明先验不同。 加了更多规则之后整体效果下降,那是稀释。

最后一种是最常见的,而且它的反直觉之处在于:每一条新加的规则单独看 都是正确的、有用的,而它们的总和让系统变差了。

8.12 真实的载体分布

作为参照,我这套系统的实际分布是:常驻文件 153 行一份, skill 21 个,guide 14 篇,路径清单 20 份,跨会话契约 2 个在跑。

注意常驻文件那一行和其余四行的比例。按字数算,常驻文件大概占全部 Agent 面向材料的百分之几 —— 而它是唯一一份每次都在场的。

这个比例本身就是这一章的论点:永远在场的东西必须极度克制, 因为它的成本乘上了会话数。而其余四类的总量可以很大 —— skill 和 guide 加起来有十几万字 —— 因为它们的成本只在被使用时才产生。

这是一个典型的”稀缺资源 vs 廉价资源”的分配问题, 而大部分团队把所有东西都堆进了稀缺的那一侧。

8.13 从写下到生效,中间有几步

这一章讲”什么时候到达”,但”到达”本身还可以再拆。一条规则真正影响到输出, 中间有四步,而每一步都可能断掉

第一步是它被写下来。第二步是它在正确的时刻出现在上下文里 —— 这一步的断点是载体错配。第三步是它的权重足够压过模型的先验 —— 这一步的断点是规则太抽象、先验太强。第四步才是它改变了这次的输出。

四步里,大部分团队只管了第一步。 这一章的三个核心建议,正好各针对一个断点:按到达时机选载体修第二步, 每条规则带失败形态修第三步,常驻文件保持极短也修第二步。

第三步的断点最容易被误诊。 症状是”规则明明写了,它就是不照做”, 而人的第一反应是加强语气 —— “必须”“绝对不能”“重要!!”。 这对权重的影响很小。有效的做法是给出一个先验里没有的事实: 不是”绝对不要用批量替换”,而是”一个被重写的迁移在已经跑过它的数据库里 无法撤销”。前者是强调,后者是信息,而只有信息能改变输出分布。

8.14 五种载体各自的失效模式

每一种载体都有它特有的坏法,而知道坏法比知道用法更实用

8.14.0.1 常驻文件会稀释

症状是规则都在,但 Agent 好像只遵守其中一部分, 而且是随机的一部分。检测很简单:数一下行数,超过两百行就该警惕, 超过五百行基本已经在发生。

8.14.0.2 skill 会漂移

症状是 skill 里描述的步骤 和实际的做法对不上了。原因是 skill 是一份被写下来的过程, 而过程会变,文档不会自动跟着变。我给 skill 加了校验, 但那只检查形式(可移植性),不检查内容是否过时 —— 这是一个没有被解决的问题,而它的严重程度随 skill 数量增长。

8.14.0.3 guide 会无人问津

症状是写了但没人(没有 Agent) 去读,原因是 guide 的到达依赖”主动查阅”,而主动查阅需要 Agent 先知道 有这份 guide 存在。缓解办法是在常驻文件里留一行索引 —— 不是内容,是指针。 一行”改路由相关的东西之前先读某某”,成本是一行, 而它把 guide 从”可能被发现”变成”一定被知道”。

8.14.0.4 路径清单会范围漂移

症状是清单还在, 但它匹配的路径在一次重构后变了。检测办法是把每份清单的命中次数记下来 —— 一份从来不命中的清单,要么是路径写错了,要么是那类改动不再发生了, 而两种情况都需要处理,它们看起来一模一样。

8.14.0.5 跨会话契约会状态腐烂

症状是契约文件还在,但里面的状态和现实对不上了, 原因是中途有人手工改了外部世界而契约不知道。那六类记忆里的”副作用回执” 就是防这个的 —— 每一个对外的动作都要记下它做了什么和怎么回滚。

8.14.1 五种失效里有三种是完全静默的

常驻文件的稀释、skill 的漂移、guide 的无人问津 —— 这三种没有任何信号。 只有路径清单(看命中次数)和跨会话契约(看回执对不对得上)有可观察的量。

这一整章讲的是”规则怎样在正确的时刻到达”—— 结果是这套机制本身, 大部分处在没有判定覆盖的状态。

这是 小节 21.4 那个缺口在载体这一层的形态,而它的解法也是同一个: 给每一种载体找一个可观测的量。常驻文件的行数、skill 的最后修改时间与 它描述的代码的最后修改时间之差、guide 的被引用次数、清单的命中次数 —— 四个数,全都是现成的,而且全都没有被采集。

8.15 五种载体和五种到达时机的完整对照

把整章压成一张表作为收尾:

时机 载体 成本 触发方式 失效形态
永远在场 常驻文件 每次会话 无条件 稀释
被调用时 skill 被调用时 Agent 决定 漂移
主动查阅时 guide 被读时 Agent 想起来 无人问津
碰到路径时 路径清单 命中时 文件路径(零主动动作) 范围漂移
跨会话 迭代契约 每轮 任务本身 状态腐烂

第四行的”零主动动作”是这张表里最特别的一格。其余四种都需要某个主体 做一个决定 —— 读(常驻)、调用(skill)、查阅(guide)、继续(契约)—— 而路径清单不需要,它的触发条件是一个客观事实。

它值得被单独发明出来,正是出于这个,而不是让那些内容散在 guide 里: guide 依赖”Agent 想起来查”,而那是一个概率事件。

8.16 ⚙️ 小规模怎么做

这一章零基建成本,一个人的项目也完全适用。

一、把你的常驻文件按那三条判据筛一遍。 大概率会掉下去一半以上。掉下去的分两拨:能写进结构的写进结构, 不能的做成 guide。

二、给留下来的每一条补上失败形态。 如果补不出来 —— 你写不出”不遵守会怎样”—— 那这条规则可能本来就不该存在。这个测试比它听起来严格得多。

三、一百行有用的常驻文件,胜过一千行没用的。 这不是审美主张。一千行里的每一条,到达概率都比一百行里的低。

四、跨会话的任务,至少要有前两类记忆。 不变意图(人写、Agent 不许改)和当前状态(覆盖写)。 后四类可以等真正需要时再加。

8.17 常驻文件的演化史

这一章讲了怎么写,但没讲它是怎么变成现在这样的。 演化的过程比结果更有信息量。

大部分常驻文件的演化路径是这样的:

① 空的
② 加了几条最明显的约定(缩进、命名)
③ 出了事故,加一条
④ 又出事故,再加一条
⑤ 变长了,有人抱怨 Agent 不遵守
⑥ 加粗、加感叹号、加"重要!"
⑦ 更长了,效果更差
⑧ 有人提议"整理一下"
⑨ 把详细的部分挪进单独的文档
⑩ 那些规则从此不再生效

第⑥步和第⑨步是两个典型的错误转向。 第⑥步(加强语气)无效的原因在 小节 8.13 的第③步: 权重不是靠语气获得的,是靠给出先验里没有的事实。

第⑨步(挪进单独文档)的错误更隐蔽,因为它看起来像是在做正确的事 —— 文件确实短了,内容确实还在。

但那些规则的到达时机变了:从”永远在场”变成”想起来才查”。 如果一条规则原本需要无条件生效, 这个改动等于删掉了它小节 8.2.1)。

8.17.1 正确的第⑧步是什么

面对”文件太长”这个真实的问题,正确的动作有三个,按优先级:

一、把能写进结构的写进结构。 通常能砍掉最多, 而且砍掉之后约束更强了(小节 6.12.1)。

二、把规则压缩,把展开部分移出去。 注意是”规则留下,细节移出”,不是”整条移出”。

三、真正只在特定任务里相关的,才移进 skill 或 guide。 三个动作的共同点:它们都不改变任何规则的到达时机。

这正是判断一次”整理”对不对的唯一标准:

整理之后,每一条规则的到达时机变了吗? 变了 → 你不是在整理,你是在改变系统的行为。

8.18 载体分类的一个边界

这套分类有一处不够用。 它假设”规则”和”知识”可以被清楚地分开。

实际上有一类东西介于两者之间:领域知识。 比如”这个业务里,一笔退款要经过三个状态, 而中间状态不能被跳过” —— 这是规则还是知识?

  • 当成规则 → 它太具体,进常驻文件会挤掉别的
  • 当成知识 → 它需要在改到相关代码时在场,而 guide 依赖主动查阅

这套系统的答案是:让它进路径清单的 invariant 字段。 因为路径清单是唯一一种”零主动动作”的载体(小节 8.15), 而领域知识恰好有一个天然的触发条件 —— 相关的代码路径。

但这个答案有它的极限:如果一个领域知识 不对应任何特定的路径(比如一条跨越十几个模块的业务约束), 那这套分类给不出好答案。 实践中的处理通常是:把它写进 guide, 然后在常驻文件里留一行指针 —— 一个妥协, 而且它的失效方式是可预测的小节 8.14.0.3)。

8.19 当一条规则需要两个载体

前面把载体讲成了一次选择:这条规则该放哪一个。 但实践中有一类规则需要两个,而怎么拆是有讲究的。

典型的形态是:一条规则必须无条件生效(所以要常驻), 而它的完整说明又太长(所以常驻文件放不下)。 “绝不批量原地替换文件”就是这样一条 —— 禁令本身必须每次都在场,而”那五类会被误伤的文件分别是什么、 为什么迁移文件的损坏不可撤销、正确的批量重命名该怎么做” 这些内容加起来是好几页。

正确的拆法有一条硬规则:判定所需的部分留在常驻文件, 理解所需的部分移出去。也就是说,常驻的那一份必须包含 足以让人(或 Agent)当场判断自己有没有违反的全部信息 —— 禁令、触发条件、以及一个能立刻执行的替代动作。 “为什么”和展开的例子可以移进 guide。

错误的拆法长得很像:把禁令留下,把失败形态移出去。 这个版本读起来更简洁,常驻文件也更短, 但它把 小节 8.3.2 那条纪律破坏掉了 —— 一条只有禁令没有失败形态的规则, 在遇到一个看起来合理的例外时会被绕过, 而失败形态正是那时候唯一能拦住人的东西。

判据可以说得很简单:如果一条规则被拆成两半之后, 读了常驻那一半的人仍然会犯同一个错,那这次拆分是错的。 拆分的目标是压缩,不是分流 —— 压缩之后每一句仍然在服务判定, 而分流之后有一半的判定信息不在场了。

顺带说一句为什么不能反过来(把完整版留常驻、摘要放 guide): 常驻文件的每一行都在和其余每一行竞争注意力(小节 8.3), 所以那里的稀缺资源是长度;而 guide 的稀缺资源是 有没有人去查,它不怕长。两个载体的稀缺资源不同, 所以同一份内容在两边的最优形态也不同—— 所以”移过去”不是一次复制粘贴,是一次重写。

8.20 载体分类和”上下文工程”的关系

这一章讲的东西,和现在常被叫做”上下文工程”的那一批实践 是同一个问题域,但切法不同。值得对照一下。

常见的切法是按”内容类型”:系统提示、示例、 工具定义、检索到的文档、对话历史。 这一章的切法是按”到达时机”。

两种切法的差别在于它们能回答什么问题:

问题 按内容切 按时机切
这段该放哪个字段? 能答 不直接答
这条规则该不该常驻? 不直接答 能答
为什么它没生效? 不直接答 能答小节 8.13
文件太长了怎么办? 不直接答 能答小节 8.17.1

按内容切解决的是”怎么组装一次请求”, 按时机切解决的是”怎么组织一个仓库的知识”。 前者是单次的,后者是持续的 —— 而在一个几十个 Agent 持续工作几个月的系统里,后者才是主要问题。

8.20.1 两种切法可以叠加

它们不冲突。实际的形态是:时机决定它住在哪个载体, 内容决定它在一次请求里被放进哪个字段。

一条常驻规则可能进系统提示,一份被查阅的 guide 可能作为检索结果进上下文 —— 而它们的”住处”是不同的, 这个不同是持久的,而组装方式是每次都可以变的。

所以载体分类是更靠上游的一层决定。

8.21 载体的成本不在写,在维护

五种载体的写作成本差不多 —— 都是把一段话写清楚。 它们的维护成本差好几倍, 而这个差别在决定”这条规则放哪”时应该被算进去。

常驻文件的维护成本最低但最持续:它不需要单独维护, 但它的每一行都在和其余每一行竞争, 所以每加一条都在给已有的所有条目加一点成本 (小节 8.9)。这是一种分摊到全体的成本, 所以它容易被忽略 —— 加第三十一条的人不会觉得自己付了什么。

skill 的维护成本最高,因为它有一个发现机制 (小节 8.4.1),而发现机制会随客户端演进而变。 它还有一个别的载体没有的负担:skill 描述的是一套流程, 而流程会随着工具变。一份三个月没被跑过的 skill, 它引用的命令可能已经改了参数。

guide 的维护成本最低也最容易归零—— 它不影响任何别的东西,所以它可以安静地过时, 而没有人会付出代价,直到某天有人照着它做了一遍 (小节 8.14.0.2)。

路径清单的维护成本集中在一处:路径。 内容几乎不需要维护(不变量很稳定), 而路径会随重构而失效(小节 14.24)。 这是一个很好的性质,因为它意味着维护点是单一且可枚举的—— 一次重构之后,你需要检查的只有 paths 字段。

跨会话契约的维护成本取决于任务本身还在不在跑。 它是唯一一种”任务结束就该被删掉”的载体, 而它最常见的失效是没有被删 —— 一份还在的契约 会让下一个会话以为那个任务还在继续。

把这五种排一遍会看到一条建议:如果一条规则可以放在 路径清单里,就放在那里。它的维护点最少、最集中、 而且失效方式最容易被检测(命中次数掉到零)。 这一条恰好和 小节 8.8 那个流程图 给出的答案一致 —— 两条不同的推理路径走到了同一个结论。

8.22 跨越两章的观察

最后把这一章和形状表连起来。 载体错配本身会制造形状 A。

具体地说:一条被放进 guide 的无条件规则, 它看起来是存在的 —— 文件在、内容对、 甚至被写得很好。

它实际上不生效。

这和”一个坏掉的检查输出一片绿色”是同一个结构: 系统的状态看起来正常,而它实际上没有在做那件事。

所以”我们的规范里写了”这句话, 和”我们的测试通过了”一样,需要被追问一层:

写在哪儿?什么时候到达?谁读了?

三个问题,而大部分团队只能答第一个。

8.23 压成三句话

一、问题不是”这条规则重不重要”,是”它什么时候需要到达”。

这个问题有答案,答案是可判定的 —— 这是这一章唯一的发明,也是全书最可移植的一个抽象。

二、必须无条件生效的规则,不能挪进按需发现的地方。

因为一条要等人(或 Agent)想起来才生效的规则, 和没有这条规则的区别不大小节 8.2.1)。

违反这一条的动机总是好的(文件太长了、写详细一点更清楚), 所以它需要被写成一条明确的约束, 而不是留给判断。

三、每条规则都要带着它的失败形态。

不是为了严谨,是因为权重不是靠语气获得的, 是靠给出先验里没有的事实(小节 8.13 的第三步)。

8.24 一个练习:重写你的第一条规则

拿你常驻文件里的第一条规则,按这个模板重写一遍:

[禁令或要求,一句话]
[失败机制:不遵守会发生什么,具体到形态]
[不可逆性:如果它不可逆,说明为什么]
[替代方案:那该怎么做]
[例外的堵截:最可能被提出的例外,以及为什么它也不成立]

五个部分,通常会从一行变成五到八行。

重写完之后问一句:这五个部分里, 有哪一个是我写不出来的?

  • 写不出失败机制 → 这条规则可能不该存在
  • 写不出替代方案 → 它会制造困惑而不是指导
  • 写不出例外的堵截 → 它会在第一个”但是”面前失效

三个”写不出”,各自指向一个不同的问题。