Módulo 11: Deep Agents

Planning con write_todos

Descripción de la cápsula

write_todos le da al agente la capacidad de descomponer tareas complejas en pasos, trackear progreso, y re-planificar dinámicamente cuando los resultados cambian. No es una lista de pendientes — es planning estratégico. En el Módulo 10 tú diseñaste el workflow: qué nodo va después de cuál, qué condiciones evaluar. Con write_todos, el agente diseña su propio workflow, lo ejecuta paso a paso, y lo adapta sobre la marcha. La diferencia entre un agente que sigue instrucciones y un agente que planea es la diferencia entre un empleado que ejecuta tareas y uno que gestiona un proyecto.


El problema del planning

Imagina que le das a tu agente esta instrucción:

"Investiga el estado de AI safety en 2025 y genera un reporte completo."

Un agente sin planning intenta resolver todo en un paso: busca algo, genera un reporte con lo que encontró, y termina. El resultado es superficial porque no descompuso la tarea.

Un humano haría algo diferente:

1. Definir el alcance: ¿qué aspectos de AI safety? (alignment, evaluations, policy, open source)
2. Buscar papers académicos recientes
3. Buscar reportes de la industria (Anthropic, OpenAI, DeepMind)
4. Buscar policy documents (EU AI Act, US executive orders)
5. Analizar hallazgos cruzados
6. Escribir reporte final con secciones por tema

Ese es el tipo de descomposición que write_todos permite. El agente recibe una tarea vaga, la descompone en pasos concretos, y los ejecuta uno a uno con tracking de progreso.

¿Por qué no basta con un buen prompt?

Podrías pensar: "Le digo al agente en el system prompt que descomponga la tarea en pasos." Y sí, funciona parcialmente. Pero tiene tres problemas:

  1. Sin tracking: el agente no sabe qué pasos completó y cuáles faltan. Si el context window crece, pierde el hilo
  2. Sin re-planning: si en el paso 3 descubre que la tarea necesita un paso adicional, no tiene mecanismo para actualizar el plan
  3. Sin visibilidad: tú no puedes ver el plan del agente ni su progreso sin parsear la conversación completa

write_todos resuelve los tres: el plan es un objeto de datos que el agente puede leer, modificar, y que tú puedes inspeccionar.


write_todos: la herramienta de planning

write_todos es un tool que Deep Agents inyecta automáticamente en el agente. El agente lo usa como usaría cualquier otra herramienta — con tool calling.

Crear un 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=(
        "Eres un agente de investigación. "
        "Cuando recibas una tarea compleja, usa write_todos para "
        "descomponerla en pasos antes de ejecutar."
    ),
)

result = agent.run("Investiga las tendencias de IA generativa en 2025")

for todo in result.todos:
    print(f"[{todo['status']:>10}] {todo['title']}")
# Output esperado (varía según el modelo):
# [ completed] Definir alcance de la investigación
# [ completed] Buscar tendencias en modelos de lenguaje
# [ completed] Buscar tendencias en generación de imágenes/video
# [ completed] Buscar tendencias en agentes autónomos
# [ completed] Sintetizar hallazgos
# [ completed] Generar reporte final

El agente decidió por sí mismo cómo descomponer la tarea. No le dijiste qué pasos seguir — lo dedujo del contexto.

Estructura de un todo

Cada todo tiene tres campos:

{
    "title": "Buscar papers académicos",   # Descripción del paso
    "status": "pending",                     # pending | in_progress | completed | skipped
    "result": ""                             # El agente puede guardar el resultado del paso
}

Los estados posibles forman un ciclo:

pending → in_progress → completed
                      → skipped (si el agente decide que no es necesario)

write_todos como tool call

Internamente, cuando el agente llama a write_todos, pasa una lista de objetos:

# Lo que el agente genera como tool call:
write_todos([
    {"title": "Definir alcance de la investigación", "status": "pending"},
    {"title": "Buscar papers académicos", "status": "pending"},
    {"title": "Buscar reportes de industria", "status": "pending"},
    {"title": "Analizar hallazgos", "status": "pending"},
    {"title": "Generar reporte final", "status": "pending"},
])

Para actualizar progreso, el agente llama a write_todos de nuevo con los estados actualizados:

# Después de completar los primeros dos pasos:
write_todos([
    {"title": "Definir alcance de la investigación", "status": "completed"},
    {"title": "Buscar papers académicos", "status": "completed"},
    {"title": "Buscar reportes de industria", "status": "in_progress"},
    {"title": "Analizar hallazgos", "status": "pending"},
    {"title": "Generar reporte final", "status": "pending"},
])

Re-planning: la capacidad más poderosa

Un plan estático es útil. Un plan que se adapta es poderoso.

Cómo funciona el re-planning

El agente está en el paso 3, buscando reportes de la industria. Encuentra un paper de Anthropic que menciona un nuevo framework de evaluación de AI safety que no esperaba. El agente decide:

"Este framework merece su propia investigación. Voy a agregar un paso entre el 3 y el 4."

Y actualiza el plan:

write_todos([
    {"title": "Definir alcance de la investigación", "status": "completed"},
    {"title": "Buscar papers académicos", "status": "completed"},
    {"title": "Buscar reportes de industria", "status": "completed"},
    {"title": "Investigar framework de evaluación de Anthropic", "status": "pending"},  # NUEVO
    {"title": "Analizar hallazgos", "status": "pending"},
    {"title": "Generar reporte final", "status": "pending"},
])

El plan pasó de 5 a 6 pasos. El agente lo hizo sin intervención humana.

Re-planning por eliminación

También funciona al revés. El agente planeó buscar en 3 fuentes diferentes, pero la primera fuente ya tiene toda la información necesaria:

# Plan original: 5 pasos
# Después de descubrir que una fuente es suficiente:
write_todos([
    {"title": "Definir alcance", "status": "completed"},
    {"title": "Buscar en fuente primaria", "status": "completed"},
    {"title": "Buscar en fuente secundaria", "status": "skipped"},   # Ya no necesario
    {"title": "Buscar en fuente terciaria", "status": "skipped"},    # Ya no necesario
    {"title": "Analizar y sintetizar", "status": "pending"},
    {"title": "Generar reporte", "status": "pending"},
])

El agente marcó 2 pasos como skipped porque determinó que no aportaban valor. Esto reduce costos (menos llamadas al LLM) y tiempo de ejecución.

Ejemplo completo: investigación con 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=(
        "Eres un investigador. Para cada tarea:\n"
        "1. Usa write_todos para crear un plan inicial\n"
        "2. Ejecuta cada paso del plan\n"
        "3. Si descubres algo inesperado, actualiza el plan con write_todos\n"
        "4. Marca cada paso como completed o skipped cuando termines\n"
        "5. Escribe el reporte final en un archivo"
    ),
)

result = agent.run(
    "Investiga los avances más recientes en RAG (Retrieval-Augmented Generation)"
)

print("=== Plan final ===")
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"\nPasos completados: {sum(1 for t in result.todos if t['status'] == 'completed')}")
print(f"Pasos skipped:     {sum(1 for t in result.todos if t['status'] == 'skipped')}")
print(f"Total planificado: {len(result.todos)}")
# Output esperado (varía según el modelo y resultados de búsqueda):
# === Plan final ===
#   1. ✅ [completed] Definir qué aspectos de RAG investigar
#   2. ✅ [completed] Buscar papers recientes sobre RAG avanzado
#   3. ✅ [completed] Investigar implementaciones de RAG en producción
#   4. ✅ [completed] Investigar GraphRAG como variante emergente
#   5. ⏭️ [skipped] Buscar benchmarks de RAG vs fine-tuning
#   6. ✅ [completed] Sintetizar hallazgos
#   7. ✅ [completed] Generar reporte final
#
# Pasos completados: 6
# Pasos skipped:     1
# Total planificado: 7

El agente podría haber descubierto GraphRAG durante la investigación y agregado un paso. Podría haber decidido que comparar con fine-tuning no es relevante y skip ese paso. El plan se adaptó a lo que el agente encontró.


Progress tracking: el agente sabe dónde está

Cada vez que el agente necesita decidir qué hacer a continuación, consulta el estado de sus todos:

Contexto interno del agente:

Tarea: Investigar RAG avanzado
Plan actual:
  1. [completed] Definir alcance → "Enfocarme en RAG para producción"
  2. [completed] Buscar papers → "Encontré 5 papers relevantes"
  3. [in_progress] Buscar implementaciones → ejecutando...
  4. [pending] Analizar hallazgos
  5. [pending] Generar reporte

→ Siguiente acción: continuar con el paso 3

Sin write_todos, el agente tendría que parsear toda su conversación anterior para saber qué hizo y qué falta. Con write_todos, la información está estructurada y es fácil de consultar.

Cómo se almacena

Los todos se almacenan como parte del estado del agente. En cada turno, el agente tiene acceso al plan actualizado. No se pierde en el context window porque es un objeto de datos, no texto libre.

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="Descompone cualquier tarea en pasos con write_todos.",
)

result = agent.run("Crea un plan para aprender Kubernetes en 2 semanas")

print("=== Estado detallado ===")
for todo in result.todos:
    print(f"\n  Título: {todo['title']}")
    print(f"  Status: {todo['status']}")
    if todo.get('result'):
        print(f"  Result: {todo['result'][:100]}...")
# Output esperado (varía según el modelo):
# === Estado detallado ===
#
#   Título: Semana 1 - Fundamentos: instalar minikube y kubectl
#   Status: completed
#   Result: Instalación completada. kubectl version funciona correctamente...
#
#   Título: Semana 1 - Conceptos core: pods, deployments, services
#   Status: completed
#   Result: Aprendidos los conceptos fundamentales. Un pod es la unidad mínima...
#   ...

Comparación: planning manual (M6-M10) vs write_todos

Esta es la conexión explícita con lo que construiste en módulos anteriores.

Planning manual: tú diseñas el workflow

En los módulos 6-10, el planning era estático. Tú decidías:

# M7: Flujo que TÚ diseñaste
builder = StateGraph(ResearchState)
builder.add_node("decompose", decompose_query)    # Paso 1: siempre
builder.add_node("search", parallel_search)         # Paso 2: siempre
builder.add_node("analyze", analyze_results)        # Paso 3: siempre
builder.add_node("report", generate_report)         # Paso 4: siempre

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

El workflow es fijo. Siempre hace: decompose → search → analyze → report. El único dinamismo es el retry condicional, que tú diseñaste. Si la tarea necesita un paso extra, tienes que modificar el código.

write_todos: el agente diseña el workflow

Con Deep Agents, el agente decide el plan en 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=(
        "Investiga el tema proporcionado. "
        "Usa write_todos para planificar tu investigación. "
        "Adapta el plan según lo que descubras."
    ),
)

result = agent.run("Compara las estrategias de AI safety de OpenAI, Anthropic y DeepMind")

print(f"Plan tuvo {len(result.todos)} pasos")
print(f"Completados: {sum(1 for t in result.todos if t['status'] == 'completed')}")
# Output esperado (varía):
# Plan tuvo 8 pasos
# Completados: 7

El agente podría haber planeado 5 pasos, descubierto que necesita 8, y completado 7 (skipping 1 que no era necesario). Todo sin que modificaras código.

Tabla de comparación

CriterioPlanning manual (M6-M10)write_todos (Deep Agents)
Quién diseña el planTú, en build timeEl agente, en runtime
AdaptabilidadFija (necesitas modificar código)Dinámica (re-planning automático)
VisibilidadTotal (ves el grafo)Parcial (ves los todos, no las decisiones internas)
DebuggingFácil (sabes qué nodo falla)Más difícil (¿por qué el agente planeó así?)
CostoPredecible (N nodos fijos)Variable (depende del plan que genera)
FlexibilidadBaja para tareas diversasAlta (cada tarea genera su propio plan)
ControlTotalLimitado (el agente decide)
CódigoMás (diseñar workflow)Menos (instrucciones + herramienta)

La elección depende de tu caso: si las tareas son diversas y no puedes predecir el flujo, write_todos ahorra tiempo. Si necesitas garantías sobre qué pasos se ejecutan, el planning manual es más confiable.


Patrones efectivos de instrucciones para planning

La calidad del plan depende mucho de cómo instruyes al agente. Estos patrones producen mejores planes:

Patrón 1: Scope antes de plan

instructions = (
    "Antes de crear el plan con write_todos, define el alcance de la tarea. "
    "Pregúntate: ¿qué aspectos cubro? ¿qué profundidad? ¿qué excluyo? "
    "Luego crea un plan basado en ese alcance."
)

Sin scope, el agente crea planes vagos como "Investigar el tema" → "Escribir reporte." Con scope, genera pasos específicos.

Patrón 2: Granularidad explícita

instructions = (
    "Cada paso del plan debe ser ejecutable en una sola acción. "
    "Si un paso requiere múltiples acciones, descompónlo. "
    "'Buscar información' es demasiado vago. "
    "'Buscar papers sobre RAG publicados en 2025' es específico."
)

Patrón 3: Criterios de re-planning

instructions = (
    "Después de cada paso, evalúa si el plan necesita ajuste:\n"
    "- ¿Descubriste un tema nuevo que merece investigación?\n"
    "- ¿Algún paso pendiente ya no es necesario?\n"
    "- ¿Necesitas cambiar el orden de los pasos restantes?\n"
    "Si es así, actualiza el plan con write_todos."
)

Troubleshooting

Problema 1: El agente crea un plan pero no lo sigue

Síntoma: El agente usa write_todos para crear un plan de 5 pasos, pero luego ejecuta acciones que no corresponden a ningún paso. Los todos quedan en pending mientras el agente trabaja en otra cosa. Causa: Las instrucciones no vinculan explícitamente la ejecución con el plan. El agente "olvida" que tiene un plan porque el context window se llena con resultados de herramientas. Solución: Agrega una instrucción explícita que fuerce la consulta del plan:

instructions = (
    "SIEMPRE consulta tu plan (write_todos) antes de decidir qué hacer. "
    "Tu siguiente acción debe corresponder al primer todo con status 'pending'. "
    "Marca el todo como 'in_progress' antes de empezar y 'completed' al terminar."
)

Problema 2: Plans demasiado granulares (20+ pasos)

Síntoma: El agente descompone una tarea simple en 20 micro-pasos, gastando tokens en planning en vez de ejecución. Causa: Las instrucciones piden "descomponer en pasos manejables" sin definir qué es "manejable." Solución: Define un rango de granularidad:

instructions = (
    "Crea un plan de 3-7 pasos para la tarea. "
    "Si necesitas más de 7, agrupa pasos relacionados. "
    "Si necesitas menos de 3, probablemente la tarea no necesita planning — ejecútala directamente."
)

Problema 3: El agente no re-planifica cuando debería

Síntoma: El agente sigue ejecutando su plan original incluso cuando los resultados sugieren un cambio de dirección. Completa pasos que ya no son relevantes. Causa: El agente no tiene instrucciones explícitas para evaluar si el plan sigue vigente. Solución: Agrega checkpoints de re-evaluación:

instructions = (
    "Después de completar cada paso, evalúa el plan completo: "
    "¿los pasos pendientes siguen siendo relevantes dado lo que descubriste? "
    "Si no, actualiza el plan con write_todos antes de continuar."
)

Problema 4: Todos sin resultados útiles

Síntoma: Todos los pasos se marcan como completed pero result está vacío o contiene texto genérico como "Completado." Causa: El agente no recibe instrucciones para documentar el resultado de cada paso. Solución: Pide resultados concretos:

instructions = (
    "Al completar un paso, incluye un resultado específico en el todo. "
    "No escribas 'Completado' — escribe qué encontraste, qué decidiste, "
    "o qué output generaste."
)

Ejercicios

Ejercicio 1: Plan básico de investigación (Fácil)

Crea un Deep Agent que planifique una investigación sobre "el impacto de LLMs en educación." Imprime el plan generado con estados y títulos.

Ver solución
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=(
        "Eres un investigador de tecnología educativa. "
        "Para cada tarea, crea un plan de 4-6 pasos con write_todos. "
        "Ejecuta cada paso y marca su progreso."
    ),
)

result = agent.run("Investiga el impacto de LLMs en educación")

print("=== Plan de investigación ===")
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)} pasos, "
      f"{sum(1 for t in result.todos if t['status'] == 'completed')} completados")
# Output esperado (varía):
# === Plan de investigación ===
#   1. ✅ Definir aspectos clave del impacto de LLMs en educación
#   2. ✅ Investigar uso de LLMs como tutores personalizados
#   3. ✅ Investigar impacto en evaluación y plagio académico
#   4. ✅ Investigar accesibilidad y democratización del conocimiento
#   5. ✅ Sintetizar hallazgos y generar conclusiones
#
# Total: 5 pasos, 5 completados

Explicación: El agente recibió una tarea abierta y la descompuso en pasos concretos. Sin write_todos, intentaría resolver todo en un paso, produciendo un resultado superficial.

Ejercicio 2: Plan con herramienta de búsqueda (Fácil)

Agrega TavilySearchResults al agente del ejercicio 1 y observa cómo cambia el plan cuando el agente tiene acceso a información real.

Ver solución
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=(
        "Eres un investigador de tecnología educativa. "
        "Usa write_todos para planificar tu investigación (4-6 pasos). "
        "Usa web_search para buscar información real en cada paso. "
        "Adapta el plan según lo que descubras."
    ),
)

result = agent.run("Investiga el impacto de LLMs en educación en 2025")

print("=== Plan ejecutado ===")
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)} pasos")
# Output esperado (varía según resultados de búsqueda):
# === Plan ejecutado ===
#   1. ✅ [completed] Definir dimensiones de impacto
#      → Tres dimensiones: personalización, evaluación, accesibilidad...
#   2. ✅ [completed] Buscar estudios sobre tutorías personalizadas con LLMs
#      → Encontrados 3 estudios: Khan Academy + GPT-4, Duolingo Max...
#   3. ✅ [completed] Buscar impacto en integridad académica
#      → Papers sobre detección de plagio con IA, políticas universitarias...
#   4. ✅ [completed] Buscar sobre accesibilidad y brecha digital
#      → UNESCO report 2025, iniciativas en América Latina...
#   5. ✅ [completed] Sintetizar hallazgos en reporte
#      → Reporte generado con 3 secciones principales...
#
# Total: 5 pasos

Explicación: Con herramientas de búsqueda, el plan se vuelve más específico porque el agente puede buscar información real. Compara este plan con el del ejercicio 1: los pasos son más concretos y los resultados incluyen datos reales.

Ejercicio 3: Observar re-planning (Medio)

Crea un agente que investigue un tema técnico. En las instrucciones, pídele explícitamente que re-planifique si descubre un subtema inesperado. Compara el número de pasos iniciales vs el plan final.

Ver solución
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=(
        "Eres un investigador técnico. Sigue estas reglas:\n"
        "1. Crea un plan inicial de 4 pasos con write_todos\n"
        "2. Después de cada búsqueda, evalúa si descubriste algo inesperado\n"
        "3. Si descubres un subtema importante, AGREGA un paso al plan\n"
        "4. Si un paso pendiente ya no es relevante, márcalo como skipped\n"
        "5. Documenta cada cambio al plan"
    ),
)

result = agent.run("Investiga el estado actual de GraphRAG y sus variantes")

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

print(f"=== Estadísticas de planning ===")
print(f"  Pasos totales en plan final: {len(result.todos)}")
print(f"  Completados: {len(completed)}")
print(f"  Skipped:     {len(skipped)}")
print(f"\n=== Plan final ===")
for i, todo in enumerate(result.todos, 1):
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"  {i}. {icon} {todo['title']}")
# Output esperado (varía):
# === Estadísticas de planning ===
#   Pasos totales en plan final: 6
#   Completados: 5
#   Skipped:     1
#
# === Plan final ===
#   1. ✅ Definir qué es GraphRAG y sus diferencias con RAG clásico
#   2. ✅ Buscar el paper original de Microsoft sobre GraphRAG
#   3. ✅ Buscar implementaciones open source
#   4. ✅ Investigar RAPTOR como variante jerárquica  ← re-planning: paso agregado
#   5. ⏭️ Comparar con RAG clásico en benchmarks   ← skipped
#   6. ✅ Sintetizar hallazgos y generar reporte

Explicación: El agente empezó con ~4 pasos pero agregó uno (RAPTOR como variante) cuando lo descubrió durante la búsqueda. También marcó un paso como skipped si determinó que no era necesario. Esto es re-planning en acción.

Ejercicio 4: Comparar ejecución con y sin planning (Medio)

Crea dos agentes: uno con instrucciones de planning explícitas y otro sin ellas. Dale la misma tarea a ambos y compara la calidad del output.

Ver solución
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=(
        "Eres un analista. Para cada tarea:\n"
        "1. Usa write_todos para crear un plan de 4-6 pasos\n"
        "2. Ejecuta cada paso metódicamente\n"
        "3. Marca el progreso de cada paso"
    ),
)

agent_without_planning = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Without Planning",
    instructions=(
        "Eres un analista. Responde la solicitud directamente "
        "sin usar write_todos."
    ),
)

task = "Analiza las ventajas y desventajas de microservicios vs monolitos"

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

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

print("\n=== Sin planning ===")
print(f"  Todos: {len(result_direct.todos)}")
print(f"  Archivos: {list(result_direct.files.keys())}")
print(f"  Output length: {len(result_direct.output)} chars")
# Output esperado (varía):
# === Con planning ===
#   Todos: 5
#   Archivos: ['output/analysis.md']
#   Output length: 2847 chars
#
# === Sin planning ===
#   Todos: 0
#   Archivos: []
#   Output length: 1203 chars

Explicación: El agente con planning produce output más estructurado y completo porque abordó la tarea paso por paso. El agente sin planning responde directamente, lo cual es más rápido pero típicamente más superficial. La diferencia se acentúa con tareas más complejas.

Ejercicio 5: Plan con dependencias entre pasos (Avanzado)

Crea un agente que investigue un tema donde los pasos posteriores dependen de los resultados de pasos anteriores. Por ejemplo: "Identifica los 3 papers más citados sobre X, luego analiza cada uno." El análisis depende de qué papers encontró.

Ver solución
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=(
        "Eres un investigador académico. Tu proceso:\n"
        "1. Crea un plan inicial con write_todos (3-4 pasos genéricos)\n"
        "2. Ejecuta el primer paso (identificar fuentes clave)\n"
        "3. BASÁNDOTE en lo que encontraste, RE-PLANIFICA: agrega pasos "
        "   específicos para analizar cada fuente encontrada\n"
        "4. Ejecuta los pasos actualizados\n"
        "5. Sintetiza al final\n\n"
        "El plan final debe reflejar las fuentes reales que encontraste, "
        "no categorías genéricas."
    ),
)

result = agent.run("Identifica y analiza los trabajos más relevantes sobre prompt engineering")

print("=== Plan con dependencias ===")
for i, todo in enumerate(result.todos, 1):
    icon = {"completed": "✅", "skipped": "⏭️"}.get(todo["status"], "⏳")
    print(f"  {i}. {icon} {todo['title']}")
# Output esperado (varía según resultados de búsqueda):
# === Plan con dependencias ===
#   1. ✅ Buscar los trabajos más citados sobre prompt engineering
#   2. ✅ Analizar: "Chain-of-Thought Prompting Elicits Reasoning" (Wei et al.)
#   3. ✅ Analizar: "Large Language Models are Zero-Shot Reasoners" (Kojima et al.)
#   4. ✅ Analizar: "Tree of Thoughts" (Yao et al.)
#   5. ✅ Comparar enfoques y sintetizar hallazgos
#   6. ✅ Generar reporte final con ranking y recomendaciones

Explicación: Los pasos 2-4 no existían en el plan original — fueron creados por re-planning después de que el paso 1 identificó papers específicos. El plan se adaptó a la información real encontrada, no a categorías genéricas predefinidas.

Ejercicio 6: Dashboard de progreso en tiempo real (Avanzado)

Usa agent.stream() en lugar de agent.run() para observar cómo el agente actualiza los todos en tiempo real. Imprime cada actualización del plan.

Ver solución
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=(
        "Descompone la tarea en 4-5 pasos con write_todos. "
        "Ejecuta cada paso y actualiza el progreso después de cada uno."
    ),
)

print("=== Progreso en tiempo real ===\n")

previous_todos_count = 0
for event in agent.stream("Crea un análisis FODA de usar IA en 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} completados")
            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("=== Ejecución completada ===")
# Output esperado (varía):
# === Progreso en tiempo real ===
#
#   [░░░░] 0/4 completados
#     ⏳ Definir componentes del FODA
#     ⏳ Analizar Fortalezas y Oportunidades
#     ⏳ Analizar Debilidades y Amenazas
#     ⏳ Generar matriz FODA y conclusiones
#
#   [█▓░░] 1/4 completados
#     ✅ Definir componentes del FODA
#     🔄 Analizar Fortalezas y Oportunidades
#     ⏳ Analizar Debilidades y Amenazas
#     ⏳ Generar matriz FODA y conclusiones
#
#   ... (progresivamente)
#
# === Ejecución completada ===

Explicación: agent.stream() emite eventos a medida que el agente trabaja. Filtrando los eventos que contienen todos, puedes construir un dashboard de progreso en tiempo real. Esto es útil en aplicaciones donde quieres mostrar al usuario qué está haciendo el agente.


Resumen

  • write_todos es una herramienta de planning estratégico, no una lista de pendientes. El agente descompone tareas complejas en pasos, trackea progreso, y re-planifica cuando los resultados cambian
  • Re-planning es la capacidad más poderosa: el agente puede agregar pasos, marcar otros como skipped, y reordenar prioridades — todo sin intervención humana
  • Cada todo tiene title, status (pending/in_progress/completed/skipped), y opcionalmente result — es un objeto de datos estructurado, no texto libre
  • La diferencia con planning manual (M6-M10): tú diseñabas el workflow en build time con nodos y edges fijos. Con write_todos, el agente diseña su propio workflow en runtime y lo adapta dinámicamente
  • Las instrucciones al agente determinan la calidad del plan: pide scope antes de planear, define granularidad, y establece criterios de re-evaluación
  • Progress tracking permite que el agente sepa exactamente qué completó y qué falta, sin parsear toda su conversación. También permite que tú inspecciones el estado del agente en cualquier momento

Próxima cápsula: Virtual Filesystem — cómo el agente usa archivos para offloadear contexto del LLM, reducir costos, y manejar investigaciones de cualquier tamaño.


Recursos adicionales

  1. Deep Agents — Planning & Todos — Documentación oficial de write_todos y planning en Deep Agents
  2. Plan-and-Solve Prompting — Paper académico sobre planning en LLMs que inspira la arquitectura de write_todos
  3. LangGraph — State Management — Cómo LangGraph maneja estado, la base sobre la que write_todos almacena el plan
  4. Inner Monologue: Embodied Reasoning through Planning — Paper sobre planning como monólogo interno en agentes, relevante para entender por qué el planning explícito mejora resultados
  5. Cognitive Architectures for Language Agents — Marco teórico de planning, memory y tool use en agentes — el "por qué" detrás de write_todos
  6. Building Effective Agents — Anthropic — Guía de Anthropic sobre descomposición de tareas, aplicable a cómo instruir agentes para que planeen bien

Módulo 11 — LangChain & LangGraph: From Chains to Agents

Siguiente cápsula: Virtual Filesystem — aprenderás cómo el agente usa archivos para manejar información sin saturar el context window, reduciendo costos y habilitando investigaciones de cualquier tamaño.