← Todo el material
Curso Básico 20 de agosto de 2026

LangGraph — Módulo 4: ramificación y routing

Cuarto módulo del curso de LangGraph: aristas condicionales para que el grafo decida su camino, funciones de routing con retorno Literal, path_map y cómo crear ciclos (bucles de reintento) sin quedar en loop infinito.

☰ Índice del contenido

Cuarto y último módulo del bloque básico del Curso completo de LangGraph. Hasta ahora nuestros grafos iban en línea recta: A → B → fin. Pero lo que separa un grafo de una cadena es poder decidir el camino. Ese es el tema de hoy: las aristas condicionales.

La tesis del módulo

Una arista condicional es una bifurcación con criterio. En vez de “después de A siempre B”, dices “después de A, una función mira el estado y decide a dónde ir”. Esa función de routing es donde vive la inteligencia de flujo de tu agente — y donde nacen los ciclos.

Aristas normales vs condicionales

  • Arista normal (add_edge): fija. “De A siempre se va a B.”
  • Arista condicional (add_conditional_edges): dinámica. “De A se va a donde diga esta función de routing, según el estado.”

La función de routing (router) es una función que recibe el estado y devuelve el nombre del siguiente nodo (un string).

Ejemplo: un clasificador que ramifica

Imagina un agente de soporte que, según el tipo de mensaje, va a un nodo u otro.

from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    mensaje: str
    categoria: str
    respuesta: str

def clasificar(state: State) -> dict:
    texto = state["mensaje"].lower()
    categoria = "factura" if "factura" in texto or "pago" in texto else "tecnico"
    return {"categoria": categoria}

def responder_factura(state: State) -> dict:
    return {"respuesta": "Te derivo con el área de facturación 💳"}

def responder_tecnico(state: State) -> dict:
    return {"respuesta": "Veamos ese problema técnico paso a paso 🛠️"}

Ahora la pieza nueva: la función de routing. Fíjate en el -> Literal[...]: declara los destinos posibles. No es obligatorio, pero LangGraph lo usa para dibujar bien el grafo y para avisarte de errores.

def elegir_ruta(state: State) -> Literal["responder_factura", "responder_tecnico"]:
    if state["categoria"] == "factura":
        return "responder_factura"
    return "responder_tecnico"

Y la conectamos con add_conditional_edges:

builder = StateGraph(State)
builder.add_node("clasificar", clasificar)
builder.add_node("responder_factura", responder_factura)
builder.add_node("responder_tecnico", responder_tecnico)

builder.add_edge(START, "clasificar")
# Después de "clasificar", el router decide a dónde ir:
builder.add_conditional_edges("clasificar", elegir_ruta)
# Ambas ramas terminan:
builder.add_edge("responder_factura", END)
builder.add_edge("responder_tecnico", END)

graph = builder.compile()
print(graph.invoke({"mensaje": "tengo un problema con mi factura"})["respuesta"])
# → Te derivo con el área de facturación 💳

El grafo quedó así:

                       ┌──► responder_factura ──► END
START ──► clasificar ──┤
                       └──► responder_tecnico ──► END

El path_map: separar la decisión del destino

A veces tu router devuelve algo que no es el nombre del nodo (un True/False, una categoría). El tercer argumento, el path_map, traduce ese valor al nodo destino:

def es_factura(state: State) -> bool:
    return state["categoria"] == "factura"

builder.add_conditional_edges(
    "clasificar",
    es_factura,
    {True: "responder_factura", False: "responder_tecnico"},
)

Esto mantiene la función de routing limpia (devuelve un booleano) y deja el mapeo a nodos en un solo lugar.

Ciclos: volver atrás para reintentar

Aquí está el superpoder que una cadena no tiene. Como una arista condicional puede apuntar a un nodo anterior, puedes crear un ciclo: intentar algo, evaluarlo y, si no sirve, volver a intentar.

from typing import Literal
from typing_extensions import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    intentos: int
    aprobado: bool

def trabajar(state: State) -> dict:
    intentos = state["intentos"] + 1
    # Simulamos que recién al tercer intento queda bien:
    return {"intentos": intentos, "aprobado": intentos >= 3}

def revisar(state: State) -> Literal["trabajar", "__end__"]:
    # Si no está aprobado, vuelve a "trabajar". Si sí, termina.
    return "__end__" if state["aprobado"] else "trabajar"

builder = StateGraph(State)
builder.add_node("trabajar", trabajar)
builder.add_edge(START, "trabajar")
builder.add_conditional_edges("trabajar", revisar)

graph = builder.compile()
print(graph.invoke({"intentos": 0, "aprobado": False}))
# → {'intentos': 3, 'aprobado': True}

El nodo trabajar se ejecuta una y otra vez hasta que la condición se cumple. Ese "__end__" es el nombre interno del nodo END (puedes devolver el string "__end__" o importar y usar END).

 START ──► trabajar ──► ?  ──(aprobado)──► END
              ▲         │
              └─────────┘  (no aprobado: reintenta)

Cuidado: el ciclo infinito y el recursion_limit

Un ciclo mal diseñado nunca termina. Para protegerte, LangGraph corta la ejecución tras un número máximo de pasos (por defecto 25) y lanza un error. Puedes ajustarlo, pero si lo alcanzas casi siempre es señal de un bug en tu condición de salida:

graph.invoke({"intentos": 0, "aprobado": False}, {"recursion_limit": 10})

Siempre asegúrate de que tu router tenga un camino claro hacia END. En el ejemplo, el contador de intentos garantiza que en algún momento se apruebe. Sin una condición de salida real, el recursion_limit es tu red de seguridad, no tu plan.

Errores comunes

  • Router que devuelve un nombre de nodo que no existe. El string debe coincidir exactamente con un add_node (o ser "__end__").
  • Olvidar terminar las ramas. Cada rama debe llevar eventualmente a END, o el grafo no sabe cuándo parar.
  • Ciclo sin condición de salida. Si la condición nunca cambia el estado hacia la salida, chocarás con el recursion_limit.
  • Usar add_edge cuando querías add_conditional_edges. add_edge es fijo; para decidir necesitas la versión condicional.

Cierre del bloque básico

Con esto ya sabes los cuatro pilares de LangGraph: estado, nodos, aristas y routing con ciclos. Con solo esto puedes construir agentes que deciden y reintentan — la base de todo lo que viene.

La frase para llevarse

“La arista normal ejecuta; la arista condicional decide. Y una arista condicional que apunta hacia atrás es un agente que aprende a reintentar.”

Qué viene

Arranca el bloque intermedio (Módulo 5): conectamos un LLM de verdad y le damos tools con el patrón ReAct, para que el agente no solo decida el flujo, sino que actúe sobre el mundo.

Material relacionado

LangGraphPythonRoutingAristas condicionalesCiclos

Preguntas sobre este curso

Regístrate gratis para hacer preguntas y guardar tu progreso.

✉ Entérate de lo nuevo

Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.