Skip to main content

LangGraph Deep Agents Quickstart: create_deep_agent

· 9 min read
Vadim Nicolai
Senior Software Engineer

This LangGraph Deep Agents quickstart shows what one function call buys you. create_deep_agent returns a LangGraph agent that already knows how to write files, spawn subagents and summarize its own history. You add a model, a tool and a system prompt, and you have a research agent in under 30 lines. The catch is that each default has a cost and a behavior, and you should know what they are before you change them. Everything below comes from the public Deep Agents quickstart and the customization guide.

LangGraph Deep Agents, a 10-part series. ← Part 1 · Part 3 →

Series: 01 Harness · 02 Quickstart · 03 MCP tools · 04 Filesystem · 05 Sandbox · 06 Skills and memory · 07 Context · 08 Subagents · 09 Human-in-the-loop · 10 Production

How do you install LangGraph Deep Agents?​

It ships as one package:

pip install deepagents

If you use uv, uv add deepagents does the same, per the Deep Agents quickstart. The quickstart builds a research agent, so it also installs a search client (pip install tavily-python). You don't need Tavily, though. The docs also show each provider's built-in web search passed as a plain dict, such as {"type": "web_search"} for OpenAI or {"google_search": {}} for Google. Those need no extra install and no extra key.

Then export the API key for the provider you picked, under the environment variable name the quickstart gives for that provider. Add Tavily's key too if you use Tavily.

Which model strings does create_deep_agent accept?​

The model argument takes a provider:model string, so switching vendors means changing one line. The docs list these:

  • google_genai:gemini-3.6-flash
  • openai:gpt-5.5
  • anthropic:claude-sonnet-5
  • openrouter:z-ai/glm-5.2
  • fireworks:accounts/fireworks/models/glm-5p2
  • baseten:zai-org/GLM-5.2
  • ollama:north-mini-code-1.0 for a local model

You can also pass an initialized chat model object instead of a string, such as ChatAnthropic(model=...). Use that when you need to set temperature, timeouts or a custom base URL. Strings are the fast path. Objects give you control. Whichever you pick, the quickstart notes one hard requirement: deep agents need a model that supports tool calling. Every default in the harness, from file tools to subagents, is reached through tool calls, so a model without that support cannot use any of it.

The provider matters beyond price. Some later features in this series are provider-specific. Anthropic prompt caching (part 07) is one example. Built-in search dicts are another, since each one only works with its own vendor.

Build your first LangGraph deep agent​

Here is the quickstart agent end to end. It uses a plain Python function as a tool, a system prompt that tells the model what the tool is for, and a single invoke:

from typing import Literal

from tavily import TavilyClient
from deepagents import create_deep_agent

tavily_client = TavilyClient() # reads the Tavily key from the environment


def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""Run a web search"""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)


research_instructions = """You are an expert researcher. Your job is to conduct thorough research and then write a polished report.

You have access to an internet search tool as your primary means of gathering information.

## `internet_search`

Use this to run an internet search for a given query. You can specify the max number of results to return, the topic, and whether raw content should be included.
"""

agent = create_deep_agent(
model="google_genai:gemini-3.6-flash",
tools=[internet_search],
system_prompt=research_instructions,
)

result = agent.invoke({"messages": [{"role": "user", "content": "What is langgraph?"}]})
print(result["messages"][-1].content)

Three things deserve a closer look.

Tools are plain functions. In LangChain tools, type hints define a tool's input schema and the docstring becomes its description. The Literal on topic is a constraint the model can see, so type hints are free guardrails. Keep the docstring short and specific. The model reads it on every turn (part 03 covers that budget).

The system prompt is one layer of the starting context, not all of it. The Deep Agents overview lists the system prompt, memory, skills and tool prompts as what the agent starts with. Your system_prompt tells the agent what job it is doing, and the quickstart's version adds a short section describing internet_search. The built-in file and subagent tools bring their own tool prompts, so spend your prompt on the domain, not on re-explaining read_file.

The input is a messages list. invoke takes {"messages": [...]} and returns the full state. The final answer is result["messages"][-1].content. Everything before it is the trace of tool calls the agent made to get there.

What defaults does a LangGraph deep agent give you?​

This is where a deep agent differs from a bare tool loop. With nothing but a model, the customization guide describes this stack:

  • A virtual filesystem. File tools such as write_file and read_file run on a StateBackend by default. That filesystem lives in LangGraph state and is scoped to the thread. The quickstart agent uses it to offload large search results, so they don't flood the context window. Part 04 swaps in disk and store backends.
  • Subagents. The subagent middleware adds a general-purpose subagent automatically, so the agent can hand off a heavy subtask and get back only the conclusion (part 08).
  • Summarization. Long histories get compressed automatically (part 07).
  • Tool-call patching and prompt caching. These are housekeeping middleware: one repairs dangling tool calls in message history, and the prompt caching middleware is always registered but no-ops on models that don't support it.

One thing is missing that you might expect: planning with write_todos is not on by default. The quickstart says it is opt-in through TodoListMiddleware. The default stack is filesystem, subagents, summarization, tool-call patching and prompt caching, with no planner in it.

Does create_deep_agent include checkpointing and streaming?​

create_deep_agent returns a compiled LangGraph graph, so everything a LangGraph graph supports works on it as-is. That includes stream alongside invoke, and thread-scoped state through config={"configurable": {"thread_id": ...}}. The thread_id is what scopes that state. There is no separate streaming API to learn.

Persistence across calls is the one default you have to wire yourself. The checkpointer parameter defaults to None. The docs say a checkpointer is required for human-in-the-loop, and the backends page says StateBackend files persist across turns within a thread via your checkpointer. Passing one is a single argument. Part 09 builds on it with interrupt_on.

Other parameters you will meet later: subagents, skills, memory, permissions, backend, interrupt_on, response_format and store. Each one defaults to off or to the state backend, so the quickstart stays small.

How do you trace a deep agent in LangSmith?​

Tracing is set entirely through environment variables. You don't change any code.

Set four variables from the quickstart: one that turns tracing on, your LangSmith API key, your agent's id, and the agent environment, such as local.

In an agent-based LangSmith workspace, the quickstart says to create an agent once before sending traces; its identifier and environment are what the agent id and environment variables select. Run the script again and each invoke shows up as a trace, with tool calls, subagent delegation and model responses visible. Open one before you tune anything. On the "What is langgraph?" prompt you will see the search calls, any write_file used to park large results, and any subagent the agent spawned. Reading that trace is the quickest way to learn which defaults your task actually uses. It is also the evidence you need before you turn one of them off.

A sensible order for changing defaults​

  1. Get a trace with the stock harness and your one tool.
  2. Change the model string and compare traces. This is the cheapest experiment.
  3. Tighten the system prompt around the domain.
  4. Only then reach for backend, subagents or extra middleware, one at a time, with a trace before and after.

The harness exists so that you don't hand-build a planner, a scratchpad and a summarizer. Pull those defaults out because a trace shows they cost you something, not because they look unfamiliar.

FAQ​

Do I need Tavily to run the LangGraph deep agents quickstart? No. Tavily is just the example tool. You can pass a provider's built-in web search as a dict, such as {"type": "web_search"} for OpenAI. Any typed Python function with a docstring also works.

Can I use a local model with create_deep_agent? Yes. Use an Ollama model string such as ollama:north-mini-code-1.0, or pass an initialized chat model object.

Is write_todos planning enabled by default? No. According to the quickstart, structured planning with write_todos requires adding TodoListMiddleware. File tools, subagents and summarization are on by default.