Inicio / Artículos / Notas prácticas: Orquestación multiagente: Construcción y observación de sistemas multiagente

Notas prácticas: Orquestación multiagente: Construcción y observación de sistemas multiagente

Guía práctica paso a paso: Orquestación multiagente: creación y observación de sistemas multiagente, incluyendo contratos, verificaciones y espacios para código adicional para los equipos que implementan este patrón.

1624 palabras

Esta guía reconstruye el proceso desde las materias primas hasta un sistema funcional para: Orquestación de múltiples agentes: Creación y observación de sistemas multiagente con LangGraph y LangSmith. El enfoque está en pasos operativos, verificaciones explícitas y código que se puede incorporar a un repositorio sin tener que adivinar su propósito. En la etapa de visión general, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el grafo.

Introducción: ¿Qué es la orquestación de agentes de LangSmith?

Al trabajar en la etapa de Introducción: ¿Qué es LangSmith?, anote primero el contrato: los datos necesarios, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente junto con ello el camino óptimo y el camino de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no de mejoras posteriores. Haga un punto de control después de los pasos costosos. Resume no debe volver a facturar la misma llamada al LLM cuando un operador vuelve a intentar un nodo posterior.

La arquitectura: cómo funciona la orquestación en LangGraph

Al trabajar en la etapa de Arquitectura y Orquestación, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Prefiera unidades pequeñas y probables sobre scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado. Haga un punto de control después de los pasos costosos. La reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior.

Cómo se configura la orquestación:

Al trabajar en la fase de “Cómo es la orquestación”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Trate esta fase como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina comprobaciones de éxito y evite completaciones parciales silenciosas. Haga un punto de control después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior. Al trabajar en la fase de “Cómo es la orquestación”, anote primero el contrato: las entradas requeridas, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.

Ejemplo reproducible paso a paso

La etapa de Ejemplo Reproducible Paso a Paso funciona mejor cuando se trata como una superficie medible. Capture un registro exitoso, un caso de fallo y la nota de reversión antes de ampliar el alcance. Documente tanto el camino óptimo como el camino de recuperación juntos. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Mantenga el estado del grafo simple y tipado; los bloques anidados ocultan qué nodo escribió qué campo y provocan interrupciones en la continuación después de las interrupciones.

Requisitos previos

La etapa de Requisitos previos funciona mejor cuando se trata como una superficie medible. Capture un registro de éxito ejemplar, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando falla un paso, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado. Mantenga el estado del grafo simple y tipado; los bloques anidados ocultan qué nodo escribió qué campo y causan interrupciones en la continuación del proceso.

Paso 1: Configuración del entorno

La etapa de Configuración del Entorno en el Paso 1 funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Trate esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace completaciones parciales silenciosas. Mantenga el estado del grafo plano y tipado. Los bloques anidados ocultan qué nodo escribió qué campo y provocan interrupciones en la continuación del proceso. La etapa de Configuración del Entorno en el Paso 1 funciona mejor cuando se trata como una superficie medible. Capture un registro ideal, un caso de fallo y la nota de reversión antes de ampliar el alcance. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin tener que leer todo el grafo.

uv add langgraph langchain-anthropic langsmith python-dotenv

Paso 2: El código en Python

En la etapa 2, correspondiente a Python, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Separe la construcción del cliente del bucle de mensajes para que sea posible cambiar los proveedores sin tener que reescribir la máquina de estados de la conversación.

import os
from typing import TypedDict, Literal
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import StateGraph, END

# ==========================================
# 0. Load Environment Variables
# ==========================================
# This loads the API keys and LangSmith configs from the .env file
load_dotenv()

# ==========================================
# 1. Define the Shared State
# ==========================================
class AgentState(TypedDict):
    messages: list
    next_agent: str

# ==========================================
# 2. Define the Nodes (The Agents)
# ==========================================
# Initialize Claude 3.5 Sonnet
llm = ChatAnthropic(model="claude-sonnet-4-5-20250929", temperature=0)

def router_node(state: AgentState):
    """Acts as the router. Classifies the user query and directs it to the correct department."""
    system_prompt = SystemMessage(content=(
        "You are a router agent. Look at the user's message and classify it as either "
        "'billing' or 'technical'. Reply with ONLY the word 'billing' or 'technical'."
    ))
    response = llm.invoke([system_prompt] + state["messages"])
    classification = response.content.strip().lower()

    # Update state with the routing decision
    return {"next_agent": classification, "messages": [response]}

def billing_node(state: AgentState):
    """Handles billing-related queries."""
    system_prompt = SystemMessage(content=(
        "You are a billing support agent. Help the user with invoices, refunds, and payments. "
        "Be polite and professional."
    ))
    response = llm.invoke([system_prompt] + state["messages"])
    return {"messages": [response]}

def tech_support_node(state: AgentState):
    """Handles technical issues."""
    system_prompt = SystemMessage(content=(
        "You are a technical support agent. Help the user troubleshoot bugs, login issues, "
        "and software errors. Be analytical and helpful."
    ))
    response = llm.invoke([system_prompt] + state["messages"])
    return {"messages": [response]}

# ==========================================
# 3. Define the Routing Logic
# ==========================================
def route_decision(state: AgentState) -> Literal["billing", "technical"]:
    """Reads the state to decide which node to visit next."""
    next_agent = state.get("next_agent", "technical")
    # Claude is highly instruction-following, but we use 'in' to safely handle
    # any edge cases where it might add conversational filler.
    if "billing" in next_agent:
        return "billing"
    return "technical"

# ==========================================
# 4. Build and Compile the Graph
# ==========================================
workflow = StateGraph(AgentState)

# Add nodes
workflow.add_node("router", router_node)
workflow.add_node("billing", billing_node)
workflow.add_node("technical", tech_support_node)

# Define edges
workflow.set_entry_point("router")
# The magic of orchestration: Conditional routing based on state
workflow.add_conditional_edges(
    "router",
    route_decision,
    {
        "billing": "billing",
        "technical": "technical",
    }
)

# Both specialized agents end the workflow
workflow.add_edge("billing", END)
workflow.add_edge("technical", END)

# Compile the graph
app = workflow.compile()

# ==========================================
# 5. Run the Orchestration
# ==========================================
if __name__ == "__main__":
    # Test Case 1: Billing Query
    print("--- Running Billing Test ---")
    inputs = {"messages": [HumanMessage(content="I was charged twice for my subscription!")]}
    result = app.invoke(inputs)
    print(result["messages"][-1].content)
    print("\n")

    # Test Case 2: Tech Support Query
    print("--- Running Tech Support Test ---")
    inputs = {"messages": [HumanMessage(content="My app keeps crashing when I click the save button.")]}
    result = app.invoke(inputs)
    print(result["messages"][-1].content)

Paso 3: Ejecutar el script para probar

En el Paso 3, Ejecutar la etapa, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Incluya la aprobación humana en aquellas acciones que implican gastos o modificaciones en datos de producción. La conexión en tiempo de compilación no equivale a la completitud del proceso empresarial.

uv run multiagent-orchestration.py

Paso 4: Ver la orquestación en LangSmith

En la etapa de Ver la vista, Paso 4, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Trate esta etapa como un contrato entre las entradas y los resultados validados. Asigne nombres a los artefactos, defina comprobaciones de éxito y rechace las completaciones parciales silenciosas. Incluya la aprobación humana en aquellas operaciones que generen gastos o modifiquen datos de producción. La conexión en tiempo de compilación no equivale a la completitud del proceso empresarial. En la etapa de Ver la vista, Paso 4, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos secretos y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema.

Qué verá en LangSmith:

Al trabajar en la etapa de “Qué verá”, anote primero el contrato: los datos requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Documente junto con ello el camino óptimo y el camino de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Haga un punto de control después de los pasos costosos. Resume no debe volver a facturar la misma llamada al LLM cuando un operador vuelve a intentar un nodo posterior.

Conclusión

Lista de verificación operativa