Module 11: Deep Agents

Planning with write_todos

Capsule overview

write_todos gives the agent the ability to break complex tasks into steps, track progress, and re-plan dynamically when results change. It isn't a to-do list — it's strategic planning. In Module 10 you designed the workflow: which node follows which, what conditions to evaluate. With write_todos, the agent designs its own workflow, executes it step by step, and adapts it along the way. The difference between an agent that follows instructions and an agent that plans is the difference between an employee who executes tasks and one who runs a project.


The planning problem

Imagine you hand your agent this instruction:

"Research the state of AI safety in 2025 and generate a complete report."

An agent without planning tries to solve everything in one step: it searches for something, generates a report from whatever it found, and stops. The result is shallow because it never broke the task down.

A human would do something different:

1. Define the scope: which aspects of AI safety? (alignment, evaluations, policy, open source)
2. Search for recent academic papers
3. Search for industry reports (Anthropic, OpenAI, DeepMind)
4. Search for policy documents (EU AI Act, US executive orders)
5. Analyze findings across sources
6. Write the final report with sections by topic

That's the kind of decomposition write_todos enables. The agent receives a vague task, breaks it into concrete steps, and executes them one by one with progress tracking.

Why isn't a good prompt enough?

You might think: "I'll just tell the agent in the system prompt to break the task into steps." And yes, that partly works. But it has three problems:

  1. No tracking: the agent doesn't know which steps it completed and which are left. If the context window grows, it loses the thread
  2. No re-planning: if at step 3 it discovers the task needs an extra step, it has no mechanism to update the plan
  3. No visibility: you can't see the agent's plan or its progress without parsing the whole conversation

write_todos solves all three: the plan is a data object the agent can read, modify, and that you can inspect.


write_todos: the planning tool

write_todos is a tool that Deep Agents automatically injects into the agent. The agent uses it the way it would use any other tool — with tool calling.

Creating a plan

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[],
    name="Planner Demo",
    instructions=(
        "You are a research agent. "
        "When you receive a complex task, use write_todos to "
        "break it into steps before executing."
    ),
)

result = agent.run("Research generative AI trends in 2025")

for todo in result.todos:
    print(f"[{todo['status']:>10}] {todo['title']}")
# Expected output (varies by model):
# [ completed] Define the scope of the research
# [ completed] Search for trends in language models
# [ completed] Search for trends in image/video generation
# [ completed] Search for trends in autonomous agents
# [ completed] Synthesize findings
# [ completed] Generate the final report

The agent decided on its own how to break the task down. You never told it which steps to follow — it inferred them from context.

The shape of a todo

Each todo has three fields:

{
    "title": "Search for academic papers",   # Description of the step
    "status": "pending",                     # pending | in_progress | completed | skipped
    "result": ""                             # The agent can store the step's result here
}

The possible statuses form a cycle:

pending → in_progress → completed
                      → skipped (if the agent decides it isn't needed)

write_todos as a tool call

Under the hood, when the agent calls write_todos, it passes a list of objects:

# What the agent generates as a tool call:
write_todos([
    {"title": "Define the scope of the research", "status": "pending"},
    {"title": "Search for academic papers", "status": "pending"},
    {"title": "Search for industry reports", "status": "pending"},
    {"title": "Analyze findings", "status": "pending"},
    {"title": "Generate the final report", "status": "pending"},
])

To update progress, the agent calls write_todos again with the updated statuses:

# After completing the first two steps:
write_todos([
    {"title": "Define the scope of the research", "status": "completed"},
    {"title": "Search for academic papers", "status": "completed"},
    {"title": "Search for industry reports", "status": "in_progress"},
    {"title": "Analyze findings", "status": "pending"},
    {"title": "Generate the final report", "status": "pending"},
])

Re-planning: the most powerful capability

A static plan is useful. A plan that adapts is powerful.

How re-planning works

The agent is on step 3, searching for industry reports. It finds an Anthropic paper mentioning a new AI safety evaluation framework it wasn't expecting. The agent decides:

"This framework deserves its own investigation. I'm going to add a step between 3 and 4."

And it updates the plan:

write_todos([
    {"title": "Define the scope of the research", "status": "completed"},
    {"title": "Search for academic papers", "status": "completed"},
    {"title": "Search for industry reports", "status": "completed"},
    {"title": "Research Anthropic's evaluation framework", "status": "pending"},  # NEW
    {"title": "Analyze findings", "status": "pending"},
    {"title": "Generate the final report", "status": "pending"},
])

The plan went from 5 steps to 6. The agent did it with no human intervention.

Re-planning by removal

It works the other way too. The agent planned to search three different sources, but the first one already has all the information needed:

# Original plan: 5 steps
# After discovering that one source is enough:
write_todos([
    {"title": "Define the scope", "status": "completed"},
    {"title": "Search the primary source", "status": "completed"},
    {"title": "Search the secondary source", "status": "skipped"},   # No longer needed
    {"title": "Search the tertiary source", "status": "skipped"},    # No longer needed
    {"title": "Analyze and synthesize", "status": "pending"},
    {"title": "Generate the report", "status": "pending"},
])

The agent marked 2 steps as skipped because it determined they added no value. That cuts cost (fewer LLM calls) and execution time.

Full example: research with re-planning

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Research Planner",
    instructions=(
        "You are a researcher. For every task:\n"
        "1. Use write_todos to create an initial plan\n"
        "2. Execute each step of the plan\n"
        "3. If you discover something unexpected, update the plan with write_todos\n"
        "4. Mark each step as completed or skipped when you finish it\n"
        "5. Write the final report to a file"
    ),
)

result = agent.run(
    "Research the most recent advances in RAG (Retrieval-Augmented Generation)"
)

print("=== Final plan ===")
for i, todo in enumerate(result.todos, 1):
    status_icon = {"completed": "✅", "skipped": "⏭️", "pending": "⏳", "in_progress": "🔄"}
    print(f"  {i}. {status_icon.get(todo['status'], '❓')} [{todo['status']}] {todo['title']}")

print(f"\nSteps completed: {sum(1 for t in result.todos if t['status'] == 'completed')}")
print(f"Steps skipped:   {sum(1 for t in result.todos if t['status'] == 'skipped')}")
print(f"Total planned:   {len(result.todos)}")
# Expected output (varies by model and search results):
# === Final plan ===
#   1. ✅ [completed] Define which aspects of RAG to research
#   2. ✅ [completed] Search for recent papers on advanced RAG
#   3. ✅ [completed] Research RAG implementations in production
#   4. ✅ [completed] Research GraphRAG as an emerging variant
#   5. ⏭️ [skipped] Search for RAG vs fine-tuning benchmarks
#   6. ✅ [completed] Synthesize findings
#   7. ✅ [completed] Generate the final report
#
# Steps completed: 6
# Steps skipped:   1
# Total planned:   7

The agent might have discovered GraphRAG during the research and added a step. It might have decided comparing against fine-tuning wasn't relevant and skipped that step. The plan adapted to what the agent found.


Progress tracking: the agent knows where it is

Every time the agent needs to decide what to do next, it consults the state of its todos:

The agent's internal context:

Task: Research advanced RAG
Current plan:
  1. [completed] Define the scope → "Focus on RAG for production"
  2. [completed] Search for papers → "Found 5 relevant papers"
  3. [in_progress] Search for implementations → running...
  4. [pending] Analyze findings
  5. [pending] Generate the report

→ Next action: continue with step 3

Without write_todos, the agent would have to parse its entire prior conversation to know what it did and what's left. With write_todos, that information is structured and easy to query.

How it's stored

The todos are stored as part of the agent's state. On every turn, the agent has access to the updated plan. It doesn't get lost in the context window because it's a data object, not free text.

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Planning Inspector",
    instructions="Break any task into steps with write_todos.",
)

result = agent.run("Create a plan to learn Kubernetes in 2 weeks")

print("=== Detailed state ===")
for todo in result.todos:
    print(f"\n  Title:  {todo['title']}")
    print(f"  Status: {todo['status']}")
    if todo.get('result'):
        print(f"  Result: {todo['result'][:100]}...")
# Expected output (varies by model):
# === Detailed state ===
#
#   Title:  Week 1 - Fundamentals: install minikube and kubectl
#   Status: completed
#   Result: Installation complete. kubectl version works correctly...
#
#   Title:  Week 1 - Core concepts: pods, deployments, services
#   Status: completed
#   Result: Learned the fundamental concepts. A pod is the smallest unit...
#   ...

Comparison: manual planning (M6-M10) vs write_todos

This is the explicit link back to what you built in earlier modules.

Manual planning: you design the workflow

In modules 6-10, planning was static. You decided:

# M7: The flow YOU designed
builder = StateGraph(ResearchState)
builder.add_node("decompose", decompose_query)    # Step 1: always
builder.add_node("search", parallel_search)         # Step 2: always
builder.add_node("analyze", analyze_results)        # Step 3: always
builder.add_node("report", generate_report)         # Step 4: always

builder.add_edge(START, "decompose")
builder.add_edge("decompose", "search")
builder.add_conditional_edges("search", check_quality, {
    "good": "analyze",
    "retry": "search",         # Retry on failure
})
builder.add_edge("analyze", "report")
builder.add_edge("report", END)

The workflow is fixed. It always does: decompose → search → analyze → report. The only dynamism is the conditional retry, which you designed. If the task needs an extra step, you have to modify the code.

write_todos: the agent designs the workflow

With Deep Agents, the agent decides the plan at runtime:

from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[TavilySearchResults(max_results=3)],
    name="Adaptive Researcher",
    instructions=(
        "Research the topic you're given. "
        "Use write_todos to plan your research. "
        "Adapt the plan based on what you discover."
    ),
)

result = agent.run("Compare the AI safety strategies of OpenAI, Anthropic, and DeepMind")

print(f"The plan had {len(result.todos)} steps")
print(f"Completed: {sum(1 for t in result.todos if t['status'] == 'completed')}")
# Expected output (varies):
# The plan had 8 steps
# Completed: 7

The agent might have planned 5 steps, discovered it needed 8, and completed 7 (skipping 1 that wasn't necessary). All without you changing a line of code.

Comparison table

CriterionManual planning (M6-M10)write_todos (Deep Agents)
Who designs the planYou, at build timeThe agent, at runtime
AdaptabilityFixed (you must change code)Dynamic (automatic re-planning)
VisibilityTotal (you see the graph)Partial (you see the todos, not the internal decisions)
DebuggingEasy (you know which node failed)Harder (why did the agent plan it that way?)
CostPredictable (N fixed nodes)Variable (depends on the plan it generates)
FlexibilityLow for diverse tasksHigh (each task generates its own plan)
ControlTotalLimited (the agent decides)
CodeMore (you design the workflow)Less (instructions + a tool)

The choice depends on your case: if the tasks are diverse and you can't predict the flow, write_todos saves you time. If you need guarantees about which steps run, manual planning is more reliable.


Effective instruction patterns for planning

The quality of the plan depends heavily on how you instruct the agent. These patterns produce better plans:

Pattern 1: Scope before plan

instructions = (
    "Before creating the plan with write_todos, define the scope of the task. "
    "Ask yourself: which aspects do I cover? at what depth? what do I exclude? "
    "Then create a plan based on that scope."
)

Without scope, the agent creates vague plans like "Research the topic" → "Write report." With scope, it generates specific steps.

Pattern 2: Explicit granularity

instructions = (
    "Each step of the plan must be executable in a single action. "
    "If a step requires multiple actions, break it down. "
    "'Search for information' is too vague. "
    "'Search for papers about RAG published in 2025' is specific."
)

Pattern 3: Re-planning criteria

instructions = (
    "After each step, evaluate whether the plan needs adjusting:\n"
    "- Did you discover a new topic that deserves research?\n"
    "- Is any pending step no longer necessary?\n"
    "- Do you need to reorder the remaining steps?\n"
    "If so, update the plan with write_todos."
)

Troubleshooting

Problem 1: The agent creates a plan but doesn't follow it

Symptom: The agent uses write_todos to create a 5-step plan, but then runs actions that don't correspond to any step. The todos sit in pending while the agent works on something else. Cause: The instructions don't explicitly tie execution to the plan. The agent "forgets" it has a plan because the context window fills with tool results. Fix: Add an explicit instruction that forces it to consult the plan:

instructions = (
    "ALWAYS consult your plan (write_todos) before deciding what to do. "
    "Your next action must correspond to the first todo with status 'pending'. "
    "Mark the todo as 'in_progress' before starting and 'completed' when you finish."
)

Problem 2: Plans that are too granular (20+ steps)

Symptom: The agent breaks a simple task into 20 micro-steps, spending tokens on planning instead of execution. Cause: The instructions ask it to "break the task into manageable steps" without defining what "manageable" means. Fix: Define a granularity range:

instructions = (
    "Create a plan of 3-7 steps for the task. "
    "If you need more than 7, group related steps. "
    "If you need fewer than 3, the task probably doesn't need planning — just execute it."
)

Problem 3: The agent doesn't re-plan when it should

Symptom: The agent keeps executing its original plan even when the results suggest a change of direction. It completes steps that are no longer relevant. Cause: The agent has no explicit instruction to evaluate whether the plan still holds. Fix: Add re-evaluation checkpoints:

instructions = (
    "After completing each step, evaluate the full plan: "
    "are the pending steps still relevant given what you discovered? "
    "If not, update the plan with write_todos before continuing."
)

Problem 4: Todos with no useful results

Symptom: Every step gets marked completed but result is empty or contains generic text like "Done." Cause: The agent has no instruction to document the result of each step. Fix: Ask for concrete results:

instructions = (
    "When you complete a step, include a specific result in the todo. "
    "Don't write 'Done' — write what you found, what you decided, "
    "or what output you generated."
)

Exercises

Exercise 1: Basic research plan (Easy)

Create a Deep Agent that plans research on "the impact of LLMs on education." Print the generated plan with statuses and titles.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Education Researcher",
    instructions=(
        "You are an education technology researcher. "
        "For every task, create a 4-6 step plan with write_todos. "
        "Execute each step and mark its progress."
    ),
)

result = agent.run("Research the impact of LLMs on education")

print("=== Research plan ===")
for i, todo in enumerate(result.todos, 1):
    icon = "✅" if todo["status"] == "completed" else "⏳"
    print(f"  {i}. {icon} {todo['title']}")
print(f"\nTotal: {len(result.todos)} steps, "
      f"{sum(1 for t in result.todos if t['status'] == 'completed')} completed")
# Expected output (varies):
# === Research plan ===
#   1. ✅ Define the key aspects of LLM impact on education
#   2. ✅ Research the use of LLMs as personalized tutors
#   3. ✅ Research the impact on assessment and academic plagiarism
#   4. ✅ Research accessibility and the democratization of knowledge
#   5. ✅ Synthesize findings and draw conclusions
#
# Total: 5 steps, 5 completed

Explanation: The agent received an open-ended task and broke it into concrete steps. Without write_todos, it would try to solve everything in one step, producing a shallow result.

Exercise 2: Plan with a search tool (Easy)

Add TavilySearchResults to the agent from exercise 1 and watch how the plan changes when the agent has access to real information.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Education Researcher v2",
    instructions=(
        "You are an education technology researcher. "
        "Use write_todos to plan your research (4-6 steps). "
        "Use web_search to look up real information at each step. "
        "Adapt the plan based on what you discover."
    ),
)

result = agent.run("Research the impact of LLMs on education in 2025")

print("=== Executed plan ===")
for i, todo in enumerate(result.todos, 1):
    status_map = {"completed": "✅", "skipped": "⏭️", "pending": "⏳", "in_progress": "🔄"}
    icon = status_map.get(todo["status"], "❓")
    print(f"  {i}. {icon} [{todo['status']}] {todo['title']}")
    if todo.get("result"):
        print(f"     → {todo['result'][:80]}...")

print(f"\nTotal: {len(result.todos)} steps")
# Expected output (varies by search results):
# === Executed plan ===
#   1. ✅ [completed] Define the dimensions of impact
#      → Three dimensions: personalization, assessment, accessibility...
#   2. ✅ [completed] Search for studies on personalized tutoring with LLMs
#      → Found 3 studies: Khan Academy + GPT-4, Duolingo Max...
#   3. ✅ [completed] Search for the impact on academic integrity
#      → Papers on AI plagiarism detection, university policies...
#   4. ✅ [completed] Search for accessibility and the digital divide
#      → UNESCO report 2025, initiatives in Latin America...
#   5. ✅ [completed] Synthesize findings into a report
#      → Report generated with 3 main sections...
#
# Total: 5 steps

Explanation: With search tools, the plan gets more specific because the agent can look up real information. Compare this plan to the one from exercise 1: the steps are more concrete and the results include real data.

Exercise 3: Observe re-planning (Medium)

Create an agent that researches a technical topic. In the instructions, explicitly ask it to re-plan if it discovers an unexpected subtopic. Compare the number of initial steps against the final plan.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=3)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Adaptive Researcher",
    instructions=(
        "You are a technical researcher. Follow these rules:\n"
        "1. Create an initial 4-step plan with write_todos\n"
        "2. After each search, evaluate whether you discovered something unexpected\n"
        "3. If you discover an important subtopic, ADD a step to the plan\n"
        "4. If a pending step is no longer relevant, mark it as skipped\n"
        "5. Document every change to the plan"
    ),
)

result = agent.run("Research the current state of GraphRAG and its variants")

completed = [t for t in result.todos if t["status"] == "completed"]
skipped = [t for t in result.todos if t["status"] == "skipped"]

print(f"=== Planning stats ===")
print(f"  Total steps in the final plan: {len(result.todos)}")
print(f"  Completed: {len(completed)}")
print(f"  Skipped:   {len(skipped)}")
print(f"\n=== Final plan ===")
for i, todo in enumerate(result.todos, 1):
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"  {i}. {icon} {todo['title']}")
# Expected output (varies):
# === Planning stats ===
#   Total steps in the final plan: 6
#   Completed: 5
#   Skipped:   1
#
# === Final plan ===
#   1. ✅ Define what GraphRAG is and how it differs from classic RAG
#   2. ✅ Find Microsoft's original GraphRAG paper
#   3. ✅ Search for open source implementations
#   4. ✅ Research RAPTOR as a hierarchical variant  ← re-planning: step added
#   5. ⏭️ Compare against classic RAG on benchmarks  ← skipped
#   6. ✅ Synthesize findings and generate a report

Explanation: The agent started with ~4 steps but added one (RAPTOR as a variant) when it discovered it during the search. It also marked a step as skipped once it determined it wasn't necessary. That's re-planning in action.

Exercise 4: Compare execution with and without planning (Medium)

Create two agents: one with explicit planning instructions and one without. Give both the same task and compare the quality of the output.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent_with_planning = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="With Planning",
    instructions=(
        "You are an analyst. For every task:\n"
        "1. Use write_todos to create a 4-6 step plan\n"
        "2. Execute each step methodically\n"
        "3. Mark the progress of each step"
    ),
)

agent_without_planning = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Without Planning",
    instructions=(
        "You are an analyst. Answer the request directly "
        "without using write_todos."
    ),
)

task = "Analyze the pros and cons of microservices vs monoliths"

result_planned = agent_with_planning.run(task)
result_direct = agent_without_planning.run(task)

print("=== With planning ===")
print(f"  Todos: {len(result_planned.todos)}")
print(f"  Files: {list(result_planned.files.keys())}")
print(f"  Output length: {len(result_planned.output)} chars")

print("\n=== Without planning ===")
print(f"  Todos: {len(result_direct.todos)}")
print(f"  Files: {list(result_direct.files.keys())}")
print(f"  Output length: {len(result_direct.output)} chars")
# Expected output (varies):
# === With planning ===
#   Todos: 5
#   Files: ['output/analysis.md']
#   Output length: 2847 chars
#
# === Without planning ===
#   Todos: 0
#   Files: []
#   Output length: 1203 chars

Explanation: The agent with planning produces more structured and complete output because it approached the task step by step. The agent without planning answers directly, which is faster but typically shallower. The gap widens with more complex tasks.

Exercise 5: Plan with dependencies between steps (Advanced)

Create an agent that researches a topic where later steps depend on the results of earlier ones. For example: "Identify the 3 most cited papers on X, then analyze each one." The analysis depends on which papers it found.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults

web_search = TavilySearchResults(max_results=5)

agent = create_deep_agent(
    "openai:gpt-4.1",
    tools=[web_search],
    name="Dependent Steps Researcher",
    instructions=(
        "You are an academic researcher. Your process:\n"
        "1. Create an initial plan with write_todos (3-4 generic steps)\n"
        "2. Execute the first step (identify the key sources)\n"
        "3. BASED on what you found, RE-PLAN: add specific steps "
        "   to analyze each source you found\n"
        "4. Execute the updated steps\n"
        "5. Synthesize at the end\n\n"
        "The final plan must reflect the real sources you found, "
        "not generic categories."
    ),
)

result = agent.run("Identify and analyze the most relevant work on prompt engineering")

print("=== Plan with dependencies ===")
for i, todo in enumerate(result.todos, 1):
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"  {i}. {icon} {todo['title']}")
# Expected output (varies by search results):
# === Plan with dependencies ===
#   1. ✅ Search for the most cited work on prompt engineering
#   2. ✅ Analyze: "Chain-of-Thought Prompting Elicits Reasoning" (Wei et al.)
#   3. ✅ Analyze: "Large Language Models are Zero-Shot Reasoners" (Kojima et al.)
#   4. ✅ Analyze: "Tree of Thoughts" (Yao et al.)
#   5. ✅ Compare approaches and synthesize findings
#   6. ✅ Generate the final report with a ranking and recommendations

Explanation: Steps 2-4 didn't exist in the original plan — they were created by re-planning after step 1 identified specific papers. The plan adapted to the real information found, not to predefined generic categories.

Exercise 6: Real-time progress dashboard (Advanced)

Use agent.stream() instead of agent.run() to watch the agent update the todos in real time. Print each update to the plan.

See solution
from dotenv import load_dotenv
load_dotenv()

from deep_agents import create_deep_agent

agent = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Streaming Planner",
    instructions=(
        "Break the task into 4-5 steps with write_todos. "
        "Execute each step and update your progress after each one."
    ),
)

print("=== Real-time progress ===\n")

previous_todos_count = 0
for event in agent.stream("Create a SWOT analysis of using AI in startups"):
    if hasattr(event, "todos") and event.todos:
        current_count = len(event.todos)
        completed = sum(1 for t in event.todos if t["status"] == "completed")
        in_progress = sum(1 for t in event.todos if t["status"] == "in_progress")

        if current_count != previous_todos_count or True:
            progress_bar = f"[{'█' * completed}{'▓' * in_progress}{'░' * (current_count - completed - in_progress)}]"
            print(f"  {progress_bar} {completed}/{current_count} completed")
            for t in event.todos:
                icon = {"completed": "✅", "in_progress": "🔄", "pending": "⏳"}.get(t["status"], "❓")
                print(f"    {icon} {t['title']}")
            print()
            previous_todos_count = current_count

print("=== Execution complete ===")
# Expected output (varies):
# === Real-time progress ===
#
#   [░░░░] 0/4 completed
#     ⏳ Define the SWOT components
#     ⏳ Analyze Strengths and Opportunities
#     ⏳ Analyze Weaknesses and Threats
#     ⏳ Generate the SWOT matrix and conclusions
#
#   [█▓░░] 1/4 completed
#     ✅ Define the SWOT components
#     🔄 Analyze Strengths and Opportunities
#     ⏳ Analyze Weaknesses and Threats
#     ⏳ Generate the SWOT matrix and conclusions
#
#   ... (progressively)
#
# === Execution complete ===

Explanation: agent.stream() emits events as the agent works. By filtering the events that contain todos, you can build a real-time progress dashboard. That's useful in applications where you want to show the user what the agent is doing.


Summary

  • write_todos is a strategic planning tool, not a to-do list. The agent breaks complex tasks into steps, tracks progress, and re-plans when results change
  • Re-planning is the most powerful capability: the agent can add steps, mark others as skipped, and reorder priorities — all without human intervention
  • Each todo has a title, a status (pending/in_progress/completed/skipped), and optionally a result — it's a structured data object, not free text
  • The difference from manual planning (M6-M10): you designed the workflow at build time with fixed nodes and edges. With write_todos, the agent designs its own workflow at runtime and adapts it dynamically
  • The instructions you give the agent determine the quality of the plan: ask for scope before planning, define granularity, and set re-evaluation criteria
  • Progress tracking lets the agent know exactly what it completed and what's left, without parsing its whole conversation. It also lets you inspect the agent's state at any moment

Next capsule: Virtual Filesystem — how the agent uses files to offload context from the LLM, cut costs, and handle research of any size.


Additional resources

  1. Deep Agents — Planning & Todos — Official documentation for write_todos and planning in Deep Agents
  2. Plan-and-Solve Prompting — Academic paper on planning in LLMs that inspires the write_todos architecture
  3. LangGraph — State Management — How LangGraph handles state, the foundation write_todos uses to store the plan
  4. Inner Monologue: Embodied Reasoning through Planning — Paper on planning as an inner monologue in agents, relevant for understanding why explicit planning improves results
  5. Cognitive Architectures for Language Agents — Theoretical framework of planning, memory, and tool use in agents — the "why" behind write_todos
  6. Building Effective Agents — Anthropic — Anthropic's guide on task decomposition, applicable to how you instruct agents to plan well

Module 11 — LangChain & LangGraph: From Chains to Agents

Next capsule: Virtual Filesystem — you'll learn how the agent uses files to handle information without saturating the context window, cutting costs and enabling research of any size.