christian-bromann

deepagents-todolist

Using TodoListMiddleware for task planning and tracking progress with the write_todos tool in Deep Agents for complex multi-step workflows.

christian-bromann 3 1 Updated 3mo ago
GitHub

Install

npx skillscat add christian-bromann/langchain-skills/skills-deepagents-todolist-python

Install via the SkillsCat registry.

SKILL.md

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:

  1. A write_todos tool for managing the task list
  2. System prompt instructions on when and how to use planning
  3. 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

# TodoListMiddleware is included by default
agent = create_deep_agent()

# Agent will automatically use write_todos for complex tasks
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

# Custom agent with customized TodoList behavior
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()

# The agent will use write_todos to plan this multi-step task
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
        """
    }]
})

# Agent's internal planning (via write_todos):
# [
#   {"content": "Design data models for Todo items", "status": "pending"},
#   {"content": "Implement CRUD endpoints (GET, POST, PUT, DELETE)", "status": "pending"},
#   {"content": "Add JWT authentication middleware", "status": "pending"},
#   {"content": "Write unit and integration tests", "status": "pending"},
#   {"content": "Generate OpenAPI documentation", "status": "pending"}
# ]

Example 2: Adaptive Planning

from deepagents import create_deep_agent

agent = create_deep_agent()

# Complex task where requirements emerge over time
result = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Debug why the application crashes on startup"
    }]
})

# Agent's evolving plan:
# Initial todos:
# [
#   {"content": "Reproduce the crash", "status": "in_progress"},
#   {"content": "Check error logs", "status": "pending"},
#   {"content": "Identify root cause", "status": "pending"}
# ]
#
# After investigation, agent updates:
# [
#   {"content": "Reproduce the crash", "status": "completed"},
#   {"content": "Check error logs", "status": "completed"},
#   {"content": "Identified missing environment variable", "status": "completed"},
#   {"content": "Add environment variable validation on startup", "status": "in_progress"},
#   {"content": "Update deployment documentation", "status": "pending"}
# ]

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()

# Run the agent
result = agent.invoke(
    {
        "messages": [{
            "role": "user",
            "content": "Create a data processing pipeline"
        }]
    },
    config={"configurable": {"thread_id": "session-1"}}
)

# Access the todo list from the final state
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

# ❌ Todo list won't persist without thread_id
agent.invoke({"messages": [{"role": "user", "content": "Task 1"}]})
agent.invoke({"messages": [{"role": "user", "content": "Task 2"}]})

# ✅ Use thread_id for persistence
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

# You cannot remove TodoListMiddleware from create_deep_agent
# It's part of the core harness

# ❌ This won't remove TodoList
from deepagents import create_deep_agent

agent = create_deep_agent(middleware=[])  # TodoList still included

# ✅ If you need full control, use create_agent from LangChain
from langchain.agents import create_agent

agent = create_agent(
    model="gpt-4",
    middleware=[]  # No middleware at all
)

3. Todos Are Not Shared Across Agents

# ❌ Subagents have their own todo lists
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"
    }]
})

# The subagent's todos are separate and won't appear in main_agent's state

4. TodoList is Optional for Simple Tasks

# The agent won't always use write_todos
# For simple tasks, it may skip planning

from deepagents import create_deep_agent

agent = create_deep_agent()

# Simple task - agent likely won't create todos
result = agent.invoke({
    "messages": [{"role": "user", "content": "What is 2+2?"}]
})
# No todos in state

# Complex task - agent will likely create todos
result = agent.invoke({
    "messages": [{"role": "user", "content": "Build a web scraper and analyze the data"}]
})
# Todos present in state

Full Documentation