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

系列目标 :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)

ini 复制代码
# 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 阶段:合并所有摘要 → 全局摘要
ini 复制代码
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 迭代优化摘要
ini 复制代码
# 创建 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 年营收目标是多少?"

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

ini 复制代码
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

ini 复制代码
# 使用 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 测试

📦 九、配套代码结构

bash 复制代码
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 助手!

相关推荐
简~76817 分钟前
python for in循环不能遍历整数怎么解决
python·大学生
aiqianji34 分钟前
垂直专业的AI短篇小说写作软件有哪些特点?
人工智能·python
aqi001 小时前
15天学会AI应用开发(十三)上下文与RAG的阶段性总结
人工智能·python·大模型·ai编程·ai应用
程序员杰哥1 小时前
接口测试知识总结
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·接口测试
大海变好AI1 小时前
专业的智能体算力架构高性价比品牌公司
python
江华森1 小时前
深入理解 Flask 实现原理
后端·python·flask
FriendshipT2 小时前
Ultralytics:解读SPPF模块
人工智能·pytorch·python·深度学习·目标检测
AOwhisky2 小时前
Python 基础语法(上篇 + 下篇)——综合自测题
linux·windows·python
埃博拉酱2 小时前
Pip/Conda 混用导致的 CRT 版本冲突问题:[WinError 1114] 动态链接库(DLL)初始化例程失败
windows·python