0Pricing
AI Prompt Engineering · Lekcja

Wzorzec podsumowywania Map-Reduce

Podsumuj każdy fragment niezależnie, a następnie połącz podsumowania w całość.

Wzorzec podsumowywania Map-Reduce to bezpłatna lekcja AI Prompt Engineering na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Prompt Engineering, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Prompt Engineering zawiera 4 lekcji w sumie.

Wzorzec Map-Reduce

Gdy dokument przekracza rozmiar okna kontekstu LLM, nie można przekazać go w całości za jednym razem. Rozwiązuje to wzorzec map-reduce:

  • Mapowanie: niezależne podsumowanie każdego fragmentu
  • Redukcja: synteza podsumowań wszystkich fragmentów w jedno podsumowanie końcowe

Przypomina to klasyczny MapReduce z systemów rozproszonych — tę samą logikę dzielenia problemu i rozwiązywania go przez podział na mniejsze części, zastosowaną do zadań językowych.

Krok 1: Faza mapowania

W fazie mapowania każdy fragment jest wysyłany do LLM wraz z promptem do podsumowania. Model zwraca krótkie podsumowanie wyłącznie tego fragmentu. Podsumowania są zbierane na liście.

Każde podsumowanie powinno być znacznie krótsze od oryginalnego fragmentu — zazwyczaj stanowić 10–20% jego pierwotnej długości. To właśnie ta kompresja umożliwia wykonanie kroku redukcji.

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]

Krok 2: Faza redukcji

W fazie redukcji wszystkie podsumowania fragmentów są łączone i wysyłane do LLM wraz z promptem syntezy. Model tworzy jedno spójne podsumowanie końcowe.

Jeśli podsumowania fragmentów nadal są zbyt długie, aby zmieścić się w jednym oknie kontekstu, należy zastosować redukcję rekurencyjnie — najpierw podsumować grupy podsumowań, a następnie dokonać ich syntezy.

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

Złożenie całości: surowe wywołania API

Oto kompletny potok map-reduce wykorzystujący bezpośrednie wywołania OpenAI API — bez użycia frameworka. Zapewnia to pełną kontrolę nad promptami i parametrami w każdej fazie.

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 udostępnia gotowy komponent MapReduceDocumentsChain, który obsługuje dzielenie na fragmenty, równoległe wywołania fazy mapowania oraz krok redukcji. Jest wygodny, ale mniej elastyczny niż bezpośrednie wywołania 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'])

Równoległość w fazie mapowania

Podsumowanie każdego fragmentu jest niezależne, dlatego fazę mapowania można wykonywać równolegle. Przy użyciu języka Python i ThreadPoolExecutor wszystkie wywołania API dla fragmentów są wysyłane jednocześnie, co znacznie skraca rzeczywisty czas wykonania.

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

Rekurencyjna redukcja

Gdy dokument jest bardzo długi, same podsumowania fragmentów mogą przekroczyć rozmiar okna kontekstu. Należy zastosować redukcję rekurencyjnie: pogrupować podsumowania w partie, zredukować każdą partię, a następnie zredukować podsumowania partii.

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]

Zachowywanie kluczowych szczegółów

Częsty problem naiwnego podejścia map-reduce polega na utracie ważnych szczegółów podczas kompresji. Można temu przeciwdziałać, stosując następujące rozwiązania:

  • Poinstruować w prompcie fazy mapowania, aby zachować nazwy, liczby i daty
  • Poprosić w fazie redukcji o sprawdzenie sprzeczności między sekcjami
  • Użyć większego rozmiaru fragmentu, aby kontekst mapowania był bogatszy
  • Wykonać etap weryfikacji: zapytać model, czy kluczowe encje z oryginału występują w podsumowaniu końcowym
MAP_PROMPT = '''Summarize the following section in 5 sentences.
Preserve all key names, numbers, dates, and conclusions.

Section:
{chunk}'''

Łańcuch refine: alternatywny wzorzec

Łańcuch refine jest alternatywą dla map-reduce. Przetwarza fragmenty sekwencyjnie: podsumowanie fragmentu N jest przekazywane wraz z fragmentem N+1, a model aktualizuje bieżące podsumowanie. Zapewnia to bardziej spójny wynik, ale uniemożliwia równoległość i działa wolniej.

Należy użyć refine, gdy ważna jest spójność narracji (np. w umowach prawnych). Map-reduce należy stosować, gdy ważna jest szybkość (np. przy przetwarzaniu partii artykułów informacyjnych).

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

Zarządzanie kosztami i tokenami

Map-reduce wymaga wielu wywołań API. W przypadku dokumentu zawierającego 100 fragmentów, przy użyciu gpt-4o za 5 USD/1M tokenów wejściowych:

  • Mapowanie: 100 fragmentów × 1000 tokenów = 100 tys. tokenów wejściowych ≈ 0,50 USD
  • Redukcja: ~10 tys. tokenów (podsumowania) ≈ 0,05 USD
  • Łącznie: ~0,55 USD na dokument

Aby ograniczyć koszt, należy użyć gpt-4o-mini w fazie mapowania (0,15 USD/1M), a gpt-4o tylko w fazie redukcji. To hybrydowe podejście obniża koszty o 70% przy minimalnej utracie jakości.

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

Kiedy stosować Map-Reduce

Podsumowywanie metodą map-reduce najlepiej sprawdza się w przypadku:

  • dokumentów dłuższych niż okno kontekstu modelu
  • wsadowego podsumowywania wielu dokumentów (również na poziomie dokumentów można zastosować równoległość)
  • sytuacji, w których potrzebna jest kontrola nad promptem na każdym etapie

Metoda ta jest mniej odpowiednia do: wyodrębniania konkretnego faktu (należy zamiast niej użyć wyszukiwania) lub sytuacji, gdy dokument jest wystarczająco krótki, aby zmieścić się w kontekście (wtedy wystarczy podsumować go bezpośrednio).

Sprawdzenie wiedzy

Co dzieje się podczas fazy redukcji we wzorcu podsumowywania map-reduce?

Podsumowanie: podsumowywanie metodą Map-Reduce

Wzorzec map-reduce obsługuje dokumenty zbyt długie dla pojedynczego wywołania LLM:

  • Mapowanie: niezależne podsumowanie każdego fragmentu — można wykonywać równolegle
  • Redukcja: synteza podsumowań fragmentów w jedno podsumowanie końcowe
  • Redukcja rekurencyjna: stosowana, gdy same podsumowania są zbyt długie
  • Wskazówka dotycząca kosztów: należy użyć taniego modelu do mapowania i bardziej zaawansowanego modelu do redukcji

LangChain's MapReduceDocumentsChain udostępnia gotową implementację. Następna lekcja obejmuje hierarchiczne podsumowywanie książek i artykułów naukowych.

Często zadawane pytania

Czy lekcja „Wzorzec podsumowywania Map-Reduce” jest bezpłatna?

Tak — pełny tekst „Wzorzec podsumowywania Map-Reduce” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Prompt Engineering, przejdź na CoddyKit PRO. Kurs AI Prompt Engineering zawiera 4 lekcji w sumie.

Co nauczysz się w „Wzorzec podsumowywania Map-Reduce”?

Podsumuj każdy fragment niezależnie, a następnie połącz podsumowania w całość. Ćwiczysz AI Prompt Engineering z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć AI Prompt Engineering?

Nie wymagamy żadnego doświadczenia. AI Prompt Engineering w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Wzorzec podsumowywania Map-Reduce”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji AI Prompt Engineering?

Tak. Każda lekcja AI Prompt Engineering zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Strategie dzielenia długich tekstów na fragmenty
  2. Wzorzec podsumowywania Map-Reduce
  3. Podsumowywanie hierarchiczne
  4. Zachowywanie kontekstu między fragmentami
← Powrót do AI Prompt Engineering