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.



