Módulo 11: Deep Agents
create_agent vs LangGraph vs Deep Agents: Árbol de Decisión
Descripción de la cápsula
Esta es la cápsula más importante del módulo. Posiblemente la más importante de toda la guía.
Después de 11 módulos, dominas tres niveles de abstracción para construir agentes. Puedes usar create_agent para resolver el 80% de los problemas con 15 líneas de código. Puedes usar LangGraph para workflows complejos donde necesitas control sobre cada nodo, edge, y condición. Y ahora puedes usar Deep Agents para tareas autónomas de larga duración con planning, filesystem, y subagentes built-in.
El problema no es que no sepas usar cada herramienta — es que en un proyecto real, necesitas decidir cuál usar. Y esa decisión impacta todo: tiempo de desarrollo, mantenibilidad, costos de ejecución, y capacidad de debugging.
Esta cápsula te da un framework de decisión definitivo. No opiniones — criterios técnicos con trade-offs explícitos.
Los tres niveles de abstracción
Vista rápida
| Nivel | Herramienta | Líneas de código | Control | Mejor para |
|---|---|---|---|---|
| Alto | create_agent | ~10-20 | Limitado (loop ReAct) | 80% de tareas de agentes |
| Medio | LangGraph | ~50-200 | Total (workflow custom) | Workflows complejos, HITL |
| Batteries | Deep Agents | ~20-50 | Moderado (framework decide) | Tareas autónomas largas |
Nivel 1: create_agent — simple, rápido, suficiente
from dotenv import load_dotenv
load_dotenv()
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
model = ChatOpenAI(model="gpt-4.1-mini")
tools = [TavilySearchResults(max_results=3)]
agent = create_agent(model, tools)
result = agent.invoke(
{"messages": [{"role": "user", "content": "¿Qué es RAG y por qué es importante?"}]}
)
print(result["messages"][-1].content[:200])
# Output esperado (varía según el modelo):
# RAG (Retrieval-Augmented Generation) es una técnica que combina la generación
# de texto con la recuperación de información de fuentes externas...
Qué hace por ti:
- ✅ Bind de tools al modelo
- ✅ Loop ReAct (reason → act → observe → repeat)
- ✅ Manejo de tool calling automático
Qué NO hace:
- ❌ Custom workflow (no puedes controlar el orden de ejecución)
- ❌ Persistencia (se pierde al terminar)
- ❌ Branching condicional
- ❌ Multi-agente
- ❌ HITL
Líneas de código: ~15
Nivel 2: LangGraph — control total, tú diseñas todo
from dotenv import load_dotenv
load_dotenv()
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
from typing import TypedDict, Annotated
import operator
class ResearchState(TypedDict):
query: str
messages: Annotated[list, operator.add]
sources: list[dict]
analysis: str
report: str
quality_score: float
model = ChatOpenAI(model="gpt-4.1-mini")
search = TavilySearchResults(max_results=5)
def search_node(state: ResearchState) -> dict:
results = search.invoke(state["query"])
sources = [{"title": r.get("title", ""), "content": r.get("content", "")[:500]} for r in results]
return {
"sources": sources,
"messages": [{"role": "system", "content": f"Found {len(sources)} sources"}],
}
def analyze_node(state: ResearchState) -> dict:
sources_text = "\n".join(s["content"][:200] for s in state["sources"])
response = model.invoke(f"Analiza estas fuentes sobre '{state['query']}':\n{sources_text}")
return {
"analysis": response.content,
"messages": [{"role": "system", "content": "Analysis complete"}],
}
def report_node(state: ResearchState) -> dict:
response = model.invoke(
f"Genera un reporte sobre '{state['query']}' basado en:\n{state['analysis']}"
)
return {
"report": response.content,
"quality_score": 0.85,
"messages": [{"role": "system", "content": "Report generated"}],
}
def route_by_quality(state: ResearchState) -> str:
if state.get("quality_score", 0) < 0.7:
return "search"
return END
builder = StateGraph(ResearchState)
builder.add_node("search", search_node)
builder.add_node("analyze", analyze_node)
builder.add_node("report", report_node)
builder.add_edge(START, "search")
builder.add_edge("search", "analyze")
builder.add_edge("analyze", "report")
builder.add_conditional_edges("report", route_by_quality, {"search": "search", END: END})
graph = builder.compile(checkpointer=MemorySaver())
result = graph.invoke(
{"query": "Estado de RAG en 2025", "messages": [], "sources": [], "analysis": "", "report": "", "quality_score": 0.0},
config={"configurable": {"thread_id": "research-001"}},
)
print(f"Sources encontradas: {len(result['sources'])}")
print(f"Quality score: {result['quality_score']}")
print(f"Report length: {len(result['report'])} chars")
# Output esperado (varía según el modelo):
# Sources encontradas: 5
# Quality score: 0.85
# Report length: ~1500 chars
Qué hace por ti:
- ✅ Estado tipado con TypedDict
- ✅ Nodos y edges definidos por ti
- ✅ Branching condicional (route_by_quality)
- ✅ Checkpointing y persistencia
- ✅ Retry, error handling, HITL
- ✅ Multi-agente (supervisors, subgraphs)
Qué NO hace:
- ❌ Planning automático (tú defines el workflow)
- ❌ Filesystem built-in
- ❌ Subagent spawning dinámico
Líneas de código: ~70 (ejemplo simplificado), ~150+ para un sistema completo
Nivel 3: Deep Agents — autónomo, batteries-included
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="Research Assistant",
instructions=(
"Investiga temas complejos. Descompone la investigación en pasos, "
"escribe hallazgos en archivos separados por fuente, "
"delega búsquedas especializadas a subagentes, "
"y genera un reporte final consolidado."
),
)
result = agent.run("Investiga el estado de RAG en 2025 y genera un reporte")
print(f"Archivos generados: {list(result.files.keys())}")
print(f"Todos completados: {sum(1 for t in result.todos if t['status'] == 'completed')}/{len(result.todos)}")
print(f"Subagentes usados: {len(result.subagents_spawned)}")
# Output esperado (varía según el modelo):
# Archivos generados: ['research/web_search.md', 'analysis/synthesis.md', 'output/report.md']
# Todos completados: 5/5
# Subagentes usados: 2
Qué hace por ti:
- ✅ Planning automático con write_todos
- ✅ Virtual filesystem para context offloading
- ✅ Subagent spawning dinámico
- ✅ Long-term memory con backends pluggable
- ✅ CLI built-in
Qué NO hace:
- ❌ Control fino sobre cada paso (el framework decide el flujo)
- ❌ Custom branching condicional
- ❌ HITL granular (apruebas la tarea, no cada paso)
Líneas de código: ~20-40
El árbol de decisión
Este es el framework que te llevas. Imprime, memoriza, o referencia cuando empieces un proyecto nuevo.
¿Tu agente necesita usar tools?
└─ NO → No necesitas un agente. Usa el modelo directamente.
└─ SÍ ↓
¿Es un patrón simple? (Q&A con tools, chatbot, búsqueda directa)
└─ SÍ → create_agent (~15 líneas, listo)
└─ NO ↓
¿Necesitas control total sobre el flujo de decisiones?
(branching condicional, retry con lógica custom, HITL en puntos específicos)
└─ SÍ → LangGraph (StateGraph o Functional API)
└─ NO ↓
¿La tarea es autónoma, de larga duración, y multi-paso?
(investigación, generación de código, análisis complejo)
└─ SÍ → Deep Agents
└─ NO ↓
¿Necesitas multi-agente con roles fijos y coordinación explícita?
└─ SÍ → LangGraph (supervisors, handoffs, subgraphs de M10)
└─ NO ↓
¿Necesitas multi-agente dinámico donde el agente decide qué delegar?
└─ SÍ → Deep Agents (subagent spawning)
└─ NO → LangGraph (más flexible que Deep Agents sin overhead de planning)
Versión simplificada para referencia rápida
┌─────────────────────────────────────────────────────────────┐
│ ¿Qué agente necesito? │
├─────────────────────────────────────────────────────────────┤
│ │
│ Simple (Q&A, chatbot, búsqueda) → create_agent │
│ │
│ Custom workflow (branching, → LangGraph │
│ HITL, retry, roles fijos) │
│ │
│ Autónomo + planning + delegación → Deep Agents │
│ │
└─────────────────────────────────────────────────────────────┘
Side-by-side: el mismo Research Agent en 3 versiones
El mejor test de un framework de decisión es aplicarlo al mismo problema. Aquí tienes el mismo agente de investigación implementado en los tres niveles.
Versión 1: create_agent (~15 líneas)
from dotenv import load_dotenv
load_dotenv()
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
model = ChatOpenAI(model="gpt-4.1-mini")
tools = [TavilySearchResults(max_results=5)]
agent = create_agent(
model,
tools,
prompt="Eres un asistente de investigación. Busca información relevante y genera respuestas claras.",
)
result = agent.invoke(
{"messages": [{"role": "user", "content": "Investiga el estado de AI agents en 2025"}]}
)
print(f"Líneas de código: ~15")
print(f"Respuesta: {result['messages'][-1].content[:100]}...")
# Output esperado:
# Líneas de código: ~15
# Respuesta: Los AI agents en 2025 han evolucionado significativamente...
- ✅ Funciona inmediatamente
- ✅ Busca con TavilySearch
- ❌ No planifica — busca y responde en un solo paso
- ❌ Sin persistencia — se pierde al terminar
- ❌ Sin organización de hallazgos — todo en el context window
Versión 2: LangGraph (~150 líneas)
from dotenv import load_dotenv
load_dotenv()
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
from langgraph.store.memory import InMemoryStore
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
from typing import TypedDict, Annotated
import operator
import json
class ResearchState(TypedDict):
query: str
messages: Annotated[list, operator.add]
plan: list[str]
sources: list[dict]
analysis: str
report: str
iteration: int
max_iterations: int
quality_score: float
model_mini = ChatOpenAI(model="gpt-4.1-mini")
model_full = ChatOpenAI(model="gpt-4.1")
search = TavilySearchResults(max_results=5)
def plan_node(state: ResearchState) -> dict:
response = model_mini.invoke(
f"Descompone esta investigación en 3-5 pasos concretos. "
f"Tema: {state['query']}. Responde SOLO como JSON array de strings."
)
try:
plan = json.loads(response.content)
except json.JSONDecodeError:
plan = ["Buscar fuentes", "Analizar información", "Generar reporte"]
return {
"plan": plan,
"messages": [{"role": "system", "content": f"Plan: {len(plan)} steps"}],
}
def search_node(state: ResearchState) -> dict:
results = search.invoke(state["query"])
sources = []
for r in results:
sources.append({
"title": r.get("title", "Unknown"),
"content": r.get("content", "")[:500],
"url": r.get("url", ""),
})
return {
"sources": sources,
"messages": [{"role": "system", "content": f"Found {len(sources)} sources"}],
}
def analyze_node(state: ResearchState) -> dict:
sources_text = "\n\n".join(
f"**{s['title']}**: {s['content']}" for s in state["sources"]
)
response = model_full.invoke(
f"Analiza estas fuentes sobre '{state['query']}'.\n\n{sources_text}\n\n"
f"Identifica: patrones principales, contradicciones, y insights clave."
)
return {
"analysis": response.content,
"messages": [{"role": "system", "content": "Analysis complete"}],
}
def report_node(state: ResearchState) -> dict:
response = model_full.invoke(
f"Genera un reporte de investigación sobre '{state['query']}'.\n\n"
f"Análisis: {state['analysis']}\n\n"
f"Fuentes: {len(state['sources'])}\n\n"
f"El reporte debe tener: Resumen Ejecutivo, Hallazgos, Conclusiones, Fuentes."
)
return {
"report": response.content,
"quality_score": 0.85,
"iteration": state.get("iteration", 0) + 1,
"messages": [{"role": "system", "content": "Report generated"}],
}
def evaluate_quality(state: ResearchState) -> str:
if state.get("quality_score", 0) < 0.7 and state.get("iteration", 0) < state.get("max_iterations", 3):
return "search"
return "end"
builder = StateGraph(ResearchState)
builder.add_node("plan", plan_node)
builder.add_node("search", search_node)
builder.add_node("analyze", analyze_node)
builder.add_node("report", report_node)
builder.add_edge(START, "plan")
builder.add_edge("plan", "search")
builder.add_edge("search", "analyze")
builder.add_edge("analyze", "report")
builder.add_conditional_edges("report", evaluate_quality, {"search": "search", "end": END})
checkpointer = MemorySaver()
store = InMemoryStore()
graph = builder.compile(checkpointer=checkpointer, store=store)
result = graph.invoke(
{
"query": "Estado de AI agents en 2025",
"messages": [],
"plan": [],
"sources": [],
"analysis": "",
"report": "",
"iteration": 0,
"max_iterations": 3,
"quality_score": 0.0,
},
config={"configurable": {"thread_id": "research-002"}},
)
print(f"Líneas de código: ~150")
print(f"Plan: {len(result['plan'])} pasos")
print(f"Sources: {len(result['sources'])}")
print(f"Quality: {result['quality_score']}")
print(f"Report length: {len(result['report'])} chars")
# Output esperado (varía según el modelo):
# Líneas de código: ~150
# Plan: 4 pasos
# Sources: 5
# Quality: 0.85
# Report length: ~2000 chars
- ✅ Planning explícito (tú diseñaste plan_node)
- ✅ Retry con evaluación de calidad (conditional edge)
- ✅ Persistencia con checkpointer
- ✅ Modelos diferentes por nodo (mini para plan, full para análisis)
- ❌ Tú escribiste cada nodo y edge — ~150 líneas
- ❌ Sin filesystem (todo en state)
- ❌ Sin subagent spawning (flujo fijo)
Versión 3: Deep Agents (~40 líneas)
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from deep_agents.memory import FilesystemMemoryBackend
from langchain_community.tools import TavilySearchResults
web_search = TavilySearchResults(max_results=5)
memory = FilesystemMemoryBackend(base_path="./research_memory")
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[web_search],
name="Research Assistant",
instructions=(
"Eres un asistente de investigación. "
"Para cada tema: "
"1) Descompone la investigación en pasos con write_todos. "
"2) Busca fuentes diversas (académicas, industria, noticias). "
"3) Escribe hallazgos de cada fuente en archivos separados. "
"4) Delega búsquedas especializadas a subagentes cuando el tema es amplio. "
"5) Sintetiza y genera un reporte final en output/report.md."
),
memory=memory,
max_iterations=15,
)
result = agent.run("Investiga el estado de AI agents en 2025 y genera un reporte detallado")
print(f"Líneas de código: ~40")
print(f"Archivos: {list(result.files.keys())}")
print(f"Todos: {sum(1 for t in result.todos if t['status'] == 'completed')}/{len(result.todos)}")
print(f"Subagentes: {len(result.subagents_spawned)}")
# Output esperado (varía según el modelo):
# Líneas de código: ~40
# Archivos: ['research/web_search.md', 'research/academic.md', 'analysis/synthesis.md', 'output/report.md']
# Todos: 5/5
# Subagentes: 2
- ✅ Planning automático (write_todos)
- ✅ Virtual filesystem (archivos organizados)
- ✅ Subagent spawning (delegación dinámica)
- ✅ Memory entre sesiones
- ❌ No controlas el flujo exacto
- ❌ No defines branching condicional
- ❌ Debugging más opaco
Tabla de comparación completa
| Aspecto | create_agent | LangGraph | Deep Agents |
|---|---|---|---|
| Líneas de código | ~15 | ~50-200 | ~20-50 |
| Planning | Ninguno | Tú lo diseñas | Automático (write_todos) |
| Workflow | Loop ReAct fijo | Custom (tú defines nodos/edges) | Framework decide |
| Persistencia | Ninguna (o +checkpointer) | Checkpointer built-in | Virtual filesystem |
| Multi-agente | No | Tú lo implementas | Subagent spawning automático |
| HITL | No | interrupt() en puntos exactos | Aprobación de tarea general |
| Memory | No | Store manual (M8) | Backend configurable |
| Branching | No | Conditional edges | El framework decide |
| Retry | No | Tú lo implementas | Automático (re-planning) |
| Debugging | Directo (logs del modelo) | Inspección de estado por nodo | Logs de planning + archivos |
| Costo por ejecución | Bajo (1-3 LLM calls) | Medio (5-15 LLM calls) | Alto (10-30+ LLM calls) |
| Setup time | 5 minutos | 30-60 minutos | 10 minutos |
| CLI | Lo construyes tú | Lo construyes tú | Built-in |
| Ideal para | Chatbots, Q&A, búsqueda | Workflows de negocio, pipelines | Investigación, análisis largo |
Qué abstrae cada nivel
Entender esto es clave para elegir bien. Cada nivel abstrae cosas que el anterior te obliga a construir.
create_agent abstrae:
- El loop reason → act → observe → repeat
- El bind de tools al modelo
- El parsing de tool calls y ejecución
Tú sigues manejando: estado, flujo, persistencia, multi-agente, HITL.
LangGraph abstrae:
- Estado tipado y accesible por todos los nodos
- Flujo de ejecución con edges (secuencial, condicional, paralelo)
- Persistencia con checkpointers
- Interrupt/resume para HITL
- Subgrafos para multi-agente
Tú sigues manejando: planning, filesystem, subagent spawning dinámico, memoria cross-session.
Deep Agents abstrae:
- Planning con write_todos (descomposición, tracking, re-planning)
- Virtual filesystem (leer, escribir, organizar outputs)
- Subagent spawning (crear agentes on-demand, contexto aislado)
- Long-term memory (backends pluggable, retrieve/store automático)
- CLI (interfaz de terminal completa)
Tú sigues manejando: las instrucciones que guían al agente, la selección de tools, y la evaluación de resultados.
Visualización del espectro
Control
▲
│
LangGraph ──┤ Total: nodos, edges, estado, flujo
│
create_agent ─────┤ Parcial: tools sí, flujo no
│
Deep Agents ────┤ Mínimo: instrucciones y tools
│
└──────────────────────────► Conveniencia
10 escenarios reales: ¿qué usarías?
Para cada escenario, la respuesta incluye la herramienta y la razón.
1. Chatbot de soporte técnico
Herramienta: create_agent
Por qué: El patrón es simple — recibir pregunta, buscar en base de conocimiento, responder. No necesita planning ni multi-agente. Un loop ReAct con tools de búsqueda resuelve el 95% de los tickets.
2. Pipeline de aprobación de préstamos
Herramienta: LangGraph
Por qué: Hay un flujo regulado: verificar identidad → evaluar crédito → análisis de riesgo → aprobación humana → notificación. Cada paso tiene reglas específicas. Necesitas HITL en puntos exactos (aprobación humana). Necesitas audit trail (checkpoints). Este es el caso textbook de LangGraph.
3. Investigación de mercado autónoma
Herramienta: Deep Agents
Por qué: "Investiga el mercado de X, compara 5 competidores, genera un reporte." Es autónomo, de larga duración, multi-paso. Planning, filesystem, y subagentes para búsquedas paralelas son exactamente lo que Deep Agents provee.
4. Generador de emails personalizados
Herramienta: create_agent
Por qué: Input → buscar datos del cliente → generar email → output. Es un flujo lineal sin branching. No necesita planning. create_agent con un tool de CRM resuelve esto en 15 líneas.
5. Sistema de code review multi-paso
Herramienta: LangGraph
Por qué: El flujo requiere: leer código → identificar issues → clasificar por severidad → generar sugerencias → si hay issues críticos, notificar al lead. Branching condicional, roles diferentes por paso, y HITL para issues críticos. LangGraph te da el control necesario.
6. Generación de documentación de proyecto completo
Herramienta: Deep Agents
Por qué: Leer todo el código del proyecto, entender la arquitectura, generar docs para cada módulo, y crear un README consolidado. Requiere planning (qué documentar primero), filesystem (un doc por módulo), y potencialmente subagentes (un subagente por módulo grande).
7. Asistente de agenda/calendario
Herramienta: create_agent
Por qué: "¿Qué tengo hoy?" → buscar en calendario → responder. "Agenda reunión con X" → crear evento. Tools simples, flujo lineal, sin necesidad de planning.
8. Pipeline de ETL con validación
Herramienta: LangGraph
Por qué: Extraer datos → validar formato → transformar → cargar. Si la validación falla, retry con diferentes parámetros. Si falla 3 veces, notificar y parar. Este flujo necesita conditional edges, retry con lógica custom, y estado persistente. LangGraph.
9. Agente de análisis financiero multi-fuente
Herramienta: Deep Agents
Por qué: Investigar indicadores de múltiples fuentes (mercado, SEC filings, noticias), analizar correlaciones, generar un reporte con gráficos. Es autónomo, multi-fuente, multi-paso, y requiere subagentes especializados por tipo de fuente. Deep Agents abstrae la coordinación.
10. Moderador de contenido con escalación
Herramienta: LangGraph
Por qué: Analizar contenido → clasificar riesgo → si es bajo, aprobar automáticamente; si es medio, flaggear para review; si es alto, bloquear y notificar. HITL en el nivel medio, reglas de negocio estrictas, y audit trail obligatorio. LangGraph da el control que un moderador de contenido requiere.
Resumen visual
create_agent (simple): 1, 4, 7
LangGraph (control): 2, 5, 8, 10
Deep Agents (autónomo): 3, 6, 9
La distribución refleja la realidad: ~30% de los proyectos son simples (create_agent), ~40% necesitan workflow custom (LangGraph), ~30% son tareas autónomas largas (Deep Agents).
Enfoques híbridos
No estás limitado a elegir uno solo. Las herramientas se componen entre sí.
create_agent dentro de LangGraph
Usa create_agent como un nodo dentro de un grafo LangGraph:
from dotenv import load_dotenv
load_dotenv()
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class State(TypedDict):
messages: Annotated[list, operator.add]
query: str
model = ChatOpenAI(model="gpt-4.1-mini")
search_tools = [TavilySearchResults(max_results=3)]
search_agent = create_agent(model, search_tools)
def search_node(state: State) -> dict:
result = search_agent.invoke(
{"messages": [{"role": "user", "content": state["query"]}]}
)
return {"messages": [{"role": "system", "content": result["messages"][-1].content}]}
def summarize_node(state: State) -> dict:
response = model.invoke(f"Resume en 3 bullet points:\n{state['messages'][-1]['content']}")
return {"messages": [{"role": "assistant", "content": response.content}]}
builder = StateGraph(State)
builder.add_node("search", search_node)
builder.add_node("summarize", summarize_node)
builder.add_edge(START, "search")
builder.add_edge("search", "summarize")
builder.add_edge("summarize", END)
graph = builder.compile()
result = graph.invoke({"query": "AI agents trends 2025", "messages": []})
print(f"Mensajes generados: {len(result['messages'])}")
print(f"Último mensaje: {result['messages'][-1]['content'][:100]}...")
# Output esperado:
# Mensajes generados: 2
# Último mensaje: • Los AI agents han evolucionado hacia...
Cuándo: cuando un nodo específico necesita tool calling autónomo pero el flujo general tiene lógica custom.
LangGraph como componente de Deep Agents
Un Deep Agent puede invocar un grafo LangGraph como herramienta:
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from deep_agents.tools import create_graph_tool
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class AnalysisState(TypedDict):
data: str
result: str
def analyze_node(state: AnalysisState) -> dict:
return {"result": f"Analysis of: {state['data'][:50]}..."}
builder = StateGraph(AnalysisState)
builder.add_node("analyze", analyze_node)
builder.add_edge(START, "analyze")
builder.add_edge("analyze", END)
analysis_graph = builder.compile()
analysis_tool = create_graph_tool(
graph=analysis_graph,
name="detailed_analysis",
description="Runs detailed analysis on provided data using a custom pipeline",
)
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[analysis_tool],
name="Research Agent",
instructions="Usa detailed_analysis para análisis que requieren pipeline custom.",
)
print(f"Tools disponibles: {[t.name for t in agent.tools]}")
# Output esperado:
# Tools disponibles: ['detailed_analysis', 'write_todos', 'read_file', 'write_file', 'spawn_agent']
Cuándo: cuando el Deep Agent necesita ejecutar un flujo con control fino como parte de una tarea autónoma más grande.
La perspectiva del profesional
Saber los tres niveles no es un lujo académico — es lo que diferencia un developer de un AI Engineer.
En entrevistas
Entrevistador: "¿Cómo construirías un agente de investigación?"
Junior: "Usaría LangGraph porque es el que conozco."
Senior: "Depende del caso. Si es Q&A simple, create_agent con TavilySearch
resuelve en 15 líneas. Si necesito un pipeline con aprobaciones y retry,
LangGraph me da control sobre cada paso. Si es investigación autónoma
de larga duración, Deep Agents incluye planning y filesystem out-of-the-box.
¿Cuál es el caso específico?"
En producción
Decisión real:
Proyecto A: Chatbot para FAQ → create_agent
→ En producción en 1 día
→ Costo: ~$0.001 por consulta
→ Mantenimiento: mínimo
Proyecto B: Pipeline de onboarding → LangGraph
→ En producción en 1 semana
→ Costo: ~$0.05 por ejecución
→ Mantenimiento: cada cambio de flujo = editar el grafo
Proyecto C: Research automation → Deep Agents
→ En producción en 2 días
→ Costo: ~$0.10-0.50 por investigación
→ Mantenimiento: ajustar instructions y tools
En tu portfolio
Si tienes las tres versiones del Research Assistant (create_agent, LangGraph, Deep Agents), puedes demostrar que:
- ✅ Sabes construir desde lo básico hasta lo complejo
- ✅ Entiendes trade-offs de conveniencia vs control
- ✅ Puedes elegir la herramienta correcta para el problema
- ✅ No eres dependiente de un solo framework
Eso es lo que te hace AI Engineer, no un "usuario de LangGraph."
Ejercicios
Ejercicio 1: Diagnóstico de nivel
Para cada descripción, identifica qué herramienta usarías y justifica en una oración:
- Un bot de Slack que responde preguntas sobre la documentación interna
- Un sistema de triage de bugs que clasifica, prioriza, y asigna
- Un agente que investiga competidores y genera un reporte semanal automático
- Un asistente de código que sugiere refactorings
- Un pipeline de generación de contratos legales con aprobaciones
Ver solución
- create_agent — Q&A con tool de búsqueda en docs, flujo lineal, sin necesidad de workflow custom.
- LangGraph — Flujo con clasificación → priorización → asignación con reglas de negocio específicas, HITL para casos ambiguos.
- Deep Agents — Investigación autónoma recurrente, multi-fuente, requiere planning y filesystem para reportes.
- create_agent — Lee código, sugiere mejoras. Un solo flujo: analizar → sugerir. No necesita planning.
- LangGraph — Flujo regulado con aprobaciones en puntos específicos, audit trail obligatorio, reglas de negocio estrictas.
Ejercicio 2: Implementación en 3 niveles
Elige un agente simple: "un agente que busca el clima de una ciudad y responde en español." Impleméntalo en create_agent (~10 líneas), LangGraph (~40 líneas), y Deep Agents (~15 líneas). Compara: ¿cuál tiene sentido para este caso?
Ver solución
create_agent:
from dotenv import load_dotenv
load_dotenv()
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
model = ChatOpenAI(model="gpt-4.1-mini")
agent = create_agent(model, [TavilySearchResults(max_results=1)])
result = agent.invoke(
{"messages": [{"role": "user", "content": "¿Cuál es el clima en Madrid hoy?"}]}
)
print(result["messages"][-1].content)
# Output esperado: El clima en Madrid hoy es...
LangGraph:
from dotenv import load_dotenv
load_dotenv()
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
from typing import TypedDict, Annotated
import operator
class State(TypedDict):
messages: Annotated[list, operator.add]
city: str
search = TavilySearchResults(max_results=1)
model = ChatOpenAI(model="gpt-4.1-mini")
def search_weather(state: State) -> dict:
results = search.invoke(f"weather today {state['city']}")
return {"messages": [{"role": "system", "content": str(results)}]}
def format_response(state: State) -> dict:
response = model.invoke(f"Di el clima de {state['city']} en español:\n{state['messages'][-1]}")
return {"messages": [{"role": "assistant", "content": response.content}]}
builder = StateGraph(State)
builder.add_node("search", search_weather)
builder.add_node("format", format_response)
builder.add_edge(START, "search")
builder.add_edge("search", "format")
builder.add_edge("format", END)
graph = builder.compile()
result = graph.invoke({"city": "Madrid", "messages": []})
print(result["messages"][-1]["content"])
# Output esperado: El clima en Madrid hoy es...
Deep Agents:
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-mini",
tools=[TavilySearchResults(max_results=1)],
name="Weather Agent",
instructions="Busca el clima y responde en español.",
)
result = agent.run("¿Cuál es el clima en Madrid hoy?")
print(result.output)
# Output esperado: El clima en Madrid hoy es...
Veredicto: create_agent es la opción correcta. LangGraph agrega complejidad innecesaria para un flujo lineal. Deep Agents tiene overhead de planning para una tarea que no necesita planificación.
Ejercicio 3: Diseño de sistema híbrido
Diseña (en pseudocódigo o diagrama) un sistema que use los tres niveles: un Deep Agent principal que coordina la investigación, un grafo LangGraph para el pipeline de análisis con aprobación humana, y create_agent para búsquedas individuales dentro de nodos.
Ver solución
Arquitectura híbrida:
Deep Agent (nivel principal):
└─ Planning: write_todos para descomponer la investigación
└─ Tools:
├─ web_search_agent (create_agent con TavilySearch)
│ → Búsquedas simples, una pregunta → una respuesta
├─ analysis_pipeline (LangGraph graph como tool)
│ → Pipeline: validate → analyze → human_approve → report
│ → HITL: aprobación antes de publicar hallazgos
└─ write_file, read_file (built-in)
└─ Filesystem: resultados en research/, análisis en analysis/
└─ Subagents: por cada fuente compleja, spawn un subagente
Flujo:
1. Deep Agent planifica: "Investigar X en 5 pasos"
2. Paso 1: usa web_search_agent (create_agent) para búsqueda rápida
3. Paso 2: escribe hallazgos en research/web.md
4. Paso 3: spawn subagente para búsqueda académica
5. Paso 4: invoca analysis_pipeline (LangGraph) con HITL
6. Paso 5: genera reporte final en output/report.md
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from deep_agents.tools import create_graph_tool
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_community.tools import TavilySearchResults
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
model = ChatOpenAI(model="gpt-4.1-mini")
simple_search = create_agent(model, [TavilySearchResults(max_results=3)])
class AnalysisState(TypedDict):
data: str
approved: bool
result: str
def validate(state): return {"result": "Validated"}
def analyze(state): return {"result": "Analyzed: " + state["data"][:50]}
def approve(state): return {"approved": True}
builder = StateGraph(AnalysisState)
builder.add_node("validate", validate)
builder.add_node("analyze", analyze)
builder.add_node("approve", approve)
builder.add_edge(START, "validate")
builder.add_edge("validate", "analyze")
builder.add_edge("analyze", "approve")
builder.add_edge("approve", END)
analysis_graph = builder.compile()
analysis_tool = create_graph_tool(analysis_graph, "analysis_pipeline", "Runs analysis with approval")
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[TavilySearchResults(max_results=5), analysis_tool],
name="Hybrid Research Agent",
instructions="Usa búsqueda web para datos, analysis_pipeline para análisis con aprobación.",
)
print(f"Tools: {[t.name for t in agent.tools]}")
# Output esperado:
# Tools: ['tavily_search_results', 'analysis_pipeline', 'write_todos', 'read_file', 'write_file', 'spawn_agent']
Ejercicio 4: Trade-off analysis
Para un proyecto de "asistente legal que revisa contratos, identifica cláusulas problemáticas, y sugiere cambios con aprobación de un abogado," argumenta:
- Por qué podrías usar LangGraph
- Por qué podrías usar Deep Agents
- Cuál elegirías y por qué
Ver solución
Argumento para LangGraph:
- Los contratos legales requieren un flujo regulado y auditable
- HITL es obligatorio en puntos específicos (un abogado aprueba cada sugerencia)
- Las reglas para identificar cláusulas son específicas y requieren branching condicional
- Necesitas audit trail detallado (qué se revisó, quién aprobó, cuándo)
- ~150-200 líneas te dan control total sobre cada paso
Argumento para Deep Agents:
- La revisión de contratos largos es una tarea autónoma multi-paso
- Planning automático: el agente decide cómo descomponer un contrato de 50 páginas
- Filesystem: cada cláusula analizada se guarda en un archivo separado
- Subagentes: un subagente por sección del contrato
Elección: LangGraph.
Las regulaciones legales requieren flujos predecibles, auditables, y con HITL en puntos exactos. Un abogado necesita aprobar cada sugerencia individualmente, no la tarea completa. Deep Agents es demasiado autónomo para un dominio donde cada paso tiene implicaciones legales. El overhead de escribir más código se justifica por la auditabilidad y el control.
Resumen
- Tienes tres niveles de abstracción:
create_agent(simple, ~15 líneas, 80% de casos), LangGraph (control total, ~150 líneas, custom workflows), y Deep Agents (autónomo, ~40 líneas, batteries-included) - El árbol de decisión es: ¿simple? → create_agent. ¿Necesitas control? → LangGraph. ¿Tarea autónoma larga? → Deep Agents
- Side-by-side: el mismo Research Agent implementado en 3 versiones demuestra que la diferencia no es la capacidad — es cuánto controlas vs cuánto delega el framework
- Cada nivel abstrae lo que el anterior te obliga a construir: create_agent abstrae el loop ReAct; LangGraph abstrae estado y flujo; Deep Agents abstrae planning, filesystem, subagentes, y memoria
- 10 escenarios reales con justificación te dan práctica aplicando el framework: ~30% create_agent, ~40% LangGraph, ~30% Deep Agents
- Enfoques híbridos son válidos y comunes: create_agent dentro de nodos LangGraph, grafos LangGraph como tools de Deep Agents
- La perspectiva profesional: saber los tres niveles te permite elegir la herramienta correcta para cada problema. Eso es lo que define a un AI Engineer
Próxima cápsula: Proyecto — reimplementar el Research Assistant como Deep Agent y hacer la comparación side-by-side definitiva con la versión LangGraph.
Recursos adicionales
- LangChain — Agent Architectures — Vista general de create_agent y su API
- LangGraph — Overview — Documentación de StateGraph, edges, checkpointers
- Deep Agents — Documentation — API reference de create_deep_agent y sus capacidades
- Building Effective Agents (Anthropic) — Perspectiva sobre cuándo usar agentes simples vs complejos
- LangGraph Multi-Agent Systems — Patrones multi-agente en LangGraph
- Cognitive Architectures for Language Agents — Paper académico que informa el diseño de los tres niveles
Módulo 11 — LangChain & LangGraph: From Chains to Agents