作者: Feiyue Zhai + LLM collaborator 日期: 2026-04-21 标签: LLM agent, skill-leak, generation-time bias, scanner, agent framework 触发事件: 调试 paper-reader v0.3 skill 在 PrfaaS notes 里反复出现的 "Scope of this file"、"Stage 4 synthesis" 等 meta-language
一次具体的调试日记, 推导出一个通用的 agent workflow 失效模式。
agent-based paper reader 用 SKILL.md 定义 workflow: "Stage 2 READ 产出 preread, Stage 3 WRITE 产出 notes, Stage 4 CONNECT 产出 synthesis"。Stage 3 WRITE 里 LLM 本该只输出 paper 内容, 但实际产出 notes 里混进了 skill 定义里教 agent 怎么写 notes 的话。
典型 leak 现场(PrfaaS v3 真实案例):
# PrfaaS notes §0
**Scope of this file**: paper-internal content only. Cross-paper
comparisons, adversarial rebuttals, ecosystem positioning, and
unexplored-hybrid directions live in `knowledge/synthesis-{id}.md`
(Stage 4, TBD).
## §3.3 双时间尺度调度器(§3.4.3 的状态机——paper 无此图, Mermaid 补充)
> **Redraw reason**: paper §3.4.3 只用文字描述 short-term + long-term
> 两层调度, 没有状态机。以下状态机是为了让 Stage 4 synthesis 能指向
> 具体的状态转移而补充的。
## §6. 论证链
**Scope note**: 这一节只**复原 paper 自己的多步论证**, 不是读者的攻击 /
ecosystem check / 反例搜索——那些是 Stage 4 synthesis 的任务。这些段落不是错误信息——内容可能是对的, 只是出现在错误的文件里。它们解释 "本节 vs 他节的分工",这是给 skill-aware 的读者看的 meta-language, 不是给 paper-reading 的读者看的内容。
实际项目里这个 pattern 在 paper-reader v0.3 一次 Stage 3 WRITE 里出现 9 次不同 surface form, 指向同一个底层语义。
LLM 不是"规则遵守机器", 是语言生成模型。当它的 context 同时装着两类内容时, 它自然混合两者生成文本:
LLM Context (Stage 3 WRITE 运行时)
┌──────────────────────────────────────────┐
│ Paper 内容 (应输出) │
│ - preread/{id}.md (992 行 raw) │
│ - papers.json 元数据 │
├──────────────────────────────────────────┤
│ Skill 内容 (不应输出) │
│ - SKILL.md (1069 行, 5 stages + meta) │
│ - deep-<category>.md │
│ - incidents.md │
│ - 8-pattern leak table 本身 │
└──────────────────────────────────────────┘
↓ 生成
notes/{id}.md ← 混合输出
三层观察:
这不是"LLM 偶尔疏忽"。只要 context 装着 SKILL.md 的结构说明, LLM 就倾向于把这个结构告诉读者。当 LLM 刚读完 "Stage 3 = paper-internal, Stage 4 = cross-paper" 的分工, 它写 notes 时的 mental model 就包含这个划分, 然后很自然地想在 notes 顶部/节末 解释这个划分。
对一个典型的 technical writing 训练目标, "解释 scope" 是 helpful 的——README 开头经常有 "What this file is about"。训练分布奖励这种 meta-orientation。
Pre-Save Self-Check 是一个写在 skill 里的指令: "写完后, 回去 review draft, catch leak patterns"。
问题: 同一个 LLM 刚写出 "Scope of this file...", 它 review 自己的输出时不会觉得是 leak。用 technical writing 标准判断, 这句话完全合理。
经验 rate: 纯指令层的 Pre-Save Self-Check 漏率 ~30%。
LLM 被训练成 "generate helpful technical documentation"。helpful documentation 的成熟 pattern 之一是 "tell the reader about scope"。任务 (paper notes) 的要求是 "don't tell the reader about skill structure"。
两个 objective 直接冲突。softmax 的默认路径倾向训练分布的 pattern, 所以 不做任何 mitigation 的 baseline 就是产出 leak。
按效力递增 + 工程复杂度递增:
在 SKILL.md 的 "Content Hygiene" 列出禁用短语 + 反例:
❌ "Scope of this file: paper-internal content only"
❌ "**Scope note**"
❌ "Stage 3 v2 (post-preread)"
❌ "本节只写 X 不讨论 Y"
强项: 改文档即可。 弱项: LLM 会 paraphrase。"Here we cover..." 绕过。每次新 iteration 追加新 phrase → whack-a-mole。
两组件:
check_paper_completeness.py 维护 _SKILL_LEAK_PHRASES 正则列表 (当前 73 条)SKILL.md Stage 3 exit 强制跑 check_paper_completeness.py {id} --check=leaks, exit != 0 不进 Stage 4强项: scanner 没有作者偏见——它看 phrase 就是 phrase, 不会 "觉得这话是 helpful". 一次 CI 级 enforce。 弱项: 仍然靠 phrase list。paraphrase 绕过。也是 whack-a-mole。
为什么新 tool call 更好:
强项: 结构上消除同一 run 内作者偏见。 弱项:
根治思路: Stage 3 WRITE 不加载完整 SKILL.md。只加载:
不加载:
原理: LLM 没法 leak 它 context 里没有的内容。如果它没看到 "Stage 4 synthesis" 这个词, 它就不会用这个词写 notes。
强项: 真·根治——消除 context 污染源头。 弱项:
stage-1-absorb.md / stage-3-write.md / ... 各自独立, 运行时按 stage 切换根治但受限: Stage 3 输出不是 free-form MD, 是 structured JSON / per-field blanks:
{
"tldr": "PrfaaS 把...",
"q1_pain": "...",
"q2_method": "...",
"q3_results": "...",
"architecture_diagrams": [...],
"author_proof": {
"notation_table": [...],
"equations": [{"latex": "$...$", "physical": "...", "load_bearing": true}]
}
}sync.sh 从 JSON 拼 MD / HTML。LLM 只能填已有 field, 没地方塞 "Scope of this file" 因为没这个 field。
强项: leak surface → 0 (结构上不可能)。 弱项:
| 层 | 效力 | 工程成本 | 对 agent framework 要求 | 失效模式 |
|---|---|---|---|---|
| L1 phrase list | 弱 | 低 (文档) | 无 | 刚学过 skill 的 LLM 仍漏; paraphrase 绕过 |
| L2 scanner gate | 中 | 中 (代码+文档) | 无 | 列表穷举困难, whack-a-mole |
| L3 two-pass | 中强 | 中高 (工作流) | split tool call | Pass 2 仍是 phrase-list based; 新 framework 支持 |
| L4 scoped context | 强 | 高 (skill 重组 + framework) | per-stage context API | 尚无 framework 原生支持 |
| L5 schema output | 最强 | 最高 (重设计协议) | structured output | 失去 free-form 灵活性, 需 exhaustive schema |
实际 9 种 leak phrase (同一个 LLM 同一个 draft 找到):
Scope of this file**Scope note**paper-internal content onlycross-paper ... lives inStage 4 synthesis(后者属 Stage 4 synthesis)paper 无此图 Mermaid 补充Redraw reason: ... Stage 4 synthesis这一节只复原论文内容同一语义 ("告诉读者本节 vs 他节的分工") 找到 9 种表达。
为什么 L1+L2+L3 永远不够:
LLM 输出倾向 (训练分布奖励)
↓
orientation meta / structure hint / reader guidance
↓
whatever phrase ← phrase list (scanner)
the LLM picks 只抓当前已知
↓
下次新 phrase 诞生 ← 追加列表
↓
whack-a-mole ∞
scanner 是后验机制——它只能抓 LLM 已经生成的东西。但 LLM 的表达空间无穷: 同一个语义能用无穷多种 phrase 表达。每抓一个, LLM 下次换一个。
根治只有 L4 或 L5——改变 LLM 的 context 或 output shape, 让它不可能 generate leak:
L1/L2/L3 本质是降低 leak rate, 不是消除 leak。这条路的天花板是: "接受残余 leak, 靠 periodic scanner phrase list 扩充和人工 review 兜底"。
这不是 paper-reader 独有的问题——所有 agent-based workflow 都有这个 pattern:
短期可行 (不改 framework):
长期正确 (需改 framework 或重设计 skill):
Cursor / Claude Code 当前都是整 skill-load。如果 Claude Projects / Claude Skills 未来支持 "per-task-phase context scoping", 就能走 L4 路线。
把 LLM 类比成上岗一周的实习工程师:
LLM 每次 call 都是"新员工第一天", 没有长时间的 scoping 训练。每次都会产同样的 leak, 除非结构上让它不可能。
这是为什么 L4 (structural context control) 比 L1-L3 (procedural enforcement) 根本——不是训练问题, 是信息论问题。
~/.cursor/skills/paper-reader/SKILL.md v0.3 — "Content Hygiene — Pre-Save Self-Check" + "Stage 3 exit gate" 段落~/.cursor/paper-db/tools/check_paper_completeness.py — _SKILL_LEAK_PHRASES 73 条列表 + --check=leaks 实现~/.cursor/paper-db/incidents.md 2026-04-21 系列 — PrfaaS v3 iteration logsamd-tools/skills/paper-reader @ v0.2-slim