Why Claude Code Uses Exactly 12 Agent Design Patterns—and How to Apply Them

The article breaks down Claude Code's twelve agent design patterns—grouped into memory, workflow, tool‑permission, and automation categories—explaining the architectural pain points each solves, when to use them, signs of over‑design, and provides concrete Python implementations and trade‑off analyses.

Data Party THU
Data Party THU
Data Party THU
Why Claude Code Uses Exactly 12 Agent Design Patterns—and How to Apply Them

Overview

This article presents twelve reusable agent patterns, grouped into four architectural problem domains: memory & context, workflow & orchestration, tools & permissions, and automation safeguards. Each pattern addresses a concrete structural pain point in agent engineering and defines when the pattern adds value versus when it is excessive.

Memory & Context – The Agent’s Cortex

Patterns 1‑5 answer the question “What should an agent remember, where, and for how long?” The three competing goals—capacity, speed, and relevance—form an impossible triangle. The five patterns resolve this trade‑off by structuring memory into layers.

Deep Dive: Pattern 3 – Hierarchical Memory

The implementation keeps a concise index (≈200 lines) in the prompt, loads relevant files on demand, and stores the full dialogue history on disk. This prevents the context window from being saturated with irrelevant data.

# memory_loader.py — Minimal three‑layer memory loader
from pathlib import Path

class MemoryLoader:
    """Three‑layer memory: index always loaded + hot layer on demand + cold search"""
    def __init__(self, memory_dir: Path):
        self.memory_dir = memory_dir
        self.index_path = memory_dir / "MEMORY.md"

    def load_index(self) -> str:
        """Layer 1: index always loaded, hard limit 200 lines"""
        if not self.index_path.exists():
            return ""
        content = self.index_path.read_text()
        lines = content.split("
")
        if len(lines) > 200:
            raise ValueError(f"MEMORY.md {len(lines)} lines > 200, please trim the index")
        return content

    def load_hot(self, topic: str) -> str:
        """Layer 2: load relevant memory files by topic"""
        matches = list(self.memory_dir.glob(f"*{topic}*.md"))
        if not matches:
            return ""
        return "
---
".join(f.read_text() for f in matches[:3])

    def search_cold(self, query: str) -> str:
        """Layer 3: cold storage full‑text search (production uses vector search)"""
        import subprocess
        result = subprocess.run(["grep", "-rl", query, str(self.memory_dir / "archive")], capture_output=True, text=True)
        return result.stdout

The hard line‑count limit on the index is the key guardrail; exceeding it collapses the hierarchy back to a flat prompt.

Pattern 1 – Persistent Instructions : a project‑level “employee handbook” that persists across sessions. Use when any cross‑session behavior must be retained. Over‑engineered if a single file < 500 lines suffices.

Pattern 2 – Scoped Context : automatically load different rule sets per directory (e.g., monorepos, multi‑language projects). Over‑engineered for a single project with ≤ 3 files.

Pattern 3 – Memory Integration : periodic cleanup of duplicate or expired memories. Useful for agents running weeks; manual cleanup is enough for short‑lived projects.

Pattern 4 – Progressive Compression : automatically compress old dialogue turns. Apply when sessions exceed 20‑30 turns; compression harms short sessions (< 10 turns).

Pattern 5 – … (other memory patterns omitted for brevity).

Workflow & Orchestration – Decoupling “Think” from “Do”

Patterns 6‑8 answer “How can an agent keep the context from becoming a garbage dump during complex tasks?” The solution is to isolate contexts for different phases.

Deep Dive: Pattern 7 – Context‑Isolated Sub‑Agents

Each sub‑agent receives its own context window and permission set. Example roles:

Research sub‑agent: read‑only, produces a report.

Planning sub‑agent: designs a solution without touching code.

Execution sub‑agent: has full tool access but only receives the blueprint and research summary.

Claude Code implements this via the subagent_type field (e.g., Explore for read‑only) and an isolation flag that creates an independent worktree for each sub‑agent.

Other Two Patterns (Quick View)

Pattern 6 – Explore‑Plan‑Execute : read‑only exploration, then planning, then acting. Suitable for unfamiliar codebases or multi‑file changes. Over‑engineered for a single‑line configuration change.

Pattern 8 – Branch‑Merge Parallel : run independent sub‑tasks in parallel (e.g., batch refactoring, independent testing). Over‑engineered when tasks have dependencies that would cause merge conflicts.

Python mapping: Pattern 7 corresponds to separate processes (via subprocess or asyncio); Pattern 8 maps to concurrent.futures.ProcessPoolExecutor with isolated working directories.

Tools & Permissions – Least‑Privilege in Agent Systems

Patterns 9‑11 answer “What operations can an agent perform, and how can we prevent it from causing trouble?”

Deep Dive: Pattern 10 – Three‑Tier Command Risk Classification

Commands are classified as low, medium, or high risk. Low‑risk commands (e.g., file reads) are auto‑approved; medium‑risk commands (e.g., installing packages) require user confirmation; high‑risk commands (e.g., sudo, rm -rf /) are blocked outright.

# command_risk.py — Three‑tier command risk classification
import shlex
from enum import Enum

class RiskLevel(Enum):
    LOW = "low"      # auto‑allow
    MEDIUM = "medium"  # require confirmation
    HIGH = "high"      # block

HIGH_RISK_PATTERNS = [
    "rm -rf /", "sudo ", "chmod 777", "> /dev/sda",
    "mkfs.", "dd if=", ":(){ :|:& }:", "chown -R /", "mv /* /dev/null"
]
MEDIUM_RISK_PREFIXES = [
    "rm ", "git push", "git reset --hard", "pip install", "npm install -g",
    "brew ", "docker rm", "kubectl delete", "chmod "
]

def classify_command(command: str):
    """Return (RiskLevel, reason)"""
    cmd = command.strip().lower()
    for pattern in HIGH_RISK_PATTERNS:
        if pattern.lower() in cmd:
            return RiskLevel.HIGH, f"Matched high‑risk pattern: {pattern}"
    for prefix in MEDIUM_RISK_PREFIXES:
        if cmd.startswith(prefix.lower()):
            return RiskLevel.MEDIUM, f"Medium‑risk operation: {prefix}"
    return RiskLevel.LOW, "Low‑risk operation"

if __name__ == "__main__":
    for cmd in ["ls -la", "git status", "rm -rf /", "pip install requests", "sudo systemctl restart nginx"]:
        level, reason = classify_command(cmd)
        print(f"[{level.value.upper():6s}] {cmd:40s} — {reason}")

Running the script yields:

[LOW   ] ls -la                                 — Low‑risk operation
[LOW   ] git status                             — Low‑risk operation
[HIGH  ] rm -rf /                               — Matched high‑risk pattern: rm -rf /
[MEDIUM] pip install requests                   — Medium‑risk operation: pip install
[HIGH  ] sudo systemctl restart nginx           — Matched high‑risk pattern: sudo

Additional patterns:

Pattern 9 – Progressive Tool Extension : start with read‑only file access and search; grant write or execution permissions only when needed. Over‑engineered when fewer than five tools are used (full open access is simpler).

Pattern 11 – Single‑Purpose Tools : separate read, write, and edit capabilities into distinct tools rather than a generic shell. Over‑engineered when fewer than three tools are required.

Automation Safeguard – Deterministic Lifecycle Hooks

Pattern 12 is the only pattern that deliberately excludes the LLM. It provides deterministic hooks that run at key lifecycle moments (pre‑tool, post‑tool, session start, stop) to enforce safety and consistency.

Deep Dive: Pattern 12 – Deterministic Lifecycle Hook System

# hook_system.py — Deterministic lifecycle hook system
import subprocess, sys
from typing import Callable

class HookManager:
    """Agent lifecycle hooks – any hook failure blocks further execution"""
    def __init__(self):
        self._pre_tool: list[Callable] = []
        self._post_tool: list[Callable] = []
        self._stop: list[Callable] = []

    def on_pre_tool(self, fn: Callable):
        """Register a PreToolUse hook"""
        self._pre_tool.append(fn)
        return fn

    def on_post_tool(self, fn: Callable):
        """Register a PostToolUse hook"""
        self._post_tool.append(fn)
        return fn

    def on_stop(self, fn: Callable):
        """Register a Stop hook"""
        self._stop.append(fn)
        return fn

    def _run(self, hooks: list[Callable], phase: str) -> bool:
        """Execute a list of hooks; any False or exception aborts"""
        for hook in hooks:
            try:
                if hook() is False:
                    print(f"[HOOK FAIL] {phase}: {hook.__name__} returned False", file=sys.stderr)
                    return False
            except Exception as e:
                print(f"[HOOK ERROR] {phase}: {hook.__name__} → {e}", file=sys.stderr)
                return False
        return True

    def pre_tool_check(self, tool_name: str) -> bool:
        return self._run(self._pre_tool, f"PreToolUse:{tool_name}")

    def post_tool_check(self, tool_name: str) -> bool:
        return self._run(self._post_tool, f"PostToolUse:{tool_name}")

    def stop_check(self) -> bool:
        return self._run(self._stop, "Stop")

# Example registration
hooks = HookManager()

@hooks.on_pre_tool
def check_dangerous_commands():
    """PreToolUse: command risk classification (Pattern 10)"""
    print("  ✓ Command risk classification passed")
    return True

@hooks.on_post_tool
def auto_format_code():
    """PostToolUse: automatically run ruff formatter after writes"""
    try:
        subprocess.run(["ruff", "check", "--fix", "."], capture_output=True, timeout=30, check=True)
        print("  ✓ ruff --fix completed")
        return True
    except subprocess.CalledProcessError:
        print("  ✗ ruff failed, fix before proceeding", file=sys.stderr)
        return False

@hooks.on_stop
def final_validate():
    """Stop: final validation before session ends"""
    checks = [
        ("ruff", ["ruff", "check", "."]),
        ("pytest", ["pytest", "--tb=short", "-q"])
    ]
    all_pass = True
    for name, cmd in checks:
        try:
            subprocess.run(cmd, capture_output=True, timeout=60, check=True)
            print(f"  ✓ {name} passed")
        except subprocess.CalledProcessError:
            print(f"  ✗ {name} failed", file=sys.stderr)
            all_pass = False
    return all_pass

if __name__ == "__main__":
    print("Agent: preparing to execute command…")
    if not hooks.pre_tool_check("bash"):
        print("→ PreToolUse blocked, command not executed")
        sys.exit(1)
    print("Agent: command executed, entering PostToolUse…")
    if not hooks.post_tool_check("bash"):
        print("→ PostToolUse blocked, retry after fixing")
        sys.exit(1)
    print("Agent: session ending, running Stop hooks…")
    if not hooks.stop_check():
        print("→ Stop hook blocked, unresolved checks")
        sys.exit(1)
    print("
✓ All hooks passed, agent session ends normally")

The three design principles are:

Hooks are deterministic – no LLM involvement.

Hooks are decoupled from prompts – they run even if the model forgets.

Any hook returning False or raising an exception aborts the pipeline.

Conclusion – From Smarter Models to More Reliable Systems

The twelve patterns address four unavoidable architectural challenges in agent engineering: memory management, task decomposition, permission control, and deterministic fallback. They are independent of any specific model or algorithm; even as LLM capabilities improve, context windows remain bounded, shell commands stay risky, memories still decay, and prompt‑only solutions will forget repeatable steps.

Thus, patterns 1‑11 act as “training wheels” that make agents safer to operate, while pattern 12 serves as the “seat belt” that guarantees safety regardless of model behavior.

References

Kubernetes Patterns: https://k8spatterns.com/

Prompt Patterns: https://promptpatterns.dev/

12 Agentic Harness Patterns from Claude Code: https://generativeprogrammer.com/p/12-agentic-harness-patterns-from

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Memory ManagementPythonWorkflow OrchestrationClaude CodeTool PermissionsAutomation HooksAgent Design Patterns
Data Party THU
Written by

Data Party THU

Official platform of Tsinghua Big Data Research Center, sharing the team's latest research, teaching updates, and big data news.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.