Módulo 1: Qué cambia cuando un componente es no determinista

Mini-proyecto: ubica una feature de IA en Mercado

Descripción

Este es el capstone del módulo. Durante siete lecciones instalaste el método para ver un componente de IA: su contrato es probabilístico (lección 2), vive tras una frontera y propone (lección 3), arrastra cinco propiedades de golpe (lección 4), es un núcleo dentro de una cáscara (lección 5), esa cáscara se dimensiona por su tolerancia (lección 6), y todo se resume en una hoja de propiedades (lección 7). Ahora lo aplicas de punta a punta a una feature real de Mercado —el generador "describe tu producto", el que a partir de los atributos que carga un vendedor le propone una descripción lista para publicar— y lo ejecutas.

El trabajo del proyecto es el trabajo real de un arquitecto antes de que AI Engineering construya nada: tomar una feature de IA que el negocio quiere, y ubicarla —decidir dónde vive, cuánta no-determinación tolera, cómo se parte en núcleo y cáscara, qué contrato tiene su salida, qué contiene sus propuestas, y emitir su hoja con un brief que justifique el diseño—. Fíjate en la frontera, que es deliberada y es la de toda la guía: este proyecto no construye el generador. No escribe el prompt, no diseña el RAG, no elige el modelo por dentro —eso es AI Engineering—. Produce lo que va antes y alrededor de eso: la arquitectura de contención que hace seguro meter ese generador a Mercado.

Conexión con el módulo. Es la integración de las siete lecciones en un solo entregable ejecutado. El paso 1 usa la tolerancia de las lecciones 1 y 6; el paso 2 usa el núcleo/cáscara de la lección 5; el paso 3 usa el contrato probabilístico de la lección 2; el paso 4 usa la frontera "propone/dispone" de la lección 3; y el paso 5 emite la hoja de la lección 7. Al terminarlo tendrás el artefacto que abre el trabajo del resto de la guía: una feature de IA ubicada, con su hoja de propiedades, lista para que los módulos 2 al 7 llenen bien cada campo. El siguiente paso —construir el presupuesto, el eval, el guardrail, el fallback— es literalmente el resto de los módulos, y el capstone final (M8) hará este mismo ejercicio construyendo cada mecanismo.

La solución de referencia, ejecutada

Vamos a construir la solución en un solo programa que hace los cinco pasos: clasifica la tolerancia, separa núcleo y cáscara, demuestra el contrato probabilístico, muestra la cáscara conteniendo una mala propuesta, y emite la hoja con el brief. Todo con datos fijos y el LLM simulado por un stub —nada de red, claves ni APIs reales—, así que la salida es reproducible.

Los cinco pasos, en un programa

# Mini-proyecto M1: arquitecta la UBICACIÓN de una feature de IA en Mercado.
# Feature elegida: "describe_your_product" (el generador para vendedores).
# Frontera: NO construimos el generador/prompt/RAG (eso es AI Engineering).
# Aquí solo sus PROPIEDADES ARQUITECTÓNICAS: tolerancia, núcleo/cáscara,
# el contrato probabilístico, y la cáscara conteniendo una mala propuesta.
import random
from dataclasses import dataclass, asdict

_RNG = random.Random(2025)

# ------------------------------------------------------------------
# Paso 1 — Clasificar su tolerancia a la no-determinación.
# ------------------------------------------------------------------
touches_money_or_state, error_cost, blast_if_wrong = 1, 3, 2
nd_tolerance = 18 - (touches_money_or_state + error_cost + blast_if_wrong)
print("=== Paso 1: tolerancia a la no-determinacion ===")
print(f"  touches_money_or_state={touches_money_or_state}  "
      f"error_cost={error_cost}  blast_if_wrong={blast_if_wrong}")
print(f"  nd_tolerance = {nd_tolerance}/15  -> feature TOLERANTE, cascara MEDIA")
print("  (el vendedor revisa el texto antes de publicar: hay un humano en el lazo)")

# ------------------------------------------------------------------
# Paso 2 — Núcleo probabilístico vs cáscara determinista.
# ------------------------------------------------------------------
print()
print("=== Paso 2: nucleo (LLM) vs cascara (determinista) ===")
print("  nucleo   : proponer una descripcion a partir de los atributos")
print("  cascara  : validar longitud, prohibir claims falsos, exigir revision humana")

# ------------------------------------------------------------------
# Paso 3 — El contrato probabilístico (el assert exacto se rompe).
# ------------------------------------------------------------------
_DRAFTS = [
    "Auriculares inalambricos livianos con hasta 30 h de bateria.",
    "Audifonos BT comodos con 30 h de autonomia y estuche de carga.",
    "Auriculares sin cables, ligeros, con gran duracion de bateria.",
]
_RNG.shuffle(_DRAFTS)
_i = 0


def ai_component(attributes):
    # SIMULA el LLM generador: mismo input -> borradores distintos.
    global _i
    text = _DRAFTS[_i % len(_DRAFTS)]
    _i += 1
    return text


attrs = {"type": "headphones", "battery_h": 30, "wireless": True}
outs = [ai_component(attrs) for _ in range(3)]
print()
print("=== Paso 3: contrato probabilistico ===")
for k, o in enumerate(outs, 1):
    print(f"  borrador {k}: {o!r}")
try:
    assert outs[0] == outs[1]
    print("  assert outs[0] == outs[1] -> PASA")
except AssertionError:
    print("  assert exacto outs[0] == outs[1] -> se ROMPE (no-determinismo)")


def satisfies_contract(text):
    return isinstance(text, str) and 0 < len(text) <= 200 and "\n" not in text


assert all(satisfies_contract(o) for o in outs)
print("  contrato por propiedades (len<=200, una linea, str) -> PASA para los 3")

# ------------------------------------------------------------------
# Paso 4 — La cáscara contiene una mala propuesta (claim prohibido).
# ------------------------------------------------------------------
BANNED_CLAIMS = ["cura", "100% garantizado", "el mejor del mundo", "milagro"]


def deterministic_shell(draft):
    low = draft.lower()
    for claim in BANNED_CLAIMS:
        if claim in low:
            return (False, f"claim prohibido: {claim!r}")
    if len(draft) > 200:
        return (False, "excede 200 caracteres")
    return (True, "apto para revision del vendedor")


print()
print("=== Paso 4: la cascara contiene una propuesta fuera de politica ===")
proposals = [
    "Auriculares comodos con 30 h de bateria.",
    "Auriculares milagro que curan el insomnio, 100% garantizado.",
]
for p in proposals:
    ok, reason = deterministic_shell(p)
    print(f"  {'APTO' if ok else 'BLOQUEADO':<9}: {reason:<28} <- {p[:40]!r}")

# ------------------------------------------------------------------
# Paso 5 — La hoja de propiedades + el brief de decisión.
# ------------------------------------------------------------------
@dataclass
class AIComponentSheet:
    name: str
    location: str
    nd_tolerance: int
    latency_budget_ms: int
    cost_budget_usd: float
    eval_gate: str
    guardrail: str
    fallback: str
    deterministic_shell: str
    feedback_loop: str


sheet = AIComponentSheet(
    name="describe_your_product",
    location="detras de catalog-service, en el flujo de alta de producto del vendedor",
    nd_tolerance=nd_tolerance,
    latency_budget_ms=3000,
    cost_budget_usd=0.004,
    eval_gate="rubrica de 20 fichas: claridad + sin claims falsos; baja -> bloquea deploy",
    guardrail="lista de claims prohibidos + limite de longitud en la salida",
    fallback="si el modelo cae -> plantilla por atributos (sin IA)",
    deterministic_shell="media: valida y exige que el vendedor apruebe antes de publicar",
    feedback_loop="ediciones del vendedor sobre el borrador alimentan el eval-set",
)

print()
print("=== Paso 5: hoja de propiedades del componente ===")
for field, value in asdict(sheet).items():
    if field == "name":
        continue
    print(f"  {field:<20} {value}")

print()
print("=== Brief de decision ===")
print("  Que: ubicar describe_your_product como componente de IA en catalog-service.")
print("  Por que contenido: toca solo un borrador editable; el vendedor aprueba;")
print("     la cascara bloquea claims prohibidos; el fallback es una plantilla sin IA.")
print("  Frontera: como se construye el generador (prompt/RAG) -> AI Engineering.")

Qué esperar. Al correr el archivo, la salida es exactamente esta:

=== Paso 1: tolerancia a la no-determinacion ===
  touches_money_or_state=1  error_cost=3  blast_if_wrong=2
  nd_tolerance = 12/15  -> feature TOLERANTE, cascara MEDIA
  (el vendedor revisa el texto antes de publicar: hay un humano en el lazo)

=== Paso 2: nucleo (LLM) vs cascara (determinista) ===
  nucleo   : proponer una descripcion a partir de los atributos
  cascara  : validar longitud, prohibir claims falsos, exigir revision humana

=== Paso 3: contrato probabilistico ===
  borrador 1: 'Audifonos BT comodos con 30 h de autonomia y estuche de carga.'
  borrador 2: 'Auriculares inalambricos livianos con hasta 30 h de bateria.'
  borrador 3: 'Auriculares sin cables, ligeros, con gran duracion de bateria.'
  assert exacto outs[0] == outs[1] -> se ROMPE (no-determinismo)
  contrato por propiedades (len<=200, una linea, str) -> PASA para los 3

=== Paso 4: la cascara contiene una propuesta fuera de politica ===
  APTO     : apto para revision del vendedor <- 'Auriculares comodos con 30 h de bateria.'
  BLOQUEADO: claim prohibido: 'cura'      <- 'Auriculares milagro que curan el insomni'

=== Paso 5: hoja de propiedades del componente ===
  location             detras de catalog-service, en el flujo de alta de producto del vendedor
  nd_tolerance         12
  latency_budget_ms    3000
  cost_budget_usd      0.004
  eval_gate            rubrica de 20 fichas: claridad + sin claims falsos; baja -> bloquea deploy
  guardrail            lista de claims prohibidos + limite de longitud en la salida
  fallback             si el modelo cae -> plantilla por atributos (sin IA)
  deterministic_shell  media: valida y exige que el vendedor apruebe antes de publicar
  feedback_loop        ediciones del vendedor sobre el borrador alimentan el eval-set

=== Brief de decision ===
  Que: ubicar describe_your_product como componente de IA en catalog-service.
  Por que contenido: toca solo un borrador editable; el vendedor aprueba;
     la cascara bloquea claims prohibidos; el fallback es una plantilla sin IA.
  Frontera: como se construye el generador (prompt/RAG) -> AI Engineering.

Recorramos la salida paso por paso, porque cada bloque es una de las lecciones del módulo, aplicada.

Paso 1 — la tolerancia (lecciones 1 y 6). "Describe tu producto" puntúa 1 en touches_money_or_state (no toca dinero ni estado: produce un borrador editable), 3 en error_cost (se va a publicar con la marca de Mercado, así que una salida mala duele), y 2 en blast_if_wrong (si falla, afecta a un vendedor, no a media plataforma). Tolerancia = 12: una feature tolerante, de cáscara media. Y una observación de diseño que baja mucho el riesgo: hay un humano en el lazo —el vendedor revisa el borrador antes de publicar—, lo que hace que aun un error del modelo no llegue solo al cliente final.

Paso 2 — núcleo y cáscara (lección 5). La partición es explícita: el núcleo tiene una sola responsabilidad —proponer una descripción a partir de los atributos, lo único que un LLM aporta aquí—; la cáscara carga todo lo demás —validar longitud, prohibir claims falsos, exigir revisión humana—. Núcleo pequeño, cáscara robusta, como manda la regla del reactor.

Paso 3 — el contrato probabilístico (lección 2). El mismo input (attrs) entra tres veces al ai_component y salen tres borradores distintos, los tres válidos. El assert outs[0] == outs[1] —el reflejo de código normal— se rompe. El contrato correcto, por propiedades (str, no vacío, ≤ 200 caracteres, una sola línea), pasa para los tres. Es la lección 2 hecha realidad en esta feature: no afirmas el texto, afirmas sus propiedades.

Paso 4 — la cáscara contiene (lección 3). Dos propuestas chocan contra la deterministic_shell. La primera ("Auriculares cómodos con 30 h de batería") es apta. La segunda ("Auriculares milagro que curan el insomnio, 100% garantizado") se bloquea por contener un claim prohibido —la cáscara detectó "cura" dentro de "curan"—. El modelo propuso; la cáscara dispuso; el claim falso nunca llegó a publicarse. Exactamente el patrón "propone/dispone" de la lección 3, aplicado a contenido en vez de a dinero.

Paso 5 — la hoja y el brief (lección 7). La hoja resume todo el diseño en diez campos: dónde vive (en catalog-service, en el flujo de alta), su tolerancia (12), su presupuesto (3 s, $0.004), su eval (una rúbrica que bloquea el deploy si baja), su guardrail (claims + longitud), su fallback (una plantilla por atributos, sin IA, para cuando el modelo caiga), su cáscara (media, con revisión del vendedor) y su lazo de datos (las ediciones del vendedor mejoran el eval-set). Y el brief cierra con el qué, el por qué está contenido, y —crucial— la frontera: cómo se construye el generador por dentro es AI Engineering, no este proyecto.

Qué entregar

Tu entregable del mini-proyecto tiene dos partes, y las dos son las que produjo el programa:

  1. El programa ejecutado, con los cinco pasos, corriendo con el LLM simulado por un stub (sin API real) y produciendo la salida literal de arriba —o tu variante, si elegiste otra feature—.
  2. La hoja de propiedades y el brief de decisión de la feature: los diez campos llenos (ninguno en blanco) y un párrafo que justifique por qué la no-determinación del componente queda contenida.

Puedes hacer el proyecto con "describe tu producto" (la solución de referencia) o, para un reto mayor, con la búsqueda semántica o el agente de soporte —las dos features que el capstone de la guía (M8) recomienda—. Si eliges el agente de soporte, tu paso 4 será el más rico: la cáscara valida propuestas de reembolso contra la política (como en la lección 3), y tu hoja tendrá todos los campos "gruesos". Sea cual sea, respeta la frontera: ubicas y contienes el componente; no lo construyes.

Errores comunes

Cruzar la frontera y empezar a construir el generador. Qué pasa: en el paso 2, en vez de declarar la responsabilidad del núcleo ("proponer una descripción"), el alumno empieza a diseñar el prompt, a elegir el modelo, a pensar en el RAG. El proyecto se convierte en un ejercicio de AI Engineering y pierde su objetivo. Por qué pasa: construir el núcleo es más concreto y tentador que ubicarlo. Cómo detectarlo: tu solución habla de tokens, de temperature, de cómo redactar el prompt. Cómo corregirlo: trata el núcleo como una caja negra que propone (el stub ai_component). Tu trabajo es todo lo que lo rodea —la tolerancia, el contrato, la cáscara, la hoja—. Si te descubres diseñando el prompt, estás en el módulo equivocado del ecosistema.

Dejar campos de la hoja en blanco "porque la feature es simple". Qué pasa: como "describe tu producto" es tolerante, el alumno llena tres campos y deja fallback y eval_gate vacíos. Por qué pasa: tolerante se confunde con descuidado. Cómo detectarlo: tu hoja tiene campos vacíos. Cómo corregirlo: los diez campos llevan algo, aunque sea ligero. La feature es tolerante, sí, pero eso significa campos ligeros (una cáscara "media", un guardrail de solo dos reglas), no campos vacíos. El fallback a una plantilla sin IA es corto pero imprescindible: sin él, la feature de alta de producto se cae cuando el modelo se cae.

Testear la salida del generador con assert exacto. Qué pasa: al validar el paso 3, el alumno escribe assert ai_component(attrs) == "un texto específico" y se frustra porque falla. Por qué pasa: es el reflejo de código normal, y el módulo entero advirtió contra él. Cómo detectarlo: tu test del generador fija un valor esperado. Cómo corregirlo: verifica propiedades, no el valor. satisfies_contract (str, ≤ 200, una línea, sin claims prohibidos) es el contrato correcto —pasa para cualquier borrador válido, falla solo cuando la salida de verdad no sirve—. Es la lección 2, y el proyecto la vuelve a poner a prueba a propósito.

Ejercicios

Ejercicio 1 — Cambia la feature, rehaz la hoja. Toma el agente de soporte que ejecuta reembolsos (tolerancia 3) y adapta el programa: cambia el paso 1 (sus tres ejes), el paso 4 (la cáscara valida una propuesta de reembolso contra la política, no un claim de texto) y el paso 5 (su hoja tendrá todos los campos "gruesos"). Describe qué cambia en cada paso respecto a "describe tu producto".

Ver solución

Los cambios, paso por paso:

  • Paso 1 (tolerancia): los ejes suben a touches_money_or_state=5 (ejecuta reembolsos), error_cost=5, blast_if_wrong=5. Tolerancia = 18 − 15 = 3: intolerante, cáscara gruesa. Y ya no hay "el vendedor revisa antes de publicar" como red de seguridad barata; aquí el humano-en-el-lazo, si existe, es un aprobador de reembolsos, más costoso.
  • Paso 4 (la cáscara): en vez de buscar claims prohibidos en un texto, la cáscara valida una propuesta estructurada {order_id, amount} contra la política de reembolso —¿el pedido existe?, ¿está en ventana?, ¿el monto no excede el total ni el máximo?— exactamente como la deterministic_shell de la lección 3. Una propuesta alucinada (monto $9999) se bloquea. Este paso es mucho más rico porque lo que está en juego es dinero, no texto editable.
  • Paso 5 (la hoja): todos los campos "gruesos". guardrail valida entrada y salida (la entrada porque el ticket del cliente es una frontera de confianza —prompt injection—). fallback degrada a humano (encolar el ticket, nunca auto-aprobar). deterministic_shell es gruesa (valida cada propuesta contra la política). nd_tolerance = 3.

Lo que no cambia: los pasos 2 y 3 conservan su forma —el núcleo sigue siendo una caja que propone, y el contrato de su salida sigue verificándose por propiedades (aquí, que la propuesta sea un JSON válido con las claves correctas)—. La diferencia entre las dos features no está en la forma del método, sino en el grosor de la cáscara. Ese es justo el punto de la lección 6: mismo patrón, dimensionado al riesgo.

Ejercicio 2 — El fallback que degrada seguro. En la hoja de "describe tu producto", el fallback es "una plantilla por atributos (sin IA)". Diseña esa plantilla determinista: qué produce a partir de {"type": "headphones", "battery_h": 30, "wireless": True}, y explica por qué es un buen fallback (honesto, seguro, útil) aunque sea peor que la salida del modelo.

Ver solución

Una plantilla determinista razonable:

def template_fallback(attrs):
    parts = []
    if attrs.get("type") == "headphones":
        parts.append("Auriculares")
    if attrs.get("wireless"):
        parts.append("inalambricos")
    if attrs.get("battery_h"):
        parts.append(f"con {attrs['battery_h']} h de bateria")
    return " ".join(parts) + "." if parts else "Producto sin descripcion."

Con {"type": "headphones", "battery_h": 30, "wireless": True} produce: "Auriculares inalambricos con 30 h de bateria."

Por qué es un buen fallback aunque sea peor que la salida del modelo:

  • Honesto: no inventa nada. Solo enuncia los atributos que el vendedor ya cargó. No hay riesgo de un claim falso ni de un dato alucinado, porque no genera nada nuevo —solo formatea lo conocido—.
  • Seguro: es 100% determinista, así que su salida siempre cumple el contrato (longitud acotada, sin claims prohibidos) por construcción. La cáscara ni siquiera tiene que bloquearla.
  • Útil: una descripción básica pero correcta es infinitamente mejor que ninguna descripción (o que una pantalla de error) cuando el modelo está caído. El vendedor puede editarla y publicar; la feature no se cae con el modelo.
  • Peor pero suficiente: sí, le falta la fluidez del LLM. Pero el fallback no compite con el modelo en su mejor día; compite con el modelo en su peor día (caído), y contra eso una plantilla honesta gana siempre. El principio de la lección 5: un fallback degrada de forma segura y honesta, no fabrica certeza que no tiene. Esta plantilla es exactamente eso.

Ejercicio 3 — El brief que justifica la contención. Escribe, en tu propio texto, el brief de decisión de "describe tu producto" en tres frases: (1) qué se está ubicando, (2) por qué la no-determinación del componente queda contenida —cita al menos dos mecanismos concretos—, y (3) qué queda explícitamente fuera de alcance por la frontera con AI Engineering. Luego explica por qué un brief así es más valioso que solo el diagrama de la feature.

Ver solución

Un brief razonable:

  1. Qué se ubica: el generador "describe tu producto" se ubica como un componente de IA en catalog-service, dentro del flujo de alta de producto del vendedor, donde propone un borrador de descripción a partir de los atributos cargados.
  2. Por qué queda contenido: su no-determinación no puede hacer daño porque (a) el modelo solo propone un borrador editable —no publica nada directamente—, (b) una cáscara determinista valida ese borrador contra una lista de claims prohibidos y un límite de longitud antes de que sea publicable, (c) el vendedor lo revisa y aprueba antes de que salga (humano en el lazo), y (d) si el modelo se cae, un fallback determinista —una plantilla por atributos— mantiene la feature en pie. La feature es tolerante (nd_tolerance 12), y estos mecanismos la contienen a la medida de ese riesgo.
  3. Fuera de alcance (frontera): cómo se construye el generador por dentro —el prompt, el modelo, si usa RAG o no, cómo se afina— es responsabilidad del ecosistema AI Engineering, no de esta arquitectura. Este diseño trata el generador como una caja que propone y se ocupa solo de ubicarlo y contenerlo.

Por qué el brief vale más que el diagrama: un diagrama de la feature mostraría "vendedor → generador → catálogo", pero no mostraría por qué es seguro meter un componente no determinista ahí, qué lo contiene, qué pasa si falla, ni dónde termina la responsabilidad de este equipo y empieza la de AI Engineering. Todo eso —lo que hace arquitectura a la arquitectura— vive en el brief, no en las cajas y flechas. El diagrama codifica qué se conecta con qué; el brief codifica las decisiones de contención y sus razones, que es justo lo que un arquitecto de sistemas AI-native aporta. Es la misma lección que la guía de decisiones enseña para la arquitectura en general, aplicada al componente de IA: el registro de la decisión, no el dibujo, es donde vive el diseño.

Resumen y siguiente paso

En este mini-proyecto integraste las siete lecciones del módulo en un solo entregable ejecutado: ubicaste el generador "describe tu producto" de Mercado de punta a punta —clasificaste su tolerancia (12, tolerante, cáscara media), separaste su núcleo (proponer) de su cáscara (validar, prohibir claims, exigir revisión), demostraste el contrato probabilístico (el assert exacto se rompió, el de propiedades pasó para los tres borradores), viste a la cáscara bloquear un claim prohibido antes de publicarse, y emitiste su hoja de propiedades y su brief de decisión—. Y lo hiciste respetando la frontera que define la guía entera: ubicaste y contuviste el componente; no lo construiste. Cómo se hace el generador por dentro es AI Engineering; cómo se mete a Mercado sin que su no-determinación toque la confianza del sistema es lo que este módulo te enseñó.

Con esto cierras el módulo 1. Sabes ver un componente de IA: su contrato probabilístico, su lugar tras una frontera, las cinco propiedades que arrastra, la forma núcleo/cáscara, su tolerancia, y su hoja de propiedades. Tienes el vocabulario y el método para ubicar cualquier feature de IA que un sistema quiera añadir.

Lo que sigue es llenar bien cada campo de la hoja, y eso es el resto de la guía. El módulo 2 toma los campos latency_budget_ms y cost_budget_usd que aquí dejaste enunciados y los vuelve arquitectura de primera clase: cómo el LLM lento y caro se domestica con presupuestos, un model cascade (barato primero, escalar solo si hace falta) y caché —medido, con el ahorro real en pantalla—. El componente ya está ubicado; ahora aprendes a pagarlo y a hacerlo rápido.

Recursos

  • Anthropic, "Building Effective Agents" (2024) — anthropic.com/engineering/building-effective-agents. La referencia para ubicar bien una feature de IA —empezar simple, contener el componente, humano en el lazo donde el riesgo lo exige—. Todo el proyecto es una aplicación de sus principios de contención. En inglés.
  • Martin Fowler y Bharani Subramaniam, "Emerging Patterns in Building GenAI Apps" — martinfowler.com/articles/gen-ai-patterns. Los patrones de evals, guardrails y fallback que la hoja del proyecto enuncia están ahí como patrones desarrollados —el mapa de lo que los módulos 2 al 7 construyen—. En inglés.
  • Michael Nygard, "Documenting Architecture Decisions" (2011) — cognitect.com/blog/2011/11/15/documenting-architecture-decisions. El brief de decisión del proyecto es un pariente ligero del ADR; el capstone de la guía (M8) escribe un ADR completo para una feature de IA. En inglés.
  • Chip Huyen, AI Engineering (O'Reilly, 2024). Para cuando cruces la frontera y quieras construir el núcleo que aquí solo ubicaste —el prompt, el RAG, la evaluación del generador—: ese es el libro del otro lado de la frontera. En inglés.
  • Documentación de Claude — docs.anthropic.com. Para aterrizar los presupuestos de latencia y costo de la hoja en números reales cuando implementes de verdad, sin fijarte en una versión de modelo. Es el puente al módulo 2. En inglés.