Skip to content

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 为什么这一招这么有效

三个叠加效应:

  1. 注意力搬运 —— 把埋在中段的内容"搬"到末尾的高权重区(见 03.2
  2. 可验证性 —— 你能立刻 Ctrl+F 检查摘录是否真实存在。摘录不存在 = 幻觉,一眼可辨
  3. 拒绝路径 —— 「摘录为空」给了模型一条明确的"说不知道"的出路,而不是硬编

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 阶段两块分别处理,各自都可能认为自己"不完整"而漏报。

缓解手段

  1. 重叠分块 —— 相邻块之间保留 10-20% 的重叠
  2. 两轮 Map —— 第一轮建立全局索引(每块的主题),第二轮带着索引再抽取
  3. 在 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
done

4.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>

延伸阅读

基于 VitePress 构建 · 内容采用原作者授权