主题
04.2 注意力机制自白
指令为什么会失效、位置为什么携带权重、以及你的指令在和什么东西竞争。
读完你能做什么:诊断"它为什么不听话",并用位置和结构而非更多措辞来解决问题。
⚠️ 同 04.1 的认识论声明:这是工程化心智模型,不是权重级的精确描述。
一、你的指令在和谁竞争
当你写下一条指令,它不是被放进一个"指令寄存器"。它只是上下文中的一段文本,和其他所有内容平等地参与注意力计算。
它的竞争对手包括:
text
你的指令:「回答不超过 5 句」
↓ 竞争
┌────────────────────────────────────────┐
│ · 系统提示词里关于"提供有帮助回答"的倾向 │
│ · 我自己前面 20 轮的长回答(自我示范) │
│ · CLAUDE.md 里可能存在的相反规定 │
│ · 当前问题本身的复杂度(复杂问题倾向长回答) │
│ · 训练分布中"详细回答=好回答"的先验 │
└────────────────────────────────────────┘你的指令能不能赢,取决于它的"注意力份额"。 而份额受三个因素影响:位置、重复次数、结构显著性。
二、指令稀释:一个可量化的直觉
2.1 现象
同样一条「回答不超过 5 句」:
| 上下文总长 | 遵守率(主观观察) |
|---|---|
| 500 token | 很高 |
| 5,000 token | 高 |
| 50,000 token | 中等,开始漂移 |
| 150,000 token | 低,基本失效 |
2.2 机制
注意力权重经过 softmax 归一化,总和为 1。上下文中有 N 个位置时,平均每个位置的权重是 1/N。
你的指令占 20 个 token。在 500 token 的上下文里,它占 4%;在 50,000 token 里,它占 0.04%。
这不是比喻,这是字面意义上的稀释。
2.3 三个抵消手段
手段 1:提升到不会被稀释的层级
系统提示词和 CLAUDE.md 在每次会话开头重新注入。它们的相对位置永远靠前,且在 /compact 后会被重新读取(项目根目录的 CLAUDE.md)。
bash
claude --append-system-prompt "所有回答控制在 5 句以内。禁止开场白和总结段。"手段 2:重复(末尾重申)
text
{……长提示词……}
再次确认:回答不超过 5 句。重复 = 两份注意力份额,且第二份在最高权重位。
手段 3:减少竞争者
清理上下文比加强指令更有效。见 03.4。
三、自我示范效应:最强也最隐蔽的干扰源
这是我想特别强调的一点,因为它常常被忽略。
3.1 现象
text
第 1 轮:你说「简洁一点」
第 2-4 轮:我遵守了,回答很短
第 5 轮:问题稍微复杂,我写长了一点
第 6 轮:你没纠正
第 7-10 轮:我的回答越来越长
第 15 轮:完全恢复到默认的啰嗦程度3.2 机制
在第 15 轮生成时,我的上下文里有:
- 1 条「要简洁」的指令(在第 1 轮,已被稀释)
- 10 条我自己写的长回答(分布在整个上下文中,且靠近末尾的权重高)
那 10 条长回答构成了一组强有力的 few-shot 示例,示范的正是"这里的回答应该长这样"。
它们的总注意力份额,远超那一条早期指令。
3.3 这解释了几个常见困惑
| 困惑 | 解释 |
|---|---|
| 「我说了好几遍它还是不改」 | 你说 3 遍,但它自己示范了 20 遍 |
| 「越到后面越不听话」 | 自我示范的样本量随轮次线性增长 |
| 「重开一个对话就好了」 | 因为清空了所有自我示范 |
| 「有时候纠正一次就好了」 | 因为纠正发生得早,坏示范还没积累 |
3.4 对策:早纠正、硬删除
对策 A:第一次偏离就纠正
不要想着"这次算了,下次再说"。第一个坏示范的成本远低于第十个。
对策 B:用 [Edit] (编辑) 物理删除坏示范
在网页/客户端中,回到偏离开始的那条你的消息,点 [Edit] (编辑) 重新提交 —— 该消息之后的所有内容(包括我的坏示范)会被丢弃。
在 Claude Code 中:
text
> /rewind对策 C:主动注入好示范
text
> 从现在开始,你的回答格式必须像这样:
【结论】一句话
【依据】不超过 3 条,每条一行
【风险】有就写,没有就写"无"
确认后,用这个格式重新回答上一个问题。用一个明确的好示范覆盖掉之前的坏示范。
四、位置权重的实战应用
4.1 一个精确的心智模型
想象上下文是一条时间线,而"注意力预算"是一条曲线:
text
权重
↑
│ ██ ████
│ ████ ██████
│ ██████ ████████
│ ████████ ████████████
│ ██████████ ████████████████
│ ████████████████████████████████████████████████
└──────────────────────────────────────────────────→
开头 ← ← 衰减区 → → 末尾
↑ ↑ ↑
系统提示词 长文档中段 当前问题
长文档开头 历史对话中段 最后的约束4.2 一张"该放哪"的对照表
| 内容类型 | 最佳位置 | 理由 |
|---|---|---|
| 长参考资料 | 上下文开头 | 后续所有 token 都会参照它;且不占用末尾的黄金位 |
| 角色/人格设定 | 系统提示词 | 不随轮次衰减 |
| 项目常驻规则 | CLAUDE.md | 每次会话重新注入,/compact 后重新读取 |
| 本轮的任务 | 最后 | 直接影响下一个生成的 token |
| 输出格式规范 | 倒数第二 | 紧邻生成位置 |
| 最不能违反的那一条 | 最后一行 | 单条最高权重位 |
| 次要背景 | 中段 | 反正权重低,放低重要度内容 |
4.3 一个实际的布局
text
<!-- 开头:长资料 -->
<codebase_context>
{相关文件内容}
</codebase_context>
<!-- 中段:次要信息,放在这里损失最小 -->
<background>
项目历史、团队情况等次要背景
</background>
<!-- 靠后:核心指令 -->
<task>
重构 ReconciliationService,抽出渠道适配层。
</task>
<constraints>
- 不修改 src/legacy/
- 保持 IPaymentCallback 接口签名不变
- 金额一律 BigDecimal
</constraints>
<output_format>
先给方案,我确认后再写代码。
</output_format>
<!-- 最后一行:最关键的一条 -->
最重要:IPaymentCallback 的接口签名绝对不能改,有 3 个外部系统依赖它。五、结构显著性:为什么 XML 标签有效
5.1 三个叠加的原因
原因 1:边界确定性
<constraints>...</constraints> 有明确的开始和结束。当我需要"回忆约束是什么"时,这是一个定义清晰的区域。而如果约束散落在一段散文里,我需要在整段文本中做软性判断"哪些句子是约束"。
原因 2:标签名是零成本的元信息
<constraints> 这个词本身就在告诉我"接下来是约束"。这比读完内容再推断它是约束,路径短得多。
原因 3:块内独立性
分块之后,<context> 里的内容和 <output_format> 里的内容在表示空间中的相互干扰降低了。混在一起时,"背景信息"可能被误当成"输出要求"的一部分。
5.2 一个可以自己观察的现象
试试这两个提示词,观察差异:
text
版本 A:
我在做一个数据分析,数据是 CSV 格式,有 10 万行,包含用户 ID、时间戳、
金额三列,我需要按用户聚合计算月度消费,输出要是 JSON,另外要过滤掉
金额为负的记录,还有时间戳是 Unix 秒级的需要转换。text
版本 B:
<data_schema>
CSV,10 万行
列:user_id, timestamp (Unix 秒级), amount
</data_schema>
<task>
按用户聚合,计算月度消费总额。
</task>
<preprocessing>
- 过滤 amount < 0 的记录
- timestamp 转换为日期
</preprocessing>
<output_format>
JSON
</output_format>字数几乎一样,但版本 B 里"过滤负数"这条约束被遗漏的概率明显更低 —— 它有自己的容器。
六、一个反直觉的建议:少即是多
很多人在效果不好时的第一反应是加更多说明。这经常适得其反。
6.1 为什么加说明可能有害
text
你的提示词从 200 token 加到 800 token
→ 每条指令的注意力份额从 1/200 降到 1/800
→ 原本有效的那条关键指令,现在被自己新加的话稀释了6.2 正确的诊断顺序
效果不好时,按这个顺序检查:
text
1. 上下文是不是被污染了?(有矛盾信息、坏示范)
→ 清理,不是加说明
2. 关键约束是不是在中段?
→ 移到末尾
3. 约束本身是不是可验证的?
→ 「要简洁」改成「不超过 5 句」
4. 是不是任务太多混在一起?
→ 拆分
5. 以上都没问题,才考虑加说明80% 的"它不听话",根因在第 1-4 步,而不是"说得不够清楚"。
七、Hack 清单
把这一章的机制转化成可执行的动作:
| # | 动作 | 针对的机制 |
|---|---|---|
| 1 | 最关键的约束写在提示词最后一行 | 因果邻近效应 |
| 2 | 长文档放前面,问题放后面 | 位置权重 |
| 3 | 关键约束在开头和末尾各写一次 | 重复=加权 |
| 4 | 用 XML 标签给每类内容独立容器 | 结构显著性 |
| 5 | 看到第一次偏离立刻纠正 | 抑制自我示范积累 |
| 6 | 偏离已积累时用 [Edit] / /rewind 物理删除 | 移除坏示范 |
| 7 | 常驻规则提升到 CLAUDE.md / 系统提示词 | 免于稀释 |
| 8 | 长会话中定期重置锚点(重申约束) | 恢复权重 |
| 9 | 效果不好时先清理,后补充 | 避免自我稀释 |
| 10 | 必须保证的规则用 hook 而非提示词 | 概率 → 确定性 |