LangChain vs LangGraph: diferencias y cuándo usar cada uno
LangChain vs LangGraph: qué hace cada uno hoy, por qué no compiten y cuándo necesitas el grafo con estado.
LangChain y LangGraph no compiten: LangChain es el framework de alto nivel con el que montas un agente en pocas líneas, y LangGraph es el runtime de bajo nivel sobre el que ese agente se ejecuta. Eliges LangGraph cuando tu flujo deja de ser una línea recta y necesita estado, ramas, pausas o reanudar.
La confusión viene de que durante años LangChain era sobre todo cadenas de prompts. Con la versión 1.0 de ambos, publicada en octubre de 2025 [1], la relación quedó clara: hoy el paquete langchain depende de LangGraph, y cada agente que creas con LangChain es, por dentro, un grafo de LangGraph [2].
LangChain vs LangGraph en una tabla
La fila que más pesa es la de control del flujo, porque es la que te dice cuál de los dos vas a abrir primero.
| LangChain | LangGraph | |
|---|---|---|
| Qué es | Framework para construir agentes: modelos, herramientas, prompts, middleware y createAgent | Runtime de orquestación para flujos y agentes con estado |
| Nivel de abstracción | Alto: describes el agente y el bucle ya viene hecho | Bajo: dibujas tú los nodos y las aristas |
| Estado y persistencia | Los hereda de LangGraph (le pasas un checkpointer al agente) | Estado tipado compartido entre nodos, checkpoints, reanudar tras un fallo |
| Control del flujo | El bucle del agente: el modelo decide qué herramienta llamar y cuándo parar | Tuyo: ramas condicionales, ciclos, pasos fijos mezclados con pasos del modelo |
| Curva de aprendizaje | Una tarde si ya sabes qué es un agente | Más larga, porque piensas en grafos, reducers y checkpoints |
| Cuándo elegirlo | Empiezas, o tu caso es “un modelo con herramientas” | El proceso tiene pasos que no puede decidir el modelo, o necesitas pausar y reanudar |
La propia documentación de LangGraph lo dice sin rodeos: es una herramienta de muy bajo nivel, centrada solo en orquestación, y recomienda empezar por los agentes de LangChain si buscas algo más directo [3]. LangChain ordena su oferta en tres capas: LangGraph (runtime), LangChain (framework) y Deep Agents (harness) [4].
LangChain hoy: un agente en quince líneas
Hoy LangChain es, ante todo, createAgent: le das un modelo y sus herramientas, y te devuelve un agente que ya sabe hacer el bucle agéntico completo, es decir, llamar al modelo, ejecutar la herramienta que pida, devolverle el resultado y repetir hasta tener respuesta.
// Un agente completo con LangChain 1.x
import { createAgent, tool } from "langchain";
import { z } from "zod";
const verPedido = tool(async ({ id }) => `Pedido ${id}: enviado`, {
name: "ver_pedido",
description: "Consulta el estado de un pedido por su id",
schema: z.object({ id: z.string() }), // el modelo ve este esquema y rellena el id
});
const agente = createAgent({
model: "anthropic:claude-haiku-4-5-20251001", // necesita @langchain/anthropic instalado
tools: [verPedido],
systemPrompt: "Eres el agente de soporte de una tienda online.",
});
await agente.invoke({ messages: [{ role: "user", content: "¿Dónde está el pedido 42?" }] });
Encima de eso aporta el middleware, piezas que se enganchan antes o después de cada llamada al modelo o a una herramienta, como humanInTheLoopMiddleware para pedir aprobación antes de ciertas herramientas [2]. Y lo de siempre: cambiar de proveedor de modelo sin reescribir la lógica.
Las cadenas de prompts y LCEL siguen existiendo, y cómo se escriben lo tienes con ejemplos en la guía de encadenamiento de prompts con LangChain. Las abstracciones antiguas que la 1.0 sacó del paquete principal viven ahora en langchain-classic (Python) y @langchain/classic (JavaScript).
Ahora bien, ¿qué pasa cuando el proceso no es “el modelo decide hasta que acaba”?
LangGraph: cuando tu flujo es un grafo con estado
LangGraph modela el proceso como un grafo: un estado compartido, unos nodos que son funciones que leen ese estado y devuelven lo que cambian, y unas aristas que dicen qué nodo va después. Su valor está en lo que los agentes de alto nivel esconden: ejecución duradera que se reanuda donde se quedó y pausas para que intervenga un humano, además de memoria entre sesiones [3].
Un caso que encaja: responder un ticket de soporte, revisar la respuesta con otra llamada al modelo, reintentar si no pasa la revisión y, si pasa, pararse hasta que una persona la apruebe. Los nodos usan un helper llamarLLM que manda un prompt y devuelve el texto.
import { StateGraph, Annotation, START, END } from "@langchain/langgraph";
declare function llamarLLM(prompt: string): Promise<string>; // tu llamada al modelo
// El estado compartido: cada nodo lo lee y devuelve solo lo que cambia
const Estado = Annotation.Root({
tema: Annotation<string>,
borrador: Annotation<string>,
aprobado: Annotation<boolean>,
intentos: Annotation<number>({ reducer: (a, b) => a + b, default: () => 0 }),
decision: Annotation<string>,
});
const redactar = async (s: typeof Estado.State) => ({
borrador: await llamarLLM(`Redacta la respuesta a este ticket: ${s.tema}`),
intentos: 1, // el reducer lo suma al total
});
const revisar = async (s: typeof Estado.State) => ({
aprobado: (await llamarLLM(`¿Es correcta? Responde solo APROBADO o RECHAZADO:\n${s.borrador}`))
.trim().toUpperCase().startsWith("APROBADO"), // tolera minúsculas y espacios
});
Un campo sin reducer se sobrescribe con el último valor; intentos lleva uno que suma, así que cada vuelta añade 1 en lugar de pisar el contador. Ahora el grafo, con la arista condicional y la pausa humana:
import { MemorySaver, interrupt, Command } from "@langchain/langgraph";
// interrupt() congela el grafo y devuelve lo que diga el humano al reanudar
const aprobarHumano = (s: typeof Estado.State) => ({ decision: interrupt({ borrador: s.borrador }) });
const app = new StateGraph(Estado)
.addNode("redactar", redactar).addNode("revisar", revisar).addNode("aprobarHumano", aprobarHumano)
.addEdge(START, "redactar").addEdge("redactar", "revisar")
// aprobado → humano; si no, reintenta hasta 3 veces y luego termina
.addConditionalEdges("revisar", (s) => (s.aprobado ? "aprobarHumano" : s.intentos < 3 ? "redactar" : END))
.addEdge("aprobarHumano", END)
.compile({ checkpointer: new MemorySaver() });
const config = { configurable: { thread_id: "ticket-42" } };
await app.invoke({ tema: "quiero devolver un pedido" }, config); // se para en aprobarHumano
await app.invoke(new Command({ resume: "enviar" }), config); // sigue donde estaba
Ejecuté este código con @langchain/langgraph 1.4.21 y un llamarLLM simulado: la primera llamada devuelve el estado con un campo __interrupt__ que lleva el borrador, y la segunda termina con decision: "enviar". Esa vuelta atrás de revisar a redactar, con su contador y su punto de pausa, es justo lo que en un agente de alto nivel no controlamos.
Cómo se combinan
Como createAgent devuelve un grafo compilado de LangGraph, los dos se mezclan sin pegamento: un agente de LangChain puede ser un nodo más dentro de tu grafo. El caso típico es el patrón Router: un nodo clasifica la petición y la arista condicional la manda al agente especialista que toque.
LangGraph tampoco te obliga a usar LangChain: comparte con él el paquete base @langchain/core, pero puedes escribir los nodos con el SDK de tu proveedor directamente [3].
¿Y LangSmith? Aparece en la misma búsqueda, pero cumple otra función: es la plataforma para ver la traza de cada ejecución y evaluar agentes, y su SDK funciona también con código que no usa LangChain [5].
Cuándo no necesitas ninguno de los dos
Si tu agente es un modelo con tres o cuatro herramientas, el bucle son unas pocas decenas de líneas con el SDK del proveedor: llamas al modelo, si pide una herramienta la ejecutas, le devuelves el resultado y repites. Cómo funciona ese mecanismo lo explicamos en tool use, y escribirlo una vez a mano es la mejor forma de entender qué hace createAgent por ti.
El framework compensa cuando aparece lo que no queremos reescribir, como la persistencia o las pausas humanas. Lo que no compensa es meter LangGraph por si acaso y acabar depurando reducers en un flujo que era una línea recta.
Si quieres practicar estos flujos, de los routers a los bucles con revisión, sin atarte a un framework concreto, es lo que trabajamos en el curso de patrones agénticos.
Dos errores al usar LangGraph
Usar interrupt() sin checkpointer
Pausar un grafo significa guardar su estado para retomarlo luego, y eso lo hace el checkpointer. Si compilas sin él, la ejecución falla con No checkpointer set en cuanto llega a un interrupt() [6]. El segundo matiz es menos ruidoso: MemorySaver guarda en memoria, así que si reinicias el proceso pierdes todos los hilos pausados. En producción toca un checkpointer con base de datos, como @langchain/langgraph-checkpoint-postgres.
Confundir un bucle infinito con un flujo largo
Un ciclo mal cerrado no se queda colgado para siempre: LangGraph corta con un GraphRecursionError al llegar al límite de pasos, que por defecto es 25 y se cambia con recursionLimit en la configuración. Si ves ese error, revisa la condición de salida antes de subir el límite.
Fuentes
- langgraph en PyPI: historial de versiones: fecha de la versión 1.0 de langgraph, publicada el 17 de octubre de 2025, el mismo día que langchain 1.0.
- langchain en npm: LangChain.js: el paquete declara
@langchain/langgraphcomo dependencia y su código construyecreateAgentsobreStateGraph; parámetrosmodel,tools,systemPrompt,checkpointer, y middleware incluido. - LangGraph overview: Docs by LangChain: LangGraph como orquestación de bajo nivel, ejecución duradera, human-in-the-loop, memoria, uso sin LangChain y recomendación de empezar por los agentes de LangChain.
- Runtimes, frameworks, and harnesses: Docs by LangChain: clasificación de LangGraph como runtime, LangChain como framework y Deep Agents como harness.
- langsmith en npm: cliente de LangSmith: LangSmith como plataforma de desarrollo y evaluación, compatible con código que no usa LangChain.
- Interrupts: Docs de LangGraph:
interrupt(), reanudación conCommandy necesidad de checkpointer ythread_id.
Preguntas Frecuentes
¿Cuál es la diferencia entre LangChain y LangGraph?
LangChain es el framework de alto nivel: con createAgent describes un agente a partir de un modelo y sus herramientas, y el bucle ya viene hecho. LangGraph es el runtime de bajo nivel sobre el que corre ese agente, y lo usas directamente cuando quieres dibujar tú el flujo con nodos, aristas, estado compartido y pausas.
¿Qué es LangGraph?
LangGraph es un runtime de bajo nivel para orquestar flujos y agentes con estado: defines nodos, aristas y un estado compartido, y él se encarga de los checkpoints, de reanudar una ejecución y de las pausas para aprobación humana. Es también la base sobre la que corren los agentes que crea createAgent en LangChain.
¿Tengo que aprender LangChain antes que LangGraph?
No es obligatorio, pero es el orden que recomienda la propia documentación. Empezar con createAgent te enseña qué hace un agente sin pelearte con grafos, y cuando un caso te pida ciclos o aprobación humana, bajas a LangGraph sabiendo qué problema resuelve.
¿LangGraph sustituye a LangChain?
No. LangGraph vs LangChain no es una elección excluyente, porque desde la versión 1.0 LangChain se apoya en LangGraph: los agentes que crea son grafos de LangGraph por dentro.
¿Qué es LangSmith y en qué se diferencia de LangChain y LangGraph?
LangSmith es la plataforma de observabilidad y evaluación del mismo equipo: registra cada ejecución de tu agente como una traza y te deja evaluar sus respuestas. No construye agentes, así que no compite con ninguno de los dos, y también la puedes usar con código que no lleva LangChain.