Deep AgentsDeep Agents 0.7 · Python 3.11+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
28 small wins to finish your pathNext lesson →

write_todos: planning with TodoListMiddleware

write_todos is a planning tool that lets a deep agent write its task as a to-do list and mark each item pending, in progress or completed; in Deep Agents 0.7 you add it with TodoListMiddleware.

Last updated: 29 Sep, 2026 · Deep Agents 0.7

Planning with a to-do list: the Paris trip · from the Complete Deep Agents Course With LangChain · 11:42 to 15:15
In the video every deep agent plans with a to-do list by default. Since deepagents 0.7 planning is opt-in: pass middleware=[TodoListMiddleware()] to create_deep_agent, and the agent gets the write_todos tool and a todos list in its state. Without it, neither exists.

The video's Paris trip to-do list

The crash course explains planning with the trip this course builds. The request: a holiday in Paris, three nights and four days, for 100,000 rupees. Before any tool runs, the deep agent writes a to-do list: travel on day one and stay in a hotel at a set price, visit the Eiffel Tower on day two, another place on day three, fly back on day four, with each day's cost. Each item can go to a subagent, the system prompt says how the agents behave, and the file system is where they leave their work for each other.

Adding TodoListMiddleware

python
from langchain.agents.middleware import TodoListMiddleware

agent = create_deep_agent(model=model, tools=[...], middleware=[TodoListMiddleware()])
result["todos"]   # [{"content": "...", "status": "completed"}, ...]

A tool for exact totals

A trip plan adds up many prices, and a model's mental arithmetic is not reliable. add_costs adds a list of rupee amounts in Python and returns the exact total. Put it in trip.py under search_travel, the tool from Tools: a travel search the agent can call. The file now reads:

python
from langchain.tools import tool

CATALOG = {
    "paris": {
        "flight": ["Return flight Delhi to Paris: 42,000 rupees"],
        "hotel": ["Seine Budget Inn, Latin Quarter: 5,200 rupees a night",
                  "Hotel Lumiere, Montmartre: 7,500 rupees a night",
                  "Le Grand Opera Hotel: 16,000 rupees a night"],
        "sight": ["Eiffel Tower summit: 3,100 rupees", "Louvre Museum: 2,000 rupees",
                  "Seine river cruise: 1,500 rupees", "Versailles day trip: 2,600 rupees",
                  "Montmartre walking tour: free"],
        "food": ["Cafe breakfast and bistro dinner: 3,000 rupees a day"],
    },
}


@tool
def search_travel(city: str, kind: str) -> str:
    """Search the travel catalog. kind is "flight", "hotel", "sight" or "food". Prices are in rupees."""
    entries = CATALOG.get(city.lower(), {}).get(kind)
    return "\n".join(entries) if entries else f"The catalog has no {kind} entries for {city}."


@tool
def add_costs(amounts: list[int]) -> int:
    """Add rupee amounts and return the exact total. Use it for every sum."""
    return sum(amounts)

The planning agent

Create the model and the agent. The prompt asks for the plan first, a lookup for every price, and add_costs for every sum.

python
from deepagents import create_deep_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("groq:openai/gpt-oss-120b", temperature=0, max_retries=6)
python
from langchain.agents.middleware import TodoListMiddleware

agent = create_deep_agent(
    model=model,
    tools=[search_travel, add_costs],
    middleware=[TodoListMiddleware()],          # adds the write_todos tool
    system_prompt="You are a travel planner. Write a todo list with write_todos first, then work through it. "
                  "Look up every price with search_travel and use add_costs for every total. "
                  "Reply with one line per day and the total cost.",
)

Planning the video's Paris trip

Send the video's request and print the finished to-do list, then the reply. A run makes about ten model calls, so it takes a minute or two on the free tier:

ExampleAPI keytrip.py, continued
request = "Plan a 3-night, 4-day trip to Paris from Delhi for a budget of 100,000 rupees."
result = agent.invoke({"messages": [{"role": "user", "content": request}]})

for todo in result["todos"]:
    print(f"[{todo['status']}] {todo['content']}")
print()
print(result["messages"][-1].text)

What the to-do list shows

  • The plan came first: the agent broke the trip into lookups (flight, hotel, food, sights), a total, and the itinerary itself.
  • Every item is completed: the agent calls write_todos again as it works, each time sending the whole list with new statuses. The list you print is the last one.
  • The total is right: 78,800 rupees is the 42,000 flight, three nights at the cheapest hotel, four days of food and the four paid sights. The prompt told the agent to add with add_costs; the Try it list shows how to print every tool call.
  • The trip fits: the plan comes in under the 100,000 rupee budget.

A plain deep agent vs one with write_todos

create_deep_agent (0.7 default)with TodoListMiddleware
write_todos toolNot offeredOffered to the model
result["todos"]MissingThe latest list, with statuses
Good forShort tasks, strong modelsLong tasks, weaker models, progress UIs

When to add planning

  • Tasks with five or more steps, such as a trip with flights, hotels, sights and a budget.
  • Smaller or cheaper models, which follow a written list more reliably than a plan in their head.
  • Apps that show progress: the todos list can be streamed to a UI as it changes.
Watch out. The to-do list is a tool the model may call when it chooses; nothing forces it. If a task looks easy, the model can skip write_todos. Say "write a todo list first" in the system prompt when you want a plan every time.
Try it yourself
  • Change the budget to 60,000 rupees and read how the plan and the reply change.
  • Remove middleware=[TodoListMiddleware()] and check that "todos" in result is now False.
  • Print [c["name"] for m in result["messages"] for c in getattr(m, "tool_calls", [])] and find the add_costs calls.

You understood something today that you didn't yesterday.