Volver al catálogo
Season 49 18 Episodios 1h 16m 2026

LangGraph

v1.1 — Edición 2026. Un completo curso en audio sobre LangGraph, un framework para crear flujos de trabajo de agentes de larga duración y con estado (stateful). Abarca modelos mentales, Graph vs Functional APIs, memoria, Time Travel, Human-in-the-Loop y despliegue en producción.

Orquestación de LLM Sistemas multiagente Frameworks de AI/ML
LangGraph
Reproduciendo ahora
Click play to start
0:00
0:00
1
El problema de la orquestación: ¿Por qué LangGraph?
Una introducción a los problemas fundamentales que resuelve LangGraph. Exploramos la transición de flujos de trabajo lineales simples a la orquestación de agentes con estado y de larga duración.
4m 14s
2
Pensando en LangGraph: El modelo mental
Aprende a traducir tareas complejas de IA al modelo mental de LangGraph. Desglosamos los conceptos fundamentales de Nodes, Edges y State.
4m 29s
3
La Graph API: State y reducers
Sumérgete en la mecánica de la Graph API. Explicamos cómo TypedDict define tu esquema y cómo los reducers gestionan las actualizaciones de estado desde múltiples nodos.
3m 27s
4
La Functional API: @entrypoint y @task
Explora la Functional API como alternativa a la Graph API. Debatimos cómo obtener persistencia a nivel empresarial utilizando el flujo de control estándar de Python.
3m 56s
5
Gestionando el historial de conversación con MessagesState
Comprende los retos del historial de chat en los agentes de IA. Exploramos MessagesState y el reducer add_messages para gestionar ediciones y deduplicación.
4m 20s
6
Eligiendo tu abstracción: Graph vs Functional
Un marco de trabajo para decidir qué API utilizar. Contrastamos el enrutamiento visual explícito de la Graph API con el flujo imperativo de la Functional API.
4m 21s
7
Enrutamiento dinámico y Conditional Edges
Ve más allá de la lógica hardcoded. Debatimos cómo usar LLMs con salidas estructuradas junto con Conditional Edges para enrutar flujos de trabajo dinámicamente.
4m 22s
8
Flujos de trabajo Map-Reduce con la Send API
Domina el patrón Orchestrator-Worker. Nos adentramos en la Send API para hacer un fan-out dinámico de nodos worker en paralelo basándonos en planes en tiempo de ejecución.
4m 46s
9
Persistencia: Threads y Checkpoints
Descubre la base de la persistencia de estado. Explicamos Threads, Checkpoints y Super-steps, mostrando cómo LangGraph garantiza la supervivencia ante caídas del sistema.
4m 08s
10
Ejecución duradera e idempotencia
Comprende los matices de reanudar flujos de trabajo. Cubrimos por qué los side-effects deben ser idempotentes y cómo estructurar los nodos para una ejecución duradera.
4m 11s
11
Human-in-the-Loop: Interrupts
Aprende a congelar agentes en mitad de su ejecución. Detallamos la función interrupt y cómo reanudar flujos de trabajo con aprobación humana externa.
4m 17s
12
Depurando el pasado: Time Travel y Forking
Explora las capacidades de Time Travel de LangGraph. Mostramos cómo navegar por el historial de estado, reproducir Checkpoints pasados y hacer un fork de rutas de ejecución alternativas.
4m 11s
13
Memoria a largo plazo: Stores a través de Threads
Ve más allá de los Threads aislados. Presentamos la interfaz Store y explicamos cómo dotar a tus agentes de memoria persistente entre sesiones.
4m 18s
14
Ejecución en streaming y el formato v2
Mejora la experiencia de usuario con feedback en tiempo real. Desglosamos los stream modes (values, updates, messages) y el formato unificado v2 StreamPart.
4m 31s
15
Componiendo la complejidad: Subgraphs
Escala tus flujos de trabajo tratando los grafos compilados como nodos. Debatimos sobre la composición de Subgraphs y la gestión de esquemas de estado compartidos frente a privados.
4m 09s
16
Persistencia de Subgraphs y patrones multi-agente
Domina el memory scoping en sistemas multi-agente. Explicamos la diferencia entre la persistencia de Subgraphs per-invocation, per-thread y stateless.
4m 03s
17
Estructura de la aplicación y preparación para el despliegue
Transición de prototipos a producción. Exploramos langgraph.json, la estructura de archivos adecuada y la gestión de dependencias para despliegues con estado.
4m 23s
18
Testeando la ejecución del Graph End-to-End
Aprende estrategias de testing robustas para flujos de trabajo de grafos. Cubrimos la integración con pytest, la ejecución aislada de nodos y la simulación de estados parciales.
4m 26s

Episodios

1

El problema de la orquestación: ¿Por qué LangGraph?

4m 14s

Una introducción a los problemas fundamentales que resuelve LangGraph. Exploramos la transición de flujos de trabajo lineales simples a la orquestación de agentes con estado y de larga duración.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 1 de 18. La mayoría de los scripts de modelos de lenguaje grandes funcionan perfectamente para un prompt rápido y una respuesta ágil. Pero cuando una tarea tarda veinte minutos en ejecutarse y la red se cae a la mitad, todo se desmorona. Pierdes tu progreso, tu contexto y tu presupuesto de la API. El problema de la orquestación: ¿Por qué LangGraph? trata precisamente de solucionar esta fragilidad. LangGraph es un framework de orquestación diseñado para gestionar aplicaciones stateful y de múltiples actores. Quizás oigas el nombre y pienses que es solo una feature de LangChain. No lo es. LangGraph es un motor de orquestación de más bajo nivel, y no necesitas usar LangChain en absoluto para utilizarlo. Existe específicamente para modelar los workflows de los agentes como grafos stateful en lugar de simples scripts lineales. Los scripts estándar se ejecutan en memoria. Si un script se detiene inesperadamente, todos esos datos de runtime desaparecen. Imagina un escenario en el que tienes un agente en background investigando un documento de cien páginas. El agente ha estado leyendo, extrayendo datos y cotejando información durante veinte minutos ininterrumpidos. Si se produce un timeout del servidor en el minuto diecinueve, un script estándar pierde todo ese estado. Tienes que empezar todo el trabajo de nuevo. LangGraph resuelve este problema de orquestación mediante una ejecución persistente. Al modelar tu workflow como un grafo, cada paso distinto se convierte en un nodo, y las conexiones lógicas entre ellos son aristas. A medida que la aplicación avanza de un nodo a otro, LangGraph guarda automáticamente su progreso. Trata los procesos de larga duración como una serie de checkpoints seguros. Si el sistema falla, LangGraph reanuda la ejecución exactamente donde la dejó. Este mecanismo de checkpointing se basa en una memoria integral. La memoria en LangGraph no es solo una lista continua de mensajes de chat. Es el estado completo del grafo. Cuando un nodo finaliza su procesamiento, actualiza un objeto de estado compartido. El siguiente nodo en la secuencia lee su input directamente de ese estado. Esto significa que la memoria persiste durante todo el lifecycle de la aplicación. El agente en background que investiga tu documento no olvida los datos críticos que encontró en la página cinco cuando por fin llega a la página noventa, porque el estado del grafo los conserva de forma segura. Aquí reside la clave. Como el estado del grafo se pausa y se guarda limpiamente entre pasos, ganas la capacidad de poner a un human in the loop. A veces, un agente autónomo llega a un punto de decisión donde necesita autorización antes de continuar, como enviar un correo electrónico final o ejecutar una transacción financiera. En un script estándar, pausar para que un usuario haga clic en un botón suele provocar timeouts de conexión. En LangGraph, simplemente configuras un nodo específico para detener la ejecución. El sistema entra en modo de suspensión y conserva el estado actual a la perfección. Un operador humano puede entonces revisar los datos recopilados, aprobar la siguiente acción o incluso modificar manualmente el estado del agente antes de darle a continuar. Una vez aprobado, el grafo se reactiva y reanuda su trabajo con el contexto actualizado. La conclusión principal es que la creación de agentes complejos depende en gran medida de la gestión del estado y de los fallos. LangGraph transforma tu arquitectura, alejándola de scripts frágiles y limitados por la memoria, y orientándola hacia grafos robustos que sobreviven a interrupciones, recuerdan su historial y esperan pacientemente la guía humana. Si quieres ayudar a apoyar el programa, puedes buscar DevStoriesEU en Patreon. Eso es todo por este episodio. ¡Gracias por escuchar y sigue construyendo!
2

Pensando en LangGraph: El modelo mental

4m 29s

Aprende a traducir tareas complejas de IA al modelo mental de LangGraph. Desglosamos los conceptos fundamentales de Nodes, Edges y State.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 2 de 18. Creas un agente de inteligencia artificial metiendo instrucciones, edge cases y ejemplos en un solo prompt enorme, lo envías y esperas que haga lo correcto. Funciona hasta que falla, y debuggear el resultado es frustrante. Para solucionar esto, tienes que dejar de escribir prompts monolíticos y empezar a diseñar sistemas. Esto nos lleva a Pensar en LangGraph: El Modelo Mental. LangGraph te obliga a alejarte de los scripts lineales. Te pide que pienses en tu aplicación como una máquina de estados. El primer paso es simplemente definir tu proceso. Antes de escribir código, mira lo que quieres que consiga el sistema y divídelo en acciones discretas. Piensa en un sistema de triaje de atención al cliente. Un usuario envía un mensaje. Un operador humano leería el email, decidiría si es un problema de facturación o de soporte técnico, y luego redactaría una respuesta adecuada. Esa secuencia es tu proceso. Mapeas ese proceso a un workflow de LangGraph usando tres componentes principales, que son State, Nodes y Edges. Empecemos con el State. El State es la memoria compartida de tu grafo. Es una estructura que guarda el contexto de toda tu operación en cualquier momento dado. Cada paso de tu workflow leerá de este State y escribirá actualizaciones en él. Los oyentes suelen cometer un error específico aquí. Intentan guardar strings de prompts totalmente formateados dentro del State. No hagas esto. El State solo debería guardar datos en crudo. Guarda el texto original del email del cliente, la categoría extraída, o una lista en crudo de mensajes anteriores. El formateo ocurre bajo demanda, más tarde, exactamente dentro del paso donde se necesita. A continuación, tenemos los Nodes. Si el State es la memoria, los Nodes son los trabajadores que hacen las tareas reales. Un node es simplemente una función de Python que ejecuta un único paso lógico de tu proceso. Recibe el State actual, realiza una acción y devuelve una actualización. En nuestro ejemplo de triaje, crearías tres nodes separados. El primero es un node Read. Coge el email entrante y guarda el texto en crudo en el State. El segundo es un node Classify. Mira el texto en crudo en el State, le pide a un modelo de lenguaje que lo categorice como facturación o técnico, y guarda esa categoría resultante de vuelta en el State. El tercero es un node Draft. Lee tanto el email como la categoría del State, los formatea en un prompt localmente, y genera una respuesta. Cada node hace exactamente un trabajo. Finalmente, necesitas una forma de conectar a estos trabajadores. Ese es el papel de los Edges. Los Edges representan la lógica de enrutamiento. Dictan qué pasa después de que un node termina su trabajo. Un edge estándar simplemente dice: una vez que el node Read termine, ve siempre al node Classify. Pero LangGraph también usa edges condicionales. Aquí es donde se pone interesante. Después del node Classify, puedes usar un edge condicional para inspeccionar el State. Si la categoría es facturación, el edge enruta el flujo a un node Draft específico de facturación. Si es técnico, lo enruta a un node Draft técnico. Los Edges toman decisiones de tráfico basándose en los datos que tus nodes acaban de producir. Empiezas con el proceso, aíslas los datos en un State compartido, defines a los trabajadores como Nodes, y dictas el flujo con Edges. Al dividir el problema, aíslas los fallos. Trata tu aplicación no como un único generador de texto, sino como una cadena de montaje coordinada donde los datos en crudo se mueven sistemáticamente de un trabajador especializado al siguiente. ¡Gracias por escuchar, feliz programación a todos!
3

La Graph API: State y reducers

3m 27s

Sumérgete en la mecánica de la Graph API. Explicamos cómo TypedDict define tu esquema y cómo los reducers gestionan las actualizaciones de estado desde múltiples nodos.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 3 de 18. Dos funciones paralelas terminan de ejecutarse en el mismo milisegundo e intentan escribir en la misma lista compartida. Normalmente, una sobrescribe a la otra y los datos desaparecen. Para evitar esto, LangGraph se basa en la Graph API: State y Reducers. La base de cualquier workflow de LangGraph es su state. Lo defines usando un TypedDict estándar de Python. Este dictionary especifica las keys exactas que tu graph va a trackear y los tipos de datos para cada key. Piénsalo como el schema que se pasa de node a node a medida que se ejecuta tu graph. Aquí está la clave. Los nodes en LangGraph no mutan el state directamente. Un node recibe una copia del state actual, hace su trabajo y devuelve un dictionary de updates. Un error común es asumir que devolver un dictionary reemplaza todo el object del state. No es así. Si tu state tiene cinco keys y tu node devuelve un dictionary con una sola key, LangGraph deja las otras cuatro intactas y solo aplica tu update específico. ¿Cómo aplica LangGraph ese update? Ahí es donde entran los reducers. Un reducer es simplemente una función que dicta cómo un valor devuelto se fusiona con el valor existente para una key específica. Por defecto, LangGraph usa un reducer de overwrite. Si tu node devuelve un nuevo string para una key de status, el string anterior desaparece, reemplazado por completo por el nuevo. A veces, hacer overwrite es exactamente lo que quieres evitar. Imagina un workflow de data fetching en paralelo. Tienes una key de state compartida llamada results, que es una lista. Levantas dos nodes ejecutándose al mismo tiempo para hacer fetch de diferentes lotes de datos. Si ambos nodes devuelven un dictionary actualizando la key results, el comportamiento de overwrite por defecto hace que el node que termine último borre el trabajo del otro. Para solucionar esto, anotas la key results en tu TypedDict con un reducer específico, como el built-in operator dot add de Python. Ahora, cuando los dos nodes devuelven sus listas, el reducer actúa como un guardia de tráfico. Coge la lista existente y hace un append seguro de los outputs de ambos nodes. No se pierde nada. Hay un edge case. ¿Qué pasa si tienes un reducer de tipo append en tu key results, pero llegas a un punto en tu graph donde realmente necesitas vaciar la lista y empezar de nuevo? Si tu node devuelve una lista vacía, el reducer simplemente añade una lista vacía a la existente, dejando los datos antiguos intactos. Para este escenario, LangGraph proporciona un tipo Overwrite especial. Cuando tu node envuelve su update en un object Overwrite, LangGraph lo detecta y se salta el reducer por completo. Tira la lista antigua y fuerza un hard reset. El state en un graph complejo no es una variable global frágil que se muta constantemente, sino un log append-only de updates controlados, regidos por reglas de reducción claras. Eso es todo por este episodio. ¡Gracias por escuchar, y seguid construyendo!
4

La Functional API: @entrypoint y @task

3m 56s

Explora la Functional API como alternativa a la Graph API. Debatimos cómo obtener persistencia a nivel empresarial utilizando el flujo de control estándar de Python.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 4 de 18. A veces, solo quieres escribir un script estándar de Python con if-statements y for-loops normales, pero sigues necesitando persistencia de estado a nivel empresarial. No quieres construir una state machine explícita solo para ejecutar unas pocas llamadas a modelos de lenguaje en secuencia. Aquí es donde la Functional API, específicamente los decorators entrypoint y task, resuelve el problema. Crear aplicaciones con nodes y edges explícitos requiere que definas manualmente cómo se enrutan los datos de un paso al siguiente. Esa estructura proporciona un control enorme, pero puede resultar pesada cuando tu lógica es muy secuencial o se basa en bucles de programación estándar. La Functional API te permite escribir código Python normal, de arriba a abajo, conservando las funciones integradas de streaming y recuperación. En lugar de instanciar un objeto graph, aplicas decorators a tus funciones de Python existentes. Empiezas con el decorator task. Lo aplicas a las unidades de trabajo individuales de tu aplicación. Piensa en un task como un paso discreto que hace algo específico, como consultar una base de datos, calcular una métrica o hacer un prompt a un modelo. Cuando una función lleva el decorator task, el framework la envuelve en una capa de seguimiento para monitorizar su ejecución. A continuación, usas el decorator entrypoint. Lo colocas en la función principal de orquestación que dirige el flujo general. Dentro de esta función entrypoint, llamas a tus tasks decorados usando el flujo de control estándar de Python. Asignas el resultado de un task a una variable y, luego, pasas esa variable al siguiente task. Puedes usar bloques try-except, list comprehensions o while-loops. La lógica de orquestación se comporta exactamente igual que el Python nativo. Como el código parece completamente estándar, podrías pensar que carece de la memoria de una estructura de estado formal. Esta es una idea errónea muy común. La Functional API sigue haciendo checkpoints de tu progreso automáticamente en segundo plano. Cada vez que un task finaliza, LangGraph intercepta el valor de retorno y lo guarda en un almacenamiento persistente. El framework registra de forma segura las entradas y salidas de cada función decorada a medida que ocurren. Imagina un script automatizado para escribir ensayos. Defines tres tasks decorados: una función para generar un esquema, una función para escribir un párrafo y una función para revisar el borrador. Dentro de tu función entrypoint principal, primero llamas al generador de esquemas. A continuación, escribes un for-loop estándar que itera sobre las secciones de ese esquema, llamando al task de escribir párrafo para cada una. Añades los resultados a una lista local. Finalmente, ejecutas el task de revisión. Usas un simple if-statement para comprobar la puntuación resultante. Si la puntuación es baja, tu código simplemente activa un while-loop para reescribir párrafos específicos hasta que la puntuación mejore. Aquí está la clave. Gracias al checkpointing oculto, si tu script sufre un timeout de red mientras escribe el tercer párrafo, no pierdes tu trabajo. Cuando reinicias el proceso con el mismo identificador de thread, LangGraph sabe que el esquema y los dos primeros párrafos ya están completos. Omite por completo la ejecución de esos tasks, recupera sus resultados almacenados en caché del state store y reanuda la ejecución justo en el tercer párrafo. La Functional API traslada la carga cognitiva de visualizar topologías de enrutamiento abstractas a leer código de arriba a abajo, dándote la resiliencia de una state machine con la simplicidad de un script normal. Eso es todo por este episodio. ¡Gracias por escuchar y sigue programando!
5

Gestionando el historial de conversación con MessagesState

4m 20s

Comprende los retos del historial de chat en los agentes de IA. Exploramos MessagesState y el reducer add_messages para gestionar ediciones y deduplicación.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 5 de 18. Imagina que creas una app de chat y un usuario encuentra una errata en su prompt. Hace clic en editar, lo corrige y le da a enviar. Pero en lugar de reemplazar su mensaje anterior, tu backend simplemente añade la versión corregida al final del historial, dejando la errata original justo donde estaba, como un fantasma duplicado. Gestionar el historial de la conversación con MessagesState es cómo evitas esto. Cuando los desarrolladores crean su primer graph, suelen definir un diccionario de state personalizado para guardar su historial de chat. Un error común es usar el append estándar de listas para gestionar este historial. Le añaden la función estándar operator dot add a su lista de messages. Esto le dice al graph que simplemente coja cualquier mensaje nuevo y lo pegue al final del array existente. Este enfoque append-only funciona bien para un bot de ping-pong sencillo donde un usuario habla, la IA responde y el historial crece secuencialmente. Pero se rompe por completo cuando el state necesita ser mutable. Si un humano edita un prompt anterior, o un agente decide regenerar su última respuesta, la suma estándar no puede manejarlo. Acabas teniendo duplicados. LangGraph proporciona una estructura de state integrada para resolver esto llamada MessagesState. Contiene una única key llamada messages. Aquí está la clave del asunto. El poder de MessagesState no es la key en sí, sino la función reducer específica que tiene asociada, llamada add messages. El reducer add messages no se limita a hacer un append ciego de los datos. Rastrea los IDs de los mensajes. Cada vez que un nuevo mensaje entra en el state, el reducer comprueba su ID único. Si ese ID ya existe en cualquier parte del historial de la conversación, el reducer sobrescribe el mensaje antiguo con el nuevo. Si el ID es nuevo, o si el mensaje aún no tiene un ID, el reducer hace un append al final de la lista. Piensa de nuevo en nuestro escenario de la errata. El usuario humano envía un prompt. El sistema le da a ese mensaje el ID 123. El usuario se da cuenta de su error, edita el texto y envía la corrección. El frontend envía el nuevo texto, etiquetándolo explícitamente con el ID 123. Cuando esos datos llegan al graph, el reducer add messages escanea el historial, encuentra el mensaje original en el ID 123 y reemplaza el texto in place. El fantasma duplicado ha desaparecido. La conversación fluye exactamente como estaba previsto. Más allá de gestionar los IDs, el reducer add messages también se encarga de la deserialización de datos. En una aplicación en producción, tus mensajes a menudo llegan en diferentes formatos. Tu frontend podría enviar diccionarios JSON raw que contienen strings de role y content. Tus nodos internos del graph podrían generar objetos de mensaje nativos de LangChain. El reducer actúa como un traductor universal para estos inputs. Si le pasas una lista de diccionarios de Python planos al state, la función add messages los convierte automáticamente en las clases de mensaje correctas de LangChain. No necesitas escribir código boilerplate para parsear un diccionario a un HumanMessage o AIMessage antes de actualizar el state. Normaliza los datos por ti. Al construir agentes de chat, el historial del state no es un log append-only, es un documento vivo, y vincular tus actualizaciones a IDs de mensaje únicos es lo que mantiene ese documento preciso. Gracias por escucharnos. ¡Hasta la próxima!
6

Eligiendo tu abstracción: Graph vs Functional

4m 21s

Un marco de trabajo para decidir qué API utilizar. Contrastamos el enrutamiento visual explícito de la Graph API con el flujo imperativo de la Functional API.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 6 de 18. Si eliges el paradigma de diseño incorrecto desde el principio, terminarás escribiendo cientos de líneas de boilerplate para un script sencillo, o creando una pesadilla de código espagueti a base de funciones básicas de Python. Hoy nos centramos en elegir tu abstracción: Graph versus Functional. Es fácil suponer que una de estas APIs es inherentemente más potente o está más production-ready que la otra. Eso es falso. Bajo el capó, tanto la Graph API como la Functional API se compilan exactamente en el mismo runtime. Ambas soportan persistencia, streaming y control de ejecución exactamente de la misma manera. La elección entre ellas depende puramente de tu modelo mental y de cómo quieras expresar tu lógica. Veamos primero la Functional API. Esta se basa en el control flow imperativo estándar de Python. Escribes funciones normales de Python, las marcas con un decorator, y haces el routing de tu ejecución usando if-statements y loops estándar. La gestión del state aquí es completamente function-scoped. Los datos fluyen estrictamente desde el return value de una función hacia los arguments de la siguiente. No hay ningún objeto de memoria global compartido flotando en segundo plano. Si tu workflow es lineal, o si tiene una lógica predecible y muy acotada, la Functional API mantiene tu código limpio y familiar. Evitas por completo el overhead de definir graph structures. La Graph API requiere una mentalidad diferente. En lugar de llamar a las funciones directamente, defines un state schema global y compartido. Luego escribes nodes, que son pequeñas funciones cuyo único trabajo es leer y mutar ese state compartido. Finalmente, conectas explícitamente esos nodes entre sí usando edges. El routing no se gestiona mediante un conditional statement oculto en lo más profundo del cuerpo de una función. En su lugar, la lógica que dicta a dónde va la aplicación a continuación se extrae en conditional edges explícitos declarados en el nivel superior del graph. Aquí está la clave. Eliges entre ellas basándote en cómo tu sistema maneja el state y el routing a lo largo del tiempo. Imagina a un desarrollador creando una herramienta básica de extracción de datos. Ejecuta un único modelo de lenguaje, parsea el output y lo guarda. La Functional API es perfecta para esto. Es rápida de escribir y fácil de leer. Pero avancemos tres meses. Ese sencillo script se está refactorizando en un complejo sistema multiagente. Ahora tienes un agente investigador que pasa datos a un agente redactor, un agente crítico que responde con correcciones, y una pausa de ejecución esperando a que un manager humano apruebe el borrador final. Si intentas construir ese workflow multiagente asíncrono con la Functional API, el enfoque imperativo se viene abajo. Acabas pasando massive data payloads de arriba a abajo por profundos function call stacks. Tu lógica de routing queda enterrada dentro de conditionals profundamente anidados. Este es el momento exacto en el que migras a la Graph API. La abstracción de Graph brilla aquí porque desacopla el state de la ejecución. Como el state es global y compartido, tus nodes de agentes individuales no necesitan pasarse estructuras de datos pesadas entre sí. Un node simplemente lee el state compartido, actualiza la key específica de la que es responsable, y termina. Los edges explícitos toman el control, haciendo que el routing sea muy visible. Puedes mirar la definición del graph y mapear inmediatamente todo el workflow sin leer ni una sola línea de business logic. Usas la Functional API cuando el control flow es lo bastante simple como para leerlo de arriba a abajo, pero cambias a la Graph API cuando el routing se vuelve tan complejo que necesitas dibujarlo en una pizarra. Gracias por escuchar. Cuidaos todos.
7

Enrutamiento dinámico y Conditional Edges

4m 22s

Ve más allá de la lógica hardcoded. Debatimos cómo usar LLMs con salidas estructuradas junto con Conditional Edges para enrutar flujos de trabajo dinámicamente.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 7 de 18. Los condicionales hardcodeados solo llegan hasta cierto punto cuando estás construyendo un agente. Si un usuario hace una pregunta compleja, una simple búsqueda por palabras clave no puede decidir de forma fiable qué debería hacer tu aplicación a continuación. ¿Y si la propia IA pudiera dictar la ruta de tu workflow? Aquí es donde entran en juego el routing dinámico y los conditional edges. En la configuración estándar de un grafo, conectas el nodo A con el nodo B. Es una ruta estática y garantizada. Pero cuando estás construyendo un mecanismo de routing inteligente, la ruta tiene que cambiar en función de los datos entrantes. Podrías pensar que los edges en LangGraph solo aceptan conexiones de strings hardcodeados. No es así. Un edge puede ser una función de Python que lee el state actual de tu grafo y calcula dinámicamente el nombre del siguiente nodo. Añades esta lógica a tu grafo usando el método add conditional edges. Este método requiere tres componentes. Primero, el nodo de inicio. Segundo, una función de routing. Tercero, un diccionario que mapea los posibles outputs de tipo string de tu función de routing a los nodos de destino reales en tu grafo. Aquí está la clave. La forma más fiable de manejar un conditional edge es combinarlo con un modelo de lenguaje que genere datos estructurados. No quieres que la función de routing en sí realice evaluaciones complejas o procesamiento de lenguaje natural. En su lugar, tienes un nodo upstream donde el modelo es forzado a devolver una estructura estricta, como un modelo de Pydantic. Imagina un router de atención al cliente. Un usuario envía un mensaje. El primer nodo de tu grafo es un clasificador de intents. Dentro de este nodo, le pasas el mensaje del usuario a un modelo de lenguaje y le pides que devuelva un output estructurado con un único campo llamado intent. El modelo evalúa el texto y rellena ese campo con un valor específico, como facturación, soporte técnico o ventas. Esta respuesta estructurada se guarda entonces en el state del grafo. Ahora el conditional edge toma el control. El edge está conectado al nodo clasificador. Cuando el nodo clasificador termina, el conditional edge ejecuta una pequeña función de Python. Esta función toma el state del grafo como su input, mira dentro del state y extrae el valor del intent que el modelo acaba de generar. Si el intent es facturación, la función devuelve el string facturación. El conditional edge mira su diccionario de mapeo, ve que el string facturación corresponde a tu nodo de facturación, y pasa la ejecución a ese nodo específico. Si el intent es soporte técnico, devuelve un string diferente, haciendo el routing del flujo hacia el nodo de soporte técnico. Estás usando el modelo de lenguaje por sus capacidades de razonamiento para categorizar el input, pero mantienes la lógica de routing real de forma determinista. La función de Python en el conditional edge simplemente está leyendo una variable y devolviendo un string. Es muy predecible y fácil de testear. La conclusión más útil aquí es que siempre deberías desacoplar la decisión de la dirección. Deja que el modelo de lenguaje decida el intent y lo escriba en el state, y luego usa un conditional edge de Python puro para leer ese state y dirigir el grafo. Antes de terminar, si estos episodios te resultan útiles y quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon, nos ayuda muchísimo. Eso es todo por este episodio. Gracias por escuchar, y sigue creando.
8

Flujos de trabajo Map-Reduce con la Send API

4m 46s

Domina el patrón Orchestrator-Worker. Nos adentramos en la Send API para hacer un fan-out dinámico de nodos worker en paralelo basándonos en planes en tiempo de ejecución.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 8 de 18. No puedes hardcodear tus rutas de ejecución cuando no tienes ni idea de cuántas subtareas decidirá crear tu agente hasta que realmente se ejecute. Si tu sistema decide sobre la marcha que necesita procesar tres elementos, o treinta, el routing estático estándar fallará. Para solucionar esto, necesitas workflows Map-Reduce con la Send API. Un error muy común al desarrollar en LangGraph es intentar usar conditional edges estándar para un fan-out dinámico. Los conditional edges son perfectos cuando quieres elegir entre rutas conocidas y predeterminadas basándote en una comprobación lógica. Sin embargo, se quedan cortos cuando necesitas lanzar un número desconocido de tareas paralelas idénticas en runtime. La paralelización estándar te permite hacer routing a múltiples nodos fijos. Nombras los nodos, y el grafo los dispara. Pero, ¿qué pasa cuando necesitas ejecutar exactamente el mismo nodo varias veces simultáneamente, cada una con un dato diferente? No puedes hacer esto con un routing básico. Esto nos lleva al patrón Orchestrator-worker. En esta arquitectura, un nodo central examina los datos entrantes, calcula cuántas tareas separadas hacen falta, y despacha workers dinámicos para manejarlas de forma concurrente. LangGraph permite este patrón específicamente a través de la Send API. Imagina un agente encargado de escribir un informe de investigación exhaustivo. El primer nodo actúa como orchestrator. Lee el prompt del usuario y genera un esquema. Dependiendo de la complejidad del tema, este esquema podría contener tres secciones, o podría contener doce. Quieres que un nodo worker independiente redacte cada sección exactamente al mismo tiempo. Para conseguir esto, defines una función de conditional edge inmediatamente después de tu nodo orchestrator. En lugar de devolver un string simple que apunte al siguiente nodo estático en el grafo, esta función de edge devuelve una lista de objetos Send. Aquí está la clave. Un objeto Send empaqueta un destino y sus datos juntos. Recibe dos argumentos. El primer argumento es el nombre del nodo worker que quieres disparar. El segundo argumento es el payload específico para ese worker aislado. En nuestro escenario del informe, la función orchestrator itera sobre el esquema generado. Por cada tema de sección que encuentra, crea un nuevo objeto Send apuntando a un único nodo llamado draft_section, pasándole el string del tema individual como payload. Cuando LangGraph evalúa esta función de edge, recibe la lista de objetos Send. Luego, levanta dinámicamente una instancia en paralelo del nodo draft_section para cada elemento de esa lista. Si el orchestrator generó un esquema con siete secciones, LangGraph lanza siete nodos de redacción en paralelo. Cada nodo ejecuta código idéntico, pero opera sobre su propio payload único. Generar estos workers dinámicos es la fase de map. Recopilar sus outputs independientes es la fase de reduce. Como estos nodos worker se ejecutan de forma concurrente, no pueden sobrescribir de forma segura un único string en el estado de tu grafo. Tu estado general debe estar configurado para recopilar múltiples actualizaciones entrantes simultáneamente. Esto lo gestionas adjuntando una función reducer al campo de estado específico que guardará tus secciones redactadas, indicándole que añada los nuevos elementos a una lista en lugar de sobrescribir el valor anterior. A medida que cada worker de redacción en paralelo termina de escribir, devuelve su bloque de texto. LangGraph captura estas respuestas y usa tu reducer para apilar de forma segura cada bloque de texto en el array compartido. Una vez que cada worker dinámico completa su ejecución, se resuelve todo el paso en paralelo. El workflow entonces avanza, llevando una lista completa y poblada de todas las secciones redactadas. La Send API saca la ejecución en paralelo de la definición estática de tu grafo y la pone directamente en manos de tus datos en runtime. Gracias por acompañarme. Espero que hayas aprendido algo nuevo.
9

Persistencia: Threads y Checkpoints

4m 08s

Descubre la base de la persistencia de estado. Explicamos Threads, Checkpoints y Super-steps, mostrando cómo LangGraph garantiza la supervivencia ante caídas del sistema.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 9 de 18. Tu servidor se cae en mitad de un proceso mientras un agente procesa un dataset masivo. No debería tener que empezar de cero. Debería retomar la ejecución exactamente donde la dejó. Esa resiliencia es lo que vamos a ver hoy con la persistencia, específicamente con los threads y los checkpoints. Para añadir persistencia a una aplicación LangGraph, tienes que entender el concepto de thread. Un thread representa una secuencia de ejecución única y aislada o una conversación de usuario específica. Mantiene el state de trabajo del graph mientras se mueve de node en node. Déjame aclarar algo desde el principio. La gente suele confundir la memoria del thread con la memoria a largo plazo entre sesiones. Un thread no es una base de datos global donde tu agente recuerda datos de diferentes tareas para siempre. Es la memoria de trabajo a corto plazo estrictamente vinculada a una secuencia en curso. Habilitas esta memoria pasándole un checkpointer cuando compilas tu graph. Un checkpointer es un objeto que se encarga de guardar y cargar el state del graph en un storage backend. Una vez que tu graph está compilado con un checkpointer, activas la persistencia pasándole un objeto de configuración que contiene un thread ID cada vez que invocas el graph. Este ID es la clave única que usa el checkpointer para rastrear el historial de ese run específico. Cuando ejecutas el graph con ese thread ID, el checkpointer guarda automáticamente el state. Pero no lo guarda continuamente. Lo guarda en límites específicos llamados super-steps. Un super-step es un ciclo de ejecución distinto en el graph. Si tu graph ejecuta el node A seguido del node B, eso son dos super-steps. Si tu graph se ramifica y ejecuta el node C y el node D al mismo tiempo, esa ejecución paralela se agrupa en un solo super-step. El checkpointer no interrumpe un node mientras está trabajando. Espera al límite del super-step. Una vez que todos los nodes programados para ese super-step terminan de ejecutarse y devuelven sus actualizaciones, LangGraph crea un checkpoint. Este checkpoint contiene un state snapshot, capturando exactamente cómo se ven las variables de state del graph en ese preciso momento. Veamos cómo se comporta esto en la práctica. Supón que tienes un agente analizando un dataset masivo. El graph tiene cuatro pasos. El paso uno hace el fetch de los datos. El paso dos hace el clean. El paso tres ejecuta un análisis costoso. El paso cuatro formatea el resumen. Inicias el run, pasando una configuración con el thread ID uno dos tres. El graph completa con éxito los pasos de fetch, clean y análisis. Al final del paso tres, el checkpointer guarda un state snapshot. Luego, antes de que el paso cuatro pueda terminar, tu servidor se cae. Como usaste un checkpointer y un thread ID, el state está a salvo. Cuando tu servidor se reinicia, simplemente invocas el graph de nuevo, pasando exactamente el mismo thread ID uno dos tres. El checkpointer busca el último checkpoint. Encuentra el state snapshot guardado justo después del paso de análisis. El graph carga ese state y reanuda la ejecución inmediatamente en el paso cuatro. Los nodes de fetch, clean y análisis se omiten por completo porque sus outputs ya están almacenados de forma segura en el checkpoint del thread. Esta es la clave. Al compilar tu graph con un checkpointer y vincular tu ejecución a un thread ID, transformas operaciones frágiles in-memory en workflows duraderos que sobreviven automáticamente a las interrupciones. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue programando!
10

Ejecución duradera e idempotencia

4m 11s

Comprende los matices de reanudar flujos de trabajo. Cubrimos por qué los side-effects deben ser idempotentes y cómo estructurar los nodos para una ejecución duradera.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 10 de 18. Tu workflow procesa un pago, se topa con un rate limit en el siguiente paso y falla. Cuando el sistema se recupera y reanuda el workflow, a tu cliente se le cobra por segunda vez. Tu código no ha cambiado, pero la suposición que hiciste sobre cómo se reanuda el grafo era incorrecta. La solución requiere entender la durable execution y la idempotencia. Muchos desarrolladores asumen que cuando un long-running process se pausa o falla, al reanudarlo retoma la ejecución desde la línea exacta de código Python donde se detuvo. Esperan que el runtime recuerde mágicamente las variables locales a mitad de la función. Eso no es lo que pasa. LangGraph no congela el intérprete de Python tal cual. El estado solo se guarda en los límites entre los nodos. La durable execution en LangGraph significa que el sistema rastrea tu progreso persistiendo el estado del grafo después de que un nodo termina su trabajo y hace un return. Si un nodo falla a mitad de su lógica, el sistema no tiene ningún registro de su progreso parcial. El último estado válido conocido es el que se le pasó al nodo cuando empezó. Cuando reinicias o haces un retry del grafo, la ejecución se reanuda volviendo a ejecutar todo ese nodo fallido desde su primera línea. Piensa en el escenario del pago. Supón que escribes un único nodo que hace dos acciones. Primero, llama a una API externa para cobrar a una tarjeta de crédito. Segundo, actualiza una base de datos remota para registrar la transacción. El cobro a la tarjeta de crédito funciona, pero la conexión a la base de datos da un timeout, haciendo que el nodo falle. El estado del grafo no avanza. Cuando el workflow se reanuda, le vuelve a pasar el estado antiguo a ese mismo nodo. El nodo empieza de cero. Llama a la API externa y cobra a la tarjeta de crédito por segunda vez. Aquí está la clave. Como los nodos pueden reiniciarse desde el principio, cualquier side effect dentro de un nodo debe ser idempotente. La idempotencia es una propiedad donde ejecutar una operación varias veces da exactamente el mismo resultado que ejecutarla una sola vez. Si tu nodo interactúa con el mundo exterior, tienes que escribir el código asumiendo que se ejecutará varias veces para el mismo paso. ¿Cómo te aseguras de esto? Tienes dos enfoques prácticos. El primero es usar idempotency keys con tus servicios externos. Cuando llamas a la API de pagos, le pasas un identificador único derivado del estado actual del grafo. Si el nodo falla y se vuelve a ejecutar, envía el mismo identificador único. El servicio externo reconoce la request duplicada y devuelve una respuesta de éxito sin llegar a mover dinero de nuevo. El segundo enfoque es el diseño estructural del grafo. Si una operación específica no es idempotente de forma nativa, no la agrupes con otros pasos que puedan fallar. Pon la operación peligrosa dentro de su propio nodo dedicado. Haz que sea lo único que hace ese nodo. Si pones el cobro en el nodo A y la actualización de la base de datos en el nodo B, un timeout de la base de datos solo hace fallar al nodo B. El grafo se reanuda en el nodo B. El cobro en el nodo A es completamente seguro, porque el nodo A terminó, y el grafo guardó su estado antes de avanzar. Tú controlas dónde guarda el sistema su progreso según cómo dibujes los límites de tus nodos. Nunca pongas una acción irreversible y no idempotente en el mismo nodo que algo que pueda fallar aleatoriamente. Eso es todo por este episodio. ¡Gracias por escuchar y sigue programando!
11

Human-in-the-Loop: Interrupts

4m 17s

Aprende a congelar agentes en mitad de su ejecución. Detallamos la función interrupt y cómo reanudar flujos de trabajo con aprobación humana externa.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 11 de 18. A veces, una IA no debería tener la última palabra. Quizás quieras congelar a un agente a mitad de pensamiento, pedir la aprobación de un humano e inyectar su respuesta directamente en la lógica en ejecución. Eso es exactamente lo que hacen los interrupts de Human-in-the-Loop. Cuando necesitas que un humano tome una decisión dentro de un workflow de LangGraph, usas una función específica llamada interrupt. Es vital entender qué hace esto realmente por debajo. Los oyentes podrían confundir esto con un input prompt estándar de Python. No lo es. Un input prompt estándar bloquea un thread activo, consumiendo memoria del sistema mientras espera a que el usuario pulse una tecla. En LangGraph, llamar a interrupt se comporta de forma muy diferente. Serializa completamente el estado del graph, lo guarda en tu base de datos del checkpointer, y suspende la ejecución por completo. El graph se pone a dormir. Puede esperar indefinidamente una respuesta sin consumir recursos de computación activos. El flow ocurre en dos fases distintas: pausar y reanudar. Primero, veamos la pausa. Dentro de uno de tus nodos, tu agente llega a un punto donde necesita autorización humana. En esa línea de código exacta, llamas a la función interrupt. Le pasas un payload a esta función, que normalmente es un objeto JSON que contiene el contexto que el humano necesita. Imagina un agente que gestiona atención al cliente automatizada. Decide preparar un reembolso de quinientos dólares. Antes de procesar el pago, el nodo del agente llama a interrupt. Entrega un payload especificando que la acción propuesta es un reembolso y el importe es quinientos. En el momento en que se llama a esa función, el graph se detiene. El runtime de LangGraph captura este evento y propaga el payload JSON hacia arriba hasta tu aplicación cliente. El proceso del graph se apaga, dejando el payload a la espera de revisión humana en una web UI. Ahora la segunda fase: despertar de nuevo al graph. Un proceso externo, como tu servidor backend recibiendo una llamada a la API desde la web UI, es responsable de reiniciar el graph. El manager humano hace clic en aprobar en su dashboard. Tu backend coge esa aprobación y arranca el graph de nuevo usando una instrucción especial llamada Command. Al enviar este Command, incluyes un argumento resume que contiene la respuesta del humano. En nuestro escenario, esta respuesta es un simple valor boolean de true. Aquí está la clave. Cuando el graph se despierta, no vuelve a ejecutar el nodo pausado desde el principio. Reanuda la ejecución en la línea de código exacta donde se paró. La función interrupt que originalmente pausó el graph termina de ejecutarse, y devuelve el valor que enviaste a través del comando resume. La respuesta boolean del humano se inyecta directamente en la variable que espera el resultado del interrupt. El agente entonces lee ese valor true, pasa su comprobación condicional, y finaliza el reembolso de quinientos dólares. Esta arquitectura crea una frontera limpia. La lógica del graph no necesita gestionar webhooks, emails, o interfaces de usuario. Simplemente llama a una función que lanza un payload por encima del muro y espera un valor de retorno. El sistema externo gestiona toda la interacción del usuario y simplemente mete la respuesta de vuelta. Al inyectar la respuesta humana directamente en el retorno de la función, evitas contaminar el estado principal de tu graph con datos de interacción temporales. El poder de la función interrupt reside en tratar el feedback humano no como un desvío arquitectónico complejo, sino como una llamada a función estándar que puede pausar el universo de forma segura hasta que obtiene una respuesta. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!
12

Depurando el pasado: Time Travel y Forking

4m 11s

Explora las capacidades de Time Travel de LangGraph. Mostramos cómo navegar por el historial de estado, reproducir Checkpoints pasados y hacer un fork de rutas de ejecución alternativas.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 12 de 18. Tu agente se descontrola, ejecutando una acción que no querías o generando una respuesta terrible. Normalmente, tienes que reiniciar todo el proceso desde cero y esperar que se comporte mejor la segunda vez. ¿Y si pudieras literalmente rebobinar la ejecución hasta el momento exacto antes del error, alterar manualmente el state, y dejar que corra por una línea temporal alternativa? Eso es exactamente lo que vamos a ver hoy con Debugging el pasado: Time Travel y Forking. Para manipular el pasado, primero necesitas verlo. Esto lo haces usando un método llamado get state history, pasándole tu thread ID. Este método devuelve un iterador que contiene cada state por el que pasó el graph durante la ejecución de ese thread. Cada uno de estos states históricos posee un identificador único llamado checkpoint ID. Puedes pensar en este ID como tus coordenadas exactas en el tiempo. Si simplemente quieres hacer un replay del graph desde un punto específico, coges el checkpoint ID de destino de ese historial. Luego, llamas al método invoke de tu graph, pasándole un objeto de configuración que incluye tanto el thread ID como ese checkpoint ID específico. El graph reanuda la ejecución inmediatamente desde ese state exacto. No vuelve a ejecutar ninguno de los nodes anteriores, ahorrando compute y tiempo. Hacer replay es útil, pero el verdadero poder reside en cambiar el pasado para hacer un fork de la ejecución. Veamos un escenario práctico. Supongamos que tu agente tenía la tarea de escribir un chiste, y generó un chiste malísimo sobre un perro. Revisas el state history y encuentras el checkpoint ID del state justo antes de que ocurriera el paso de generación. En lugar de simplemente hacer replay desde ese punto, usas el método update state. Proporcionas el thread ID, el checkpoint ID histórico específico, y los nuevos valores del state que quieres inyectar. En este caso, actualizas manualmente la variable topic, cambiándola de un perro a gallinas. Aquí está la clave. Los developers suelen pensar que actualizar un state pasado hace un rollback de la ejecución, sobrescribiendo o eliminando el historial original. No es así. LangGraph opera con una arquitectura append-only. Cuando llamas a update state en un checkpoint histórico, el sistema crea de forma segura un checkpoint totalmente nuevo que se ramifica a partir de ese antiguo. Tu línea temporal original, con el chiste malísimo del perro incluido, permanece totalmente intacta y accesible. No has borrado el pasado; has hecho un fork de una nueva realidad. Una vez que aplicas esa actualización, el graph se sitúa en un checkpoint recién creado con el state alterado. Para continuar por esta nueva línea temporal, simplemente vuelves a invocar el graph con el thread ID, omitiendo cualquier checkpoint ID específico. El graph toma por defecto el state más reciente en esta rama recién forkeada y reanuda la ejecución. Tu agente lee el state actualizado y, en su lugar, genera un chiste sobre gallinas. Si te resultan útiles estas explicaciones técnicas y quieres apoyar el programa, puedes buscar DevStoriesEU en Patreon. El time travel transforma el debugging, pasando de adivinar qué salió mal a manipular con precisión el historial del graph para explorar resultados alternativos, sin perder ni un solo rastro del run original. Eso es todo por este episodio. Gracias por escuchar, y a seguir construyendo.
13

Memoria a largo plazo: Stores a través de Threads

4m 18s

Ve más allá de los Threads aislados. Presentamos la interfaz Store y explicamos cómo dotar a tus agentes de memoria persistente entre sesiones.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 13 de 18. Creas un agente y un usuario le indica que siempre quiere su código en Python 3.11. Al día siguiente, inicia una nueva conversación y el agente se olvida por completo, generando Python 3.9 en su lugar. La memoria del thread está aislada en una sola conversación. Cuando tu agente necesita conservar datos entre sesiones completamente separadas, necesitas memoria a largo plazo usando Stores entre threads. Un error muy común es intentar solucionar esto metiendo datos a largo plazo en el state del checkpointer. Los checkpointers son memoria a corto plazo. Son estrictamente snapshots del state por thread, diseñados para pausar, reanudar o hacer replay de una sola conversación. Si un usuario indica una preferencia en el thread A, el thread B no tiene absolutamente ninguna forma de verla. Para compartir conocimiento entre múltiples threads, LangGraph proporciona la interfaz Store. Un Store es una capa de memoria key-value que se encuentra fuera de los states individuales de los threads. Lo configuras pasando un objeto store, como un PostgresStore, como argumento cuando compilas tu graph. Una vez compilado, ese store se adjunta al entorno de ejecución del graph. Dentro de tu graph, los nodos acceden a esta capa de memoria a través del objeto Runtime. Cuando defines un nodo, puedes acceder al contexto del runtime, que expone el store. Simplemente accedes a runtime punto store para interactuar con tu memoria a largo plazo. Aquí está la clave. Los datos en un store se organizan usando namespaces. Un namespace es una lista jerárquica de strings que particiona tus datos, muy parecido a la ruta de una carpeta en tu ordenador. Para una aplicación multi-tenant, podrías definir un namespace que empiece con el string users, seguido de un ID de usuario específico, y que termine con preferences. Piensa en ese escenario del asistente de programación. Un usuario inicia una sesión el lunes. Durante el chat, menciona que prefiere Python 3.11 y el modo oscuro. Un nodo en tu graph reconoce esto como una preferencia permanente. Llama al método put en runtime punto store. Le pasa el namespace para ese usuario específico, una key única para el elemento, y un dictionary que contiene las preferencias. Los datos ahora están guardados fuera del thread. El viernes, el mismo usuario abre tu aplicación y empieza un thread completamente nuevo. El state del checkpointer para este nuevo thread está completamente vacío. Sin embargo, tu graph incluye un nodo de setup que se ejecuta primero. Este nodo llama al método search en runtime punto store, proporcionando el prefijo del namespace del usuario. El store devuelve las preferencias guardadas. Luego, el nodo coloca esas preferencias en el state del thread actual. A partir de ese momento, el agente sabe que tiene que usar Python 3.11 y el modo oscuro para esta nueva conversación. La interfaz del store proporciona tres operaciones principales. Usas put para guardar o sobrescribir un elemento. Usas get para recuperar un solo elemento cuando conoces su namespace y key exactos. Usas search para recuperar múltiples elementos que comparten un prefijo de namespace. Hacer search es especialmente útil cuando has guardado varios fragmentos de memoria distintos para un usuario a lo largo del tiempo y necesitas traerlos todos al contexto actual. Al separar el state a corto plazo de un store entre threads, desacoplas el ciclo de vida del conocimiento de tu agente del ciclo de vida de una sola conversación. Gracias por escuchar, nos vemos en la próxima.
14

Ejecución en streaming y el formato v2

4m 31s

Mejora la experiencia de usuario con feedback en tiempo real. Desglosamos los stream modes (values, updates, messages) y el formato unificado v2 StreamPart.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 14 de 18. A los usuarios les molesta tener que esperar treinta segundos viendo un spinner de carga estático mientras tu sistema trabaja en segundo plano. Quieres mostrarles el proceso de razonamiento del sistema en tiempo real, pero capturar esas señales internas suele requerir la configuración de complejos callbacks personalizados. La ejecución en streaming y el formato v2 solucionan este problema unificando cada evento interno en un flujo único y predecible. Primero, aclaremos un malentendido común. Los ingenieros suelen confundir el streaming de tokens del modelo de lenguaje con el streaming del state de la aplicación. Son capas de información completamente diferentes. Un stream de tokens es simplemente texto que aparece palabra por palabra. Un stream de state registra el progreso general de tu workflow, pasando de una tarea a la siguiente. LangGraph gestiona ambos simultáneamente. Puedes acceder a este comportamiento solicitando un stream y pasando el argumento version v2 a tu método de ejecución. Esto estandariza la salida. En lugar de lidiar con tipos de datos mixtos, cada evento que sale de tu graph se convierte en un diccionario unificado que contiene exactamente tres campos: type, ns y data. El campo type define la categoría del evento. El campo ns significa namespace, indicando la ruta exacta en la jerarquía de tu graph donde se originó el evento. Esto es fundamental cuando tienes subgraphs anidados y necesitas saber con precisión qué subcomponente generó el evento. Finalmente, el campo data contiene el payload real. Controlas con exactitud qué se incluye en este stream seleccionando uno o más stream modes. El modo values te envía el state completo y actualizado del graph cada vez que un nodo finaliza su trabajo. Esto resulta útil si tu aplicación requiere una visión completa en cada paso. El modo updates es mucho más ligero. Transmite solo los datos específicos devueltos por un nodo, representando únicamente el delta o cambio en el state general. El modo messages opera a un nivel más granular, transmitiendo los chunks individuales de un mensaje de chat generado a medida que los produce un modelo de lenguaje subyacente. Imagínate una interfaz frontend. Quieres un indicador de status brillante que resalte el paso activo, por ejemplo, obteniendo el contexto, luego evaluando documentos y luego redactando, mientras muestras simultáneamente el texto del borrador token por token. Para construir esto, inicias la ejecución de tu graph con los stream modes configurados en updates y messages, asegurándote de pasar el flag de version v2. Tu frontend empieza a recibir un stream continuo y unificado de estos diccionarios. Cuando llega un diccionario con el type configurado en updates, lees el campo namespace. Esto te indica exactamente qué nodo acaba de terminar su trabajo. Usas esa señal para cambiar tu indicador de status brillante al siguiente paso en la interfaz de usuario. Milisegundos después, el stream entrega un nuevo diccionario con el type configurado en messages. Extraes el token de raw text del campo data y lo añades directamente al párrafo que tu usuario está leyendo. Tanto los cambios de state de alto nivel como la generación de texto de bajo nivel llegan a través del mismo pipe. Aquí está la clave. Al forzar los tokens, los cambios de state y el progreso de los nodos en una única estructura de diccionario de tres campos, el formato v2 elimina por completo la necesidad de escribir lógica de handling separada o callbacks asíncronos complejos para diferentes tipos de eventos en tiempo real. Eso es todo por este episodio. ¡Nos vemos en el próximo!
15

Componiendo la complejidad: Subgraphs

4m 09s

Escala tus flujos de trabajo tratando los grafos compilados como nodos. Debatimos sobre la composición de Subgraphs y la gestión de esquemas de estado compartidos frente a privados.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 15 de 18. Cuando tu agente de IA se vuelve complejo, a menudo terminas con un mega-graph enorme e ilegible donde un solo cambio lo rompe todo. No tienes por qué construir así: en su lugar, puedes crear mini-graphs especializados y unirlos. Hoy hablamos de componer complejidad: subgraphs. Los subgraphs te permiten reutilizar lógica y distribuir el desarrollo entre diferentes equipos. En lugar de meter cada paso de tu aplicación en un solo archivo, creas graphs más pequeños e independientes. Una vez que un graph está compilado, se comporta exactamente como una función callable estándar. Esto significa que puedes coger un graph compilado entero y meterlo directamente en otro graph como un único node. Piensa en un sistema de routing maestro para un asistente empresarial. El graph principal gestiona el input del usuario, comprueba la seguridad y decide qué hacer a continuación. Cuando un usuario hace una pregunta técnica profunda, el router necesita realizar una recopilación de datos compleja. En lugar de programar esa lógica directamente en el router, la delegas a un subgraph de Research dedicado. Un equipo de ingeniería completamente distinto puede construir, testear y refinar este graph de Research de forma aislada. Al parent graph le da igual cómo se haga la investigación. Simplemente llama al node. Hay una tendencia muy común a complicar en exceso cómo se pasan los datos entre estos graphs. Si el parent graph y el subgraph usan exactamente el mismo state schema, es decir, comparten las mismas state keys, no necesitas ningún adaptador especial. Simplemente pasas el subgraph de Research compilado directamente a la función add node de tu master graph. El motor inyecta automáticamente el parent state en el subgraph, ejecuta la lógica y hace un merge de los resultados de vuelta en el parent state cuando termina. Ahora bien, ¿qué pasa cuando los equipos no se coordinan a la perfección? Supongamos que tu parent router usa una state key llamada user query, pero el otro equipo de ingeniería construyó el subgraph de Research para que espere una key llamada search term. No puedes meter el subgraph compilado directamente en el parent graph. Las keys no coincidirán y la ejecución fallará. Aquí está la clave. Puedes solucionar este desajuste usando una simple función wrapper. En tu parent graph, defines una node function estándar que acepta el parent state. Dentro de esta función, extraes el valor de la user query. Luego llamas manualmente al subgraph de Research compilado, pasándole un payload donde mapeas esa user query a la key search term. El subgraph ejecuta su lógica interna y devuelve su final state. Tu función wrapper coge ese output, traduce los resultados de vuelta a las keys específicas que espera el parent, y los devuelve. Para el parent graph, este wrapper parece un node normal y corriente. No tiene ni idea de que un subgraph enorme y complejo se acaba de ejecutar en su interior. Simplemente le pasó datos y recibió un state actualizado de vuelta. Este patrón te da una modularidad estricta sin sacrificar el control. Tratar un graph compilado como una función callable más detrás de un simple wrapper es la forma más potente de escalar una arquitectura de IA sin colapsar bajo tu propio código. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!
16

Persistencia de Subgraphs y patrones multi-agente

4m 03s

Domina el memory scoping en sistemas multi-agente. Explicamos la diferencia entre la persistencia de Subgraphs per-invocation, per-thread y stateless.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 16 de 18. Si se llama a un subagente experto dos veces en una misma conversación, ¿debería recordar la primera llamada o empezar de cero con amnesia total? Esta decisión cambia por completo el comportamiento de los sistemas multi-agent y está totalmente controlada por la Subgraph Persistence y los Multi-Agent Patterns. Cuando creas un parent graph que enruta tareas a subgraphs, el state management se complica. Piensa en un bot principal de atención al cliente que gestiona el chat general. Cuando un usuario hace una pregunta compleja sobre una factura, el bot principal enruta la solicitud a un subgraph dedicado de Billing Expert. Por defecto, los subgraphs son totalmente stateless. Cuando compilas ese Billing Expert sin especificar un checkpointer, opera estrictamente por invocación. El bot principal le pasa los inputs necesarios, el experto ejecuta sus pasos internos, devuelve un resultado e inmediatamente descarta su state interno. Si el usuario hace una pregunta de seguimiento sobre la factura cinco minutos después, el bot principal vuelve a llamar al experto. El experto no recuerda la conversación anterior; empieza totalmente de cero. Para un subgraph simple de extracción de datos, esa amnesia está perfectamente bien. Pero para un agente interactivo y especializado, es increíblemente frustrante para el usuario. Para solucionar esto, el experto necesita su propia memoria entre turnos. Un error muy común aquí es pasar una instancia de checkpointer totalmente nueva, como un objeto memory saver, directamente al método compile del subgraph. No hagas esto a menos que quieras que el subgraph comparta exactamente el mismo state entre usuarios y sesiones completamente diferentes. Si el usuario A y el usuario B hablan con el sistema al mismo tiempo, pasar una instancia explícita de checkpointer al subgraph significa que sus datos se mezclan en un único state global. Esto crea una interferencia masiva entre parent threads aislados. En su lugar, simplemente pasas el valor boolean True al argumento checkpointer cuando compilas el subgraph. Aquí es donde se pone interesante. Ponerlo en True le dice al subgraph que dependa del mecanismo de checkpointer del parent graph, pero que mantenga un historial multi-turn completamente aislado específicamente para sí mismo. Por debajo, el framework se encarga del namespacing. Crea automáticamente un thread ID único para el subgraph que está permanentemente vinculado al thread ID del parent. Ahora mira de nuevo el escenario del Billing Expert con esta configuración. El usuario hace una pregunta sobre una factura. El bot principal la enruta al experto. El experto responde y se queda inactivo. Más adelante en la misma conversación, el usuario hace una pregunta de seguimiento. El bot principal vuelve a enrutarla al experto. Como se compiló con el checkpointer puesto en True, el experto se despierta, comprueba su sub-thread dedicado y carga el contexto de la factura del turno anterior. Actúa como un participante persistente en la conversación. Y como ese sub-thread está estrictamente limitado al thread del parent, un usuario diferente que hable con el sistema obtiene su propia instancia completamente limpia del Billing Expert. La forma en que configuras el checkpointer de un subgraph dicta toda su identidad en tu sistema: dejarlo en blanco crea una función de utilidad desechable y stateless, mientras que ponerlo en True crea un colaborador continuo y context-aware. Gracias por pasar unos minutos conmigo. Hasta la próxima, cuídate.
17

Estructura de la aplicación y preparación para el despliegue

4m 23s

Transición de prototipos a producción. Exploramos langgraph.json, la estructura de archivos adecuada y la gestión de dependencias para despliegues con estado.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 17 de 18. Un script de Python que se ejecuta correctamente en tu portátil no es una aplicación de producción. Si intentas ejecutar agentes stateful mediante scripts independientes, inevitablemente te darás contra un muro a la hora de escalar. Para solucionar esto, analizaremos la estructura de la aplicación y cómo prepararla para el deploy. Cuando creas un agente LangGraph por primera vez, probablemente lo prototipas en un Jupyter notebook o en un único archivo de Python. Defines los nodes, conectas los edges, compilas el graph y llamas al método invoke ahí mismo en el mismo archivo para comprobar si funciona. Eso está bien para hacer pruebas. Sin embargo, un servidor de producción no puede leerte la mente. Necesita una forma estandarizada de servir ese graph como API, instalar los paquetes necesarios e inyectar las environment variables. Para que tu prototipo esté listo para el deploy, debes organizar tu código en una estructura de directorios limpia. Supongamos que creas una nueva carpeta llamada my-app. Mueves tu código de Python fuera del notebook a un archivo limpio dentro de esta carpeta. A continuación, añades un archivo de dependencias, normalmente requirements punto txt. Finalmente, creas un archivo de configuración llamado langgraph punto json en la raíz de la carpeta my-app. El archivo langgraph punto json es el plano principal de tu aplicación. Cuando usas la Command Line Interface de LangGraph, o haces deploy a un entorno de producción, este archivo de configuración le dice al sistema subyacente exactamente cómo construir y ejecutar tu proyecto. Requiere tres datos principales sobre las dependencias, las environment variables y los entry points del graph. Primero, declaras tus dependencias. Esto es solo un string de path en el archivo JSON que apunta a tu archivo de requirements. Esto asegura que el servidor de deploy instale los paquetes de Python exactos de los que depende tu agente, evitando errores de missing module en producción. A continuación, defines el string de environment. Este apunta a tu archivo punto env. Los agentes stateful siempre necesitan secretos, como credenciales de base de datos o API keys del modelo. Apuntar al archivo de environment asegura que el runtime cargue estas claves de forma segura antes de intentar iniciar el graph. Esta es la parte que importa. El tercer requisito en el archivo de configuración es el mapping de graphs. Esto le dice al servidor exactamente dónde vive tu graph compilado en el código fuente. Actúa como un diccionario. Le asignas un ID a tu graph, que se convierte en su nombre oficial en la API generada. Luego, mapeas ese ID a un módulo de Python y nombre de variable específicos. Por ejemplo, podrías mapear el ID customer-support-agent al string agent punto py dos puntos compiled-graph. El servidor mira el archivo agent punto py, encuentra la variable llamada compiled-graph, y la carga en memoria. Esta estructura requiere un cambio deliberado en cómo escribes tu código. Los principiantes a menudo ejecutan graphs mediante scripts de Python independientes que ejecutan acciones tan pronto como se ejecutan. Pero el runtime de LangGraph depende de langgraph punto json para exponer el graph dinámicamente como un servicio web. No ejecuta tu script de arriba a abajo. Solo importa el objeto de graph compilado que especificaste en el archivo de configuración. Por esto, tu archivo de Python solo debería definir los nodes, conectarlos, y asignar el graph compilado a una variable. Debes eliminar cualquier código de prueba sobrante al final que invoque manualmente el graph. Si dejas código de prueba en el archivo, se ejecutará durante la fase de importación en el servidor, causando fallos de deploy o llamadas a la API no deseadas solo por iniciar el servicio. Al declarar explícitamente tus dependencias, environment y paths del graph en un archivo JSON central, separas la definición de tu agente de su ejecución, convirtiendo un script local en un servicio robusto y deployable. Eso es todo por este episodio. ¡Gracias por escuchar, y sigue construyendo!
18

Testeando la ejecución del Graph End-to-End

4m 26s

Aprende estrategias de testing robustas para flujos de trabajo de grafos. Cubrimos la integración con pytest, la ejecución aislada de nodos y la simulación de estados parciales.

Descargar
Hola, soy Alex de DEV STORIES DOT EU. LangGraph, episodio 18 de 18. Tienes un workflow multi-agent complejo y necesitas probar un routing edge case específico en el paso cuatro. No deberías tener que ejecutar todo el sistema de principio a fin solo para comprobar esa condición. Probar la ejecución del graph end-to-end es la forma de apuntar exactamente a la lógica que necesitas. Cuando los desarrolladores intentan aislar partes de un graph para probarlas, suelen recurrir a mock objects complejos. Intentan hacer mock de la estructura del graph circundante o hacer stub de todos los nodos anteriores. En LangGraph, no necesitas hacer esto. La arquitectura gira completamente en torno al state. Dado que los nodos son simplemente funciones que leen y escriben state, puedes inyectar manualmente un state payload específico y probar fragmentos de nodos aislados de forma nativa. Aquí es donde la state injection y los breakpoints se vuelven increíblemente útiles en tu test suite. Solo necesitas dos herramientas para saltar directamente al medio de un graph. La primera es el método update state. La segunda es un parámetro de configuración llamado interrupt after. Usar estas herramientas dentro de un testing framework estándar como pytest te permite simular condiciones exactas sin ejecutar la aplicación completa. Apliquemos esto a un escenario concreto. Imagina que tienes un graph donde el nodo tres hace una API call externa, y el nodo cuatro comprueba el resultado. Quieres verificar que, si el API payload contiene un código de fallo específico, el nodo cuatro redirige correctamente el execution flow a tu nodo error handler. En lugar de ejecutar los nodos uno y dos para activar esto, aíslas el problema. Inicializas el graph con un thread identifier. Luego, usas update state para insertar un API payload simulado y fallido directamente en el thread state. Actúas como si el nodo tres estuviera a punto de ejecutarse con esos datos específicos. A continuación, invocas el graph, pero pasas un diccionario de configuración ajustando interrupt after al nodo cuatro. Cuando inicias el graph, la ejecución comienza inmediatamente en el nodo tres usando tu failure state inyectado. El nodo tres procesa el bad payload y pasa el state resultante al nodo cuatro. El nodo cuatro evalúa la lógica y decide redirigir al error handler. Como has establecido un breakpoint, el graph pausa la ejecución en el momento en que el nodo cuatro termina. Ahora tu test puede evaluar el resultado. Extraes el state actual del graph. Puedes escribir tus assertions para asegurarte de que el nodo cuatro actualizó las state variables correctamente. Y lo que es más importante, puedes inspeccionar el graph execution plan. Al mirar el siguiente nodo pendiente en la state metadata, puedes confirmar que la routing logic funcionó a la perfección y que el error handler está en cola. Esta es la clave. Al manipular el state directamente, conviertes una chain de agentes altamente interconectada e impredecible en un test case determinista y paso a paso. Verificas exactamente cómo el graph transita de un nodo al siguiente sin esperar a los language models o network calls en los pasos anteriores. Como este es el último episodio de la serie, te animo a explorar la documentación oficial y a probar a construir estos workflows hands-on. Si tienes ideas sobre qué deberíamos tratar a continuación, visita devstories dot eu y sugiere un tema. Eso es todo por este episodio. Gracias por escuchar, ¡y sigue construyendo!