Шаблон обобщения Map-Reduce
Обобщайте каждый блок отдельно, а затем объединяйте получившиеся резюме
«Шаблон обобщения Map-Reduce» — бесплатный урок AI Prompt Engineering на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Prompt Engineering, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Prompt Engineering содержит 4 уроков всего.
Паттерн MapReduce
Если документ превышает контекстное окно LLM, его нельзя передать целиком за один раз. Паттерн MapReduce решает эту задачу:
- Сопоставление: независимо суммировать каждый фрагмент
- Сведение: объединить сводки всех фрагментов в одну итоговую сводку
Это повторяет классический 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: фаза сведения
На фазе сведения все сводки фрагментов объединяются и передаются 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
Ниже приведен полный конвейер MapReduce с использованием прямых вызовов API OpenAI — фреймворк не требуется. Это дает полный контроль над запросами и параметрами на каждом этапе.
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)MapReduceDocumentsChain в LangChain
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'])Параллельное выполнение фазы сопоставления
Сводка каждого фрагмента не зависит от остальных, поэтому фазу сопоставления можно выполнять параллельно. С помощью ThreadPoolExecutor из Python все вызовы 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]Сохранение важных деталей
Распространенная проблема простого MapReduce: важные детали теряются во время сжатия. Меры против этого:
- В запросе для этапа сопоставления просите сохранять имена, числа и даты
- Просите на этапе сведения проверять наличие противоречий между разделами
- Используйте фрагменты большего размера, чтобы контекст на этапе сопоставления был более содержательным
- Проводите этап проверки: спрашивайте модель, присутствуют ли ключевые сущности из исходного документа в итоговой сводке
MAP_PROMPT = '''Summarize the following section in 5 sentences.
Preserve all key names, numbers, dates, and conclusions.
Section:
{chunk}'''Цепочка уточнения: альтернативный паттерн
Цепочка уточнения — это альтернатива MapReduce. Она последовательно обрабатывает фрагменты: сводка фрагмента N передается вместе с фрагментом N+1, а модель обновляет текущую сводку. Это дает более связный результат, но не позволяет выполнять обработку параллельно и работает медленнее.
Используйте уточнение, когда важна связность повествования (например, в юридических договорах). Используйте MapReduce, когда важна скорость (например, при пакетной обработке новостных статей).
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Управление стоимостью и токенами
MapReduce требует множества вызовов API. Для документа из 100 фрагментов при использовании gpt-4o по цене $5 за 1 млн входных токенов:
- Сопоставление: 100 фрагментов × 1000 токенов = 100 тыс. входных токенов ≈ $0.50
- Сведение: около 10 тыс. токенов (сводки) ≈ $0.05
- Итого: около $0.55 на документ
Чтобы снизить стоимость, используйте gpt-4o-mini на этапе сопоставления ($0.15 за 1 млн токенов), а 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')Когда использовать MapReduce
Суммаризация MapReduce лучше всего подходит для:
- документов, длина которых превышает контекстное окно модели
- пакетной суммаризации множества документов (параллельно обрабатывайте документы и на уровне документов)
- ситуаций, в которых нужен контроль над запросом на каждом этапе
Этот подход хуже подходит для извлечения конкретного факта (вместо него используйте поиск) и для документов, которые достаточно коротки и помещаются в контекстное окно (просто суммируйте их напрямую).
Проверка знаний
Что происходит на фазе сведения в паттерне суммаризации MapReduce?
Повторение: суммаризация MapReduce
Паттерн MapReduce позволяет обрабатывать документы, слишком длинные для одного вызова LLM:
- Сопоставление: независимо суммировать каждый фрагмент — можно выполнять параллельно
- Сведение: объединить сводки фрагментов в одну итоговую сводку
- Рекурсивное сведение: применять, когда сами сводки слишком длинные
- Совет по стоимости: используйте недорогую модель для сопоставления и качественную модель для сведения
MapReduceDocumentsChain в LangChain предоставляет готовую реализацию. В следующем уроке рассматривается иерархическая суммаризация книг и научных статей.
Часто задаваемые вопросы
Урок «Шаблон обобщения Map-Reduce» бесплатный?
Да — полный текст урока «Шаблон обобщения Map-Reduce» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Prompt Engineering, подпишись на CoddyKit PRO. Курс AI Prompt Engineering содержит 4 уроков всего.
Чему я научусь в уроке «Шаблон обобщения Map-Reduce»?
Обобщайте каждый блок отдельно, а затем объединяйте получившиеся резюме Ты практикуешь AI Prompt Engineering с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Prompt Engineering?
Предыдущий опыт не требуется. AI Prompt Engineering на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Шаблон обобщения Map-Reduce»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Prompt Engineering?
Да. Каждый урок AI Prompt Engineering включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Стратегии разбиения длинных текстов
- Шаблон обобщения Map-Reduce
- Иерархическое обобщение
- Сохранение контекста между блоками