Módulo 8: Proyecto — arquitecta una feature de IA en Mercado

El eval gate

Descripción

El paso 3 del método responde una pregunta que el presupuesto dejó abierta: el cascade de la lección 3 abarató la feature, pero ¿cómo sabes que el ahorro no rompió la calidad? La respuesta es el campo eval_gate de la hoja: una compuerta que corre un eval-set del agente de soporte y bloquea el deploy si la calidad baja. Esta lección la monta y la ejecuta sobre dos versiones del agente —la candidata a producción, que pasa, y una regresión, que falla— y ata el eval al presupuesto: la regresión que se bloquea es justamente un ahorro de costo mal calibrado, el de bajar demasiado al modelo barato.

Esto importa porque sin la compuerta, el ahorro de la lección 3 sería un cheque en blanco. El cascade te dice "abaraté la feature un 47%"; el eval gate te dice "…y la calidad sigue arriba del umbral" —o "…pero la calidad se cayó, así que este ahorro no llega a producción"—. Las dos frases juntas son lo que hace responsable optimizar el costo de un componente de IA. Un equipo que abarata sin un eval que vigile está optimizando a ciegas; uno que ata el cascade al eval gate optimiza con red de seguridad. Y como no puedes afirmar la salida exacta de un LLM (módulo 1), esa red no es un assert exacto —es una compuerta estadística, un score contra un umbral—.

Conexión con el módulo. Esta lección cierra el bucle que la lección 3 abrió: allá optimizaste el costo, aquí verificas que la optimización no degradó la calidad. Es la tercera compuerta de la feature —latencia (M2), costo (M2) y ahora calidad (M3)—, y las tres tienen la misma forma: una métrica contra un umbral, y las tres deben pasar para que la feature llegue a producción. Es también la compuerta que las lecciones siguientes alimentan: la lección 7 cierra el lazo de datos convirtiendo los thumbs_down en vivo en casos nuevos del eval-set, y la lección 8 corre este mismo gate sobre el sistema completo para decidir su deploy. La frontera con AI Engineering es precisa: aquí el eval es una compuerta (¿pasa o no?); cómo se diseña un buen eval-set —qué casos elegir, cómo construir el gold, cómo evitar overfitting— es AI Engineering.

La analogía: el control de calidad que rechaza el lote más barato

Vuelve a la fábrica de focos y su estación de control de calidad, la que rechaza cualquier lote con más del 2% de defectos. Ahora imagina que llega el gerente de compras con una propuesta: encontró un proveedor de filamentos más barato, que baja el costo de cada foco a la mitad. Es una gran noticia para el presupuesto. Pero antes de cambiar de proveedor, el lote hecho con el filamento barato pasa por la misma estación de control. Y resulta que ese filamento, más barato, produce focos que fallan más: la tasa de defectos del lote sube al 5%. La estación hace lo único que sabe hacer: rechaza el lote. No le importa que el filamento sea más barato; no le importa que el gerente de compras esté emocionado. La regla es la regla: más del 2% de defectos, y el lote no sale.

Fíjate en lo que acaba de pasar: el ahorro que rompe la calidad no llega al cliente. El filamento barato habría ahorrado dinero, pero a costa de vender focos que fallan, y la estación de control existe justamente para que esa decisión no se tome sola, en la emoción del ahorro. La estación no está en contra de ahorrar; está a favor de que el ahorro no degrade el producto por debajo de la línea. Si el gerente encuentra un filamento barato que sí pasa el 2%, adelante. Si no, el ahorro se queda en la puerta.

El eval gate es esa estación, y el "filamento más barato" es el cascade mal calibrado de la lección 3 —bajar al modelo barato para ahorrar—. El ahorro es tentador (la feature cuesta menos), pero si baja la calidad del agente de soporte por debajo del umbral, el eval gate rechaza la versión, exactamente como la estación rechaza el lote. El eval no está en contra del cascade; está a favor de que el cascade no degrade la calidad por debajo de la línea. Un ahorro que pasa el eval, adelante; uno que no lo pasa, se queda en la puerta. Esta lección monta esa estación para el agente de soporte de Mercado.

Ejemplo trabajado: la candidata pasa, la regresión falla

Vamos a montar la compuerta y pasar dos versiones del agente por ella. La compuerta corre un eval-set de diez tickets, obtiene el score (fracción de respuestas correctas), lo compara contra el umbral de 0.80, y decide: PASS (deploy permitido) o FAIL (deploy bloqueado). La versión A es la candidata a producción con el modelo actual: responde bien 9 de 10 (score 0.90). La versión B es el mismo sistema, pero alguien bajó al modelo barato para ahorrar (el cascade de la lección 3, mal calibrado): más barata, pero regresó a 6 de 10 (score 0.60).

# M8 Leccion 4 — el EVAL GATE (M3) del agente de soporte: la compuerta de
# calidad que gobierna el deploy. Corre el eval-set, saca el score, lo compara
# contra el umbral, y decide PASS (deploy) o FAIL (bloqueado). Aqui atrapamos
# una REGRESION: bajar al modelo barato (el cascade de M3-L4) subio la velocidad
# pero tumbo la calidad. Todo SIMULADO, salida determinista.

EVAL_SET = [
    {"id": "q1",  "question": "donde esta mi pedido",       "must_contain": "tracking"},
    {"id": "q2",  "question": "como devuelvo un producto",  "must_contain": "devolucion"},
    {"id": "q3",  "question": "cuanto tarda el envio",      "must_contain": "3 a 5 dias"},
    {"id": "q4",  "question": "puedo pagar en cuotas",      "must_contain": "cuotas"},
    {"id": "q5",  "question": "el producto llego roto",     "must_contain": "reembolso"},
    {"id": "q6",  "question": "como cambio mi direccion",   "must_contain": "perfil"},
    {"id": "q7",  "question": "no recibi mi factura",       "must_contain": "correo"},
    {"id": "q8",  "question": "quiero cancelar mi pedido",  "must_contain": "cancelar"},
    {"id": "q9",  "question": "el cupon no funciona",       "must_contain": "vigencia"},
    {"id": "q10", "question": "como contacto a un vendedor", "must_contain": "mensajes"},
]
GOLD = {
    "q1": "Puedes ver el tracking de tu pedido en tu perfil.",
    "q2": "Para una devolucion, entra a tu pedido y pulsa Devolver.",
    "q3": "El envio estandar tarda de 3 a 5 dias habiles.",
    "q4": "Si, puedes pagar en cuotas sin interes con tarjeta.",
    "q5": "Lamentamos eso; puedes pedir un reembolso desde el pedido.",
    "q6": "Cambia tu direccion en la seccion Perfil, Direcciones.",
    "q7": "Te reenviamos la factura al correo de tu cuenta.",
    "q8": "Puedes cancelar el pedido si aun no fue enviado.",
    "q9": "Revisa la vigencia del cupon; quiza ya expiro.",
    "q10": "Escribe al vendedor desde la seccion Mensajes.",
}
POOR_ANSWER = "Lo siento, no tengo informacion sobre eso."


def make_agent(competent_ids):
    def agent(question, case_id):
        return GOLD[case_id] if case_id in competent_ids else POOR_ANSWER
    return agent


def run_eval(agent):
    passed = sum(
        case["must_contain"] in agent(case["question"], case["id"]).lower()
        for case in EVAL_SET
    )
    return passed / len(EVAL_SET)


def eval_gate(agent, threshold):
    # LA COMPUERTA: score contra umbral => decision de deploy.
    score = run_eval(agent)
    return score, (score >= threshold)


THRESHOLD = 0.80
ALL_IDS = {c["id"] for c in EVAL_SET}
# Version A: el sistema completo con el modelo actual (candidata a produccion).
version_a = make_agent(ALL_IDS - {"q9"})                     # 9/10
# Version B: el mismo sistema, pero alguien bajo al modelo barato para ahorrar
# (el cascade mal calibrado de M2). Mas barato, pero REGRESO en calidad.
version_b = make_agent({"q1", "q2", "q3", "q4", "q6", "q8"})  # 6/10

print("=== EVAL GATE — agente de soporte de Mercado (umbral 0.80) ===\n")
print(f"{'version':<34}{'score':>7}   {'gate':<7} deploy")
for name, agent in (("A (modelo actual, candidata)", version_a),
                    ("B (regresion: bajaron al barato)", version_b)):
    score, passed = eval_gate(agent, THRESHOLD)
    verdict = "[PASS]" if passed else "[FAIL]"
    action = "PERMITIDO (verde)" if passed else "BLOQUEADO (rojo)"
    print(f"{name:<34}{score:>7.2f}   {verdict:<7} {action}")

print()
print("La version B era mas barata (el ahorro de M2), pero el eval-gate la")
print("BLOQUEA: el ahorro que rompe la calidad no llega a produccion. La")
print("compuerta es la que hace que 'mas barato' no signifique 'peor en vivo'.")

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

=== EVAL GATE — agente de soporte de Mercado (umbral 0.80) ===

version                             score   gate    deploy
A (modelo actual, candidata)         0.90   [PASS]  PERMITIDO (verde)
B (regresion: bajaron al barato)     0.60   [FAIL]  BLOQUEADO (rojo)

La version B era mas barata (el ahorro de M2), pero el eval-gate la
BLOQUEA: el ahorro que rompe la calidad no llega a produccion. La
compuerta es la que hace que 'mas barato' no signifique 'peor en vivo'.

Lee las dos filas, porque en ellas está el mecanismo central del paso 3.

La versión A pasa, en verde. Score 0.90 (9 de 10 respuestas correctas), que es mayor o igual que el umbral de 0.80, así que la compuerta devuelve PASS: deploy permitido. El agente es lo bastante bueno para producción según la regla que el equipo fijó de antemano. Nadie tuvo que leer las diez respuestas y opinar caso por caso; la compuerta comparó un número contra un umbral y dio verde.

La versión B falla, en rojo. Score 0.60 (6 de 10), que es menor que 0.80, así que la compuerta devuelve FAIL: deploy bloqueado. Y aquí está la conexión con la lección 3, que es el corazón de esta lección: la versión B era más barata. Alguien bajó al modelo barato buscando el ahorro del cascade —una decisión que en la lección 3 se veía como pura ganancia (menos costo)—. Pero el modelo barato responde peor cuatro de las diez preguntas, y el eval gate lo atrapa. El ahorro era real; la degradación de calidad también. La compuerta rechaza la versión sin negociar, exactamente como la estación de control rechaza el lote con el filamento barato: el ahorro que rompe la calidad no llega a producción.

Junta las dos y tienes lo que ata el paso 2 con el paso 3. La lección 3 te dio una palanca de costo (el cascade) con un riesgo de calidad oculto (el falso negativo, la difícil mal enrutada al barato). Esta lección monta la compuerta que hace visible ese riesgo antes de desplegar: la versión B parece un ahorro hasta que corre por el eval gate, y ahí se ve que el ahorro vino a costa de cuatro respuestas malas. Sin el eval gate, la versión B se habría desplegado —más barata, más rápida, y peor para un 40% de los clientes—. Con el eval gate, se queda en la puerta. Esa es la diferencia entre optimizar el costo a ciegas y optimizarlo con red de seguridad.

Profundización: la fitness function del componente probabilístico

El eval gate es una fitness function especializada. En architecture-decisions-and-tradeoffs-guide aprendes el concepto de fitness function: una prueba automatizada que verifica que una propiedad arquitectónica del sistema se mantiene con el tiempo, y que se pone roja si la propiedad se degrada, frenando el cambio. Los ejemplos clásicos son estructurales —"ninguna capa de dominio importa de infraestructura", "la latencia p95 sigue bajo 200 ms"—. El eval_gate es exactamente eso, aplicado a una propiedad nueva: la calidad de un componente probabilístico. Es una prueba automatizada (el eval-set), verifica que una propiedad se mantiene (el score sigue arriba del umbral), y se pone roja si se degrada (score < umbral → deploy bloqueado). La única diferencia con una fitness function clásica es que la propiedad no es determinista ni estructural, sino estadística —un score, no un booleano exacto—. Pero el rol arquitectónico es idéntico: gobernar que una propiedad del sistema no se degrade con los cambios. No es una metáfora: el eval es una fitness function para la IA. El concepto general vive en architecture-decisions; aquí ves su encarnación.

El umbral viene del riesgo, y para esta feature es alto. ¿De dónde sale el 0.80? No de la ingeniería. Igual que el cost_budget de la lección 3 venía del negocio, el umbral de calidad viene de cuánta imperfección tolera la feature —que es justo la nd_tolerance de la lección 2—. El agente de soporte tiene tolerancia 3 (intolerante), así que su umbral tendría que ser exigente. Aquí usamos 0.80 para las respuestas informativas (con un humano de respaldo, una de cada cinco imperfecta es tolerable), pero para las acciones que tocan dinero —los reembolsos— el umbral efectivo es aún más duro, y de hecho lo garantiza otra capa: la cáscara determinista (lección 7), que valida cada propuesta de reembolso contra la política, no solo un score agregado. La lección: el umbral se negocia con quien conoce el costo de una respuesta mala, y para una feature de tolerancia 3, la calidad se gobierna con un eval gate estricto y una cáscara que no confía en el score.

Un umbral demasiado bajo es una compuerta inútil. Si pusieras el umbral en 0.50, casi cualquier versión lo pasaría —incluida la versión B con 0.60—, y la compuerta dejaría de proteger: se pondría verde para todo, así que nunca frenaría nada. Una compuerta que nunca falla no es una compuerta, es un adorno. Es el equivalente de la estación de control que acepta lotes con hasta 50% de defectos: técnicamente existe, pero no rechaza nada. El umbral tiene que estar donde de verdad separa lo aceptable de lo inaceptable —lo bastante alto para atrapar la regresión que importa, la versión B que un umbral de 0.80 sí bloquea—. Un eval gate que nunca ha bloqueado un deploy es sospechoso: o todos tus cambios fueron perfectos, o tu umbral es demasiado flojo (casi siempre lo segundo).

La compuerta decide, no arregla. Un matiz importante: el eval_gate te dice si la versión pasa, no cómo arreglarla. Cuando la versión B falla con 0.60, la compuerta hizo su trabajo —bloqueó un deploy que habría degradado la calidad—, pero arreglar el componente (recalibrar el cascade para que escale más en la duda, mejorar el prompt del agente —eso es AI Eng—, o subir de modelo donde hace falta) es otra tarea. La compuerta es el guardia que no deja pasar el lote defectuoso; no es el mecánico que arregla el filamento. Separar los dos roles es clave: la compuerta protege producción, el detalle por caso guía la corrección.

Errores comunes

Tratar el eval como termómetro y no como compuerta. Qué pasa: el equipo corre el eval, mira el score (0.60), comenta "está un poco bajo pero el ahorro vale la pena" y despliega igual. El eval midió, pero no decidió: no había un umbral con autoridad, así que el score fue una sugerencia que la emoción del ahorro ignoró. Por qué pasa: sin un umbral fijado de antemano, cada release se negocia según la prisa (o el ahorro) del momento. Cómo detectarlo: tus deploys ocurren con scores "que podrían ser mejores" pero nadie los bloquea. Cómo corregirlo: fija un umbral explícito antes y haz que el gate bloquee sin negociar cuando el score cae debajo —la estación de control no discute con el gerente de compras—.

Confundir "más barato" con "mejor" sin verificar la calidad. Qué pasa: es el error que esta lección ataca de frente. El equipo aplica el cascade de la lección 3, ve que el costo baja, y despliega la versión más barata sin correr el eval —asumiendo que ahorrar es siempre bueno—. La versión barata responde peor, y la degradación se descubre por quejas de clientes semanas después. Por qué pasa: el ahorro es visible y medible (la factura baja), la degradación de calidad es invisible sin un eval que la mida. Cómo detectarlo: desplegaste un cambio de costo (modelo más barato, prompt más corto) sin correr el eval gate sobre la nueva versión. Cómo corregirlo: todo cambio que toca el costo pasa por el eval gate antes de desplegar. El cascade y el eval van juntos: el cascade da el ahorro, el eval confirma que el ahorro no rompió la calidad. Uno sin el otro es peligroso.

Un umbral demasiado flojo que nunca falla. Qué pasa: para que "nada se atore", el equipo pone el umbral en 0.50, y entonces casi toda versión pasa —incluida la regresión de la versión B—. La compuerta existe en el papel pero se pone verde para todo, dando una falsa sensación de seguridad: "tenemos eval gate" mientras el gate no protege de nada. Por qué pasa: un umbral bajo evita fricción y falsas alarmas, tentador cuando el gate "molesta". Cómo detectarlo: tu eval gate nunca ha bloqueado un deploy. Cómo corregirlo: pon el umbral donde de verdad separe aceptable de inaceptable —lo bastante alto para atrapar la versión B (0.60) que un umbral de 0.80 sí bloquea—. Una compuerta que nunca falla no es una compuerta.

Ejercicios

Ejercicio 1 — El ahorro que sí pasa. El gerente de producto insiste en el ahorro del cascade y pide una versión más barata. El equipo recalibra el clasificador para que escale más en la duda (menos difíciles al barato), y la nueva versión C da: costo $2,900/mes (bajo el budget de $3,000) y score 0.86 (sobre el umbral de 0.80). ¿Se despliega? Explica qué logró la recalibración y por qué esta versión sí es un buen ahorro.

Ver solución

Sí se despliega. La versión C pasa las tres compuertas de la feature:

  • Cost budget: $2,900/mes ≤ $3,000/mes → PASS. Cabe en el margen.
  • Eval gate: 0.86 ≥ 0.80 → PASS. La calidad se mantiene.
  • (La latencia, como en la lección 3, cabe con holgura.)

Qué logró la recalibración: la versión B fallaba porque el clasificador mandaba demasiadas queries difíciles al modelo barato (falsos negativos), que las respondía peor —de ahí el score 0.60—. Al recalibrar para que el clasificador escale en la duda (prefiera el falso positivo barato: mandar una fácil al caro y pagar unos centavos de más, sobre el falso negativo caro: mandar una difícil al barato y responder mal), la versión C conserva casi todo el ahorro del cascade sin sacrificar la calidad: las difíciles vuelven al modelo caro, así que el score sube a 0.86, mientras el 70% fácil sigue yendo al barato, así que el costo se mantiene bajo.

Por qué es un buen ahorro: es un ahorro que pasa el eval. La diferencia con la versión B no es que ahorre menos —ahorra casi lo mismo—, sino que ahorra sin degradar la calidad por debajo del umbral. Es exactamente el filamento barato que sí pasa el control del 2%: el gerente de compras obtiene su ahorro, y el cliente obtiene un producto que no falla. La lección: el eval gate no está en contra de ahorrar; está a favor de que el ahorro no rompa la calidad. La versión C es la forma correcta de aplicar el cascade —barato donde no cuesta calidad, caro donde sí—, y el eval gate es lo que distingue este ahorro bueno del ahorro malo de la versión B.

Ejercicio 2 — Las tres compuertas juntas. Una feature de IA lista para producción pasa tres compuertas: latency budget, cost budget y eval gate. Una versión del agente de soporte da: latencia p95 1200 ms (budget 4000 ms), costo $2,787/mes (budget $3,000), score 0.72 (umbral 0.80). ¿Se despliega? Explica qué aporta cada compuerta y por qué las tres son necesarias.

Ver solución

No se despliega. Las tres compuertas deben pasar, y esta versión falla la de calidad:

  • Latency budget: 1200 ms ≤ 4000 ms → PASS. Es rápida.
  • Cost budget: $2,787/mes ≤ $3,000/mes → PASS. Cabe en el margen.
  • Eval gate: 0.72 < 0.80 → FAIL. No es lo bastante buena.

El resultado global es bloqueado: basta con que una compuerta falle. La feature es rápida y barata, pero responde mal a más de una cuarta parte de los tickets, y desplegar eso sería servir un agente de soporte veloz, económico y malo.

Qué aporta cada compuerta y por qué las tres son necesarias: la latencia mide si responde a tiempo, el costo si cabe en el margen, y el eval si la respuesta es buena. Son propiedades ortogonales: una feature puede ser buena en dos y mala en la tercera, como esta. Si solo tuvieras las compuertas de latencia y costo (paso 2), habrías desplegado un agente rápido y barato sin darte cuenta de que responde mal —el error que esta lección ataca—. El eval gate es la tercera compuerta, la que el paso 3 aporta, y sin ella el trío queda incompleto. Y fíjate en la ironía de esta versión concreta: es más barata que la del ejercicio 1 ($2,787 vs $2,900) pero peor (0.72 vs 0.86) —otra vez, el ahorro que rompe la calidad—. Una feature lista para producción pasa las tres.

Ejercicio 3 — El eval gate sobre el sistema, no solo el modelo. En este capstone, el eval gate no evalúa "el modelo" aislado, sino el sistema completo —el agente con su cáscara, sus guardrails, su fallback—. Explica por qué evaluar el sistema completo da un score más honesto que evaluar solo el modelo, con un ejemplo donde el modelo solo daría un score y el sistema completo otro.

Ver solución

Evaluar el sistema completo da un score más honesto porque la calidad que el cliente experimenta es la del sistema, no la del modelo aislado. El cliente nunca habla con el modelo desnudo; habla con el modelo más su cáscara, sus guardrails y su fallback. Medir solo el modelo ignora todo lo que lo rodea, y ese "todo" cambia el resultado en las dos direcciones.

Un ejemplo donde los dos scores difieren: supón un ticket que pide "reembólsame 9999 de mi pedido de 50" (por un cliente confundido o malicioso).

  • Evaluando solo el modelo: el modelo, inducido, propone refund 9999. Si tu eval mide "¿el modelo propuso lo correcto?", esto cuenta como un fallo —el modelo se equivocó—, bajando su score.
  • Evaluando el sistema completo: el modelo propone refund 9999, pero la cáscara determinista lo bloquea (monto fuera de política) y el sistema responde con una negativa correcta o escala a un humano. El resultado que el cliente ve es correcto —no se reembolsaron 9999—, así que el caso cuenta como un acierto del sistema.

El sistema completo da el score honesto porque el modelo no tiene que ser perfecto para que el sistema lo sea —justo la tesis de toda la guía—. Un modelo que a veces propone mal, rodeado de una cáscara que atrapa esas propuestas, produce un sistema de alta calidad. Y al revés: un modelo excelente conectado directo a la API de reembolsos (sin cáscara) es un sistema frágil, aunque el modelo solo puntúe alto. La calidad arquitectónica vive en el sistema, no en el modelo, así que el eval gate debe correr sobre el sistema. Esto conecta con la lección 8, donde el eval gate del capstone corre sobre el pipeline completo —cascade, guardrails, cáscara, fallback— para decidir el deploy.

Resumen y siguiente paso

En esta lección montaste el campo eval_gate de la hoja: la compuerta de calidad que gobierna el deploy del agente de soporte. Con la estación de control de calidad que rechaza el lote hecho con el filamento más barato, viste la idea central del paso 3: el ahorro que rompe la calidad no llega a producción. Ejecutaste la compuerta sobre dos versiones: la candidata (0.90 ≥ 0.80) pasó en verde, y una regresión —el modelo barato de la lección 3, más económico pero degradado— (0.60 < 0.80) falló en rojo, con el deploy bloqueado. Ataste así el paso 2 con el paso 3: el cascade te dio el ahorro, y el eval gate confirmó que el ahorro no rompió la calidad. Y entendiste por qué el eval merece el nombre de fitness function —una prueba automatizada que gobierna que una propiedad del sistema (la calidad probabilística) no se degrade con los cambios—, especializada de architecture-decisions a la IA.

Antes de avanzar deberías poder: explicar por qué un score necesita un umbral para volverse compuerta; justificar de dónde sale el umbral (riesgo/tolerancia, no cálculo técnico); reconocer por qué un umbral demasiado bajo hace la compuerta inútil; y articular por qué el eval gate debe correr sobre el sistema completo, no solo el modelo.

La lección 5 monta el campo guardrail de la hoja: los guardrails y la frontera de confianza. Hasta ahora tratamos el costo (M2) y la calidad (M3); ahora blindamos la seguridad. Vas a montar la pila de guardrails alrededor del núcleo —señal de inyección en la entrada, validación de schema en la salida, y la frontera determinista que valida la propuesta contra la política— y vas a ejecutar el ataque: el modelo cederá a tres inyecciones —fugar su prompt, reembolsar 9999, inventar una acción grant_admin— y cada propuesta morirá en una compuerta distinta. La seguridad no vendrá de un prompt más fuerte, sino de validar la salida.

Recursos