Summarization reduces a growing agent context by replacing older intermediate work with a shorter account of its meaning. Preserve the user's original question and the most recent messages; summarize the tools used, findings, and progress between them. section 6.2.5
A practical strategy is to:
- Measure context size and try compaction first; summarize only if compaction does not bring usage below the threshold. section 6.2.6
- Find the original user message, retain a configured number of recent messages, and summarize the intervening history. section 6.2.5
- Record the last summarized position so later calls do not summarize the same history again. section 6.2.5
Unlike compaction, which is deterministic and reversible, summarization relies on an LLM to select what matters and permanently discards the original reasoning. It adds LLM-call cost, so use it when cheaper reduction is insufficient. section 6.2.5
Listing 6.6 Summarization strategy implementation
async def apply_summarization(
context: ExecutionContext,
request: LlmRequest,
llm_client: LlmClient,
keep_recent: int = 5
) -> None:
"""Replace old messages with a summary"""
contents = request.contents
# Find user message position
user_idx = None
for i, item in enumerate(contents):
if isinstance(item, Message) and item.role == "user":
user_idx = i
break
if user_idx is None:
return
# Check previous summary position (skip already-summarized portions)
last_summary_idx = context.state.get("last_summary_idx", user_idx)
# Calculate summarization target range
summary_start = last_summary_idx + 1
summary_end = len(contents) - keep_recent
# Overlap prevention: exit if nothing to summarize or range is invalid
if summary_end <= summary_start:
return
# Determine portions to preserve (no overlap)
preserved_start = contents[:last_summary_idx + 1]
preserved_end = contents[summary_end:]
to_summarize = contents[summary_start:summary_end]
# Generate summary
history_text = format_history_for_summary(to_summarize)
summary = await generate_summary(llm_client, history_text)
# Add summary to instructions
request.append_instructions(f"[Previous work summary]\n{summary}")
# Keep only preserved portions in contents
request.contents = preserved_start + preserved_end
# Record summary position
context.state["last_summary_idx"] = len(preserved_start) - 1
def format_history_for_summary(items: List[ContentItem]) -> str:
"""Convert ContentItem list to text for summarization"""
lines = []
for item in items:
if isinstance(item, Message):
lines.append(f"[{item.role}]: {item.content[:500]}...")
elif isinstance(item, ToolCall):
lines.append(f"[Tool Call]: {item.name}({item.arguments})")
elif isinstance(item, ToolResult):
content_preview = str(item.content[0])[:200] if item.content else ""
lines.append(f"[Tool Result]: {item.name} -> {content_preview}...")
return "\n".join(lines)
async def generate_summary(llm_client: LlmClient, history: str) -> str:
"""Generate history summary using LLM"""
request = LlmRequest(
instructions=[SUMMARIZATION_PROMPT.format(history=history)],
contents=[Message(role="user", content="Please summarize.")]
)
response = await llm_client.generate(request)
for item in response.content:
if isinstance(item, Message):
return item.content
return ""
See the asynchronous implementation for target-range calculation, recent-message preservation, and summary-position tracking. section 6.2.5











