Build a Deep Research Agent
Build an autonomous research agent that plans, searches, and writes reports.
Introduction
30 Second Summary
When you research a topic online, you naturally break it into subtopics, open multiple tabs, jot down notes, and then pull everything together into a coherent summary.
In this project, you will build a Deep Research Agent that does exactly this process autonomously. It plans its research, searches the web, saves findings, delegates subtasks to a specialized sub-agent, and synthesizes everything into a polished Markdown report.
What You'll Build
A Deep Research Agent powered by LangChain's deepagents library and Google Gemini that autonomously researches any topic and produces a structured report.
By the end of this project, you'll have:
- A Python project scaffolded with uv containing a fully functional deep research agent.
- A custom internet_search tool powered by Tavily for real-time web search.
- Hands-on experience with all three pillars of Deep Agents: planning, context management, and sub-agent delegation.
- A generated Markdown research report produced entirely by your agent, with an optional structured output upgrade in the Secret Mission.
Are there any prerequisites?
You should be familiar with Python basics and have a general understanding of LLM concepts like prompts, tokens, and tool calling. You will also need a Google Gemini API key and VS Code installed on your machine.
Set Up Your Project and API Keys
Before you can build a deep research agent, you need a Python project with the right packages and API keys configured.
In this step, you will scaffold a project using uv, install the deepagents library and its dependencies, and wire up your API keys so everything is ready to go.
In This Step, Get Ready To:
- Scaffold a Python project with uv and install all required packages.
- Get a free Tavily API key and configure your environment variables.
- Verify your setup loads correctly by running a test script.
Scaffold the Project With uv
uv is a fast Python package manager that creates a virtual environment and manages dependencies for you. You will use it to initialize a new project and install four key packages.
- Open your terminal.
- Run the following commands to create your project directory, initialize it, and install dependencies.
mkdir deep-research-agent
cd deep-research-agent
uv init
uv add deepagents tavily-python langchain-google-genai python-dotenv
uv sync
What do these packages do?
- deepagents: LangChain's deep agent harness with built-in planning, file system, and sub-agent tools.
- tavily-python: Web search API client. The free tier gives you 1,000 API credits per month.
- langchain-google-genai: Google Gemini integration for LangChain.
- python-dotenv: Loads API keys from a .env file so you don't hardcode secrets.
The uv init command creates a pyproject.toml and a .venv virtual environment. The uv add command registers your dependencies, and uv sync installs everything into the virtual environment.
Get Your Tavily API Key and Configure Environment Variables
Your agent needs two API keys to function: one for Google Gemini (the LLM that powers the agent's reasoning) and one for Tavily (the web search tool). You already have your Google API key. Now you need to get a Tavily key.
- Go to tavily.com and sign up for a free account.
- No credit card is required. The free tier includes 1,000 API credits per month.
- Copy your API key from the Tavily dashboard.
Now create a .env file in your project root to store both keys.
GOOGLE_API_KEY=your-google-api-key-here
TAVILY_API_KEY=your-tavily-api-key-here
What should I put in the .env file?
Replace your-google-api-key-here with your actual Google API key and your-tavily-api-key-here with the Tavily key you just copied. Never commit this file to version control.
Next, create a main.py file to verify that your setup loads correctly.
- Create a new file called main.py in your project root.
- Add the following code.
import os
from dotenv import load_dotenv
load_dotenv()
print("Google API Key loaded:", bool(os.environ.get("GOOGLE_API_KEY")))
print("Tavily API Key loaded:", bool(os.environ.get("TAVILY_API_KEY")))
What does this script do?
This script loads your .env file into environment variables using python-dotenv. It then checks whether each key exists and prints True or False. This confirms your keys are accessible without printing the actual secrets.
- Run the script with the following command.
uv run python main.py
I see True for both keys
Your environment is configured correctly. Both API keys are loaded and ready to use.
I see False for one or both keys
If either key shows False, double-check the following:
- Make sure the .env file is in the same directory as main.py.
- Confirm there are no extra spaces around the = sign in your .env file.
- Verify that the variable names are exactly GOOGLE_API_KEY and TAVILY_API_KEY.
Still stuck? Help me troubleshoot my .env file.
Open the Project in VS Code
- Open VS Code.
- Open the deep-research-agent folder.
- Make sure the Python interpreter is set to the .venv created by uv. You can check this by pressing Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows) and selecting Python: Select Interpreter.
Pro tip: check your project structure
Your project should now contain a pyproject.toml, a .venv directory, a .env file, and main.py. If you see all four, you are ready to start building your agent.
Your project is scaffolded and your API keys are working. Next up, you will create your first deep agent and see the built-in planning and file system tools in action.
Create a Basic Deep Agent
Your project is set up and your API keys are loaded. Now it is time to create your first Deep Agent and see what makes it different from a regular LLM call.
A Deep Agent is not just a model that responds to prompts. It is an autonomous system that can plan tasks, manage its own context through a virtual file system, and delegate work to sub-agents. In this step, you will build a minimal Deep Agent, give it a simple tool, and then watch it use its built-in planning and file system capabilities without any extra configuration.
In This Step, Get Ready To:
- Build and run your first Deep Agent using create_deep_agent with Google Gemini.
- Observe the agent's built-in planning and file system tools in action.
- Inspect the virtual file system to see how the agent organizes its work.
Build Your First Deep Agent With Gemini
The create_deep_agent function is the entry point for building a Deep Agent. You give it a model, a list of custom tools, and a system prompt. Behind the scenes, it wraps LangGraph orchestration and automatically attaches built-in tools for planning, file system access, and sub-agent spawning.
- Replace the contents of main.py with the following code:
import os
from dotenv import load_dotenv
from deepagents import create_deep_agent
load_dotenv()
# A simple custom tool for the agent to call
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
# Create the deep agent with Gemini as the LLM
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
tools=[get_weather],
system_prompt="You are a helpful assistant.",
)
# Invoke the agent with a user message
result = agent.invoke(
{"messages": [{"role": "user", "content": "What is the weather in Tokyo?"}]}
)
# Print the agent's final response
print(result["messages"][-1].content)
What does this code do?
This code creates a Deep Agent powered by Google Gemini. The model string google_genai:gemini-3.1-pro-preview uses the provider:model format that Deep Agents use to identify which LLM provider and model to use.
The get_weather function is a toy tool. Deep Agents read the function's type hints and docstring to generate the tool schema that the LLM sees. The agent decides when to call this tool based on the user's message.
- Run the agent:
uv run python main.py
The agent should call the get_weather tool and respond with a message about Tokyo's weather.
What are the built-in tools?
Even though you only passed get_weather as a custom tool, your agent already has access to much more. create_deep_agent automatically attaches three categories of built-in tools:
- Planning: write_todos for breaking tasks into steps with statuses like pending, in_progress, and completed.
- File system: read_file, write_file, edit_file, ls, glob, and grep for managing a virtual file system.
- Sub-agent delegation: task for spawning specialized sub-agents.
Seeing an authentication error?
Make sure your .env file is in the same directory as main.py and contains a valid GOOGLE_API_KEY. The key must have access to the Gemini API. Help me debug my API key error.
Observe the Built-In Planning and File System
The toy weather tool proved your agent works. Now let's give it a more complex task to see the built-in planning and file system tools activate automatically.
- Change the user message in main.py to a multi-step task:
result = agent.invoke(
{"messages": [{"role": "user", "content": "Create a plan for researching the history of artificial intelligence, then write a brief summary."}]}
)
print(result["messages"][-1].content)
What does this command do?
This prompt asks the agent to do two things: plan and then write. Even without a search tool that returns real data, the agent will use write_todos to break down the task into steps and write_file to save its work. These tools activate automatically because they are part of the Deep Agent's default middleware.
- Run the updated script:
uv run python main.py
The agent's response should be noticeably more structured than a plain LLM response. It planned its approach before writing.
Inspect the Agent's Virtual File System Output
Deep Agents use an ephemeral virtual file system powered by the StateBackend. This means files exist only in memory during execution. After the agent finishes, you can access everything it wrote through the result["files"] dictionary.
- Add the following code after the print statement in main.py:
# Inspect the virtual file system
print("\n--- Files created by the agent ---")
if result.get("files"):
for filename, content in result["files"].items():
print(f"\nFile: {filename}")
print(f"Content preview: {content[:300]}...")
else:
print("No files were created.")
What does this code do?
This iterates over the result["files"] dictionary, which contains every file the agent wrote during execution. Each key is a filename and each value is the file's content. This lets you see exactly how the agent organized its research and output.
- Run the script one more time:
uv run python main.py
You should see the agent's response followed by a list of files it created. The agent may have written a summary file, notes, or other artifacts depending on how it chose to break down the task.
Why is the virtual file system important?
LLMs have a limited context window. If an agent tries to hold all of its research in the conversation history, it quickly runs out of space. The virtual file system solves this by letting the agent write findings to files and read them back later when needed. This is one of the three pillars that makes Deep Agents effective for long-running, multi-step tasks.
Your basic Deep Agent is working. It can call custom tools, plan with write_todos, and persist its work to the virtual file system. Next up, you will replace the toy weather tool with a real web search tool powered by Tavily so your agent can gather actual information from the internet.
Add Web Search with Tavily
Your basic deep agent can plan and write files, but it has no way to gather real information from the internet. A research agent without web access is like a librarian locked outside the library. In this step, you'll replace the toy get_weather tool with a real web search tool powered by Tavily, then craft a system prompt that turns your agent into an expert researcher.
In This Step, Get Ready To:
- Build a custom internet_search tool using the Tavily API.
- Write a research-focused system prompt that guides the agent's workflow.
- Run the agent on a real research question and inspect its output files.
Create the internet_search Tool
Deep Agents discover your custom tools through their function signatures and docstrings. The LLM reads the function's name, type hints, and docstring to understand when and how to call it. A clear, descriptive signature is what makes the difference between a tool the agent uses correctly and one it ignores.
- Open main.py and replace its entire contents with the following code:
import os
from typing import Literal
from dotenv import load_dotenv
from tavily import TavilyClient
from deepagents import create_deep_agent
load_dotenv()
# Initialize the Tavily client with your API key
tavily_client = TavilyClient(api_key=os.environ["TAVILY_API_KEY"])
def internet_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news", "finance"] = "general",
include_raw_content: bool = False,
):
"""Run a web search to find information on any topic."""
return tavily_client.search(
query,
max_results=max_results,
include_raw_content=include_raw_content,
topic=topic,
)
What does this code do?
This creates a TavilyClient that connects to the Tavily search API. The internet_search function wraps the client's search method with typed parameters. The Literal type hint on topic tells the LLM exactly which values are valid, so it won't hallucinate unsupported options.
Why Tavily for Web Search?
Tavily is the default search provider in the official deepagents quickstart. It has a generous free tier of 1,000 API credits per month with no credit card required. Other search APIs like SerpAPI or Brave Search are also supported, but Tavily has the most documented integration path with LangChain.
Write a Research-Focused System Prompt
The system prompt is how you shape your agent's behavior. Without specific instructions, the agent will use its tools randomly. A well-structured system prompt gives the agent a clear workflow to follow: plan first, research systematically, then synthesize.
- Add the following system prompt below the internet_search function in main.py:
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.
## Research workflow
1. First, create a plan using your planning tools to break down the research task.
2. Research systematically, saving important findings to files.
3. Synthesize your findings into a well-structured Markdown report.
4. Save the final report to a file called `report.md`.
"""
Why describe the tool inside the system prompt?
The LLM already sees the tool's function signature and docstring automatically. The system prompt description adds strategic guidance about when and how to use the tool in the context of the research workflow. The numbered workflow section is especially important because it tells the agent to plan before searching, which produces more organized results.
Run Your Research Agent on a Real Topic
Now it's time to wire everything together and watch your agent conduct real research. You'll create the agent with your custom tool and system prompt, then give it a research question that requires multiple searches.
- Add the following code to the bottom of main.py:
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
tools=[internet_search],
system_prompt=research_instructions,
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Research the current state of quantum computing in 2026. What are the latest breakthroughs and which companies are leading?"}]}
)
# Print the agent's final response
print(result["messages"][-1].content)
# Print all files the agent created
print("\n--- Files created by the agent ---")
for filename, content in result["files"].items():
print(f"\nFile: {filename}")
print(content[:500])
- Run the agent:
uv run python main.py
This will take a minute or two as the agent works through its research workflow. Behind the scenes, the agent is automatically using write_todos to plan its approach, calling internet_search multiple times, saving findings with write_file, and synthesizing everything into a report.
What should I see?
You should see the agent's final response summarizing its research, followed by a list of files it created. Look for a file called report.md in the output. The agent may also create intermediate files where it saved raw search results or notes.
What is the virtual file system?
The files in result["files"] live in an ephemeral, in-memory file system called the StateBackend. They are not written to your actual disk. This virtual file system is one of the three pillars of Deep Agents. It lets the agent offload information from its context window into files, keeping the context clean for reasoning while still being able to retrieve details later with read_file.
Getting an API error from Tavily?
Double-check that your TAVILY_API_KEY is correctly set in your .env file with no extra spaces or quotes around the value. Also confirm you haven't exceeded the free tier limit of 1,000 API credits per month. Help me debug my Tavily API connection.
Your agent can now plan, search the web, and organize its findings into files. Next up, you'll add a specialized sub-agent that handles deep dives into specific subtopics, keeping the main agent's context clean.
Add a Specialized Sub-Agent
Your research agent can now plan tasks, search the web, and save findings to its virtual file system. That covers two of the three Deep Agents pillars. The third pillar is sub-agent delegation. When a research topic has multiple subtopics, the main agent's context window fills up with details from every area. Sub-agents solve this by handling focused research in their own isolated context, then returning only a summary to the main agent.
In This Step, Get Ready To:
- Define a specialized sub-agent for focused research tasks.
- Attach the sub-agent to your main research agent.
- Test delegation with a complex, multi-part research question.
Define a Sub-Agent for Focused Research
A sub-agent is a separate agent that the main agent can spawn using the built-in task tool. The sub-agent gets its own clean context window, does its work, and returns only a final result. This keeps the main agent's context uncluttered while still producing detailed research.
You define a sub-agent as a Python dictionary with four keys.
- Add the following sub-agent definition to your main.py, after the internet_search function and before the create_deep_agent call.
research_subagent = {
"name": "deep-dive-researcher",
"description": "Used to do a deep dive into a specific subtopic. Delegates focused research to avoid cluttering the main agent's context.",
"system_prompt": "You are a specialist researcher. You will be given a specific subtopic to research in depth. Use the internet_search tool to find detailed information. Write a thorough analysis and save it to a file before responding with your findings.",
"tools": [internet_search],
}
What makes this a sub-agent instead of just another tool?
A tool runs a single function and returns a result. A sub-agent is a full agent with its own reasoning loop. It can call tools multiple times, plan its own approach, and write files. The key benefit is context isolation. All the detailed search results and intermediate reasoning stay inside the sub-agent's context window. The main agent only receives the final summary, keeping its own context clean for higher-level synthesis.
Why delegation instead of doing everything in one agent?
When a single agent handles every subtopic, its context window fills up with raw search results, intermediate notes, and reasoning traces from all areas. This leads to less focused analysis and can hit token limits on longer research tasks. Sub-agents let the main agent act as a coordinator, delegating deep dives while staying focused on the big picture.
Attach the Sub-Agent to Your Main Agent
The create_deep_agent function accepts a subagents parameter. When you pass your sub-agent dictionary here, the main agent automatically gets access to a task tool that it can use to spawn the sub-agent.
- Update your create_deep_agent call to include the subagents parameter.
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
tools=[internet_search],
system_prompt=research_instructions,
subagents=[research_subagent],
)
- Next, update your research_instructions system prompt to tell the main agent about the sub-agent. Add the following section at the end of the prompt string.
# Add this to the end of your research_instructions string:
## Sub-agents
You have a specialized sub-agent called "deep-dive-researcher" that you can delegate to when you need an in-depth analysis of a specific subtopic. Use it to keep your own context clean while still getting detailed research on focused areas.
Why do I need to mention the sub-agent in the system prompt?
The task tool is automatically available to the agent when you pass subagents. However, the LLM makes better delegation decisions when the system prompt explicitly describes when and why to use the sub-agent. Without this guidance, the agent might try to handle everything itself.
Test With a Complex Multi-Part Research Question
To see delegation in action, you need a research question that naturally breaks into distinct subtopics. This gives the agent a reason to delegate focused areas to the sub-agent instead of handling everything itself.
- Replace your agent.invoke() call with the following multi-part research question.
result = agent.invoke(
{"messages": [{"role": "user", "content": "Research the impact of AI on healthcare in 2026. Cover three areas: (1) AI-powered diagnostics, (2) drug discovery, and (3) patient care automation. Provide a comprehensive report with findings from each area."}]}
)
print(result["messages"][-1].content)
- Run the agent with uv run python main.py.
The agent should plan the three research areas using write_todos, delegate at least one area to the sub-agent via the task tool, and synthesize everything into a final report. This may take a minute or two depending on the number of searches.
- After the agent finishes, inspect the files it created by printing result["files"].
# Print all files created by the main agent and sub-agent
for filename, content in result["files"].items():
print(f"\n{'='*60}")
print(f"File: {filename}")
print(f"{'='*60}")
print(content[:500]) # Print first 500 chars of each file
What does this code do?
The result["files"] dictionary contains every file that both the main agent and the sub-agent wrote to the virtual file system during execution. You should see files from the sub-agent's focused research alongside the main agent's final report. This is the virtual filesystem in action. The default backend is StateBackend, which stores files ephemerally in LangGraph state.
What should I see?
You should see multiple files in the output. Look for a report.md file with the synthesized report, plus additional files containing detailed research on individual subtopics. The exact number of files depends on how the agent chose to organize its work. The final message should contain a comprehensive overview of AI in healthcare across the three areas you requested.
Agent not delegating to the sub-agent?
If the agent handles all three areas itself without using the task tool, make sure your system prompt includes the sub-agents section describing when to delegate. You can also make the prompt more explicit by adding instructions like "Delegate at least one subtopic to the deep-dive-researcher sub-agent." Help me debug why my sub-agent is not being called.
You now have all three pillars of Deep Agents working together: planning with write_todos, context management with the virtual file system, and sub-agent delegation with the task tool. Next up, you will add streaming so you can watch the agent's decision-making process in real time.
Add Streaming for Real-Time Output
So far, you've been running your agent with invoke(), which waits for the entire research process to finish before showing any output. For a deep research agent that might run for several minutes, that's like staring at a blank screen while a colleague works in another room. Streaming lets you watch the agent's decision-making process in real time, seeing every tool call, every search, and every piece of reasoning as it happens.
In This Step, Get Ready To:
- Replace the blocking invoke() call with real-time streaming output.
- Extract the final report and files from the stream after the agent finishes.
Enable Streaming to Watch the Agent Work
Deep Agents use LangGraph's streaming under the hood. Instead of waiting for a single final result, the stream() method emits events as they happen. Each event contains information about tool calls, tool results, and LLM responses from whichever node in the agent graph is currently active.
- Replace the agent.invoke() call at the bottom of main.py with the following streaming loop:
# Replace agent.invoke() with streaming
for event in agent.stream(
{"messages": [{"role": "user", "content": "Research the latest developments in renewable energy technology in 2026."}]},
stream_mode="updates",
):
for node_name, node_output in event.items():
if "messages" in node_output:
for msg in node_output["messages"]:
# Print text content from the agent
if hasattr(msg, "content") and msg.content:
print(f"[{node_name}] {msg.content[:200]}...")
# Print tool calls as they happen
if hasattr(msg, "tool_calls") and msg.tool_calls:
for tc in msg.tool_calls:
print(f"[{node_name}] Tool call: {tc['name']}({tc['args']})")
What does this code do?
The stream_mode="updates" parameter tells LangGraph to emit an event every time a node in the agent graph produces new output. Each event is a dictionary keyed by the node name (like agent or tools).
The loop checks each message for two things: text content (the agent's reasoning or responses) and tool calls (when the agent decides to use a tool like internet_search or write_file). The [:200] truncation keeps the output readable while the agent works.
- Run the agent with uv run python main.py.
What should I see?
You should see output appearing line by line as the agent works. You'll see tool calls for write_todos (planning), internet_search (searching), write_file (saving findings), and possibly task (delegating to the sub-agent). The agent's reasoning appears between tool calls, giving you a window into its decision-making process.
Pro tip: streaming reveals debugging gold
Streaming is not just for user experience. When your agent makes an unexpected decision or gets stuck in a loop, the streaming output shows you exactly where things went wrong. This is far more useful than debugging a single final output.
Extract the Final Report From the Stream
Streaming gives you real-time visibility, but you still need to capture the final result. The last event emitted by the stream contains the agent's completed state, including all files it created.
- Update your streaming code to capture the final event and print the report. Replace the streaming loop with this version:
# Stream events and capture the final state
last_event = None
for event in agent.stream(
{"messages": [{"role": "user", "content": "Research the latest developments in renewable energy technology in 2026."}]},
stream_mode="updates",
):
last_event = event
for node_name, node_output in event.items():
if "messages" in node_output:
for msg in node_output["messages"]:
if hasattr(msg, "content") and msg.content:
print(f"[{node_name}] {msg.content[:200]}...")
if hasattr(msg, "tool_calls") and msg.tool_calls:
for tc in msg.tool_calls:
print(f"[{node_name}] Tool call: {tc['name']}({tc['args']})")
# Print the final report
print("\n" + "="*60)
print("RESEARCH COMPLETE")
print("="*60)
# Access the final message
for node_name, node_output in last_event.items():
if "messages" in node_output:
final_message = node_output["messages"][-1]
if hasattr(final_message, "content") and final_message.content:
print(f"\nFinal response:\n{final_message.content}")
# List all files created during research
if "files" in node_output:
print(f"\nFiles created: {list(node_output['files'].keys())}")
# Print the report if it exists
if "report.md" in node_output["files"]:
print(f"\n--- report.md ---\n{node_output['files']['report.md']}")
What does this code do?
The key change is the last_event = event line inside the loop. This continuously updates a variable with the most recent event. After the loop finishes, last_event holds the agent's final output, including the completed message and any files written to the virtual file system.
The code then checks for a report.md file in the output, which your system prompt instructed the agent to create. This is the polished Markdown report containing all the research findings.
- Run the updated script with uv run python main.py.
You should see the streaming output scroll by as the agent works, followed by a clean summary of the final report and all files created during the research.
Not seeing any files in the output?
The files key may appear in a different event than the final message, depending on when the agent writes its last file. If you don't see files in the last event, try collecting all events and checking each one for a files key. Help me debug my streaming output.
Your deep research agent is now fully functional with real-time streaming output. You can watch it plan, search, delegate, and synthesize in real time. Next up, you'll take on a secret mission to add structured output to your agent.
Secret mission
Add Structured Output to Your Research Agent
Your agent produces a great Markdown report, but what if downstream code needs to process the results programmatically? In this secret mission, you will add a Pydantic model that validates and structures the agent's research metadata alongside the full report.
Before you go
Clean Up Your Resources
This project runs entirely locally with free-tier API usage, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Resources you used:
- deep-research-agent project directory (including main.py, .env, pyproject.toml, and .venv).
- Tavily API key (free tier, 1,000 API credits/month).
- Google Gemini API key.
Keep everything running
No action needed. Choose this if you are still actively building or plan to extend the agent with new tools and sub-agents.
- Your project directory, virtual environment, and API keys remain in place.
- Your Tavily free tier resets monthly, so credits will replenish automatically.
- There are no running processes or cloud resources to manage.
Pause - I'll come back to this later
Free up disk space while keeping your project files intact so you can return later.
- Delete the .venv folder inside your project directory to reclaim disk space. You can recreate it later by running uv sync.
- Keep your .env file, main.py, and pyproject.toml so you can pick up where you left off.
- Your Tavily and Google API keys remain active. No action needed on those accounts.
Delete - I don't want to use this again
Remove all project resources and start fresh.
- Delete the entire deep-research-agent directory. This removes all project files, the virtual environment, and your .env file.
- Optionally, revoke your Tavily API key by visiting your Tavily dashboard at tavily.com and deleting the key.
- Optionally, revoke your Google API key from the Google Cloud Console if you created it specifically for this project.
Mission accomplished
That's a Wrap!
Nice work! You just built a fully autonomous Deep Research Agent from scratch using LangChain's deepagents library and Google Gemini. Your agent can take any research topic, plan its approach, search the web, manage its own context, delegate subtasks, and produce a polished Markdown report.
Here's what you learned:
- Built a deep agent with all three pillars of the Deep Agents architecture in action: planning with write_todos, context management with the virtual file system, and sub-agent delegation with the task tool.
- Created a custom internet_search tool powered by Tavily that gives your agent real-time access to web information.
- Configured a specialized sub-agent for focused research with its own context window, keeping the main agent's context clean and efficient.
- Added structured output with a Pydantic model so the agent returns validated, programmatically-usable research metadata alongside the Markdown report.
Ready to quiz yourself?