🌟 LangChain 30 天保姆级教程 · Day 22|长文档处理三剑客!MapReduce、Refine、Map-Rerank,让 AI 消化整本手册

15 阅读4分钟

系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解长文档挑战 → 掌握三种 Chain 策略 → 实现“100 页 PDF 自动摘要”!


📚 一、为什么需要长文档处理?

Qwen 7B 的上下文窗口约 32K tokens(≈2万汉字),看似很大,但:

  • 一份技术白皮书 ≈ 50K tokens
  • 公司年度报告 ≈ 100K+ tokens
  • 法律合同/用户协议 ≈ 超长且关键

直接塞入 Prompt?

  • ❌ Token 超限 → 报错
  • ❌ 截断开头/结尾 → 丢失关键信息

解决方案

✅ 分而治之 —— 将长文档分块,用不同策略聚合结果!

💡 今天,我们就实测 LangChain 三大长文档 Chain:MapReduce、Refine、Map-Rerank


🧱 二、核心策略对比

表格

策略原理优点缺点适用场景
MapReduce分块独立处理 → 合并结果并行快、简单合并可能丢失细节摘要、关键词提取
Refine逐块迭代优化答案保留细节、连贯串行慢、token 多精细问答、总结
Map-Rerank分块打分 → 取最高分快速定位关键段只返回单片段事实核查、精准检索

✅ 所有策略都基于 StuffDocumentsChain(Day 18)的扩展!


🛠️ 三、准备工作:加载长文档

假设我们有一份 50 页公司年报(PDF)

# day22_long_document.py
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 加载 PDF
loader = PyPDFLoader("docs/annual_report_2025.pdf")
docs = loader.load()

# 分块(比 RAG 更大,因需完整语义)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=2000,      # 每块约 1000 字
    chunk_overlap=200,
    separators=["\n\n", "\n", "。", ";", " ", ""]
)
chunks = text_splitter.split_documents(docs)

print(f"📄 文档共 {len(chunks)} 个片段,总长度 ≈ {sum(len(c.page_content) for c in chunks) // 1000}K 字")

🔁 四、策略 1:MapReduce(快速摘要)

原理

  1. Map 阶段:每个 chunk 生成局部摘要
  2. Reduce 阶段:合并所有摘要 → 全局摘要
from langchain_ollama import ChatOllama
from langchain.chains.summarize import load_summarize_chain

llm = ChatOllama(model="qwen:7b", temperature=0)

# 创建 MapReduce Chain
map_reduce_chain = load_summarize_chain(
    llm,
    chain_type="map_reduce",
    verbose=True
)

# 执行摘要
summary = map_reduce_chain.run(chunks)
print("📝 全局摘要:\n", summary)

✅ 优势:可并行处理(未来支持),速度快
❌ 劣势:合并时可能泛化过度


🔍 五、策略 2:Refine(精细总结)

原理

  1. 用第一个 chunk 生成初始摘要
  2. 依次用后续 chunk 迭代优化摘要
# 创建 Refine Chain
refine_chain = load_summarize_chain(
    llm,
    chain_type="refine",
    verbose=True,
    return_intermediate_steps=False
)

# 执行
refined_summary = refine_chain.run(chunks)
print("✨ 精炼摘要:\n", refined_summary)

✅ 优势:保留更多细节,逻辑更连贯
❌ 劣势:串行执行,耗时较长(N 次 LLM 调用)

💡 提示:可在 Prompt 中指定格式,如“用 bullet points 列出三大重点”


🎯 六、策略 3:Map-Rerank(精准问答)

场景

用户问:“年报中提到的 2025 年营收目标是多少?”

我们不需要全文摘要,只需最相关的片段

from langchain.chains import RetrievalQA
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings

# 先建向量库(复用 Day 17)
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma.from_documents(chunks, embeddings)

# 创建 Map-Rerank 风格的 QA(通过高 k + 精排)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="map_rerank",  # 注意:此模式要求 LLM 输出分数
    retriever=vectorstore.as_retriever(search_kwargs={"k": 10}),
    return_source_documents=True
)

# 但注意:Qwen 不原生支持打分,改用以下方式模拟

⚠️ 现实问题map_rerank 需 LLM 在回答中包含置信度分数(如 {"answer": "...", "score": 0.9}),多数开源模型不支持。

替代方案:用 Reranker(Day 19)+ Top-1

# 使用 Day 19 的重排序器
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain_community.cross_encoders import HuggingFaceCrossEncoder

model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-large")
compressor = CrossEncoderReranker(model=model, top_n=1)
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=vectorstore.as_retriever(search_kwargs={"k": 5})
)

# 直接取最相关片段回答
query = "2025年营收目标是多少?"
docs = compression_retriever.get_relevant_documents(query)
answer = docs[0].page_content
print("🎯 精准答案:", answer)

✅ 更实用!尤其适合事实型问答


⚙️ 七、如何选择策略?

表格

需求推荐策略
快速生成全文摘要MapReduce
高质量总结/报告Refine
精准问答(单事实)Map-Rerank(或 Reranker + Top-1)
多跳推理(需多片段)Refine + 自定义 Prompt

💡 混合策略:先用 MapReduce 得全局概览,再对关键章节用 Refine 深挖。


⚠️ 八、注意事项 & 最佳实践

表格

问题建议
分块太小导致上下文断裂 → 增大 chunk_size(2000~4000)
分块太大超过 LLM 单次处理能力 → 监控 token 使用
Refine 太慢对超长文档,先用 MapReduce 筛选关键章节
中文标点分割差在 separators 中显式加入 ["。", "!", "?"]
成本控制MapReduce 可缓存中间结果;Refine 难以缓存

💡 生产建议

  • 对 >100 页文档,先做章节分割(用 PDF 目录)
  • 记录每种策略的耗时与效果,A/B 测试

📦 九、配套代码结构

langchain-30-days/
└── day22/
    ├── long_doc_summarize.py     # MapReduce / Refine 摘要
    └── long_doc_qa.py            # Map-Rerank 风格问答

📝 十、今日小结

  • ✅ 理解了长文档处理的核心挑战
  • ✅ 掌握了 MapReduce、Refine、Map-Rerank 三大策略
  • ✅ 实现了 50 页 PDF 的自动摘要与精准问答
  • ✅ 知道了如何根据场景选择最优策略
  • ✅ 学会了用 Reranker 替代原生 Map-Rerank

🎯 明日预告:Day 23 —— Agent 进阶!Function Calling 与 Tool 自动注册,打造智能 AI 助手!