Empezar aquí
Patrón · workflow

Encadenado de prompts

Divide una tarea en pasos fijos, cada uno una llamada al modelo que recibe la salida del anterior, con comprobaciones en código entre ellos.

Coste
2 de 3
Latencia
2 de 3
Cuándo usarlo
Cuando la tarea se descompone limpiamente en pasos fijos y conocidos de antemano.
Cuándo no
Si no puedes escribir los pasos antes de ver la entrada, o si una sola llamada ya acierta.
Publicado 3 min de lectura

El encadenado de prompts convierte una tarea grande en una secuencia de llamadas pequeñas: cada una procesa la salida de la anterior y hace una sola cosa. Entre paso y paso puedes meter comprobaciones programáticas (el artículo las llama gates, puertas) que detienen o corrigen el flujo antes de que un error se propague. Es el workflow más simple de Building effective agents (Anthropic, 2024) y, a menudo, el que más rinde.

Qué es

Cambias latencia por precisión. Cada llamada recibe una tarea más estrecha, con su propio prompt y sus propios ejemplos, y eso la hace más fácil de acertar y de probar por separado. El orden de los pasos lo fija tu código: el modelo nunca decide qué viene después.

flowchart LR
  E[Entrada] --> P1[LLM: paso 1]
  P1 --> G{Puerta}
  G -- pasa --> P2[LLM: paso 2]
  G -- falla --> X[Corregir o parar]
  P2 --> P3[LLM: paso 3]
  P3 --> S[Salida]

Cuándo usarlo

  • La tarea tiene fases naturales que podrías describir a una persona: extraer, luego transformar, luego redactar.
  • Una sola llamada mezcla objetivos y falla en alguno. Por ejemplo, escribe bien pero se salta requisitos de formato.
  • Quieres puntos de control donde validar con código: que el JSON sea válido, que el esquema tenga los apartados exigidos, que una cifra cuadre.

Ejemplos del artículo: generar un texto de marketing y después traducirlo; escribir el esquema de un documento, comprobar que cumple los criterios y redactar el documento a partir del esquema.

Cuándo no

  • Pasos que dependen de la entrada. Si para unos casos hacen falta tres pasos y para otros siete distintos, necesitas Orquestador y trabajadores o un agente.
  • Pasos independientes entre sí. Si el paso 2 no usa la salida del 1, lánzalos en paralelo: Paralelización.
  • Cadenas largas sin medir. Cada eslabón suma latencia y una probabilidad de fallo. Si no tienes evals por paso, no sabrás cuál rompe la cadena.

Ejemplo concreto

Convertir la transcripción de una reunión en un acta con tareas asignadas:

  1. Extraer: el modelo lista decisiones y tareas con su responsable, en JSON.
  2. Puerta: el código valida el JSON y comprueba que cada responsable aparece en la lista de asistentes. Si falla, reintenta el paso 1 con el error como contexto.
  3. Redactar: el modelo escribe el acta en el formato de la empresa a partir del JSON validado, sin volver a leer la transcripción.

Separar extracción y redacción tiene una ventaja concreta: puedes evaluar la extracción con código (¿están todas las tareas?) y la redacción con un modelo juez. Tienes la receta completa en Resumen de reuniones con acciones y responsables.

Esqueleto de código

import json
import anthropic

MODEL = "claude-sonnet-5-5"
client = anthropic.Anthropic()

def llm(prompt: str, system: str | None = None, max_tokens: int = 2048) -> str:
    extra = {"system": system} if system else {}
    resp = client.messages.create(model=MODEL, max_tokens=max_tokens,
                                  messages=[{"role": "user", "content": prompt}], **extra)
    if resp.stop_reason == "max_tokens":
        raise RuntimeError("Respuesta truncada: sube max_tokens o acorta el paso")
    return "".join(b.text for b in resp.content if b.type == "text")

def extraer(transcripcion: str, error_previo: str = "") -> dict:
    aviso = f"\nEl intento anterior falló: {error_previo}. Corrígelo." if error_previo else ""
    salida = llm(
        "Extrae decisiones y tareas de esta reunión. Devuelve solo JSON con la forma "
        '{"decisiones": [str], "tareas": [{"tarea": str, "responsable": str}]}.'
        f"{aviso}\n<transcripcion>\n{transcripcion}\n</transcripcion>"
    )
    return json.loads(salida)

def puerta(datos: dict, asistentes: set[str]) -> str | None:
    """Devuelve None si todo está bien, o el motivo del fallo."""
    for t in datos.get("tareas", []):
        if t["responsable"] not in asistentes:
            return f"'{t['responsable']}' no está entre los asistentes {sorted(asistentes)}"
    return None

def acta(transcripcion: str, asistentes: set[str], reintentos: int = 2) -> str:
    error = ""
    for _ in range(reintentos + 1):
        try:
            datos = extraer(transcripcion, error)
        except json.JSONDecodeError as e:
            error = f"JSON inválido ({e})"
            continue
        error = puerta(datos, asistentes)
        if error is None:
            break
    else:
        raise ValueError(f"La extracción no pasa la puerta: {error}")
    return llm(
        "Redacta el acta en Markdown con secciones Decisiones y Tareas a partir de estos datos. "
        f"No añadas nada que no esté en ellos.\n{json.dumps(datos, ensure_ascii=False)}"
    )

Para asegurar el JSON sin reintentos puedes usar salidas estructuradas con esquema; lo explica Salida estructurada. Aun así, la puerta con reglas de negocio sigue siendo tuya.

Coste y latencia

Coste medio: tantas llamadas como pasos, aunque cada una suele ser más corta que una llamada única que lo hiciera todo. La latencia se suma en serie, y eso es lo que más se nota: tres pasos son tres esperas. Para recortarla, usa un modelo más rápido en los pasos sencillos (consulta Elegir modelo por tarea) y haz streaming solo del último paso, que es el que ve el usuario.

Patrones relacionados