The short version: A deep agent isn't a new architecture — it's a plain tool-calling loop with four batteries bolted on: an explicit planner, a virtual filesystem to offload context, subagents to delegate, and automatic context management. LangChain's open-source deepagents package hands you all four behind a single factory function, built on LangGraph so you inherit streaming, persistence, and checkpointing for free. Below: a working research agent in about fifteen lines, then how to add your own subagent.

Install and the minimal agent#

pip install deepagents      # or: uv add deepagents

The whole surface is create_deep_agent. Give it a model, your own tools, and a system prompt; it adds the planning and filesystem tools itself and returns a LangGraph agent you invoke like any other.

from deepagents import create_deep_agent

def web_search(query: str) -> str:
    """Search the web and return top results."""
    ...  # your real search implementation

agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    tools=[web_search],
    system_prompt="You are a thorough research assistant. "
                  "Plan first, then research, then write.",
)

result = agent.invoke({"messages": "Research CrewAI Flows and write a brief."})
print(result["messages"][-1].content)

That's a complete deep agent. You wrote one tool and a prompt; the harness supplied the rest.

Battery 1 — the planner (write_todos)#

The first thing a deep agent does on a non-trivial task is call the built-in write_todos tool to lay out a plan, then update it as steps complete. This isn't cosmetic: an explicit, re-readable todo list is what keeps a long task from drifting — the agent can look at what's done and what's left instead of re-deriving it from a bloated message history every turn. You don't add this tool; it's part of the harness.

Battery 2 — the virtual filesystem#

This is the capability that most changes how the agent behaves. deepagents exposes ls, read_file, write_file, edit_file, glob, and grep over a store that lives in the agent's state — in-memory by default, pluggable to a real backend.

The point is context offloading. Instead of carrying a 4,000-token search dump in the prompt for the rest of the run, the agent writes it to research/raw.md and reads back only the parts it needs later. The context window stays small; the "memory" lives in files. It's the same instinct we covered in context offloading for AI agents — deepagents just makes it a first-class, always-present tool surface rather than something you bolt on.

Battery 3 — subagents (delegation with isolated context)#

The main loop shouldn't do everything itself — a deep research task might spawn a "critic" or a per-source "reader" so the main thread stays clean. That's the subagents parameter: a list of dicts, each a small agent the main one can call.

research_subagent = {
    "name": "source-reader",
    "description": "Reads one source URL and extracts the key claims.",
    "prompt": "You read a single source and return only verified claims, "
              "each with a one-line quote. Be skeptical.",
    "tools": [web_search],          # optional: scope its tools
    # "model": "...", "middleware": [...]  # both optional
}

agent = create_deep_agent(
    model="anthropic:claude-sonnet-5",
    tools=[web_search],
    system_prompt="Delegate each source to source-reader, then synthesize.",
    subagents=[research_subagent],
)

Each subagent runs with its own isolated context and returns only its result to the parent. That isolation is the value: the messy intermediate work of reading five sources never pollutes the main agent's window — it gets five clean summaries back. A declarative subagent doesn't inherit filesystem access by default; give it a FilesystemMiddleware in its own middleware field if it needs to read or write files independently.

Battery 4 — context management#

For long threads, the harness summarizes older turns automatically so the conversation doesn't overflow the window mid-task. Combined with the filesystem (durable results live in files) and subagents (heavy work happens off the main thread), it's what lets a deep agent run for many steps without falling over — the difference between an agent that handles a ten-step task and one that forgets step two by step eight.

When to use it — and when not to#

deepagents is the opinionated, batteries-included layer over LangChain's lower-level create_agent. Reach for it when you want planning, a filesystem, delegation, and context management assembled for you. Drop to raw LangGraph when you need a bespoke control-flow graph or custom state channels the harness doesn't express. The harness-vs-graph choice is the same axis the whole field is arguing over right now — deepagents is a clean way to take the harness side without building the harness. If you'd rather run the same shape on a different stack, the pattern ports: see deep agents on Pydantic AI.