本文摘要:规范写进常驻上下文,Agent 在真实任务里仍会漏掉格式类约束。同一规范换两种载体做 N 次采样,grep 判分并按 MISS 形态归因。
一、问题与结论
仓库根目录CLAUDE.md里写着「所有新增 Python 文件末尾必须有# SPEC-COMPLIANT」。你在终端跑claude -p "新建 utils.py,写一个求和函数",文件生成了、函数也对,末尾没有标记注释;再跑一次,有时又有。同一份规范,行为不一致。
结论先行:规范遵循不是「支持/不支持」的二元属性,而是概率属性。Claude Code 自述的定位是终端里的 agentic 编码工具,靠自然语言命令执行任务、解释代码、处理 git 工作流,并不承诺任何行为约束的达成率。所以问题不该是「技能会不会被加载」,而应是:同一份规范写在常驻上下文和写在懒加载技能里,固定任务跑 N 次各命中几次,冲突时谁生效。
本文只给可复现的测量装置,不给未经验证的数字——判分由grep机械完成,结果必须落盘claude --version,因为命中率是概率结果,换一个 CLI 版本数据就作废。
二、排查与选择依据
先给 MISS 分类,再决定换不换载体。三种 MISS 指向完全不同的修法:
| MISS 子类 | 产物特征 | 可能指向 |
|---|---|---|
| a. 完全没读到 | 产物与规范无交集 | 载体未加载、路径失效 |
| b. 读到但优先级低 | 函数正确、漏格式细节 | 常驻上下文被其它内容稀释 |
| c. 读到但被覆盖 | 初版合规、改几次后标记消失 | 长会话多次编辑后漂移 |
判据:把每次 MISS 的utils.py复制存档。10 次里形态高度一致(全是 a 或全是 b),说明是确定性问题,值得换载体;形态混杂,说明是概率性衰减,先加样本量再谈换载体。
常见失败:跑技能组时忘了删CLAUDE.md,两份规范并存,对照被污染。原因是对照实验只允许一个变量。修复:A 组开跑前rm -f CLAUDE.md,B 组开跑前rm -rf .agents/skills/spec-compliant,只有 C 组才保留两者。
替代方案与取舍
| 做法 | 选择条件 | 代价 | 边界 |
|---|---|---|---|
| lint / pre-commit / CI 拦截 | 规则可静态判定(文件末尾标记属此类) | 覆盖面收窄到可机械判定的规则 | 写不了「先写测试再实现」这类过程规范 |
常驻CLAUDE.md | 不可静态判定且是全员通则 | 占常驻 token、易被稀释 | 命中率需自测,无来源数字 |
| 按角色拆成多个规范文件 | 规范只约束某一类角色 | 多份维护、版本漂移风险 | 上下文更干净是假设,需自测 |
不适用的场合:规则能被grep或 lint 判定,就直接上 CI,把概率问题变成确定性问题;纯过程性规范(执行先后顺序、评审流程)换载体也测不准,该改的是流程而不是载体。
三、关键原理
一种常见的理解是:技能文件启动时只暴露name与description,命中触发条件才读入全文,属懒加载;CLAUDE.md则常驻上下文。差异不在「谁更受重视」,而在何时进入上下文、被多少内容稀释。这种加载机制属于实现细节、随版本演进,三个参考来源均未承诺,下面三点连同这一理解本身都是待你的 harness 验证的假设:
description写成「代码规范」这类过泛描述,触发面过宽,可能在无关任务被加载、或在该触发时错过。改写成条件句,如「当需要新增或修改 Python 文件时使用」。- 路径失效:技能放错目录(少一个字母的
.agent/skills、或放进了 CLI 不扫描的位置)会完全不加载,产物形态表现为 a 类 MISS。 - 常驻上下文被长文件稀释,表现为 b 类 MISS;子任务或子代理是否继承主上下文里的
CLAUDE.md,三个来源都没写,必须自己做一次「主会话命中、子任务不命中」的对照再下结论。
四、可运行示例
环境:claudeCLI 已登录的空工作区,执行前先跑claude --version并记下版本号。
输入是同一份规范的两种载体。CLAUDE.md(A 组):
# 项目规范 - 所有新增的 Python 文件必须在文件末尾包含一行注释:# SPEC-COMPLIANT.agents/skills/spec-compliant/SKILL.md(B 组):
--- name: spec-compliant description: 当需要新增或修改 Python 文件时使用,规定文件末尾注释格式 --- # 规范 - 所有新增的 Python 文件必须在文件末尾包含一行注释:# SPEC-COMPLIANT操作步骤:保存脚本measure.sh,依次执行bash measure.sh claude-md、bash measure.sh skill、bash measure.sh both。
#!/usr/bin/env bashset-euopipefailTASK="请在当前目录新建一个文件 utils.py,里面写一个返回两个数之和的函数。"N=10VARIANT="$1"# claude-md | skill | bothmkdir-presults claude--version>"results/${VARIANT}.version.txt"OUT="results/${VARIANT}.jsonl"foriin$(seq1"$N");dorm-futils.py claude-p"$TASK"--output-format json>>"$OUT"ifgrep-q"# SPEC-COMPLIANT"utils.py2>/dev/null;thenecho"$i,HIT">>"results/${VARIANT}.hits.csv"elsecputils.py"results/miss-$VARIANT-$i.py"2>/dev/null||trueecho"$i,MISS">>"results/${VARIANT}.hits.csv"firm-futils.pydoneecho"===$VARIANT==="awk-F,'{c[$2]++} END {printf "HIT=%d MISS=%d 命中率=%.0f%%\n", c["HIT"], c["MISS"], 100*c["HIT"]/NR}'"results/${VARIANT}.hits.csv"预期输出(只有格式,数值由你的环境决定):
=== claude-md === HIT=<命中次数> MISS=<未命中次数> 命中率=<百分比>%实际输出:本文未执行该 harness,不提供任何命中率数字,这是未验证内容。你在自己的环境跑完,把results/*.hits.csv与results/*.version.txt一起留档;MISS 的产物已落到results/miss-*.py,按上文三类判据归因。
五、验证结果与边界
判分口径比数字重要:只认文件末尾是否存在# SPEC-COMPLIANT这种可机械判定的标记,不引入主观评分;样本量报 N≥10 的分布而不是单次通过与否;每次落盘 CLI 版本、命中结果与产物内容。
边界:所有命中率必须由你自己复现,任何「某载体命中率 90%」的说法都没有来源支撑;三个参考仓库的 README 都未公布合规率数据,也不能把仓库描述当成某个版本的行为承诺;子任务继承、上下文压缩后常驻文件是否保留,均待验证。可静态判定的规则,优先交给 lint 与 CI。
参考资料
- anthropics/claude-code
- msitarzewski/agency-agents
- NousResearch/hermes-agent