| name | deepagents-todolist |
| description | Using TodoListMiddleware for task planning and tracking progress with the write_todos tool in Deep Agents for complex multi-step workflows. |
| language | python |
deepagents-todolist (Python)
Overview
TodoListMiddleware provides agents with task planning and progress tracking capabilities through the write_todos tool. It's automatically included in every deep agent and helps agents break down complex, multi-step tasks into manageable pieces.
Planning is integral to solving complex problems. The middleware enables agents to:
- Break down complex tasks into discrete steps
- Track progress as tasks are completed
- Adapt plans dynamically as new information emerges
- Provide visibility into long-running operations
When to Use TodoList Middleware
| Use TodoList When | Skip TodoList When |
|---|
| Complex multi-step tasks requiring coordination | Simple, single-action tasks |
| Long-running operations where progress visibility matters | Quick operations (< 3 steps) |
| Tasks that may need plan adaptation | Fixed, predetermined workflows |
| Multiple tools need to be orchestrated | Single tool invocation |
How It Works
TodoListMiddleware is automatically included in create_deep_agent(). The agent receives:
- A
write_todos tool for managing the task list
- System prompt instructions on when and how to use planning
- State persistence for the todo list across agent steps
The write_todos Tool
write_todos(todos: list[dict]) -> None
Each todo item has:
content: Description of the task
status: One of "pending", "in_progress", "completed"
Basic Usage
Default Configuration (Included Automatically)
from deepagents import create_deep_agent
agent = create_deep_agent()
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Create a Python web scraper that extracts product data from an e-commerce site, stores it in a database, and generates a report."
}]
})
Customizing TodoList Middleware
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
agent = create_agent(
model="claude-sonnet-4-5-20250929",
middleware=[
TodoListMiddleware(
system_prompt="""Use the write_todos tool to plan your work:
1. Break down the task into 3-5 major steps
2. Mark tasks as 'in_progress' when you start
3. Mark tasks as 'completed' when done
4. Update the list if plans change
""",
tool_description="Manage your task list for complex multi-step work"
),
],
)
Decision Table: Todo List Patterns
| Task Type | Todo List Strategy | Example |
|---|
| Sequential steps | Create all todos upfront, complete in order | Build app: setup → code → test → deploy |
| Discovery-based | Add todos as you learn what's needed | Research: initial search → follow-up → synthesis |
| Parallel work | Multiple "in_progress" items allowed | Data processing: extract + transform + load |
| Iterative refinement | Update todo content as you refine approach | Debugging: reproduce → isolate → fix → verify |
Code Examples
Example 1: Sequential Task Breakdown
from deepagents import create_deep_agent
agent = create_deep_agent()
result = agent.invoke({
"messages": [{
"role": "user",
"content": """Create a REST API for a todo application:
1. Design the data models
2. Implement CRUD endpoints
3. Add authentication
4. Write tests
5. Create API documentation
"""
}]
})
Example 2: Adaptive Planning
from deepagents import create_deep_agent
agent = create_deep_agent()
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Debug why the application crashes on startup"
}]
})
Example 3: Custom TodoList Instructions
from langchain.agents import create_agent
from langchain.agents.middleware import TodoListMiddleware
from langchain.tools import tool
@tool
def run_tests(test_suite: str) -> str:
"""Run a test suite."""
return f"Tests in {test_suite} passed"
@tool
def deploy_code(environment: str) -> str:
"""Deploy code to an environment."""
return f"Deployed to {environment}"
agent = create_agent(
model="gpt-4",
tools=[run_tests, deploy_code],
middleware=[
TodoListMiddleware(
system_prompt="""For deployment tasks, always:
1. Create a todo list with safety checks
2. Run tests before deployment
3. Mark each step as completed before proceeding
""",
),
],
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "Deploy the application to production"
}]
})
Accessing Todo State
The todo list is stored in the agent's state under the todos key:
from deepagents import create_deep_agent
agent = create_deep_agent()
result = agent.invoke(
{
"messages": [{
"role": "user",
"content": "Create a data processing pipeline"
}]
},
config={"configurable": {"thread_id": "session-1"}}
)
todos = result.get("todos", [])
for todo in todos:
print(f"[{todo['status']}] {todo['content']}")
Boundaries
What Agents CAN Do with TodoLists
✅ Create todo lists with custom content and structure
✅ Update todo status (pending → in_progress → completed)
✅ Add new todos as work progresses
✅ Remove todos that become irrelevant
✅ Reorganize or reprioritize todos
✅ Use todos for any task complexity level
What Agents CANNOT Do
❌ Change the tool name from write_todos
❌ Use custom status values (must be pending/in_progress/completed)
❌ Access todos from other threads without the thread_id
❌ Disable TodoListMiddleware in create_deep_agent (it's always included)
❌ Share todos across multiple agents (each agent has its own state)
Gotchas
1. TodoList is Stateful - Requires Thread ID
agent.invoke({"messages": [{"role": "user", "content": "Task 1"}]})
agent.invoke({"messages": [{"role": "user", "content": "Task 2"}]})
config = {"configurable": {"thread_id": "user-session"}}
agent.invoke({"messages": [{"role": "user", "content": "Task 1"}]}, config=config)
agent.invoke({"messages": [{"role": "user", "content": "Task 2"}]}, config=config)
2. TodoList Middleware is Always Present
from deepagents import create_deep_agent
agent = create_deep_agent(middleware=[])
from langchain.agents import create_agent
agent = create_agent(
model="gpt-4",
middleware=[]
)
3. Todos Are Not Shared Across Agents
from deepagents import create_deep_agent
main_agent = create_deep_agent()
result = main_agent.invoke({
"messages": [{
"role": "user",
"content": "Use a subagent to process data"
}]
})
4. TodoList is Optional for Simple Tasks
from deepagents import create_deep_agent
agent = create_deep_agent()
result = agent.invoke({
"messages": [{"role": "user", "content": "What is 2+2?"}]
})
result = agent.invoke({
"messages": [{"role": "user", "content": "Build a web scraper and analyze the data"}]
})
Full Documentation