主题
03.3 长文档处理战术
超出上下文窗口,或者虽然装得下但效果不好的时候,你需要战术。
读完你能做什么:处理任意长度的文档,并且知道每种方法的准确率代价。
一、先判断:你的文档属于哪一档
| 规模 | 策略 |
|---|---|
| < 3 万 token(约 40 页) | 直接全量放入,用位置布局优化即可 |
| 3-15 万 token | 全量放入 + 强制引文抽取 |
| 15 万 token 以上 | 必须分块处理 |
| 持续增长的文档集 | 需要索引化 / RAG 方案 |
二、战术一:引文抽取法(Quote Extraction)
适用:文档装得下,但你需要高准确率。
2.1 标准模板
text
<document title="2026 年度产品规划" pages="45">
{完整文档}
</document>
<instructions>
分两步作答,不要跳过第一步。
第一步:在 <quotes> 标签内,逐字摘录文档中与问题直接相关的原文片段。
- 每条摘录必须是原文,不要改写
- 每条标注出处(章节标题或页码)
- 最多 8 条
- 如果找不到相关内容,写 <quotes>无相关内容</quotes>
第二步:在 <answer> 标签内作答。
- 只能基于 <quotes> 中的内容
- 如果 <quotes> 为空或不足以回答,直接说「文档中未涵盖此信息」
- 禁止用通用知识补充文档没写的内容
</instructions>
<question>
公司计划在哪几个市场投入最多资源?各自的预算是多少?
</question>2.2 为什么这一招这么有效
三个叠加效应:
- 注意力搬运 —— 把埋在中段的内容"搬"到末尾的高权重区(见 03.2)
- 可验证性 —— 你能立刻
Ctrl+F检查摘录是否真实存在。摘录不存在 = 幻觉,一眼可辨 - 拒绝路径 —— 「摘录为空」给了模型一条明确的"说不知道"的出路,而不是硬编
2.3 变体:带置信度的摘录
text
每条摘录标注相关度:
- [直接] 原文直接回答了问题
- [间接] 原文提供了推导所需的部分信息
- [背景] 相关但不足以回答
在 <answer> 中,明确区分「文档明确说明的」和「我基于间接信息推导的」。三、战术二:Map-Reduce 分块
适用:文档超出窗口,或者你要处理多份文档。
3.1 基本流程
text
Map: 每块独立处理 → 结构化中间结果
Reduce:只把中间结果汇总 → 最终答案3.2 完整实现(Claude Code)
bash
#!/usr/bin/env bash
# process-long-doc.sh —— 分块处理超长文档
set -euo pipefail
INPUT="$1" # 需替换为你的文档路径
CHUNK_LINES=800 # 每块行数,按文档密度调整
QUESTION="$2" # 你的问题
WORKDIR=$(mktemp -d)
# 1. 分块(保留 50 行重叠,避免切断关键段落)
split -l "$CHUNK_LINES" --numeric-suffixes=1 --suffix-length=3 \
--additional-suffix=.txt "$INPUT" "$WORKDIR/chunk_"
echo "分成 $(ls "$WORKDIR"/chunk_*.txt | wc -l) 块"
# 2. Map 阶段:用 Haiku 快速抽取(便宜)
for chunk in "$WORKDIR"/chunk_*.txt; do
echo "处理 $(basename "$chunk")..."
claude -p --model haiku --bare "$(cat <<EOF
<chunk id="$(basename "$chunk")">
$(cat "$chunk")
</chunk>
<task>
从这个片段中,逐字摘录与以下问题相关的原文。
问题:$QUESTION
输出格式(严格遵守,只输出这个 JSON,不要任何其他内容):
{"chunk": "$(basename "$chunk")", "relevant": true|false, "quotes": ["原文1", "原文2"]}
如果本片段与问题无关,输出 {"chunk": "...", "relevant": false, "quotes": []}
EOF
)" >> "$WORKDIR/extracted.jsonl"
done
# 3. Reduce 阶段:用 Opus 综合(贵但只跑一次)
claude -p --model opus "$(cat <<EOF
<extracted_quotes>
$(cat "$WORKDIR/extracted.jsonl")
</extracted_quotes>
<task>
以上是从一份长文档的各个片段中抽取的相关原文。
基于这些摘录回答:$QUESTION
要求:
- 每个结论标注来自哪个 chunk
- 如果不同 chunk 的内容矛盾,明确指出矛盾
- 摘录不足以回答的部分,说明「需要补充哪部分文档」
</task>
EOF
)"
rm -rf "$WORKDIR"用法:
bash
chmod +x process-long-doc.sh
./process-long-doc.sh ./big-report.txt "公司在哪些市场投入最多?"3.3 Map-Reduce 的准确率代价
必须知道的局限:Map-Reduce 会丢失跨块的关联。
text
块 1 说:"预算总额 1000 万"
块 7 说:"其中欧洲市场占 40%"
如果 Map 阶段两块分别处理,各自都可能认为自己"不完整"而漏报。缓解手段:
- 重叠分块 —— 相邻块之间保留 10-20% 的重叠
- 两轮 Map —— 第一轮建立全局索引(每块的主题),第二轮带着索引再抽取
- 在 Map 提示词中给出全局背景:
text
<global_context>
这是一份 200 页的年度规划文档。已知它包含以下章节:
1. 战略概述 2. 市场分析 3. 预算分配 4. 风险 5. 附录数据表
你现在看到的是其中一个片段。
</global_context>四、战术三:索引化预处理
适用:需要反复查询同一批文档。
4.1 建立结构化索引
一次性投入,长期收益:
bash
# 为每份文档生成结构化摘要
for f in docs/*.md; do
claude -p --model haiku "$(cat <<EOF
<document>
$(cat "$f")
</document>
<task>
输出这份文档的索引条目,严格按此 JSON 格式:
{
"file": "$f",
"title": "",
"one_line": "一句话说清这份文档讲什么",
"topics": ["主题1", "主题2"],
"sections": ["章节1标题", "章节2标题"],
"key_entities": ["提到的关键系统/人名/产品"],
"date": "文档中提到的最新日期"
}
只输出 JSON。
EOF
)" >> docs-index.jsonl
done4.2 查询时先查索引
text
<index>
{docs-index.jsonl 的内容 —— 通常只有几千 token}
</index>
<question>
关于对账模块的性能问题,我该看哪些文档?
</question>
<instructions>
只基于索引判断,列出最相关的 3 份文档及理由。不要凭空猜测文档内容。
</instructions>然后只读那 3 份。这把"读 50 份文档"变成了"读 1 份索引 + 3 份文档"。
4.3 在 Claude Code 里自动化
把索引查询做成 Skill:
markdown
---
name: doc-lookup
description: 在项目文档库中定位相关文档。当用户问到需要查阅项目文档才能回答的问题时使用。
allowed-tools: Read, Grep, Glob
---
# 文档定位
1. 读 `docs-index.jsonl`(这是全部文档的结构化索引)
2. 根据用户问题,判断哪些文档相关
3. 只读取相关文档的内容
4. 回答时标注来源文件
**不要**一次读取超过 3 份文档。如果索引显示需要更多,先告诉用户你的判断依据。五、战术四:渐进式深入(Progressive Drilling)
适用:探索性任务,你自己也不确定要找什么。
text
# 轮 1:地形勘察(低成本)
> 只看目录和每章的第一段,告诉我这份文档的结构和每一章大概讲什么。
不要读正文细节。
# 轮 2:定向深入
> 好,我关心第 3 章和第 7 章。现在只读这两章,
分别总结核心论点和支撑证据。
# 轮 3:精读
> 第 3 章里提到的那个数据模型,把相关原文完整摘录出来,
然后逐条分析它的假设是否成立。优势:每一轮的上下文都很小,注意力集中。而且你在过程中不断校准方向,避免在错误的地方深挖。
六、四种战术的选择矩阵
| 引文抽取 | Map-Reduce | 索引化 | 渐进深入 | |
|---|---|---|---|---|
| 文档能装进窗口 | ✅ | — | — | ✅ |
| 文档超出窗口 | ❌ | ✅ | ✅ | ✅ |
| 需要跨块关联 | ✅ | ⚠️ 弱 | ✅ | ✅ |
| 一次性任务 | ✅ | ✅ | ❌ 投入大 | ✅ |
| 反复查询 | ⚠️ 每次重读 | ⚠️ 每次重跑 | ✅ | ⚠️ |
| 准确率 | 高 | 中 | 高 | 高 |
| 成本 | 中 | 低(Map 用 Haiku) | 前期高、后期低 | 低 |
| 自动化难度 | 低 | 中 | 中 | 高(需人参与) |
七、通用注意事项
7.1 PDF 优先上传原文件,不要复制文本
PDF 中的表格、层级、页码在复制成纯文本时会丢失。直接上传 PDF 让模型看到原始版式,信息保真度高得多。
7.2 给文档加"元数据头"
在文档最前面加一段:
text
<document_meta>
标题:2026 年度产品规划
版本:v3.2(终稿)
日期:2026-03-15
页数:45
状态:现行有效
注意:本文档取代了 v2.x 系列的所有版本
</document_meta>这能有效避免"引用了废弃版本"的问题。
7.3 明确禁止跨文档知识混入
text
<constraints>
只使用 <document> 标签内的信息。
禁止使用你的通用知识补充文档中没有的内容。
如果需要外部知识才能回答,明确说明「需要文档之外的信息:xxx」。
</constraints>