OpenClaw 中 AGENTS.md 文件,一行尽量不超过 240 个字符,因为有特殊截断规则.
大家好, 上篇内容和大家聊了 MEMORY.md在记忆系统中的流程, 以及它的截断规则。 这次我们继续补充一下上次的一些细节。
我们上次聊过, workspace 跟目录中会有下面几个 bootstrap 文件:AGENTS.md,SOUL.md,TOOLS.md,IDENTITY.md,USER.md,HEARTBEAT.md,BOOTSTRAP.md,MEMORY.md
也说过有一个长度限制规则,总长度 60000个字符, 单个文件 20000 个字符。
如果超过长度,内容就会被截断。 具体细节上篇文章中有过介绍
为什么你的 OpenClaw 总是记不住? 记忆系统探索之 MEMORY.md
但是这次要和大家聊的是一个例外, 就是 AGENTS.md 这个文件的截断规则,和上面列出的几个其他文件不太一样。
简单来说, 其他几个文件, 如果遇到超过默认字符上限, 需要截断的情况,规则是文件开头留一部分,然后结尾留一部分,中间部分截掉。
AGENTS.md 的截断规则,也有留头尾,去掉中间的逻辑。 但是它还多了一个保留关键内容的流程。
具体是这样,下面是它的三个分配常量值:
const AGENTS_POLICY_DIGEST_RATIO = 0.35;const AGENTS_POLICY_HEAD_RATIO = 0.45;const AGENTS_POLICY_TAIL_RATIO = 0.15;
也就是:
头部:45%规则摘要:35%尾部:15%提示和换行预留:约 5%
当 maxChars = 20,000:
headChars = 9,000digestBudget = 7,000tailChars = 3,000
这里边的 digestBudget, 就是和其他文件不同的一点。 假设超过默认 20000 字符长度限制, 其中 7000个字符,要留给 digestBudget。
240个字符限制
下面我们来聊聊标题中数说的 240字符问题,AGENTS.md ,发生截断的时候, 会进入这样一个特殊为他处理的子程序,先把每一行做标准化,这里面就会把每行最大限制到 240字符。
从完整文件中查找规则候选行
等价于:
const candidates = content .split(/\r?\n/u) .map((line, index) => ({ index, line: normalizePolicyDigestLine(line), })) .filter(({ line }) => { return line.length > 0 && isPolicyDigestCandidate(line); });
这里扫描的是完整 AGENTS.md,包括最终不会出现在头部和尾部的中间区域。
每个候选行保留:
index:原始行号line:标准化后的文本
标准化每一行
逻辑等价于:
let normalized = line.trim();normalized = normalized.replace(/\s+/gu, " ");if (normalized.length > 240) { normalized = utf16SafePrefix(normalized, 239) + "…";}
具体规则:
-
删除行首和行尾空白。 -
连续空格、Tab 等压缩成一个空格。 -
单行最多 240 个字符。 -
超长行保留前 239 个字符,再添加 …。
例如:
- Never commit secrets
会变为:
- Never commit secrets
候选行匹配
对所有的行做了标准化之后,就会进入候选行匹配, 这里的意思是那些行有资格被当作重要的内容,有两种主要类别:
第一类:Markdown 结构行。
const markdownRule = /^(?:#{1,6}|\s*[-*+]|\s*\d+[.)])\s+\S/u.test(line);
可以匹配:
# Heading## Heading- Rule* Rule+ Rule1. Rule2) Rule
第二类:包含规则关键词的普通行。
等价关键词集合:
const candidateKeywords = [ "AGENTS.md", "scoped", "required", "must", "never", "do not", "before subtree", "read scoped", "owner", "security", "secret", "credential", "test", "validation", "command", "commit", "push", "github", "pr",];
匹配大小写不敏感。
具体调用代码:
return markdownRule || containsCandidateKeyword(line);
高优先级规则
上面两条规则,会把特殊 markdown 标题语法作为重要内容, 也会把包含上述关键字的行作为重要内容。
除此之外,还有一个高优先级关键字规则, 就是上述关键字列表的一个子集:
const highPriorityKeywords = [ "AGENTS.md", "scoped", "required", "must", "never", "do not", "before subtree", "read scoped", "security", "secret", "credential",];
这些内容优先于 test、commit、push、github 等普通候选。
选取顺序
接着会先从 highPriorityKeywords 这个匹配行中优先选择, 如果都选完, 还有可以用的字符配额, 再去从普通匹配行中选取。只要知道这点 highPriorityKeywords 中关键字匹配到的行, 会优先占用 AGENTS_POLICY_DIGEST_RATIO 的字符配额。
篇幅原因这个过程就不展开了。
总结
总之前面说这这么多, 主要是给大家展示 AGENTS.md 特殊截断规则的一些具体过程,让大家有一个实际的理解。
我们的标题已经把最核心要表达的事情说了, 尽量每行不要超过 240 字符,特别是中间位置的内容。
解释一下,一旦 AGENTS.md 超过 20000 个字符的上限,哪怕只多了 1 个字符,也会走到截断流程, 这时候文件内容的 前 45% 和 后 15% 还会原样保留, 但是中间那 35% 的内容, 就一定会变成标准化后,也就是 240 字符以后都别截掉的样子。 一旦这件事情发生,而你这里面很多行都超过 240 字符的话,就会损失很多语义内容。
然后大家是不是还会有一个发现, candidateKeywords 的列表是英文关键词。 如果你是用英语之外的语言写的 AGENTS.md, 在关键字匹配这个流程上,你的内容基本不会被匹配到。 因为这个阶段没有到达语言模型的参与,无法理解语义,只能用程序的快速字符串匹配。
当然,匹配规则不只有关键字, 还有 markdown 语法元素。 其实从这个匹配规则上,大家也能更了解 AGENTS.md 这个文件的优化的写法怎么来写。
这次我们就先聊这么多, 希望对你有帮助。
夜雨聆风