Cheatsheet 4 — Agentic Patterns
Domain 1 (27%) — the largest domain. Plus the Domain 4 output patterns.
The agentic loop
messages = [{"role": "user", "content": user_input}]
while True:
response = client.messages.create(model="claude-opus-5", tools=TOOLS,
messages=messages, max_tokens=4096)
if response.stop_reason != "tool_use": # "end_turn" → done
break
messages.append({"role": "assistant", "content": response.content}) # verbatim
results = [
{"type": "tool_result", "tool_use_id": b.id, "content": execute(b)}
for b in response.content if b.type == "tool_use"
]
messages.append({"role": "user", "content": results}) # ALL results, ONE message
Four facts
stop_reason == "tool_use"means continue."end_turn"means stop. That is the termination condition.- Append the assistant turn verbatim — every block, unmodified.
- All
tool_resultblocks for one assistant turn go in ONE user message, each keyed by itstool_use_id. - The loop is driven by the API's structured signal, never by text interpretation.
🚫 The four loop anti-patterns
| Anti-pattern | Why it breaks |
|---|---|
| Parsing natural-language termination signals ("I'm done") | Phrasing varies; false positives and false negatives |
| An arbitrary iteration cap as the primary stop condition | Truncates legitimate work; a cap is a safety net, not a termination condition |
| Checking whether the assistant text is non-empty | Text and tool calls co-occur in one turn |
| Terminating on the first tool result | Most real work needs several turns |
Model-driven vs pre-configured
| Use | |
|---|---|
| Model-driven decisions | Inputs vary; the path can't be enumerated; judgement required |
| Pre-configured decision tree | Path is fixed, auditable, and must be identical every time |
Support triage → model-driven. Compliance-mandated approval sequence → pre-configured (or a hook, below).
Coordinator ↔ subagent (hub-and-spoke)
┌──────────────┐
┌────────│ COORDINATOR │────────┐
│ └──────┬───────┘ │
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ web-search │ │ doc-analysis│ │ synthesis │
└────────────┘ └────────────┘ └────────────┘
isolated ctx isolated ctx isolated ctx
Two structural rules:
- All inter-subagent communication goes through the coordinator. Subagents do not talk to each other.
- Subagents have isolated context and inherit nothing. Not the conversation, not prior findings, not the original question.
Consequence: every delegating prompt must carry, literally, every identifier, path, constraint, and prior finding the subagent needs. "The company we discussed" means nothing to it.
Delegation mechanics
- Delegate with the
Tasktool. The coordinator'sallowedToolsmust include"Task"— omit it and the coordinator silently does the work itself. - Parallel subagents = multiple
Taskcalls in a single coordinator response. There is noparallel: trueflag. AgentDefinitioncarries three things: a description (used by the coordinator for routing), a prompt, and scoped tools.
The four decomposition patterns
| Pattern | What it fixes |
|---|---|
| Scope partitioning | Duplicated work — give each subagent a disjoint scope and tell it what others cover |
| Dynamic subagent selection | Rigid pipelines — choose subagents based on what the task actually needs |
| Iterative refinement | Gaps — check coverage and re-delegate for what's missing |
| Goal-oriented prompts | Brittleness — state the goal and quality criteria, not procedural steps |
🎯 Narrow decomposition is the signature Domain 1 bug. Symptom: the coordinator's subtasks all cluster in one or two dimensions of a multi-dimensional question. Fix: explicit scope partitioning across named dimensions + a coverage check that re-delegates. Blame the coordinator's decomposition, not the subagents that worked correctly within the scope they were given.
Enforcement beats guidance
| Deterministic (hooks, gates, code) | Probabilistic (prompt instructions) |
|---|---|
| Runs every time | Non-zero failure rate |
| Correct choice when errors are financial, compliance, or irreversible | Fine for style and preference |
def pre_tool_use(tool_name, tool_input, state):
if tool_name == "process_refund" and not state.get("verified_customer_id"):
return {"block": True, "reason": "Call get_customer first and confirm a single match."}
if tool_name == "process_refund" and tool_input["amount"] > 500:
return {"block": True, "reason": "Refunds over $500 require escalate_to_human."}
return {"block": False}
| Hook | Use for |
|---|---|
PreToolUse |
Block or redirect a call — prerequisite gates, limits, permissions |
PostToolUse |
Transform a result — normalize heterogeneous formats, trim verbose payloads |
The canonical PostToolUse case: one tool returns Unix epoch timestamps, another ISO 8601, another numeric status codes. Normalize them all before they reach the model rather than explaining the formats in the prompt.
Structured handoffs
When escalating to a human or handing off between agents, pass a structured summary, not the transcript:
customer_id · root_cause · refund_amount · recommended_action
Same principle at all three boundaries: agent→human, agent→agent, tool→model. Structured, minimal, complete.
Attention dilution
Symptom: a review or refactor across many files misses issues it would catch in one file.
Fix: per-file passes + a separate cross-file integration pass.
- Per-file passes catch local issues dilution would hide.
- The integration pass catches what per-file passes structurally cannot — a changed signature and its unchanged callers.
🚫 A larger context window does not fix attention dilution. It's an attention-distribution problem, not a capacity problem.
Prompt chaining vs dynamic decomposition
| Use | |
|---|---|
| Prompt chaining | Fixed sequence of known steps |
| Dynamic adaptive decomposition | The steps depend on what earlier steps discover |
Independent review
Ranked, as the guide ranks it:
- ✅ A separate instance with fresh context
- ❌ The generating instance reviewing its own work
- ❌ Extended thinking on the generating instance
Independence, not depth. More reasoning inside a contaminated context doesn't help.
Session state
| Situation | Mechanism |
|---|---|
| Continuing, history valid | --resume <session-name> |
| Branching to try a variant | fork_session |
| Old tool results now stale | Fresh session + structured summary |
| Context filling, same session | /compact |