Módulo 6: Functional API
Introducción: Otra Forma de Construir Agentes
Descripción
En el Módulo 5 construiste workflows con StateGraph. Definiste nodos como funciones, los conectaste con edges y conditional edges, diseñaste estado tipado con TypedDict y Annotated, y visualizaste tus grafos con draw_mermaid_png(). Potente, explícito, visual. Todo funciona.
Pero miraste el código y pensaste: "para un pipeline que es básicamente get query → descomponer → buscar → sintetizar... ¿necesito definir un StateGraph con nodos, edges, compilar, y todo lo demás? Eso son funciones llamando funciones."
Sí. Algunos workflows no necesitan grafos explícitos. Un pipeline de investigación es esencialmente una función que llama a otras funciones en secuencia, con algo de branching y error handling. Forzarlo en un grafo explícito agrega ceremonia sin agregar claridad.
La Functional API de LangGraph te deja escribir ese pipeline como Python normal — y obtener los superpoderes de LangGraph (durabilidad, checkpointing, streaming) automáticamente. No es la versión "fácil" de la Graph API. Es una forma diferente de expresar workflows, optimizada para flujos que se leen como código Python secuencial.
¿Dónde estamos en la guía?
Este es el Módulo 6 de la guía LangChain & LangGraph: From Chains to Agents. Es el segundo módulo del Bloque 2 (LangGraph Fundamentals).
Bloque 1: LangChain Core (Módulos 1-4) ✅ Completado
Bloque 2: LangGraph Fundamentals (Módulos 5-7) ← ESTÁS AQUÍ (Módulo 6)
Bloque 3: LangGraph Avanzado (Módulos 8-10)
Bloque 4: Producción (Módulos 11-12)
Tu progreso:
Bloque 1 — LangChain Core ✅ Completado
│
│ Módulo 1: Modelos y Proveedores ✅
│ Módulo 2: Tools y Tool Calling ✅
│ Módulo 3: Agents (create_agent) ✅
│ Módulo 4: Middleware y Customización ✅
│
▼
Bloque 2 — LangGraph Fundamentals
│
│ Módulo 5: Introducción a LangGraph ✅ Completado
│ Módulo 6: Functional API ← ESTÁS AQUÍ
│ Módulo 7: Flujos Avanzados 🔒 Siguiente
│
▼
Bloques 3-4 — Avanzado + Producción 🔒
En el Módulo 5 dominaste la Graph API: StateGraph, nodos, edges, conditional edges, estado tipado, compilación, visualización. Ahora aprendes la alternativa: la Functional API, que te permite expresar workflows como funciones Python con decoradores.
El puente: de grafos explícitos a funciones con superpoderes
Lo que ya sabes hacer
En el Módulo 5 construiste workflows como este:
from typing import TypedDict, Annotated
import operator
from langgraph.graph import StateGraph, START, END
class ResearchState(TypedDict):
messages: Annotated[list[str], operator.add]
query: str
results: Annotated[list[str], operator.add]
def decompose(state: ResearchState) -> dict:
return {"messages": ["Descomponiendo query..."], "results": [f"Sub-query de: {state['query']}"]}
def search(state: ResearchState) -> dict:
return {"messages": ["Buscando información..."], "results": ["Resultado de búsqueda"]}
def synthesize(state: ResearchState) -> dict:
return {"messages": [f"Síntesis: {len(state['results'])} resultados procesados"]}
graph_builder = StateGraph(ResearchState)
graph_builder.add_node("decompose", decompose)
graph_builder.add_node("search", search)
graph_builder.add_node("synthesize", synthesize)
graph_builder.add_edge(START, "decompose")
graph_builder.add_edge("decompose", "search")
graph_builder.add_edge("search", "synthesize")
graph_builder.add_edge("synthesize", END)
graph = graph_builder.compile()
result = graph.invoke({"messages": [], "query": "¿Qué es prompt engineering?", "results": []})
print(result["messages"])
# ['Descomponiendo query...', 'Buscando información...', 'Síntesis: 2 resultados procesados']
Funciona. Pero mira la estructura: es una secuencia lineal. START → decompose → search → synthesize → END. No hay branching complejo, no hay conditional edges. Es, en esencia, tres funciones llamándose en secuencia.
Lo que aprenderás aquí
El mismo pipeline, con la Functional API:
from langgraph.func import entrypoint, task
@task
def decompose(query: str) -> list[str]:
return [f"Sub-query de: {query}"]
@task
def search(sub_queries: list[str]) -> list[str]:
return ["Resultado de búsqueda"]
@task
def synthesize(results: list[str]) -> str:
return f"Síntesis: {len(results)} resultados procesados"
@entrypoint()
def research_agent(query: str) -> str:
sub_queries = decompose(query).result()
results = search(sub_queries).result()
summary = synthesize(results).result()
return summary
result = research_agent.invoke("¿Qué es prompt engineering?")
print(result)
# Síntesis: 1 resultados procesados
Lee el código de research_agent. Es Python normal: llama funciones, guarda resultados en variables, retorna un valor. Pero por debajo, LangGraph está construyendo un grafo implícito, gestionando checkpoints de cada @task, y habilitando streaming de progreso.
Parece Python. Tiene superpoderes.
La motivación: no todo necesita un grafo explícito
La Graph API de LangGraph brilla cuando tu workflow tiene topología compleja:
┌──── code_handler ────┐
│ │
START → classify ─── qa_handler ──── END
│ │
└── creative_handler ──┘
Ese diagrama tiene branching condicional. La Graph API lo expresa perfectamente: defines nodos, les pones conditional edges, compilas, y visualizas con draw_mermaid_png().
Pero muchos workflows del mundo real son secuenciales:
query → descomponer → buscar → analizar → sintetizar → output
Forzar esa secuencia en un grafo explícito con StateGraph, add_node, add_edge, TypedDict con reducers, y compilación... agrega ceremonia sin agregar claridad. Es como usar un diagrama de flujo para describir una receta de cocina: técnicamente correcto, pero una lista de pasos sería más clara.
La Functional API existe para esos casos. Escribes la secuencia como funciones Python, y LangGraph agrega la infraestructura por debajo.
Qué es la Functional API
La Functional API de LangGraph usa dos decoradores:
| Decorador | Qué hace | Equivalente en Graph API |
|---|---|---|
@entrypoint | Define el punto de entrada del workflow. Es tu función principal. | StateGraph + compile() |
@task | Define una unidad de trabajo independiente. Retorna un Future. | Un nodo (add_node) |
from langgraph.func import entrypoint, task
Lo que obtienes gratis al usar estos decoradores:
- ✅ Durabilidad: si el workflow crashea, el checkpointing permite retomarlo desde el último
@taskcompletado - ✅ Streaming: puedes streamear el progreso del workflow paso a paso
- ✅ Human-in-the-loop: puedes pausar el workflow con
interrupt()para pedir aprobación humana - ✅ Misma interfaz:
workflow.invoke()yworkflow.stream()— idéntico a un grafo compilado
Lo que parece Python normal, tiene la infraestructura de LangGraph por debajo. Esa es la idea central.
Dos APIs, un toolkit
Graph API y Functional API no compiten. Son dos formas de expresar workflows que comparten el mismo runtime:
| Aspecto | Graph API (StateGraph) | Functional API (@entrypoint) |
|---|---|---|
| Cómo defines el flujo | Nodos + edges explícitos | Control flow de Python (if, for, try) |
| Estado | TypedDict con reducers | Variables locales de la función |
| Visualización | draw_mermaid_png() | No disponible (grafo se genera en runtime) |
| Checkpointing | Checkpoint después de cada superstep | Checkpoint por cada @task completado |
| Ideal para | Topologías complejas, branching visual | Flujos secuenciales, branching con Python |
| Curva de aprendizaje | Mayor (nuevo paradigma de grafos) | Menor (es Python con decoradores) |
| Verbosidad | Más código de setup | Menos código de setup |
Puedes mezclar ambas APIs en el mismo proyecto. Un @entrypoint puede llamar a un StateGraph compilado, y un nodo de StateGraph puede usar @task. No es una decisión permanente — es una decisión por workflow.
La regla de decisión
¿Tu workflow tiene más de 5 nodos con routing condicional complejo?
→ Graph API (StateGraph). La visualización te va a salvar.
¿Es un flujo secuencial con branching simple (if/else, try/except)?
→ Functional API (@entrypoint + @task). Lee como Python, tiene superpoderes.
¿No estás seguro?
→ Empieza con Functional API. Es más rápido de prototipar.
Si el flujo se complica, migra a Graph API.
Mapa del módulo
| Cápsula | Tema | Qué aprenderás |
|---|---|---|
| 02 | @entrypoint: definir un agente como función | Decorador @entrypoint(), parámetros inyectables, ejecución con invoke y stream, comparación side-by-side con StateGraph |
| 03 | @task: tareas que componen el agente | Decorador @task, Futures y .result(), tareas como unidades checkpointeables, cuándo usar @task vs código directo |
| 04 | Control flow nativo | while loops para agent loops, if/else para routing, try/except para error handling, for loops para iteración — todo sin edges explícitos |
| 05 | Graph API vs Functional API: comparación profunda | Tabla comparativa detallada, el mismo problema resuelto con ambas APIs, criterios de migración, ventajas de cada approach |
| 06 | Patterns con Functional API | Tool execution pattern, multi-step reasoning, parallel task execution con futures, composición de tasks |
| 07 | Combinar Graph y Functional API | Usar @task dentro de StateGraph, llamar grafos compilados desde @entrypoint, cuándo la combinación tiene sentido |
| 08 | Proyecto evolutivo: Research Agent base | AI Research Assistant v1 con Functional API — recibe tema, descompone, busca, sintetiza reporte structured |
Flujo de aprendizaje: Empiezas entendiendo @entrypoint como el equivalente funcional de StateGraph (02). Luego dominas @task y el concepto de Futures (03). Con ambos decoradores, aprendes a dirigir el flujo con Python nativo (04). Después desarrollas criterio comparando ambas APIs en profundidad (05). Con ese criterio, aprendes patterns profesionales (06) y cómo combinar ambas APIs (07). Finalmente, construyes la primera versión del proyecto evolutivo (08).
El proyecto evolutivo empieza aquí
Este módulo marca un cambio importante en la guía. En los Módulos 1-5, cada módulo tenía su propio mini-proyecto independiente. A partir de ahora, construyes un solo proyecto que evoluciona módulo a módulo: el AI Research Assistant.
Módulo 6: Research Agent base (Functional API)
│
▼
Módulo 7: + Retry logic, branching paralelo, error handling
│
▼
Módulo 8: + Persistencia (checkpointing, memoria)
│
▼
Módulo 9: + Aprobaciones humanas (human-in-the-loop)
│
▼
Módulo 10: + Multi-agente (researcher, analyst, writer, supervisor)
│
▼
Módulo 11: + Deep Agent (planning, filesystem, subagents)
│
▼
Módulo 12: + Observability y producción (LangSmith, evaluation)
Lo que construyas en este módulo será la base de 6 módulos más de iteración. La v1 es simple pero funcional: recibe un tema de investigación, descompone la tarea en sub-queries con un LLM, ejecuta búsquedas, y genera un resumen structured. Cada módulo posterior agrega una capa de sofisticación.
Haz la v1 bien. Vas a iterar sobre ella durante mucho tiempo.
Qué hace el Research Assistant v1
En la Cápsula 08 de este módulo construirás un workflow funcional que:
- Recibe un tema de investigación — una pregunta o tema que el usuario quiere entender
- Descompone la tarea con un LLM — genera sub-queries específicas para investigar diferentes ángulos del tema
- Ejecuta búsquedas con
@task— cada sub-query busca información (con tools o búsqueda simulada) - Sintetiza un reporte — combina los resultados en una respuesta structured con secciones, fuentes, y conclusiones
Es simple comparado con lo que será en el Módulo 12. Pero es funcional end-to-end, y cada pieza que construyas aquí (@entrypoint, @task, el flujo de descomposición → búsqueda → síntesis) sobrevive hasta la versión final.
Qué NO cubre este módulo
- ❌ Flujos avanzados (ciclos con retry, branching paralelo, subgraphs) — Se cubre en Módulo 7. Aquí dominas los fundamentos de la Functional API.
- ❌ Persistencia (checkpointing, memoria entre sesiones) — Se cubre en Módulo 8. Aquí verás un preview del concepto, pero no profundizamos.
- ❌ Human-in-the-loop (interrupts, approvals) — Se cubre en Módulo 9. Mencionaremos que la Functional API lo habilita con
interrupt(), pero no lo implementamos. - ❌ Multi-agent systems — Se cubre en Módulo 10. Aquí trabajas con un solo workflow funcional.
- ❌ Deep Agents — Se cubre en Módulo 11. Aquí construyes con LangGraph directo.
Lo que pasa por debajo: no es solo Python
La Functional API parece "solo escribir Python." Y esa es la intención: que parezca familiar. Pero por debajo, LangGraph está haciendo trabajo serio:
- Grafo implícito: Cuando LangGraph ejecuta tu
@entrypoint, construye un grafo de ejecución en runtime basado en los@taskque llamas. No lo ves, pero existe. - Checkpointing automático: Cada
@taskcompletado se guarda en un checkpoint. Si tu workflow falla después de 3 de 5 tasks, al retomar, las 3 primeras no se re-ejecutan. - Streaming granular: Cada
@taskemite un evento en el stream. Puedes dar feedback progresivo al usuario sin implementar nada extra. - Soporte para interrupts: Puedes pausar tu workflow con
interrupt()en cualquier punto para pedir input humano. LangGraph maneja la pausa y la reanudación.
Esa es la diferencia entre "escribir Python" y "escribir Python con @entrypoint". Parece igual. No lo es.
Setup técnico
Prerequisitos
Antes de continuar, verifica que tienes:
- ✅ Módulo 5 completado — sabes crear grafos con StateGraph, nodos, edges, conditional edges, estado tipado, compilación y ejecución
- ✅ Python 3.11+ instalado
- ✅ Al menos una API key de un proveedor (OpenAI recomendado para este módulo)
Instalación
Si completaste el Módulo 5, ya tienes todo instalado. Verifica:
pip install langgraph langchain-openai python-dotenv
La Functional API viene incluida en langgraph — no hay paquetes adicionales:
from langgraph.func import entrypoint, task
print("Functional API disponible")
# Output esperado: Functional API disponible
Verificar que todo funciona
from langgraph.func import entrypoint, task
@task
def greet(name: str) -> str:
return f"¡Hola, {name}!"
@entrypoint()
def my_workflow(name: str) -> str:
greeting = greet(name).result()
return greeting
result = my_workflow.invoke("LangGraph")
print(result)
# Output esperado: ¡Hola, LangGraph!
Si ves el saludo, tu setup está listo para la Functional API.
Si algo falla:
| Error | Causa | Solución |
|---|---|---|
ImportError: cannot import name 'entrypoint' from 'langgraph.func' | Versión de langgraph antigua | pip install --upgrade langgraph (necesitas v0.2.60+) |
TypeError: 'task' object is not callable | Llamaste @task sin estar dentro de un @entrypoint | Los @task solo pueden ejecutarse dentro de un @entrypoint o de un nodo de StateGraph |
SerializationError al usar invoke | Input o output no es JSON-serializable | Usa tipos primitivos (dict, list, str, int, bool). No pases objetos custom como input |
Evidencia de éxito
Al terminar este módulo, sabrás que tuviste éxito si:
- ✅ Puedes crear workflows con
@entrypointcomo punto de entrada y@taskpara tareas independientes - ✅ Entiendes que
@taskretorna un Future y sabes usar.result()para obtener el valor - ✅ Usas control flow nativo de Python (if/else, for, try/except) para dirigir la ejecución sin edges explícitos
- ✅ Puedes comparar Graph API vs Functional API y decidir cuándo usar cada una
- ✅ Sabes combinar ambas APIs cuando el caso lo requiere
- ✅ Tu AI Research Assistant v1 funciona end-to-end: tema → descomposición → búsqueda → síntesis
Vista previa: del workflow funcional a flujos avanzados
En este módulo construirás workflows con la Functional API: @entrypoint para definir el punto de entrada, @task para las unidades de trabajo, y Python nativo para el control flow. El resultado será un Research Agent que funciona, pero con flujo lineal y sin manejo de errores robusto.
En el Módulo 7 (Flujos Avanzados), le agregarás patrones de producción: retry con backoff cuando una API falla, branching paralelo para buscar en múltiples fuentes simultáneamente, subgraphs para encapsular lógica, y error handling que hace tu workflow resiliente.
El Research Agent pasará de "funciona en el happy path" a "funciona en el mundo real."
Resumen
- En el Módulo 5 dominaste la Graph API: StateGraph, nodos, edges, conditional edges, estado tipado, compilación, y visualización
- La Functional API es una forma alternativa de construir workflows en LangGraph — usa decoradores (
@entrypoint,@task) y control flow de Python en vez de grafos explícitos - No es la versión fácil. Es una forma diferente, optimizada para flujos que se leen como código Python secuencial
- Parece Python normal, pero tiene superpoderes: durabilidad, checkpointing, streaming, human-in-the-loop — todo viene gratis con los decoradores
- Graph API y Functional API comparten el mismo runtime. Son complementarias, no competidoras. Puedes mezclarlas en el mismo proyecto
- Regla de decisión: >5 nodos con routing complejo → Graph API. Flujo secuencial con branching simple → Functional API. No estás seguro → empieza con Functional
- El proyecto evolutivo empieza aquí. El AI Research Assistant v1 será la base de 6 módulos más de iteración
- La Functional API no soporta visualización con
draw_mermaid_png()— el grafo se genera dinámicamente en runtime
Recursos adicionales
- Functional API Overview — Documentación oficial de la Functional API de LangGraph
- How to use the Functional API — Guía práctica con ejemplos paso a paso
- Choosing between Graph API and Functional API — Criterios oficiales para elegir entre ambas APIs
- Introducing the LangGraph Functional API — Blog post de lanzamiento con motivación y ejemplos
- @entrypoint Reference — Referencia completa del decorador @entrypoint
- @task Reference — Referencia completa del decorador @task
Módulo 6 — LangChain & LangGraph: From Chains to Agents
Siguiente cápsula: @entrypoint: Definir un Agente como Función — aprenderás cómo @entrypoint reemplaza a StateGraph + compile() y cómo ejecutar workflows funcionales con invoke y stream.