Careeroza
Map-Reduce Summarization
Map, Then Reduce
long document ──► chunk 1 ──► summary 1 ─┐
├─► chunk 2 ──► summary 2 ─┼──► combine ──► final summary
└─► chunk 3 ──► summary 3 ─┘
(map: parallel, small model) (reduce: one call, stronger model)Map-Reduce with a Stand-in Summarizer
Example
fake_summarize stands in for a model call (it keeps sentences containing numbers or key words). The structure (chunk, parallel map, reduce) is the real pattern:
import re
from concurrent.futures import ThreadPoolExecutor
report = """Q3 sales grew 18% to Rs 42 crore. Festive season orders doubled in October.
Most growth came from tier-2 cities. The team also redesigned the app home screen.
Return rates fell from 9% to 6% after better size charts. Customer support tickets dropped 12%.
Delivery times in the North-East are still slow at 7 days. A new warehouse in Guwahati opens in Q4.
The office moved to a new floor. Marketing spend rose 25% for the Diwali campaign."""
KEY = ("grew", "fell", "dropped", "rose", "opens", "doubled", "slow")
def fake_summarize(text, max_sentences=2):
"""Stand-in for a model call: keep the sentences with numbers or key verbs."""
sentences = re.split(r"(?<=\.)\s+", text.strip())
scored = sorted(sentences, key=lambda s: -(bool(re.search(r"\d", s)) + any(k in s for k in KEY)))
keep = set(scored[:max_sentences])
return " ".join(s for s in sentences if s in keep)
chunks = report.splitlines()
with ThreadPoolExecutor() as pool: # map (in parallel)
partials = list(pool.map(lambda c: fake_summarize(c, max_sentences=1), chunks))
for i, s in enumerate(partials, 1):
print(f"chunk {i}: {s}")
final = fake_summarize(" ".join(partials), max_sentences=4) # reduce
print("\nFINAL:", final)Output
chunk 1: Q3 sales grew 18% to Rs 42 crore. chunk 2: Most growth came from tier-2 cities. chunk 3: Return rates fell from 9% to 6% after better size charts. chunk 4: Delivery times in the North-East are still slow at 7 days. chunk 5: Marketing spend rose 25% for the Diwali campaign. FINAL: Q3 sales grew 18% to Rs 42 crore. Return rates fell from 9% to 6% after better size charts. Delivery times in the North-East are still slow at 7 days. Marketing spend rose 25% for the Diwali campaign.
With Real Calls
import asyncio, anthropic
client = anthropic.AsyncAnthropic()
async def summarize(text: str, instruction: str, model: str) -> str:
r = await client.messages.create(
model=model, max_tokens=800,
messages=[{"role": "user", "content": f"{instruction}\n\n<text>\n{text}\n</text>"}],
)
return next(b.text for b in r.content if b.type == "text")
async def map_reduce(chunks: list[str]) -> str:
partials = await asyncio.gather(*[
summarize(c, "Summarize this part of a business report in 5 bullets. Keep all numbers.", "claude-haiku-5-5")
for c in chunks
])
joined = "\n\n".join(f"<part n='{i}'>{p}</part>" for i, p in enumerate(partials, 1))
return await summarize(joined, "Combine these part summaries into one executive summary: "
"8 bullets max, most important first, keep numbers.", "claude-sonnet-5-5")
print(asyncio.run(map_reduce(chunks)))Tip: Limit concurrency (for example with an asyncio.Semaphore(5)) so a 300-chunk document does not hit your rate limit all at once.