0Pricing
AI Prompt Engineering · 课时

Map-Reduce 摘要模式

分别总结每个文本块,然后综合这些摘要

Map-Reduce 摘要模式 是 CoddyKit 上的免费 AI Prompt Engineering 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Prompt Engineering 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Prompt Engineering 课程共包含 4 节课。

映射-归约模式

当文档超出 LLM 的上下文窗口时,您无法一次性传入全部内容。映射-归约模式可以解决这一问题:

  • 映射:独立摘要每个文本块
  • 归约:将所有文本块摘要综合为一份最终摘要

这与分布式系统中的经典 MapReduce 相似——将同样的分而治之逻辑应用于语言任务。

第 1 步:映射阶段

在映射阶段,每个文本块都会连同摘要提示词发送给 LLM。模型只返回该文本块的简短摘要。这些摘要会收集到一个列表中。

每份摘要都应比原始文本块短得多——通常只有原始长度的 10–20%。正是这种压缩使归约步骤成为可能。

import openai

client = openai.OpenAI(api_key='sk-...')

def summarize_chunk(chunk, model='gpt-4o'):
    resp = client.chat.completions.create(
        model=model,
        messages=[
            {'role': 'system', 'content': 'Summarize the following text concisely in 3-5 sentences.'},
            {'role': 'user', 'content': chunk}
        ]
    )
    return resp.choices[0].message.content

def map_phase(chunks):
    return [summarize_chunk(c) for c in chunks]

第 2 步:归约阶段

在归约阶段,所有文本块摘要会被拼接起来,并连同 synthesis 提示词发送给 LLM。模型会生成一份连贯的最终摘要。

如果文本块摘要仍然过长,无法放入一个上下文窗口中,就递归地应用归约——先摘要若干摘要组成的分组,再综合这些摘要。

def reduce_phase(chunk_summaries, model='gpt-4o'):
    combined = '\n\n'.join(
        f'Section {i+1}:\n{s}'
        for i, s in enumerate(chunk_summaries)
    )
    resp = client.chat.completions.create(
        model=model,
        messages=[
            {'role': 'system', 'content': 'You are given summaries of consecutive sections of a document. Write a single coherent summary of the entire document.'},
            {'role': 'user', 'content': combined}
        ]
    )
    return resp.choices[0].message.content

整合应用:原始 API

下面是使用原始 OpenAI API 调用实现的完整映射-归约流水线——无需框架。这让您可以完全控制每个阶段的提示词和参数。

def map_reduce_summarize(document, chunk_size=1000):
    chunks = fixed_chunk(document, max_tokens=chunk_size)
    print(f'Chunks: {len(chunks)}')

    chunk_summaries = map_phase(chunks)
    print(f'Map phase complete. Summaries: {len(chunk_summaries)}')

    final_summary = reduce_phase(chunk_summaries)
    return final_summary

with open('long_report.txt') as f:
    doc = f.read()

result = map_reduce_summarize(doc)
print(result)

LangChain MapReduceDocumentsChain

LangChain 提供了现成的 MapReduceDocumentsChain,可以处理分块、并行映射调用和归约步骤。它使用方便,但不如原始 API 调用灵活。

from langchain_openai import ChatOpenAI
from langchain.chains.summarize import load_summarize_chain
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.docstore.document import Document

llm = ChatOpenAI(model='gpt-4o', openai_api_key='sk-...')
splitter = RecursiveCharacterTextSplitter(chunk_size=3000, chunk_overlap=200)

with open('long_report.txt') as f:
    text = f.read()

docs = splitter.create_documents([text])
chain = load_summarize_chain(llm, chain_type='map_reduce')
result = chain.invoke(docs)
print(result['output_text'])

并行化映射阶段

每个文本块的摘要彼此独立,因此可以并行执行映射阶段。使用 Python 的 ThreadPoolExecutor,所有文本块的 API 调用都会并发发送,从而显著减少实际耗时。

from concurrent.futures import ThreadPoolExecutor, as_completed

def map_phase_parallel(chunks, max_workers=10):
    summaries = [None] * len(chunks)
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = {
            executor.submit(summarize_chunk, chunk): i
            for i, chunk in enumerate(chunks)
        }
        for future in as_completed(futures):
            idx = futures[future]
            summaries[idx] = future.result()
    return summaries

递归归约

当文档非常长时,文本块摘要本身可能会超出上下文窗口。请递归地应用归约:将摘要分成多个批次,分别归约每个批次,然后再归约各批次的摘要。

def recursive_reduce(summaries, batch_size=10):
    while len(summaries) > 1:
        batches = [
            summaries[i:i + batch_size]
            for i in range(0, len(summaries), batch_size)
        ]
        summaries = [reduce_phase(batch) for batch in batches]
        print(f'Reduced to {len(summaries)} summaries')
    return summaries[0]

保留关键细节

朴素映射-归约方法常见的问题是:重要细节会在压缩过程中丢失。可以采取以下缓解措施:

  • 提示映射步骤保留名称、数字、日期
  • 要求归约步骤检查各章节之间是否存在矛盾
  • 使用更大的文本块大小,使映射上下文包含更丰富的信息
  • 执行一个验证环节:询问模型原文中的关键实体是否出现在最终摘要中
MAP_PROMPT = '''Summarize the following section in 5 sentences.
Preserve all key names, numbers, dates, and conclusions.

Section:
{chunk}'''

精炼链:另一种模式

精炼链是映射-归约的另一种方案。它按顺序处理文本块:将文本块 N 的摘要与文本块 N+1 一起传入,模型再更新当前摘要。这会生成更连贯的输出,但无法并行执行,速度也更慢。

当叙事连贯性很重要时(例如法律合同),请使用精炼链。当速度更重要时(例如新闻文章批次),请使用映射-归约。

def refine_summarize(chunks):
    current_summary = summarize_chunk(chunks[0])
    for chunk in chunks[1:]:
        prompt = (
            f'Existing summary:\n{current_summary}\n\n'
            f'New section:\n{chunk}\n\n'
            'Update the summary to incorporate the new section.'
        )
        resp = client.chat.completions.create(
            model='gpt-4o',
            messages=[{'role': 'user', 'content': prompt}]
        )
        current_summary = resp.choices[0].message.content
    return current_summary

成本与词元管理

映射-归约会发起许多 API 调用。对于一个包含 100 个文本块、使用 gpt-4o 且输入词元价格为每 100 万个 5 美元的文档:

  • 映射:100 个文本块 × 1000 个词元 = 100k 个输入词元 ≈ 0.50 美元
  • 归约:约 10k 个词元(摘要)≈ 0.05 美元
  • 总计:每份文档约 0.55 美元

要降低成本,可以在映射阶段使用 gpt-4o-mini(每 100 万个词元 0.15 美元),仅在归约阶段使用 gpt-4o。这种混合方案只会造成极小的质量损失,却能将成本降低 70%。

def map_phase_cheap(chunks):
    # Use mini model for map — cheaper, sufficient for chunk summaries
    return [
        summarize_chunk(c, model='gpt-4o-mini')
        for c in chunks
    ]

def reduce_phase_quality(summaries):
    # Use full model for final synthesis
    return reduce_phase(summaries, model='gpt-4o')

何时使用映射-归约

映射-归约摘要最适合以下情况:

  • 长度超过模型上下文窗口的文档
  • 批量摘要多个文档(还可以在文档层面进行并行处理)
  • 需要控制每个步骤所使用提示词的情况

它不太适合以下情况:提取特定事实(应改用检索),或者文档足够短、可以放入上下文中(直接进行摘要)。

知识检查

在映射-归约摘要模式中,归约阶段会执行什么操作?

回顾:映射-归约摘要

映射-归约模式可以处理无法通过一次 LLM 调用完成的超长文档:

  • 映射:独立摘要每个文本块——可以并行执行
  • 归约:将文本块摘要综合为一份最终摘要
  • 递归归约:当摘要本身过长时应用
  • 成本提示:映射阶段使用廉价模型,归约阶段使用高质量模型

LangChain 的 MapReduceDocumentsChain 提供了现成的实现。下一课将介绍适用于书籍和研究论文的层级式摘要。

常见问题解答

「Map-Reduce 摘要模式」课时是免费的吗?

是的 — 「Map-Reduce 摘要模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Prompt Engineering 课程的其余内容,请升级到 CoddyKit PRO。 AI Prompt Engineering 课程共包含 4 节课。

「Map-Reduce 摘要模式」这节课中我会学到什么?

分别总结每个文本块,然后综合这些摘要 你通过在浏览器中直接运行的动手代码来练习 AI Prompt Engineering,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Prompt Engineering 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Prompt Engineering 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「Map-Reduce 摘要模式」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Prompt Engineering 课中编写并运行代码吗?

能。每节 AI Prompt Engineering 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 长文本分块策略
  2. Map-Reduce 摘要模式
  3. 层次化摘要
  4. 在文本块之间保持上下文
← 返回 AI Prompt Engineering