Monday, May 4, 2026

Claude Subagents: A Practical Guide with Working Code

 

Claude Subagents: A Practical Guide with Working Code

How to orchestrate multiple AI agents to solve complex problems faster


What Is a Subagent?

subagent is a Claude call that you launch from inside another Claude call—or from your own orchestration code—to handle a focused subtask. The parent agent breaks a problem into pieces, delegates them out, then assembles the results.

Think of it like managing a team: you don't do everything yourself. You break down the project, assign tasks to specialists who work in parallel, and integrate their outputs into a final deliverable.

In code, a subagent is just a regular Anthropic SDK call—there is no special API. What makes it a "subagent" is the pattern: parent spawns children, children return results, parent consolidates.


Why Subagents Matter

Three concrete benefits:

1. Parallel speed. If you need 4 independent analyses, running them in parallel with asyncio.gather() cuts wall-clock time from 4× to 1×.

2. Specialization. Each subagent gets a tightly focused system prompt. A "security reviewer" subagent performs better at security review than a generalist prompted with "also check security."

3. Context isolation. Subagents don't share context windows. The parent can fire off large parallel workloads without any single Claude call paying the token cost of everything combined.


The Three Core Patterns

PatternShapeWhen to use
Fan-out + ConsolidateParent → N parallel children → Parent synthesizesIndependent analyses on the same input
PipelineA → B → C (sequential)Each step needs the prior step's output
Fire and ForgetParent launches child, continues without waitingBackground tasks, non-critical enrichment

Pattern 1: Fan-out + Consolidate

Example: Reviewing a code snippet for security, style, and test coverage—simultaneously, in parallel.

Each specialist subagent receives the same code and returns a focused report. The parent synthesizes them into one final review.

                  ┌─── Security Agent ──┐
Parent (code) ───►├─── Style Agent ─────┼──► Parent (consolidates)
                  └─── Test Agent ───────┘

Full Working Script

See 01_fanout_code_review.py

The code can be seen at https://github.com/lachwaninitin/subagents-demo


Pattern 2: Pipeline

Example: Research → Draft → Edit

The output of each stage flows directly into the next. This is sequential by design—you can't draft before you research.

Research Agent → Draft Agent → Edit Agent → Final output

Full Working Script

See 02_pipeline_research.py

The code can be seen at https://github.com/lachwaninitin/subagents-demo



Pattern 3: Fire and Forget

Example: After generating a main response, kick off a background task (e.g., summarize for a log, translate to another language) without blocking the user-facing response.

Main response ──► user (immediately)
                  └──► Background agent (runs async, result logged)

Full Working Script

See 03_fire_and_forget.py

The code can be seen at https://github.com/lachwaninitin/subagents-demo



Prompt Caching with Subagents

When multiple subagents share the same large system prompt or context (e.g., all reviewing the same large document), prompt caching prevents re-paying that token cost on each call.

Mark stable content with cache_control: {"type": "ephemeral"}. The first call writes the cache; subsequent calls read from it at ~10% of the original cost.

See 04_cached_subagents.py for a working example where 4 subagents all analyze the same AIGP framework document.

The code can be seen at https://github.com/lachwaninitin/subagents-demo


Saturday, April 25, 2026

Building Your First AI Agent from Scratch

 Building Your First AI Agent from Scratch 

                                                                                              AI agents are one of the most exciting developments in software right now. But if you cut t  through the hype, the core idea is surprisingly simple. In this post, I'll explain what agents actually are, how they work, and walk you through building one from scratch in Python.                                                                                                  

What Is an AI Agent?             

An agent is an AI system that can autonomously plan, reason, and take actions to accomplish a goal — going beyond simple Q&A to actually do things on your behalf.


The key difference from a chatbot: agents take actions and iterate. A chatbot answers your question once and stops. An agent keeps working — using the result of one action to decide what to do   next — until the task is complete.                                                                  

Every agent, no matter how complex, is built on four core concepts:


  - Perceive — take in inputs: user messages, tool results, environment state                 - Reason — decide what to do next

  - Act — call a tool, run code, hit an API                                                   - Loop — repeat until the task is done



The Agent Loop


  User Input

     

  ┌─────────────────────────────────┐

    LLM thinks: "What should I do?" │

                                 

    Call a tool? → Get result      

                                 

    Call another tool? → Get result │

                                 

    Done? → Return final answer    

  └─────────────────────────────────┘

     

  Final Response


  This loop is the heartbeat of every agent. Let's build one.                                                                                                                                           

  Building the Agent


  Prerequisites


  You'll need Python installed and an Anthropic API key from https://console.anthropic.com.


  mkdir my-agent && cd my-agent

  python3 -m venv venv

  source venv/bin/activate

  pip install anthropic

  export ANTHROPIC_API_KEY="sk-ant-your-key-here"


 Step 1 Define the Tools


 Tools are regular Python functions — the agent can call them to take actions. Here we'll give our agent two abilities: a calculator and a weather lookup.                                              

   

  def calculator(operation: str, a: float, b: float) -> float:                                                                                                                                           

      ops = {                                                                                                                                                                                            

          "add": a + b,                                                                                                                                                                                  

          "subtract": a - b,                                                                                                                                                                             

          "multiply": a * b,                                                                                                                                                                             

          "divide": a / b if b != 0 else "Error: division by zero",                                                                                                                                      

      }                                                                                                                                                                                                  

      return ops.get(operation, "Unknown operation")                                                                                                                                                     

                                                                                                                                                                                                         

  def get_weather(city: str) -> str:                                                                                                                                                                     

      # In production, call a real weather API here                                                                                                                                                      

      data = {                                                                                                                                                                                           

          "london": "15°C, cloudy",                                                                                                                                                                      

          "new york": "22°C, sunny",                                                                                                                                                                     

          "tokyo": "28°C, humid",                                                                                                                                                                        

      }                                                                                                                                                                                                  

      return data.get(city.lower(), f"No weather data for {city}")                                                                                                                                       

                                                                                                                                                                                                         

Step 2 — Describe the Tools to the LLM                                                                                                                                                                 

The LLM never sees your Python functions directly. You give it a description — a menu of what's available, what each tool does, and what inputs it expects. The LLM reads this to decide when and how  

  to use each tool.

                                                                                                                                                                                                         

  TOOLS = [                                                                                                                                                                                              

      {

          "name": "calculator",                                                                                                                                                                          

          "description": "Perform basic arithmetic. Use this for any math.",                                                                                                                             

          "input_schema": {                                                                                                                                                                              

              "type": "object",                                                                                                                                                                          

              "properties": {                                                                                                                                                                            

                  "operation": {                                                                                                                                                                         

                      "type": "string",                                                                                                                                                                  

                      "enum": ["add", "subtract", "multiply", "divide"],                                                                                                                                 

                  },                                                                                                                                                                                     

                  "a": {"type": "number"},                                                                                                                                                               

                  "b": {"type": "number"},                                                                                                                                                               

              },                                                                                                                                                                                         

              "required": ["operation", "a", "b"],                                                                                                                                                       

          },                                                                                                                                                                                             

      },                                                                                                                                                                                                 

      {                                                                                                                                                                                                  

          "name": "get_weather",                                                                                                                                                                         

          "description": "Get the current weather for a city.",                                                                                                                                          

          "input_schema": {                                                                                                                                                                              

              "type": "object",                                                                                                                                                                          

              "properties": {                                                                                                                                                                            

                  "city": {"type": "string"},                                                                                                                                                            

              },                                                                                                                                                                                         

              "required": ["city"],                                                                                                                                                                      

          },                                                                                                                                                                                             

      },                                                                                                                                                                                                 

  ]                                                                                                                                                                                                      

                                                                                                                                                                                                         

Step 3 — The Tool Dispatcher                                                                               

When the LLM says "call calculator with these inputs", we need something to route that request to the right Python function. That's the dispatcher:                                                    

                  

  def run_tool(name: str, inputs: dict):                                                                                                                                                                 

      if name == "calculator":                                                                                                                                                                           

          return calculator(**inputs)                                                                                                                                                                    

      elif name == "get_weather":                                                                                                                                                                        

          return get_weather(**inputs)                                                                                                                                                                   

      else:                                                                                                                                                                                              

          return f"Unknown tool: {name}"                                                                                                                                                                 

                                                                                                                                                                                                         

Think of it as a switchboard — it takes the LLM's instruction and hands it off to the right function.                                                                                                                                                                                                

  Step 4 — The Agent Loop                                                                                  

                  

  This is where everything comes together. The loop is the agent.                                          

                  

  import anthropic                                                                                         

  client = anthropic.Anthropic()                                                                           

  def run_agent(user_message: str):                                                                                                                                                                      

  print(f"\nUser: {user_message}")

  messages = [{"role": "user", "content": user_message}]                                               

      while True:                                                                                          

          response = client.messages.create(                                                               

              model="claude-sonnet-4-6",                                                                    max_tokens=1024,

              tools=TOOLS,                                                                                  messages=messages,

          )                                                                                                                                                                                                   

          # Agent is done — return the final answer                                                     if response.stop_reason == "end_turn":

              final = next(b.text for b in response.content if hasattr(b, "text"))                         

              print(f"\nAgent: {final}")                                                                                                                                                                  return final                                                                                 

          # Agent wants to use tools                                                                       

          if response.stop_reason == "tool_use":                                                           

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

              tool_results = []                                                                            

              for block in response.content:                                                               

                  if block.type == "tool_use":                                                             

                      print(f"  [tool call] {block.name}({block.input})")                                  

                      result = run_tool(block.name, block.input)                                           

                      print(f"  [tool result] {result}")                                                   

                      tool_results.append({                                                                

                          "type": "tool_result",                                                           

                          "tool_use_id": block.id,                                                         

                          "content": str(result),                                                          

                      })                                                                                                                                                                                 

              messages.append({"role": "user", "content": tool_results})                                                                                                                                  # Loop continues — agent reasons again with new information                                                                                                                                                                                                                              

  run_agent("What's the weather in London and Tokyo? Also, what's 847 times 23?")                          

  ---             

  What Happens When You Run It

                                                                                                                                                                                                         

  User: What's the weather in London and Tokyo? Also, what's 847 times 23?

                                                                                                                                                                                                         

    [tool call] get_weather({'city': 'London'})                                                                                                                                                          

    [tool result] 15°C, cloudy                                                                                                                                                                           

    [tool call] get_weather({'city': 'Tokyo'})                                                                                                                                                           

    [tool result] 28°C, humid                                                                                                                                                                            

    [tool call] calculator({'operation': 'multiply', 'a': 847, 'b': 23})                                                                                                                                 

    [tool result] 19481.0                                                                                                                                                                                

                                                                                                                                                                                                         

  Agent: Here's what I found:                                                                                                                                                                            

  - London: 15°C, cloudy                                                                                                                                                                                 

  - Tokyo: 28°C, humid                                                                                                                                                                                   

  - 847 × 23 = 19,481                                                                                      

The agent autonomously decided which tools to call, called them in the right order, and synthesized the results into a final answer.                                                                   

How the Four Agent Concepts Map to the Code

                                                                                                           

  Perceive

                                                                                                messages = [{"role": "user", "content": user_message}]

  # ...                                                                                                                                                                                                  

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

The messages list is everything the agent knows — the user's question, tool results, conversation history. It grows every iteration. The agent perceives again after every action, which is what makes it an agent rather than a chatbot.                                                                                                                                                   

  Reason                                                                                                                                                                                    response = client.messages.create(

      model="claude-sonnet-4-6",                                                                           

      tools=TOOLS,                                                                                         

      messages=messages,                                                                        )                                                                                                                                                                                                      

  Claude looks at the full history and available tools and decides: Do I have enough to answer? (end_turn) or Do I need more information? (tool_use). The TOOLS list is the agent's awareness of its own

  capabilities.                                                                                                                                                                                          

                  

  Act                                                                                                      

                  

  result = run_tool(block.name, block.input)

  

This is the agent doing something in the world. Here it's calling a calculator. In a more powerful agent it could be browsing the web, writing and running code, sending an email, or updating a database. The pattern is identical regardless.                                                   

  Loop                                                                                                                                                                                                        

  while True:

      # reason                                                                                             

      # act                                                                                                                                                                                       # perceive result                                                                                    

      # repeat                                                                                                                                                                                           

The while True is the agent. A chatbot stops after one LLM call. An agent keeps looping until it decides it's done.                                                                                    

                                                                                                           

The One-Line Summary                                                                                                                                                                      The while True loop is the agent. Everything else is just giving it eyes (perceive), a brain (reason), and hands (act).

                                                                                                  

The full code is available above. To run it, save it as agent.py, set your ANTHROPIC_API_KEY, and run python3 agent.py.