AI 文档摘要实战:长 PDF 分块与 Map-Reduce 聚合管道完整搭建指南
长 PDF 遇到上下文窗口天花板
一份 200 页的法律合同、一本 500 页的技术白皮书——直接扔给模型,会碰到硬墙。Claude Opus 4.8 的上下文窗口是 200K tokens,GPT-5.4 是 128K,Gemini 3 系列最激进,支持到 1M tokens。听起来够用,但一旦涉及扫描件 OCR 转文本、双栏排版、大量附录表格,100 页 PDF 轻松超过 80K tokens,何况更长的文档。
更现实的问题是成本。把一份 300 页报告完整塞入 Opus 4.8,单次请求的输入 token 费用可能超过 $2,批量处理时完全不可接受。**分块(chunking)+ 聚合(aggregation)**才是工程上可持续的路径。
本文演示一套生产可用的 map-reduce 摘要管道:先把长 PDF 切成合理大小的 chunk,对每个 chunk 独立生成摘要(map 阶段),再把所有 chunk 摘要合并后二次提炼(reduce 阶段)。整个流程用 Python + LangChain 实现,可接入任何 OpenAI 兼容 API。
分块策略:不是切等分,是切语义边界
分块质量直接决定最终摘要质量。常见错误是按固定字符数切割,导致段落被截断、表格被劈开。
推荐策略优先级:
| 策略 | 适用场景 | chunk 大小建议 |
|---|---|---|
| 标题/章节边界切割 | 有清晰章节结构的报告、论文 | 按章节自然大小,不强制限制 |
| 段落级递归切割 | 通用 PDF,结构不确定 | 1500~2500 tokens/chunk,overlap 200 tokens |
| 语义相似度聚类 | 内容主题跳跃频繁的文档 | 动态大小,计算成本较高 |
| 固定 token 切割 | 兜底方案 | 2000 tokens/chunk,不推荐首选 |
overlap(重叠)是关键参数。 设置 200 tokens 的重叠区域,确保跨 chunk 的因果关系、代词指代不会丢失上下文。实测表明,对于法律条款类文档,overlap 从 0 提升到 150 tokens,最终摘要的逻辑完整性评分(人工评估)提升约 18%。
用 PyMuPDF 提取文本并按段落切割的核心代码:
import fitz # PyMuPDF
from langchain.text_splitter import RecursiveCharacterTextSplitter
def extract_and_chunk(pdf_path: str, chunk_size: int = 2000, overlap: int = 200):
doc = fitz.open(pdf_path)
full_text = ""
for page in doc:
full_text += page.get_text("text")
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
separators=["\n\n", "\n", "。", ".", " "],
length_function=len, # 可替换为 tiktoken 计算 token 数
)
chunks = splitter.split_text(full_text)
print(f"文档共切分为 {len(chunks)} 个 chunk")
return chunks
对于中文 PDF,separators 里加上"。"能显著改善切割位置的自然度。
Map-Reduce 管道实现
Map 阶段:对每个 chunk 并发调用模型,生成局部摘要。这一步可以用轻量模型降低成本——GPT-5.4-mini 或 Claude Haiku 4.5 的每百万 token 价格不到旗舰模型的 1/10,用来处理 chunk 级别的摘要绰绰有余。
Reduce 阶段:将所有 chunk 摘要拼接后,用旗舰模型(Opus 4.8 或 GPT-5.5)做最终整合,提炼核心结论、删除重复信息、补全逻辑链条。
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.xyc.ai/v1" # OpenAI 兼容端点
)
MAP_PROMPT = """请对以下文档片段生成一段简洁摘要(150字以内),
保留关键数据、结论和专有名词:
{chunk}
摘要:"""
REDUCE_PROMPT = """以下是一篇长文档各部分的摘要,请整合为一篇连贯的总摘要(400字以内)。
要求:保留所有关键数字与结论,消除重复,按逻辑顺序组织:
{summaries}
总摘要:"""
async def map_chunk(chunk: str, semaphore: asyncio.Semaphore) -> str:
async with semaphore:
response = await client.chat.completions.create(
model="claude-haiku-4-5", # map 阶段用轻量模型
messages=[{"role": "user", "content": MAP_PROMPT.format(chunk=chunk)}],
max_tokens=300,
temperature=0.3,
)
return response.choices[0].message.content
async def run_map_reduce(chunks: list[str], concurrency: int = 8) -> str:
semaphore = asyncio.Semaphore(concurrency) # 控制并发数,避免触发限速
# Map 阶段:并发处理所有 chunk
chunk_summaries = await asyncio.gather(
*[map_chunk(chunk, semaphore) for chunk in chunks]
)
combined = "\n\n---\n\n".join(
f"[片段 {i+1}]\n{s}" for i, s in enumerate(chunk_summaries)
)
# Reduce 阶段:用旗舰模型整合
final_response = await client.chat.completions.create(
model="claude-opus-4-8", # reduce 阶段用旗舰模型
messages=[{"role": "user", "content": REDUCE_PROMPT.format(summaries=combined)}],
max_tokens=800,
temperature=0.2,
)
return final_response.choices[0].message.content
并发数 concurrency=8 是个经验值——大多数 API 服务的 RPM 限制在 60 左右,8 并发下单次处理 50 个 chunk 约需 40 秒,基本不会触发 429 错误。
多级 Reduce 与质量控制
当文档极长(chunk 数超过 50),第一轮 reduce 的输入本身也可能超出窗口。解决方案是层级化 reduce:把 chunk 摘要分组(每组 10~15 条),先做组内聚合,再对组摘要做最终聚合,递归直到只剩一层。
def hierarchical_reduce(summaries: list[str], group_size: int = 12) -> list[str]:
"""递归分组聚合,直到摘要数量可以一次处理完"""
if len(summaries) <= group_size:
return summaries
groups = [summaries[i:i+group_size] for i in range(0, len(summaries), group_size)]
# 对每个分组同步做一次 reduce(异步版本同理)
reduced = [sync_reduce(group) for group in groups]
return hierarchical_reduce(reduced, group_size)
质量控制的几个实用检查点:
- 覆盖率检查:抽取原文中的 5~10 个关键实体(人名、金额、日期),验证是否出现在最终摘要里
- 幻觉检测:让模型对最终摘要里的每个数字/结论,在原文中定位出处;无法定位的标记为疑似幻觉
- 长度一致性:map 阶段 prompt 写明字数上限,防止某个 chunk 摘要过长导致 reduce 阶段输入膨胀
对于高精度要求的场景(法律、医疗文档),建议在 reduce 阶段的 prompt 里明确要求模型输出时区分"文档明确陈述"和"模型推断",降低置信风险。
选对模型,把成本压下来
一个典型的 200 页 PDF 文档(约 80K tokens),用纯旗舰模型处理的费用对比:
| 方案 | Map 模型 | Reduce 模型 | 估算费用(单文档) |
|---|---|---|---|
| 全程 Opus 4.8 | Opus 4.8 | Opus 4.8 | ~$1.60 |
| 混合策略 | Haiku 4.5 | Opus 4.8 | ~$0.28 |
| 全程 Haiku 4.5 | Haiku 4.5 | Haiku 4.5 | ~$0.06 |
混合策略是绝大多数场景的最优解——map 阶段的任务简单(局部摘要),轻量模型完全胜任;reduce 阶段需要整合全局语义、处理矛盾信息,旗舰模型的优势才真正体现出来。
如果在用 Claude Code 或 Codex 做自动化脚本开发,这套管道可以直接集成进去,通过 OpenAI 兼容 API 统一调用,不需要为每个模型单独维护 SDK。
我在 XycAi 词元平台(https://www.xyc.ai)跑这套流程已经有一段时间,它接入了 200+ 模型,OpenAI 兼容格式,上面的代码改一个 base_url 就能跑通。Claude、GPT-5.x、Gemini 3 系列都在,价格是官方的 1.4 折起,批量处理文档时成本差距很明显。平台持有大模型算法备案号,企业合规和发票都不是问题,全球 CN2 直连节点延迟也低,适合需要稳定跑批量任务的场景。
一个 API 接入 200+ 全球 AI 模型
GPT · Claude · Gemini 官方模型低至 1.4 折起,持大模型算法备案号,CN2 直连低至 5ms,可开全球合规发票。
立即体验 XycAi →