Summarization Before the Call: Condensing Long Documents for Claude
When a document is too long, too unfocused, or reused across many prompts, the fix isn't to send it whole to your strongest model every time.
Search across all documentation pages
When a document is too long, too unfocused, or reused across many prompts, the fix isn't to send it whole to your strongest model every time.
It's to condense it once with a cheap model, then send the condensed version to the model doing the real work.
Pre-call summarization is a two-step pattern: a fast, inexpensive model reads a long document and produces a shorter representation of it.
That shorter representation, not the original document, is what gets sent to the costlier model handling the actual task.
This adds one extra API call, but that call is cheap, so the net cost is lower whenever the document is long or gets reused across multiple prompts.
The technique is most valuable for reference material read many times: a large log file, a long support thread, a lengthy contract, or a big changelog.
This page shows how to build a summarization step with the anthropic Python SDK, when it pays off, and where it can go wrong.
Quick-reference recipe card - copy-paste ready.
import anthropic
client = anthropic.Anthropic()
def summarize(document: str, focus: str = "") -> str:
instruction = f"Summarize this document in under 200 words."
if focus:
instruction += f" Focus especially on: {focus}."
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=300,
messages=[{"role": "user", "content": f"{instruction}\n\n{document}"}],
)
return response.content[0].textWhen to reach for this:
import anthropic
client = anthropic.Anthropic()
def summarize(document: str, focus: str = "", max_words: int = 200) -> str:
"""Condense a long document with a cheap model."""
instruction = f"Summarize this document in under {max_words} words."
if focus:
instruction += f" Focus especially on: {focus}."
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=400,
messages=[{"role": "user", "content": f"{instruction}\n\n{document}"}],
)
return response.content[0].text
def answer_with_summary(summary: str, question: str) -> str:
"""Send the condensed summary, not the original document, to the stronger model."""
response = client.messages.create(
model="claude-sonnet-5",
max_tokens=500,
messages=[{
"role": "user",
"content": f"Based on this summary of a support thread, answer the question.\n\n"
f"Summary:\n{summary}\n\nQuestion: {question}",
}],
)
return response.content[0].text
with open("support_thread.txt") as f:
long_thread = f.read() # 8,000 words of back-and-forth messages
condensed = summarize(long_thread, focus="what the customer's unresolved issue is")
answer = answer_with_summary(condensed, "What's still unresolved for this customer?")
print(answer)What this demonstrates:
summarize runs on Haiku 4.5, the cheapest tier, since condensing text doesn't require deep reasoning.answer_with_summary runs on Sonnet 5, the model actually doing the reasoning the user cares about, but it only ever sees the condensed text.focus parameter steers the summary toward what the downstream question needs, instead of producing a generic summary that might drop the relevant detail.condensed across multiple downstream questions about the same thread.| Situation | Pays Off? | Why |
|---|---|---|
| Document used once, in one prompt | Rarely | The summarization call adds cost with no reuse to offset it |
| Document referenced across many prompts | Usually | Summarization cost is paid once; savings compound with each reuse |
| Document is already short (under a few hundred words) | No | The overhead of an extra call isn't worth it |
| Document is long and only the gist matters | Yes | This is the core use case: compress bulk, keep meaning |
# Cache the summary so repeated questions about the same document don't
# re-run the summarization call.
_summary_cache: dict[str, str] = {}
def cached_summarize(doc_id: str, document: str, focus: str = "") -> str:
key = f"{doc_id}:{focus}"
if key not in _summary_cache:
_summary_cache[key] = summarize(document, focus=focus)
return _summary_cache[key]A simple in-memory dict is enough for a single process; a production system would back this with a persistent cache keyed by document hash and focus.
focus parameter that tells the summarizer what to preserve, and consider a longer summary length for high-stakes documents.| Alternative | Use When | Don't Use When |
|---|---|---|
| Minimal-context prompting (extract, don't summarize) | You know exactly which section of the document is relevant | The relevant section isn't known ahead of time, or relevance spans the whole document |
| Send the full document every time | The document is short, or used only once | The document is long and reused across many prompts |
| Chunking with retrieval (embeddings) | The document is very large and questions target specific unpredictable sections | A single short summary already captures what most questions need |
| Structured extraction (pull fields into JSON, skip prose) | The document has a predictable structure (invoices, forms) | The document is unstructured prose where a narrative summary is more useful |
Trimming selects an existing subset of the text; summarization generates new, shorter text that represents the whole document.
Use trimming when you know which part is relevant, and summarization when the relevant information is spread across the whole document.
The cheapest model in your lineup that produces summaries accurate enough for your downstream task, often Haiku 4.5 in the current Claude lineup.
Validate this choice against a few real documents rather than assuming the cheapest tier is always sufficient.
Yes, if the document is only used once.
You can, but each summarization pass loses some information, so summarizing a summary compounds that loss.
Prefer summarizing from the original source whenever it's still available.
focus instruction, or move up a model tier for the summarization step.It helps whenever you know what the downstream task cares about, since a focused summary is less likely to drop the detail that matters.
For a general-purpose summary that will answer varied future questions, a broader, unfocused summary may be more appropriate.
Be cautious.
Long enough to preserve what downstream questions need, short enough to be meaningfully cheaper than the original.
There's no universal number; a 200-word cap works for many support threads and articles, but a technical document may need more.
Yes, this technique is itself an application of model tiering: the cheap model handles the simpler task (compression), and the costlier model handles the harder task (reasoning over the result).
A cached summary becomes stale once the source document changes.
Key the cache by a hash or version identifier of the source document, not just its name, so a change invalidates the cached summary automatically.
Yes.
A focused summary removes noise the original document may have carried, which can reduce the risk of irrelevant material diluting the model's attention on what matters.
Stack versions: Written against the Claude model lineup current as of ~June 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (the default), and Claude Haiku 4.5 - and the official
anthropicPython SDK (latest 0.x release). Model names, pricing, and SDK versions move quickly - verify current specifics at platform.claude.com/docs before relying on them.
Reviewed by Chris St. John·Last updated Jul 19, 2026