Architecting Complex Agents with Deep Agents

BYEden Marco
LangChainAI AgentsDeepAgentsLangGraphResearch Agent

This blog post was inspired by:


TLDR

Building AI agents for complex, multi-step tasks is hard. While simple tool-using agents are common, robust systems that can plan, delegate, and manage files are difficult to engineer.

Deep Agents, an open-source framework from LangChain, solves this. It gives you a pre-built structure based on advanced agent patterns. You can focus on your specific problem, not the core infrastructure.

Core Concepts

  • Atomic Built-in Tools: Deep Agents provides a foundation of tools for filesystem access, shell commands, and to-do list management. These allow the agent to maintain state and perform computer-based tasks reliably.
  • Planning First: The harness includes planning tools like write_todos and read_todos. This forces the agent to decompose complex problems into manageable steps before execution.
  • Sub-Agent Delegation: Use the native task tool to delegate complex jobs to specialized sub-agents. This creates a hierarchical agent architecture that improves modularity and isolates context.
  • Deep Customization: Extend the framework with your own tools, system prompts, and custom sub-agents.
  • Workflow as Prompts: You control the agent's behavior with detailed instructions. These prompts guide the agent on when and how to use its tools, turning your workflow into a prompt.
  • LangGraph Middleware: Deep Agents uses LangGraph's middleware system for its built-in tools and for features like automatic context summarization.
  • Secure Operations: All filesystem actions happen in a secure, in-memory LangGraph state object. This prevents side effects on your local system and improves reliability.

In-Depth Analysis

Deep Agents provides a structured way to build complex agents. It gives you a foundation of general capabilities so you can build specific, high-performing agents on top. This guide shows you how to build a research agent.

The Deep Agent Architecture

A Deep Agent coordinates an LLM's interaction with a set of tools. This structure implements a hierarchical agent architecture. The main Deep Agent acts as a supervisor, orchestrating sub-agents through delegation. This approach enables scalable task decomposition for complex workflows, addressing the limitations of single-agent systems by distributing specialized roles.

Loading Visual...

The LLM gets a set of built-in, atomic tools for basic computer access. These include:

  • Planning: write_todos, read_todos
  • Filesystem: list_files, read_file, write_file, edit_file, glob, grep
  • Execution: execute_shell_commands
  • Delegation: task (for invoking sub-agents)

This toolset is intentional. It gives the supervisor agent core abilities to plan, manage files, and delegate work. This design improves reliability and performance by breaking down large problems into smaller, manageable parts.

Building a Research Agent: A Practical Walkthrough

This guide uses the "Deep Agent Quickstart" repository to show you how to build a research agent. You will customize three key areas: tools, instructions, and sub-agents.

Step 0 : Setting Up Your Environment

First, set up your project. The quickstart uses uv, a Python package installer. Clone the repository and run the following commands.

Install uv:

curl -LsSf https://astral.sh/uv/install.sh | sh

Navigate into the research example directory and sync the dependencies:

cd deep_research
uv sync

Next, configure your API keys. Create a .env file or export them in your shell. The default model is Claude, which requires an Anthropic key. You also need keys for Tavily (web search) and LangSmith (tracing).

export ANTHROPIC_API_KEY=your_anthropic_api_key_here
export TAVILY_API_KEY=your_tavily_api_key_here
export LANGSMITH_API_KEY=your_langsmith_api_key_here

Step 1: Defining Custom Tools

Most tasks require custom tools beyond the built-in set. This research agent needs a web search tool and a tool to enforce structured thinking, defined in a tools.py file.

from research_agent.tools import tavily_search, think_tool
tools = [tavily_search, think_tool]

The think_tool improves agent reliability. It does not perform an external action. Instead, it forces the agent to pause and state its reasoning. This helps you audit and debug the agent's behavior. Using a dedicated tool makes this reflection process model-agnostic and controllable.

Step 2: Crafting a Robust System Prompt

Your system prompt is critical for agent performance. Deep Agents lets you provide detailed instructions to guide the agent. The example research agent combines several prompts into one.

from utils import show_prompt, format_messages
from research_agent.prompts import (
    RESEARCHER_INSTRUCTIONS,
    RESEARCH_WORKFLOW_INSTRUCTIONS,
    SUBAGENT_DELEGATION_INSTRUCTIONS,
)
from datetime import datetime
 
max_concurrent_research_units = 3
max_researcher_iterations = 3
current_date = datetime.now().strftime("%Y-%m-%d")
 
INSTRUCTIONS = (
    RESEARCH_WORKFLOW_INSTRUCTIONS
    + "\n\n"
    + "=" * 80
    + "\n\n"
    + SUBAGENT_DELEGATION_INSTRUCTIONS.format(
        max_concurrent_research_units=max_concurrent_research_units,
        max_researcher_iterations=max_researcher_iterations,
    )
)
 
show_prompt(INSTRUCTIONS)

This combined prompt uses several strategies:

  1. Workflow Instruction: Tell the agent how to use its tools in a specific order. For example, instruct it to first save the user's request with write_file. This "recitation" technique creates a record the agent can reference to stay on task.
  2. Delegation Strategy: Define clear rules for when to delegate tasks to sub-agents. This prevents the LLM from creating too many sub-agents for a single task.
  3. Heuristics to Prevent "Spin-out": Add rules to prevent the agent from getting stuck in loops. Set limits on tool calls and define clear stopping conditions.
  4. Enforced Thinking: Instruct the agent to use the think_tool at key moments to record its reasoning in the execution trace.

Step 3: Implementing Sub-Agents for Context Isolation

Research tasks can fill an LLM's context window. Sub-agents solve this with context isolation.

A sub-agent is a temporary agent focused on one specific task. It runs its own loop, collects information, and returns a final summary to the parent agent. In the hierarchical model, the parent (or supervisor) uses this mechanism to keep its own context clean and focused on high-level coordination.

Here is how you define a research sub-agent:

# Create research sub-agent
research_sub_agent = {
    "name": "research-agent",
    "description": "Delegate research to the sub-agent researcher. Only give this researcher one topic at a time.",
    "system_prompt": RESEARCHER_INSTRUCTIONS.format(date=current_date),
    "tools": [tavily_search, think_tool],
}

This dictionary defines the sub-agent's name, description, instructions, and available tools.

Step 4: Assembling and Running the Agent

Once you define your tools, prompts, and sub-agents, you can create the main agent with the create_deep_agent function.

from IPython.display import Image, display
from langchain.chat_models import init_chat_model
from deepagents import create_deep_agent
 
# Model
model = init_chat_model(model="anthropic:claude-sonnet-4-5-20250929", temperature=0.0)
 
# Create the agent
agent = create_deep_agent(
    model=model,
    tools=[tavily_search, think_tool],
    system_prompt=INSTRUCTIONS,
    subagents=[research_sub_agent],
)
 
# Show the agent
display(Image(agent.get_graph().draw_mermaid_png()))

This function handles the complex setup. It assembles the LangGraph graph, adds middleware, and compiles the agent.

Under the Hood: LangGraph and Middleware

Deep Agents is built on LangGraph. LangGraph helps you build stateful, multi-agent applications. The agent's execution is a state machine, not a simple loop. Technically, it leverages LangGraph's StateGraph to define the workflow, where agents and tools are nodes and the transitions between them are edges. The supervisor agent uses LLM-driven routing to manage these transitions and hand off tasks.

Loading Visual...

Core features and built-in tools come from middleware. Middleware acts as hooks that intercept the agent's execution loop to add functionality. Key middleware includes:

  • ToDoListMiddleware & FileSystemMiddleware: Provides write_todos and file tools. Manages the agent's state for planning and file access.
  • SubAgentMiddleware: Provides the task tool. Manages sub-agent creation and execution.
  • SummarizationMiddleware: Summarizes conversation history automatically when the context window is nearly full. This is critical for long-running tasks.
  • PatchToolCallsMiddleware: Fixes common malformed tool calls from the LLM.
  • HumanInTheLoopMiddleware: Pauses execution to wait for your approval before continuing.

Execution and Deployment

After creating the agent, invoke it with your request.

result = agent.invoke(
    {
        "messages": [
            {
                "role": "user",
                "content": "Give me an overview of context engineering.",
            },
        ],
    }
)
 
format_messages(result["messages"])

The agent follows your prompt's instructions. It saves the request, creates a plan, and delegates research to a sub-agent using the task tool. The sub-agent runs its search and returns a report.

The Final Step: Verification the agent doesn't just finish when the report is written. A key part of the workflow is the Self-Correction Loop: the agent is instructed to open the original research_request.md and compare it to the final_report.md. It asks itself: "Did I actually answer every part of the user's question?" If not, it continues to work. This significantly reduces hallucinations and incomplete answers.

Building deep agents is an iterative process. Using LangSmith, you can visualize the agent's "thought process" in real-time. This includes:

Tracing: Seeing exactly which tool was called and what it returned.

Cost Monitoring: Tracking how many tokens each research step consumes.

Debugging: Identifying where an agent might get stuck in a "spin-out" loop and adjusting your system prompt accordingly.

The Quickstart repository shows two ways to run the agent. You can run it in a Jupyter Notebook for development. You can also deploy it as a service with the langgraph dev command. This command starts a local server with a web UI.

END OF WORKSPACE