Claude Code · Agent SDK

Agent SDK & Multi-Agent Systems

The Anthropic Agent SDK lets you build networks of AI agents that collaborate, specialize, and execute complex tasks in parallel. This is where Claude goes from a tool to an autonomous engineering team.

🤖 Agent Architecture 🔄 Agentic Loops 👥 Multi-Agent Patterns ⚡ Parallel Execution 🪝 Hooks & Control

What the Agent SDK Is

Claude Code (CLI) is great for interactive development sessions. The Anthropic Agent SDK is for embedding agentic Claude behavior directly into your applications — as Python or TypeScript code that you control programmatically.

🔑 CLI vs. SDK

Claude Code CLI → you interact with Claude in your terminal. Great for development-time tasks.
Agent SDK → Python/TypeScript code that creates Claude agents programmatically as part of your production application. Great for automated workflows and multi-agent pipelines.

The Agentic Loop

Every agentic Claude interaction follows the same fundamental observe-plan-act-evaluate cycle:

YOUR CODE sends task + tools
         │
    ┌────▼────────────────┐
    │     Claude API       │
    │  Decides: respond    │
    │  OR call a tool      │
    └────┬────────────────┘
         │
    ┌────▼──────────────┐
    │  text response?    │──► DONE (task complete)
    │  tool_use block?   │──► Execute tool → get result
    └───────────────────┘         │
                                   └─► Send result back → Loop again
python — agentic loop skeleton
import anthropic
client = anthropic.Anthropic()

def run_agent(task: str, tools: list):
    messages = [{"role": "user", "content": task}]
    while True:
        r = client.messages.create(model="claude-sonnet-4-5",
                                    max_tokens=4096, tools=tools,
                                    messages=messages)
        messages.append({"role": "assistant", "content": r.content})
        if r.stop_reason == "end_turn":
            return next(b.text for b in r.content if b.type == "text")
        tool_results = []
        for b in r.content:
            if b.type == "tool_use":
                result = execute_tool(b.name, b.input)
                tool_results.append({"type": "tool_result",
                                     "tool_use_id": b.id, "content": str(result)})
        messages.append({"role": "user", "content": tool_results})

Multi-Agent Patterns

Pattern 1: Coordinator + Subagents

A coordinator breaks down complex tasks and delegates to specialized subagents. Most common pattern for enterprise workflows.

User task
    │
┌───▼──────────────────┐
│  COORDINATOR AGENT    │ (Orchestrates, plans, merges)
└──┬────────┬─────────┬─┘
   │        │         │
┌──▼──┐  ┌──▼──┐  ┌──▼──┐
│ Sub │  │ Sub │  │ Sub │  (Specialists run in PARALLEL)
│  A  │  │  B  │  │  C  │
└──┬──┘  └──┬──┘  └──┬──┘
   └────────┴─────────┘
            │
    ┌───────▼──────────┐
    │  SYNTHESIS AGENT  │ (Combines all subagent outputs)
    └──────────────────┘

Pattern 2: Sequential Pipeline

Each agent hands off to the next. Great for ETL, content processing, and multi-stage validation workflows.

📌 Pipeline Example

Ingest AgentValidate AgentTransform AgentLoad AgentVerify Agent. Each stage's output becomes the next stage's input.

Pattern 3: Specialist Pool (Parallel)

Same task runs on multiple independent agents simultaneously. Results are compared or voted on for higher reliability. Used for document review, code auditing, and confidence-critical decisions.

Building a Multi-Agent System

python — parallel subagents via asyncio
import asyncio

async def research_company(company: str) -> dict:
    # Each subagent uses cheaper/faster Haiku model
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1500,
        messages=[{"role": "user",
                   "content": f"Research {company}: products, strengths, weaknesses. JSON."}]
    )
    return {"company": company, "data": response.content[0].text}

async def coordinator(competitors: list) -> str:
    # Run ALL subagents in parallel — critical for performance
    results = await asyncio.gather(*[research_company(c) for c in competitors])
    
    # Synthesis uses more capable Sonnet model
    final = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=4000,
        messages=[{"role": "user",
                   "content": f"Synthesize into competitive brief: {results}"}]
    )
    return final.content[0].text
💡 Model Tier Strategy

Use Claude Haiku for subagents doing focused, scoped tasks (cheaper, faster). Use Claude Sonnet for coordinator and synthesis (better reasoning). This asymmetric approach cuts cost by 60-80% vs. using Sonnet for everything.

Hooks — Interception & Control

Hooks intercept tool calls before or after execution. Essential for production safety, auditing, and data protection:

python — before and after hooks
def before_tool(name: str, inputs: dict) -> dict:
    # 1. Audit log
    audit_log(name, inputs)
    # 2. Safety gate — block dangerous ops
    if name == "execute_sql" and "DROP" in inputs.get("query","").upper():
        raise ValueError("DROP statements blocked")
    # 3. Inject context Claude shouldn't manage
    inputs["tenant_id"] = get_current_tenant()
    return inputs

def after_tool(name: str, result: str) -> str:
    # Redact PII before Claude sees it
    if name == "get_customer":
        result = redact_fields(result, ["ssn", "dob", "credit_card"])
    return result
✅ Production Hooks Checklist

Session Management

Long-running agentic tasks need persistent sessions — so they can be paused, resumed, and audited across restarts:

python — Redis session persistence
import redis, json
r = redis.Redis()

def save(session_id, messages):
    r.setex(f"session:{session_id}", 86400, json.dumps(messages))

def resume(session_id):
    data = r.get(f"session:{session_id}")
    if not data: raise ValueError("Session expired")
    return json.loads(data)

# Checkpoint after each major step
save("research-2024-001", messages)
# Next day: messages = resume("research-2024-001")
Error TypeHandlerAction
Transient (timeout, rate limit)Subagent auto-retryExponential backoff, max 3 attempts
Data error (bad inputs)CoordinatorValidate and resubmit with corrected data
Authorization errorHuman escalationPause task, alert team, await resolution
Catastrophic (data loss risk)Immediate haltStop all agents, preserve state, alert on-call

Hands-on Exercises

🧪 Exercise 1: Research Pipeline

1
Create 3-agent pipeline: Researcher → Summarizer → Formatter
2
Researcher fetches content from 3 URLs in parallel using asyncio.gather
3
Summarizer condenses each to 3 bullet points
4
Formatter combines into structured markdown report
5
Compare time: parallel subagents vs. single agent doing all three sequentially. Measure speedup.

🧪 Exercise 2: Safety Hook Lab

1
Build agent with write_file(path, content) tool
2
Add before-hook that blocks writes outside ./output/
3
Test: ask Claude to write to /etc/hosts — does the hook catch it?
4
Add after-hook that logs every successful write to a CSV audit file

Corporate Multi-Agent Deployments

💼

HR Automation Pipeline

Resume Scanner → Culture Fit Evaluator → Skills Assessor → Decision Coordinator. 500 applications processed overnight; only top 10% go to human recruiters.

🔒

Security Audit System

Parallel agents scan repos simultaneously — OWASP vulnerabilities, dependency issues, secrets in commits. Coordinator merges into prioritized report.

📊

Financial Reporting

Monthly reports that took analysts 2 weeks now run as overnight agentic pipeline — data collection, validation, analysis, and narrative all automated.

🎧

Customer Support Triage

Triage Agent classifies tickets → routes to specialist subagents (billing, technical, returns) → escalates to human only when confidence is low.