Agentes especializados, un enrutador de palabras clave e interrupt(): Un asistente de LangGraph
Diseñe un asistente LangGraph con dos especialistas, que cuente con un enrutador determinista, un estado compartido que persiste durante los cambios de turno, y un punto de control humano basado en interrupciones y comandos.
Un único prompt para asistente que debe abarcar dos dominios no relacionados generalmente lo hace de forma deficiente en ambos. Este artículo propone, en su lugar, un sistema LangGraph pequeño que funcione como coach de fitness y nutrición: dos agentes especializados, un enrutador determinista que decide quién responde, un estado compartido que transmite las restricciones entre los intercambios de información, y un punto de control humano que detiene el proceso después de cada respuesta. Al final conocerá cómo funciona cada componente, por qué el sistema tiene esa estructura y cómo reutilizarla para cualquier par de especialistas.
El escenario y la conversación de prueba
El producto objetivo es un asistente dentro de una aplicación de fitness que ayuda tanto con el entrenamiento como con la dieta. El diseño se valida mediante una conversación de tres turnos: una solicitud de un plan de entrenamiento, una pregunta adicional sobre alimentos y una restricción dietética que debe modificar los consejos alimenticios:
Scenario: A fitness app wants an assistant that helps with workouts and nutrition.
Test conversation flow (3 turns):
"Build muscle, lose fat — suggest a workout plan"
"What should I eat to support this workout plan?"
"I'm vegetarian, adjust the meal suggestion"
Toda decisión de diseño aquí presentada tiene como objetivo garantizar que estas tres interacciones funcionen correctamente y de manera predecible.
Por qué un chatbot genérico no es suficiente
Si le pides a un chatbot de uso general que te proporcione un plan de entrenamiento y, además, una dieta adecuada, las respuestas suelen ser superficiales. El modelo debe manejar todas las funciones dentro de una misma instrucción, por lo que los consejos de entrenamiento pierden precisión y las particularidades dietéticas se simplifican excesivamente. Esta debilidad surge cuando tres requisitos distintos afectan al mismo prompt al mismo tiempo:
- Separar los dominios. La planificación de entrenamientos y la gestión nutricional son áreas de especialización diferentes, por lo que los consejos en una no deben influir ni contradecir a los de la otra.
- Mantener el contexto entre interacciones. Una restricción mencionada anteriormente, como una lesión en la rodilla señalada hace tres mensajes, debe seguir influyendo en el plan de alimentación o entrenamiento que se genere más tarde.
En lugar de pedirle a una sola instrucción que realice tres tareas, la arquitectura asigna a cada requisito su propio mecanismo: especialistas para la separación de dominios, un estado compartido para el contexto y un nodo humano para la supervisión.
Los cinco componentes básicos
El sistema consta de cinco partes, cada una con una única responsabilidad:
- Asesor de ejercicios: un agente responsable de las rutinas de ejercicio, la distribución del entrenamiento y las modificaciones debido a lesiones.
- Asesor de nutrición: un agente responsable de los planes alimenticios, los macronutrientes y las restricciones dietéticas como las dietas vegetarianas, veganas o relacionadas con alergias.
- Ruteador: un paso de toma de decisiones ligero que examina el último mensaje del usuario y elige qué asesor responderá a continuación.
- Nodo humano: una pausa intencionada en la que el grafo espera a la persona en lugar de generar más resultados.
- Estatuto compartido: un registro autoritativo del diálogo hasta el momento, además de indicar qué asesor respondió por último.
Se trata de un patrón jerárquico u de orquestación. Un coordinador delega tareas en subagentes especializados en lugar de que un único agente intente asumir todos los roles al mismo tiempo.
La regla que mantiene predecibles las transferencias de tarea
Una regla estructural hace que todo el diseño sea fiable: un asesor nunca transfiere el control directamente al otro asesor. Cada salida de un agente pasa por el nodo humano y luego por el enrutador. Por lo tanto, dos agentes nunca pueden intercambiar el control de forma silenciosa, y cada transición es visible e interrumpible.
Cómo se mueve un mensaje a través del grafo
Sigue un único mensaje de usuario. Este llega primero al nodo humano y es enviado al enrutador, el cual lee el texto del mensaje; si solo el texto no es concluyente, consulta el campo last_active_agent en el estado compartido para elegir entre el Asesor de Entrenamiento y el Asesor de Nutrición. El asesor elegido puede llamar a su herramienta correspondiente y luego devuelve el control al nodo humano, que vuelve a pausarse y espera el siguiente mensaje. Por lo tanto, el ciclo siempre sigue el orden de nodo humano, enrutador, asesor, herramienta opcional y nuevamente nodo humano.
El modelo: un LLM de pesos abiertos en Groq
Ambos asesores utilizan el modelo de pesos abiertos openai/gpt-oss-120b proporcionado por Groq, configurado con temperature=0:
- Por qué este modelo: cuenta con una sólida capacidad para llamar a herramientas nativas, algo de lo que depende el flujo de trabajo basado en herramientas.
Nada en el gráfico depende específicamente de Groq. Cualquier modelo de chat que permita llamar a herramientas funciona, por ejemplo Google Gemini o Anthropic Claude, siempre y cuando se proporcione la clave API correspondiente (como GEMINI_API_KEY o ANTHROPIC_API_KEY) y se dirija el wrapper del modelo a dicho proveedor. La disponibilidad de los modelos en las plataformas alojadas cambia con el tiempo, por lo que se debe verificar el identificador del modelo en el catálogo actual del proveedor.
Estatuto compartido: dónde reside realmente la memoria
Cada nodo lee y escribe en un único objeto de estado tipado, MultiAgentState, que cuenta con dos campos:
messages: la conversación completa en curso, heredada deMessagesStatede LangGraph; este también proporciona el reductor que agrega nuevos mensajes en lugar de sobrescribir la lista.
last_active_agent: debe establecerse en "workout_advisor" o "nutrition_advisor"; el enrutador recurre a este valor cada vez que no puede determinar a qué dominio pertenece una solicitud de seguimiento como "tell me more">.Este estado es la razón por la cual una restricción establecida en una fase sigue aplicándose en fases posteriores, incluso cuando es otro nodo el que genera la respuesta. No se realiza resumen ni reexplicación alguna al modelo. Cada asesor simplemente recibe la misma lista acumulada de messages cada vez que se ejecuta, y un checkpointer mantiene el estado entre las fases de la misma conversación.
La decisión clave en el diseño es que la memoria es centralizada, no por agente. Si cada asesor conservara su propia historia privada, el Asesor de Nutrición nunca conocería los objetivos que el usuario haya indicado al Asesor de Entrenamiento.
Una herramienta limitada por asesor
Cada agente puede utilizar exactamente una herramienta, y dicha herramienta pertenece a su área de especialización. En esta versión las herramientas se basan en reglas y coinciden con palabras clave, lo que hace que su comportamiento sea predecible y fácil de demostrar. En entornos reales se podrían sustituir por una API real de condición física o nutrición, o permitir que el modelo genere la respuesta por completo. Dado que el resto del grafo solo depende de la interfaz de la herramienta, cambiar su implementación no requiere otros ajustes.
Mantener a cada asesor en su área
No basta con contar con una herramienta dedicada para que un agente se mantenga en el tema; el modelo también debe saber qué no debe responder. Por lo tanto, cada instrucción del sistema establece un límite explícito:
- Al Asesor de Entrenamiento se le indica que no dé consejos sobre comidas o nutrición, ya que el grafo enviará esas solicitudes al Asesor de Nutrición.
El principio detrás de esto es que el modelo no se dirige a sí mismo. La indicación de límites hace que cada agente opte por esperar en lugar de improvisar fuera de su área de especialización, y el grafo, y no el LLM, es quien toma explícitamente todas las decisiones de enrutamiento.
Un enrutador basado en palabras clave, no en otra llamada al LLM
Dado que los asesores están restringidos, el enrutador solo tiene que elegir al siguiente orador, y lo hace mediante una simple coincidencia de palabras clave:
- Palabras como gimnasio, repeticiones, cardio o músculo se dirigen al Asesor en Entrenamientos.
- Palabras como dieta, alimentos, comer, proteínas o vegetariano se dirigen al Asesor en Nutrición.
- Cualquier elemento que no coincida con ninguna de las listas se envía al asesor almacenado en
last_active_agent.
El enrutamiento determinista es predecible y fácil de probar: el mismo mensaje siempre produce el mismo salto, y se pueden realizar pruebas unitarias del enrutador sin llamar a un modelo. El sacrificio es su fragilidad. Las listas de palabras clave omiten sinónimos, y un mensaje que mencione ambos dominios (una pregunta sobre comer antes del gimnasio) necesita una regla explícita para resolver conflictos. Cuando la redacción se vuelve demasiado variada para las palabras clave, una pequeña clasificación es una mejora razonable, pero se pierde parte de esa predecibilidad.
Intervención humana con interrupción y comando
La decisión de diseño más importante no es el enrutador, sino el nodo humano. Después de cada turno del asesor, el grafo se detiene intencionadamente. No adivina la siguiente pregunta del usuario ni sigue generando contenido. Dos primitivas de LangGraph implementan esta pausa:
interrupt(...)detiene la ejecución justo en el punto donde se llama y devuelve el control al que lo invocó, junto con cualquier valor que le hayas pasado.Command(resume=...)continúa con el grafo pausado, entregando la entrada del humano como valor de retorno de la llamada ainterruptdentro del mismo nodo.
Las interrupciones dependen de la creación de puntos de control: el grafo debe guardar su estado en el momento de la pausa para poder reanudarse posteriormente, y por eso el grafo compilado necesita un punto de control y un ID de hilo.
Para un asistente de fitness y nutrición, esta pausa es más que una simple comodidad en la interfaz de usuario. Es el momento en que el usuario puede agregar una restricción relacionada con la seguridad, como una lesión, una alergia o una restricción alimentaria, antes de que continúe la conversación, en lugar de después de que el sistema ya haya dado consejos que la ignoran.
Recorriendo las tres etapas
Etapas 1: inicio de la conversación
El usuario indica dos objetivos: ganar músculo y perder algo de grasa, y solicita un plan de entrenamiento. Cada nueva conversación comienza siempre con el Asesor de Entrenamientos. Este recoge los términos “músculo” y “grasa”, utiliza su herramienta correspondiente y devuelve un plan estructurado, por ejemplo, un entrenamiento dividido en cuatro días entre parte superior e inferior del cuerpo, con 8 a 12 repeticiones por serie y de 3 a 4 series en total, combinando tres días de fuerza con dos sesiones de cardio.
Etapas 2: paso a la nutrición
A continuación, el usuario desea saber qué alimentos podrían apoyar ese plan. El nodo humano reenvía el mensaje, el enrutador coincide con “comer”, y la ejecución pasa al Asesor Nutricional. Este devuelve un plan diario de aproximadamente 2,500 kcal, centrado en proteínas magras y carbohidratos complejos, con comidas cada tres a cuatro horas.
Turno 3: aplicación de una restricción proveniente del estado compartido
Finalmente, el usuario menciona que es vegetariano y pide que se ajuste la sugerencia de comida, añadiendo un bocadillo. El mensaje vuelve al Asesor Nutricional, y aquí coinciden dos mecanismos: “vegetariano” es en sí mismo una palabra clave relacionada con la nutrición, y last_active_agent sigue apuntando a temas nutricionales; por lo tanto, incluso una solicitud más vaga como “ajustarlo y añadir un bocadillo” terminaría en el mismo lugar. El asesor vuelve a leer todo el historial, mantiene el objetivo establecido en la primera consulta, añade la restricción vegetariana y elabora un plan sin carne para ganar masa muscular basado en tofu, paneer y lentejas, con bocadillos como edamame y tazas de hummus. Nadie tuvo que reiterar el objetivo, ya que estaba incluido en el estado actual.
Esta etapa representa el resultado del estado centralizado. Un nodo diferente al que recibió el objetivo original está generando la respuesta, pero cuenta con todo el contexto necesario.
Construcción de la máquina de estados
Estructuralmente, el resultado es un pequeño grafo de estados con tres nodos (los dos asesores y el nodo humano), además del enrutador que actúa como lógica condicional entre ellos, y un punto de entrada fijo:
- Cada hilo nuevo comienza en el Asesor de Entrenamientos. Esta configuración por defecto refleja las necesidades reales de los usuarios: quienes abren una aplicación de fitness suelen preguntar primero sobre entrenamientos y luego sobre alimentación.
- Después de esa primera respuesta, el nodo humano y el enrutador deciden cada paso posterior.
MemorySaver y cada conversación se identifica mediante un thread_id. Cada vez que el llamante reutiliza el mismo thread_id, el grafo restaura automáticamente los mensajes y al last_active_agent; el llamante nunca tiene que volver a enviar la historia.MemorySaver almacena los puntos de verificación en la memoria del proceso, lo cual es ideal para un cuaderno de notas, pero pierde todo al reiniciarse. Una versión desplegada debería utilizar un puntero de verificación persistente respaldado por una base de datos.
La implementación completa y funcional está disponible como cuaderno de trabajo complementario. Para conocer otras formas de estructurar los pasos de enrutamiento y aprobación, consulte cinco patrones LangGraph que abarcan enrutamiento, distribución, crítica y aprobación.
Reutilizar el patrón más allá del área de la condición física
Nada aquí es específico de los entrenamientos o las comidas. Los mismos tres componentes —agentes especializados, un enrutador controlado por estado compartido y un punto de control humano que utiliza funciones de interrupción y reanudación— son adecuados para cualquier sistema que cuente con:
- más de una área de especialización que pueda ser necesaria en una conversación,
Si se reemplazan los dos asesores por otro par de expertos en el área, la estructura del gráfico permanece idéntica. Solo cambian las herramientas, los prompts y las palabras clave del enrutador.
Principios de diseño
- Orquestación en lugar de monolitos. Utilice el gráfico para establecer canales separados según las áreas de especialización, en lugar de depender de un único agente para hacer todo.
- Centralizar el estado. Un
MultiAgentStatepersistido por un checkpointer es lo que permite que las transferencias de responsabilidades sean fluidas y que las restricciones se mantengan constantes. - El control es crucial. Evite bucles completamente autónomos en producción; utilice
interrupt()para mantener la intervención humana en los puntos importantes.
Puntos clave
- Divida el trabajo por dominio y deje que la lógica de grafos determinista tome las decisiones de enrutamiento en lugar del modelo.
- Mantenga un historial compartido de
messagesjunto conlast_active_agentpara que una restricción establecida temprano influya en las respuestas generadas posteriormente por otro agente. - Considere
interrupt()yCommand(resume=...)como mecanismos que permiten a una persona agregar restricciones de seguridad antes de la próxima respuesta, y recuerde que se necesita un checkpointer y un ID de hilo. - Mantenga las herramientas limitadas detrás de una interfaz estable para que la lógica basada en reglas pueda convertirse posteriormente en una verdadera API o en un razonamiento de modelo completo sin afectar el grafos.
- La forma más rápida de internalizar el patrón es seleccionar a dos especialistas bien definidos y crear un grafo funcional completo; la topología se mantiene sin cambios, mientras que los prompts y las herramientas son las partes que usted adapta.
Lecturas relacionadas
- Agentes con control de aprobación en LangGraph: interrupt(), puntos de control y un almacén — Construya un agente de LangGraph paso a paso: un grafo ReAct explícito, aprobación humana mediante interrupt(), y memoria entre hilos con un almacén, todo ello culminando en un asistente de bandeja de entrada que pregunta primero.
- Construyendo un agente de investigación ReAct en LangGraph: Cerebro, Manos, Router — Aprenda cómo implementar el bucle de razonamiento-acción-observación ReAct como un sub-gráfico de LangGraph, con reflexión forzada, presupuestos de iteración e investigación paralela de tipo scatter-gather.
- Puertas de aprobación en LangGraph.js: Pausando agentes con interrupt() y Command — Cree una puerta de aprobación mínima en LangGraph.js que pause antes de un efecto secundario, recoja una decisión humana en la terminal y reanude de forma segura a partir de un punto de control.
- Pausar y reanudar agentes LangGraph con interrupt() y Command — Cómo interrupt() y Command de LangGraph se basan en puntos de control y hilos para pausar un agente a fin de obtener la aprobación, ediciones o entrada humana, y luego reanudarlo de forma segura más tarde.