每日新闻 / 2026-05-25

AI 文档摘要实战:长 PDF 分块与 Map-Reduce 聚合管道完整搭建指南

XycAi
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)

质量控制的几个实用检查点:

对于高精度要求的场景(法律、医疗文档),建议在 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 →