Después de haber escrito mi primer agente para Officia Agent con TypeScript, bajo un modelo funcional sin ningún tipo de patrón, me di cuenta de que la tarea de construir un agente de IA no es sencilla.
Luego, investigando y leyendo sobre Agentes de IA, descubrí LangGraph, un framework especializado para construir agentes de IA disponible en dos lenguajes: TypeScript (JavaScript tipado) y Python. Así que me di a la tarea de aprenderlo. Inicié viendo algunos videos en YouTube para visualizar sus capacidades y luego me inscribí en el curso gratuito que ofrecen en LangChain Academy, altamente recomendado para iniciarse bien en este mundo de construcción de Agentes de IA.
💡 Si te gusta tener el control y programar, LangGraph es lo que necesitas para construir tus agentes de IA.
¿Qué es LangGraph?
Utilizado por las compañías más grandes del mundo como Replit, Klarna, Elastic y muchas más, LangGraph es un framework que te permite construir agentes de IA a bajo nivel. Esto quiere decir que vos como desarrollador tenés la libertad de programar al agente con tus propias reglas y tu propia lógica.
Beneficios
Es de código abierto, lo que significa que lo podés desplegar localmente o en tus propios servidores. Por supuesto, LangChain también te provee de un ecosistema avanzado de pago con funcionalidades adicionales.
«El cielo es el límite», y en este caso la capacidad de abstraer un problema y llevarlo a una solución con LangGraph es posible para casi cualquier cosa. No te voy a mentir, será laborioso, pero si estás buscando una solución robusta y estable para construir un Agente de IA, esto es lo que necesitas.
Además, hay más beneficios propios del framework:
- Ejecución Duradera: Los agentes pueden ejecutarse por largos períodos de tiempo, manteniendo su estado y retomando donde lo dejaron, incluso después de interrupciones.
- Human-in-the-loop: Te permite crear una lógica donde puedas incorporar al humano en la toma de decisiones del grafo. Por ejemplo, antes de agendar una cita importante, el agente puede pausar y pedir confirmación.
- Memoria: Los agentes tienen un State (estado) de memoria a corto plazo que podés usar para el razonamiento continuo, y memoria a largo plazo en cada sesión.
- Debugging: En el ecosistema se te provee de una aplicación web llamada LangSmith que te ayuda a tener visibilidad completa de qué está pasando con tu agente, cómo lo está haciendo, cuánto te está costando y más.
Caso de Uso: Agente de Gestión de Calendario
Cuando vas a desarrollar un Agente en LangGraph, lo primero que necesitás hacer es descomponer la tarea en pasos, llamados Nodos. Un nodo puede ser un proceso determinista, no determinista (como una llamada a un LLM), o una decisión. Finalmente, debés tener claro que todos los nodos comparten un estado (estructura de objeto) global, que podés consultar y actualizar en cada paso.
Veamos el siguiente proceso con un ejemplo práctico:
Piensa qué querés automatizar
Para este ejemplo, lo que quiero automatizar es el proceso de gestión de calendario con las siguientes capacidades:
Ejemplos de escenarios
-Buscar espacio en la agenda: "Podrías buscar un hueco en mi agenda para el martes"
-Verificar horario: "Revisa si tengo libre el jueves a las 3"
-Agendar cita: "Agendame una reunión para mañana a la 1"
-Cancela una cita: "Cancela la cita del viernes a las 4 de la tarde"
-Reprogramar una cita: "Reprograma la reunión de mañana a las 2 y ponela para la próxima semana"
El Agente debe poder
-Conectarse a mi calendario
-Consultar disponibilidad de horarios
-Agendar una cita
-Cancelar una cita
-Reprogramar una cita
Paso 1: Diagrama el flujo
Comienza identificando cada uno de los pasos en el proceso. Cada paso se convierte en un nodo, una función que hace solo una tarea específica.
Las flechas declaran las rutas posibles, pero la decisión de qué ruta tomar se encuentra dentro de cada nodo o en nodos de decisión especiales.
A esto le podemos llamar Agente. Un agente puede ser simple o muy complejo, y el ejemplo anterior lo demuestra. El proceso de agendar una cita, cancelarla, reagendarla o buscar espacios no se puede simplificar en una sola función, y para ello introduzco el concepto de sub-agentes, donde cada nodo podría ser un sub-agente con su propia lógica de nodos para intentar resolver una petición del usuario.
Por ejemplo, para nuestro agente de calendario, el flujo podría verse así:
- Inicio → Recibir mensaje del usuario
- Analizar intención → ¿Qué quiere hacer el usuario? (Nodo LLM)
- Decisión de ruta → ¿Buscar disponibilidad, agendar, cancelar o reprogramar?
- Ejecutar acción correspondiente → Sub-agente específico para cada caso
- Confirmar con usuario → Mostrar resultado
Paso 2: Identifica cada paso
Una vez identificado cada paso, ahora tenemos que decidir qué tipo de paso será. Podemos definir 4 tipos de pasos:
- Paso LLM: Se usa cuando se necesita analizar, entender, generar texto o tomar una decisión de razonamiento. Ejemplo: Conocer la intención del usuario cuando dice «necesito un hueco mañana» → interpretar que quiere buscar disponibilidad.
- Paso de Datos: Usado para extraer información de una fuente de datos externa. Ejemplo: Consultar el calendario mediante la API de Google Calendar para obtener las citas existentes del martes.
- Paso de Acción: Usado para ejecutar una acción que modifica el estado del sistema. Ejemplo: Crear una cita en el calendario mediante una llamada POST a la API.
- Paso de Solicitud de Usuario: Utilizado cuando se necesita la intervención humana. Ejemplo: «Encontré 3 horarios disponibles el martes: 10am, 2pm y 4pm. ¿Cuál preferís?»
Para nuestro agente de calendario, tendríamos algo así:
- «Agendame una reunión mañana a la 1» → Paso LLM para extraer: acción=agendar, fecha=mañana, hora=1pm
- Verificar disponibilidad → Paso de Datos consultando el calendario
- Si hay conflicto → Paso de Solicitud de Usuario «Ya tenés una reunión a la 1pm, ¿querés reprogramarla?»
- Si está libre → Paso de Acción crear la cita
Paso 3: Diseña el estado
El State (estado) es la memoria compartida entre los nodos de tu agente. Pensá en todo lo que tus nodos deberían saber para ejecutar sus tareas de manera armónica.
¿Qué se incluye en el state? Los datos que son necesarios en varios pasos. Por ejemplo:
- La intención del usuario (agendar, cancelar, buscar)
- Fecha y hora solicitadas
- Resultado de la consulta al calendario
- Mensajes del historial de la conversación
- Estado de confirmación (¿el usuario confirmó la acción?)
¿Qué no incluir? Lo contrario, datos que no necesitamos en todos los pasos y que podemos extraer en un momento específico. Por ejemplo:
- El token de autenticación de la API (se puede obtener cuando se necesite)
- Detalles internos de la respuesta de la API que no afectan decisiones futuras
Un ejemplo de estructura de estado para nuestro agente sería:
{
messages: Message[], // Historial de conversación
intent: "agendar" | "cancelar" | "buscar" | "reprogramar",
requestedDate: string, // "2026-01-15"
requestedTime: string, // "13:00"
availability: TimeSlot[], // Horarios disponibles encontrados
conflictingEvent: Event | null, // Si hay conflicto
needsConfirmation: boolean,
actionCompleted: boolean
}
Paso 4: Crea los nodos (Funciones)
Creá las funciones para cada uno de tus nodos, usando el patrón de diseño que mejor se adecúe a tu solución. Cada función recibe el estado actual y devuelve una actualización del estado.
Por ejemplo, el nodo que analiza la intención del usuario:
async function analyzeIntent(state: AgentState): Promise<Partial<AgentState>> {
const userMessage = state.messages[state.messages.length - 1];
// Llamada al LLM para extraer la intención
const response = await llm.invoke([
{ role: "system", content: "Extrae la intención del usuario: agendar, cancelar, buscar o reprogramar" },
{ role: "user", content: userMessage.content }
]);
// Parsear la respuesta
const { intent, date, time } = parseIntentResponse(response);
return {
intent,
requestedDate: date,
requestedTime: time
};
}
Y el nodo que consulta disponibilidad:
async function checkAvailability(state: AgentState): Promise<Partial<AgentState>> {
// Consultar el calendario externo
const events = await calendarAPI.getEvents(state.requestedDate);
// Verificar si el horario solicitado está ocupado
const conflict = events.find(e => e.time === state.requestedTime);
if (conflict) {
return {
conflictingEvent: conflict,
needsConfirmation: true
};
}
return {
availability: [{ date: state.requestedDate, time: state.requestedTime }],
conflictingEvent: null
};
}
Paso 5: Une todo
Ahora conectás todos tus nodos usando el API de LangGraph. Definís las conexiones entre nodos y las condiciones para cada transición:
import { StateGraph } from "@langchain/langgraph";
// Crear el grafo
const workflow = new StateGraph({
channels: agentStateSchema
});
// Agregar nodos
workflow.addNode("analyzeIntent", analyzeIntent);
workflow.addNode("checkAvailability", checkAvailability);
workflow.addNode("createAppointment", createAppointment);
workflow.addNode("askConfirmation", askConfirmation);
// Definir las conexiones
workflow.addEdge("__start__", "analyzeIntent");
workflow.addEdge("analyzeIntent", "checkAvailability");
// Agregar lógica condicional
workflow.addConditionalEdges(
"checkAvailability",
(state) => {
if (state.conflictingEvent) {
return "askConfirmation";
}
return "createAppointment";
}
);
workflow.addEdge("createAppointment", "__end__");
workflow.addEdge("askConfirmation", "__end__");
// Compilar el grafo
const app = workflow.compile();
Paso 6: Prueba y refina
Finalmente, probá tu agente con diferentes escenarios y refiná el comportamiento. LangGraph te permite inspeccionar el estado en cada paso, lo que facilita el debugging:
// Ejecutar el agente
const result = await app.invoke({
messages: [{ role: "user", content: "Agendame una reunión mañana a la 1" }]
});
console.log("Estado final:", result);
// Usar LangSmith para ver el trace completo
// Podés visualizar cada nodo ejecutado, su entrada y salida
Algunos tips para probar:
- Probá casos simples primero: «Tengo libre el martes?»
- Luego probá casos con conflictos: «Agendame algo cuando ya tengo una cita»
- Probá casos ambiguos: «Necesito reunirme la semana que viene» (sin especificar día ni hora)
- Usá LangSmith para visualizar el flujo completo y detectar dónde el agente se confunde
Con este enfoque paso a paso, podés construir agentes de IA complejos y robustos que resuelvan problemas reales. LangGraph te da el control total sobre la lógica, mientras te abstrae de la complejidad de manejar el estado y las transiciones entre pasos.
Conceptos claves
Estado (State)
La memoria compartida entre todos los nodos del agente. Contiene toda la información necesaria para que los diferentes pasos del flujo puedan ejecutarse de manera coordinada. Se actualiza conforme el agente avanza por los diferentes nodos.
Nodo (Node)
Una función individual que realiza una tarea específica dentro del flujo del agente. Cada nodo recibe el estado actual, ejecuta su lógica particular, y devuelve una actualización del estado. Los nodos pueden ser de tipo LLM, de datos, de acción o de solicitud al usuario.
Arista (Edge)
Una conexión entre dos nodos que define el flujo de ejecución. Las aristas pueden ser simples (conexión directa) o condicionales (la ruta depende de una condición evaluada en tiempo de ejecución).
Grafo (Graph)
La estructura completa del agente, compuesta por todos los nodos y aristas que definen el flujo de ejecución. LangGraph utiliza esta representación para orquestar la ejecución del agente.
Sub-agente
Un agente completo que funciona como un nodo dentro de otro agente más grande. Permite modularizar lógica compleja dividiendo el problema en sub-problemas más manejables, cada uno con su propio flujo de nodos.
LLM (Large Language Model)
Modelo de lenguaje grande utilizado para tareas que requieren comprensión, razonamiento o generación de texto. En el contexto de agentes, se usa típicamente para analizar intenciones, extraer información o tomar decisiones basadas en lenguaje natural.
Nodo LLM
Un nodo que utiliza un modelo de lenguaje para procesar información. Se usa cuando se necesita analizar, entender, generar texto o tomar decisiones de razonamiento sobre el input del usuario.
Nodo de Datos
Un nodo especializado en consultar información de fuentes externas (APIs, bases de datos, servicios). No modifica datos, solo los recupera y los añade al estado.
Nodo de Acción
Un nodo que ejecuta operaciones que modifican el estado del sistema externo (crear, actualizar, eliminar recursos). Por ejemplo, crear una cita en un calendario o enviar un email.
Nodo de Solicitud de Usuario
Un nodo que requiere intervención humana. Detiene el flujo automático del agente y espera input adicional del usuario antes de continuar con el siguiente paso.
Arista Condicional (Conditional Edge)
Una conexión entre nodos que evalúa una condición basada en el estado actual para determinar qué nodo debe ejecutarse a continuación. Permite crear flujos dinámicos que se adaptan según el contexto.
Compilación (Compile)
El proceso de convertir la definición del grafo (nodos y aristas) en una aplicación ejecutable. Una vez compilado, el grafo puede ser invocado para procesar requests.
Invocación (Invoke)
La acción de ejecutar el agente compilado con un estado inicial. El agente procesará el input siguiendo el flujo definido hasta llegar a un nodo final.
LangSmith
Herramienta de observabilidad y debugging para aplicaciones con LangChain y LangGraph. Permite visualizar el trace completo de ejecución, ver el estado en cada paso, y detectar problemas en el flujo del agente.
Intención (Intent)
La acción o propósito que el usuario quiere realizar, extraída de su mensaje. Por ejemplo: agendar, cancelar, buscar, reprogramar. Identificar correctamente la intención es crucial para dirigir el flujo del agente.
Historial de Conversación (Message History)
El registro de todos los mensajes intercambiados entre el usuario y el agente. Almacenado en el estado, permite al agente mantener contexto de conversaciones multi-turno.
StateGraph
La clase principal de LangGraph para crear agentes basados en grafos con estado. Permite definir la estructura del estado, agregar nodos, conectarlos con aristas, y compilar el grafo resultante.
Channels
Los campos que componen el esquema del estado en LangGraph. Cada channel representa una propiedad específica del estado que puede ser leída y actualizada por los nodos.
Reducer
Una función opcional que controla cómo se combinan múltiples actualizaciones a un mismo campo del estado. Útil cuando varios nodos pueden modificar la misma propiedad y necesitas definir la lógica de merge.
¿Quien soy yo?
Te cuento quien soy en la siguiente entrada Por qué inicié mi blog personal sobre tecnología y emprendimiento
