Keeping Context Clean and Output Trustworthy

Context engineering — window management, drift and bloat from accumulating tool output, and isolation via subagents — plus output handling: guaranteeing structure with the parse helper and tool use, defensive parsing, and healthy skepticism toward confident output.

Every model call is an exchange with two ends: the context you send in and the output you get back. The reliability of the whole thing rests on both. Context engineering is keeping the input clean enough that the model reasons well. A window that fills with noise reasons worse, so what you leave out counts as much as what you put in. Output handling is making the result structured enough that your code can trust it. Then comes the sharper half: remembering that a perfectly-formed answer can still be a wrong one. Both are developer disciplines rather than model magic, and this chapter takes them in turn.

Context is a budget you actively manage

Recall two facts. The API is stateless, so a conversation is a messages list you resend each turn (chapter 1). And the context window is a hard input-plus-output ceiling (chapter 13). Together they mean context is a finite budget that fills up. Left unmanaged it degrades in specific ways the exam names:

  • Bloat from accumulating tool output. Every tool result piles into the history. A tool that returns 40 fields when 5 matter (chapter 10) spends context out of all proportion to its value. Over a long agent run that noise crowds out what matters. The fix: prune tool output to the relevant fields before it enters context.
  • Drift. As a conversation grows, precise early facts get buried — an order number, an amount, a stated expectation. Summarize to save room, and they compress into vague prose the model can no longer act on. Summarization is lossy exactly where it hurts.
  • The window simply filling. Eventually a long session approaches the ceiling and something has to give.

The context-engineering toolkit

The techniques, established in the companion Architect work and applicable directly:

  • Prune and trim. Keep only the fields and turns that matter; drop verbose tool dumps before they accumulate.
  • Compaction. Summarize or compact the history when it grows large. The Agent SDK exposes this through the /compact operation and a PreCompact hook, and the parse/create endpoints accept a context_management parameter. Compaction is a first-class concern, not a hack. The key discipline: keep the precise, load-bearing facts out of the lossy summary. Put them in a small structured “facts” block you always include verbatim, separate from the summarized prose.
  • Context isolation via subagents. This is the most powerful lever: push verbose sub-work into a subagent whose context is separate (chapters 7–8). The subagent does the heavy exploration in its own window and returns a compact result. The main agent’s context stays clean. This is why subagents improve task execution — they keep each window small enough to reason well.

The through-line: decide deliberately what stays in context and what gets externalized or dropped. Context is not a place things accumulate by default; it’s a working set you curate.

Output handling: guarantee the shape

On the other end, your code has to consume what the model produces, and hoping for well-formed output is not a strategy. There are two ways to guarantee structure rather than request it.

The parse helper with a schema. The SDK’s beta.messages.parse takes an output_format — a Pydantic model — and returns a validated instance of it.

import anthropic
from pydantic import BaseModel

client = anthropic.Anthropic()
DOC = "Order A17: 3 books at $15 each. Printed total: $50.00."

class OrderExtract(BaseModel):
    order_id: str
    total: float
    conflict_detected: bool

r = client.beta.messages.parse(model="claude-haiku-4-5", max_tokens=256,
    messages=[{"role": "user", "content": f"Extract the order.\n{DOC}"}],
    output_format=OrderExtract)
# r.parsed_output == OrderExtract(order_id='A17', total=50.0, conflict_detected=True)

You get back a typed OrderExtract object, not a string to parse — the model’s output is coerced and validated against your schema before it reaches you.

Forced tool use. The portable path (chapter 10): define a tool whose input_schema is your output shape, and force it with tool_choice. Extracting the same order:

{"order_id": "A17", "calculated_total": 45, "stated_total": 50, "conflict_detected": true}

Either way, the shape is guaranteed by structure, not by prompt wording.

Structure guarantees shape, not truth

A schema guarantees the shape, not the correctness. That’s why “skepticism toward confident output” is a named skill. The parse helper will hand you a perfectly-typed object full of wrong values just as happily as right ones. The model can be confidently, fluently wrong, and a well-formed JSON object is not evidence of a correct one.

So you design for checkable correctness. Notice the extraction pulled both calculated_total (45, the item sum) and stated_total (50, the printed total), and set conflict_detected: true. That’s the pattern: extract the pieces that let your code verify the result. A discrepancy the model might paper over becomes a flag your validator catches. You’re not trusting the model’s confidence; you’re building the schema so correctness is testable.

Defensive parsing

Even with structural guarantees, consume output defensively:

  • Validate semantically, not just structurally. Shape-valid isn’t value-valid — check ranges, cross-field consistency (does the calculated total match the stated one?), and required-but-empty cases.
  • Handle the absent gracefully. Design schemas so the model can return null/"unclear" for what it genuinely can’t determine, rather than forcing it to fabricate — and then handle those nulls.
  • Retry with the specific error. When validation fails, feed the exact failure back for a corrected attempt — but recognize when the information is simply absent from the source, where retrying can’t help.
  • Never trust confident prose. Treat the model’s certainty as unrelated to its accuracy; verify anything load-bearing.

Final thoughts

The two ends of a reliable exchange. Context is a finite budget you actively curate: prune tool output before it bloats, compact with the precise facts kept out of the lossy summary, and isolate verbose work in subagents so the main window stays clean. Output you guarantee structurally. The parse helper returns a validated Pydantic object; forced tool use returns schema-shaped input. But structure buys shape, not truth. So extract checkable fields (calculated and stated), validate semantically, handle the absent, and stay skeptical of confident output. Curate what goes in and verify what comes out, and the model becomes a component you can build on rather than one you cross your fingers over. That completes Domain 6.

Next: Arc 6 opens with application security — prompt injection, untrusted input, and the attacks a shipped Claude app has to survive.

Comments