> Claude Agent Patterns

Budding
planted Jan 8, 2026tended Jan 8, 2026
#ai-agents#claude#anthropic#patterns#best-practices

Claude Agent Patterns

🌿 Budding note β€” Claude-specific agent development.

Why Claude for Agents?

Claude excels at agent tasks due to:

  • Native tool use: Built-in function calling
  • Long context: 200K tokens for extensive memory
  • Reasoning: Strong chain-of-thought capabilities
  • Safety: Reduced prompt injection vulnerability
  • Extended thinking: Deep reasoning with thinking blocks

Related: AI Agents Fundamentals for core concepts

Basic Agent Loop

from anthropic import Anthropic

client = Anthropic()

def claude_agent(task: str, tools: list, max_iterations: int = 10) -> str:
    """Simple Claude agent with tool use"""
    messages = [{"role": "user", "content": task}]

    for iteration in range(max_iterations):
        response = client.messages.create(
            model="claude-sonnet-4-5-20250929",
            max_tokens=4096,
            tools=tools,
            messages=messages
        )

        # Add assistant response
        messages.append({
            "role": "assistant",
            "content": response.content
        })

        # Check if done
        if response.stop_reason == "end_turn":
            return extract_text(response)

        # Execute tools
        elif response.stop_reason == "tool_use":
            tool_results = []

            for block in response.content:
                if block.type == "tool_use":
                    result = execute_tool(block.name, block.input)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": str(result)
                    })

            messages.append({
                "role": "user",
                "content": tool_results
            })

    raise Exception("Max iterations reached")

def extract_text(response) -> str:
    """Get text from response"""
    for block in response.content:
        if hasattr(block, "text"):
            return block.text
    return ""

def execute_tool(name: str, args: dict) -> any:
    """Execute tool by name"""
    tools_map = {
        "web_search": web_search,
        "calculator": calculator,
        # Add your tools here
    }
    return tools_map[name](**args)

Extended Thinking for Complex Tasks

Use thinking blocks for deep reasoning:

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=16000,
    thinking={
        "type": "enabled",
        "budget_tokens": 10000
    },
    messages=[{
        "role": "user",
        "content": "Plan a complex software refactoring"
    }]
)

# Access thinking process
for block in response.content:
    if block.type == "thinking":
        print(f"Reasoning: {block.thinking}")
    elif block.type == "text":
        print(f"Response: {block.text}")

When to use extended thinking:

  • Complex reasoning tasks
  • Planning and strategizing
  • Code analysis and debugging
  • Mathematical proofs

Prompt Caching for Efficiency

Cache system prompts and tools:

response = client.messages.create(
    model="claude-sonnet-4-5-20250929",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "You are an expert coding assistant...",
            "cache_control": {"type": "ephemeral"}
        }
    ],
    tools=tools,  # Tools are automatically cached
    messages=messages
)

# Cached content reused across requests
# Significant cost savings for repeated agent loops

Benefits:

  • Reduce latency (cached content processes faster)
  • Lower costs (5 minute cache window)
  • Ideal for agent loops with stable system prompts

Tool Use Patterns

Structured Tool Output

tools = [
    {
        "name": "analyze_code",
        "description": "Analyze code for issues",
        "input_schema": {
            "type": "object",
            "properties": {
                "code": {"type": "string"},
                "language": {"type": "string"}
            },
            "required": ["code", "language"]
        }
    }
]

# Claude returns structured calls
# {
#     "name": "analyze_code",
#     "input": {
#         "code": "def foo():\n  pass",
#         "language": "python"
#     }
# }

Tool Chaining

Claude naturally chains tools:

# Task: "Find weather in user's location and convert to Fahrenheit"
# Claude will:
# 1. Call get_user_location()
# 2. Call get_weather(location)
# 3. Call convert_temperature(celsius, "fahrenheit")
# 4. Respond with result

Error Handling

def execute_tool_safely(name: str, args: dict) -> dict:
    """Execute with error context"""
    try:
        result = tools[name](**args)
        return {
            "success": True,
            "result": result
        }
    except Exception as e:
        return {
            "success": False,
            "error": str(e),
            "suggestion": f"Try checking the {name} parameters"
        }

# Return error details to Claude
tool_result = {
    "type": "tool_result",
    "tool_use_id": tool_use_id,
    "content": json.dumps(execute_tool_safely(name, args)),
    "is_error": not result["success"]
}

Related: Tool Use and Function Calling

Memory Management

Conversation Summarization

async def summarize_if_needed(messages: list, client) -> list:
    """Compress old messages when context gets full"""
    if estimate_tokens(messages) > 150000:  # Leave room for response
        # Summarize first half
        to_summarize = messages[:len(messages)//2]

        summary = await client.messages.create(
            model="claude-sonnet-4-5-20250929",
            max_tokens=2000,
            messages=[{
                "role": "user",
                "content": f"Summarize this conversation:\n{to_summarize}"
            }]
        )

        # Keep summary + recent messages
        return [
            {"role": "user", "content": f"[Previous conversation summary: {summary.content[0].text}]"},
            *messages[len(messages)//2:]
        ]

    return messages

Long-Term Memory with RAG

from qdrant_client import QdrantClient

class ClaudeAgentWithMemory:
    def __init__(self):
        self.client = Anthropic()
        self.vector_store = QdrantClient(":memory:")

    async def process_with_memory(self, query: str):
        # Recall relevant memories
        relevant = self.vector_store.search(query, limit=5)

        # Build context
        memory_context = "\n".join([
            f"- {mem.payload['text']}" for mem in relevant
        ])

        # Query with memory
        response = await self.client.messages.create(
            model="claude-sonnet-4-5-20250929",
            max_tokens=4096,
            system=f"Relevant past context:\n{memory_context}",
            messages=[{"role": "user", "content": query}]
        )

        # Store new memory
        await self.store_memory(f"Q: {query}\nA: {response.content[0].text}")

        return response

Related: Agent Memory Systems

Self-Reflection Pattern

Claude critiques its own work:

async def self_reflecting_agent(task: str):
    """Agent reflects on and improves its work"""
    # Initial attempt
    response = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{"role": "user", "content": task}]
    )

    initial_work = response.content[0].text

    # Self-critique
    critique = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Review this work and provide critique:
Task: {task}
Work: {initial_work}

What could be improved? Be specific and constructive."""
        }]
    )

    critique_text = critique.content[0].text

    # Revise based on critique
    revised = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Improve this work based on the critique:
Original: {initial_work}
Critique: {critique_text}

Provide the improved version."""
        }]
    )

    return revised.content[0].text

Multi-Turn Workflows

Complex tasks need planning:

async def planning_agent(goal: str):
    """Agent plans then executes"""
    # Planning phase
    plan = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Break down this goal into steps:
Goal: {goal}

Create a detailed step-by-step plan."""
        }]
    )

    steps = parse_steps(plan.content[0].text)

    # Execution phase
    results = []
    for step in steps:
        result = await execute_step(step)
        results.append(result)

    # Synthesis
    final = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{
            "role": "user",
            "content": f"""Synthesize these results:
Goal: {goal}
Results: {results}"""
        }]
    )

    return final.content[0].text

Safety Patterns

Sandboxed Tool Execution

def safe_code_execution(code: str) -> str:
    """Execute with restrictions"""
    # Use restricted Python environment
    import RestrictedPython

    compiled = RestrictedPython.compile_restricted(
        code,
        filename='<agent_code>',
        mode='exec'
    )

    safe_globals = {
        '__builtins__': safe_builtins,
        'print': safe_print,
        # Limited standard library
    }

    exec(compiled, safe_globals)

Human-in-the-Loop

async def human_approval_agent(task: str):
    """Require approval for sensitive actions"""
    response = await client.messages.create(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        tools=tools,
        messages=[{"role": "user", "content": task}]
    )

    # Check for sensitive tools
    for block in response.content:
        if block.type == "tool_use" and is_sensitive(block.name):
            print(f"Agent wants to: {block.name}({block.input})")
            approval = input("Approve? (yes/no): ")

            if approval.lower() != "yes":
                return "Action rejected by human"

    # Proceed if approved
    return await execute_with_approval(response)

Related: Agent Security Considerations

Performance Optimization

Batched API Calls

Use message batches API:

# Create batch of requests
requests = [
    {
        "custom_id": f"request-{i}",
        "params": {
            "model": "claude-sonnet-4-5-20250929",
            "max_tokens": 1024,
            "messages": [{"role": "user", "content": task}]
        }
    }
    for i, task in enumerate(tasks)
]

# Submit batch
batch = client.batches.create(requests=requests)

# Check status later
while batch.processing_status != "ended":
    await asyncio.sleep(10)
    batch = client.batches.retrieve(batch.id)

# Get results
for result in batch.results:
    print(result.result.message.content[0].text)

Streaming Responses

def streaming_agent(task: str):
    """Stream responses for better UX"""
    with client.messages.stream(
        model="claude-sonnet-4-5-20250929",
        max_tokens=4096,
        messages=[{"role": "user", "content": task}]
    ) as stream:
        for text in stream.text_stream:
            print(text, end="", flush=True)

Connection Points

Prerequisites:

Related:

Advanced:

>> referenced by (8)

Agent Frameworks Comparison
...g Claude-specific features - Simple agent that doesn't need framework Related: [[Claude Agent Patterns]] LlamaIndex Best for: RAG + agents, document-heavy workflows Overv...
Agent Memory Systems
...t basics - [[Tool Use and Function Calling]] β€” Memory as a tool Related: - [[Claude Agent Patterns]] β€” Claude-specific memory handling - [[Agent Security Considerations]] β€” Memory...
Agent Security Considerations
...ag in response.lower(): return False return True `` Related: [[Claude Agent Patterns]] for Claude's built-in protections Tool Security Least Privilege ``py...
AI Agents
...ecific - [[Building Agents with LangChain]] 🌿 β€” LangChain development guide - [[Claude Agent Patterns]] 🌿 β€” Best practices for Claude - [[Agent Frameworks Comparison]] 🌿 β€” LangChain...
AI Agents Fundamentals
...Open-source alternative - Gemini Pro: Google's multimodal option Related: [[Claude Agent Patterns]] for Claude-specific best practices 2. Memory Systems Agents need memory t...
Building Agents with LangChain
...basics - [[Agent Frameworks Comparison]] β€” Framework comparison Related: - [[Claude Agent Patterns]] β€” Using Claude with LangChain - [[Tool Use and Function Calling]] β€” Tool patter...
Production Agent Deployment
...uction Related: - [[Agent Frameworks Comparison]] β€” Framework deployment - [[Claude Agent Patterns]] β€” Claude optimization - [[Agent Memory Systems]] β€” Production memory **Testing...
Tool Use and Function Calling
...tools=tools, messages=messages ) ``` Related: [[Claude Agent Patterns]] for Claude-specific patterns Tool Definition Best Practices 1. Clear D...