05 Cerrar el ciclo
Convierte un fallo de producción revisado en datos dorados, un evaluator calibrado de políticas de negocio y protección para el tráfico futuro.
Punto de partida
El Módulo 04 terminó con una anotación humana completada para el chat_turn autoritativo del Módulo 03. Ten a mano el SQL corregido y la hoja de procedencia:
source=production-feedback
source_trace_id=<authoritative Chat trace ID>
failure_category=stale-business-policy
source_policy_version=policy-v1
annotation_id=<completed task ID when available>La traza original de producción tiene sql-execution-success=true y user-thumbs=false. El pulgar hacia abajo encontró una traza que merecía revisión; la anotación completada aportó el diagnóstico y la verdad de referencia corregida.
El ciclo continuo de evaluación y mejora
Este módulo completa una vuelta del ciclo:
- el feedback del usuario revela un punto ciego del evaluator online actual;
- una persona investiga y aprueba una corrección;
- el incidente revisado amplía el dataset dorado;
- las versiones de referencia y candidata se ejecutan sobre el mismo dataset ampliado;
- un evaluator general se calibra offline antes de habilitarlo online; y
- el tráfico futuro sigue recopilando tanto puntuaciones del evaluator como feedback de usuarios.
El último paso es importante: publicar un evaluator mejor no pone fin al feedback. Un evaluator solo puede medir las dimensiones representadas en su catálogo de políticas y su prompt. Un futuro 👎 puede revelar otra política ausente, una petición ambigua o un nuevo modo de fallo, e iniciar el mismo ciclo otra vez.
Objetivo
Superar cinco puertas de pruebas: promover, referencia, candidato, calibrar y, por último, habilitar y reproducir. Usa el ganador del Módulo 02 en ambos experimentos, de modo que la versión de política sea el único cambio de tratamiento intencionado.
Ejecuta todos los comandos siguientes desde ClickHouse_Demos/workshops/agent_arena:
cd ClickHouse_Demos/workshops/agent_arena
source .env
export WINNER_MODEL="${WINNER_MODEL:-qwen3.7-flash}"
export WINNER_PROMPT="${WINNER_PROMPT:-P2_fewshot}"
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"Los valores predeterminados corresponden al ganador verificado del taller. Si tu grupo eligió otro config_id, ajusta los tres valores al modelo y prompt correspondientes y no los cambies en ninguna puerta.
Puerta de pruebas 1 — Promover el incidente revisado
Crea reviewed.json en la raíz del laboratorio con los tres registros siguientes. Sustituye en todas partes los dos valores de ejemplo antes de ejecutar la promoción. Si Langfuse no muestra un ID de tarea de anotación, elimina annotation_id de los tres registros en vez de dejar el placeholder; ese campo es opcional, mientras que los demás campos de procedencia de producción son obligatorios.
[
{
"id": "prod-active-001",
"question": "How many active customers do we have?",
"golden_sql": "SELECT uniqExact(customer_id) FROM v_orders WHERE order_ts >= now() - INTERVAL 30 DAY AND status NOT IN ('cancelled', 'returned')",
"tier": 2,
"ordered": false,
"source": "production-feedback",
"source_trace_id": "<paste the Module 03 Chat trace ID>",
"failure_category": "stale-business-policy",
"source_policy_version": "policy-v1",
"annotation_id": "<paste the completed task ID>"
},
{
"id": "prod-active-002",
"question": "What is our active customer count right now?",
"golden_sql": "SELECT uniqExact(customer_id) FROM v_orders WHERE order_ts >= now() - INTERVAL 30 DAY AND status NOT IN ('cancelled', 'returned')",
"tier": 2,
"ordered": false,
"source": "production-feedback",
"source_trace_id": "<paste the Module 03 Chat trace ID>",
"failure_category": "stale-business-policy",
"source_policy_version": "policy-v1",
"annotation_id": "<paste the completed task ID>"
},
{
"id": "prod-active-003",
"question": "How many customers qualify as active under our business definition?",
"golden_sql": "SELECT uniqExact(customer_id) FROM v_orders WHERE order_ts >= now() - INTERVAL 30 DAY AND status NOT IN ('cancelled', 'returned')",
"tier": 2,
"ordered": false,
"source": "production-feedback",
"source_trace_id": "<paste the Module 03 Chat trace ID>",
"failure_category": "stale-business-policy",
"source_policy_version": "policy-v1",
"annotation_id": "<paste the completed task ID>"
}
]Solo prod-active-001 es la pregunta exacta de la traza de feedback. prod-active-002 y prod-active-003 son paráfrasis escritas por el revisor a partir del mismo incidente investigado. Usan la misma traza de origen y la misma anotación completada para mantener la auditabilidad; no son dos trazas adicionales de feedback de producción. Las tres entradas invocan intencionadamente la métrica gobernada de clientes activos, de modo que la referencia no pueda parecer correcta gracias a recuentos no relacionados.
Promueve el lote revisado:
source .env
.venv/bin/python -m scripts.promote_to_golden reviewed.jsonEspera tres líneas prepared prod-active-* seguidas de:
promoted 3 question(s) into the 'arena-golden' datasetAbre Langfuse → Datasets → arena-golden e inspecciona los metadatos de cada elemento nuevo. Verifica source=production-feedback, el mismo source_trace_id real, failure_category=stale-business-policy y source_policy_version=policy-v1.
El corpus fuente contiene 20 preguntas YAML. q019 y q020 son ejemplos few-shot reservados, por lo que un proyecto limpio comienza con 18 elementos de experimento en arena-golden. Esta promoción de tres elementos lleva el dataset limpio a 21. Un proyecto reutilizado puede contener más elementos aprobados; registra su procedencia en vez de eliminarlos para forzar un número y exige que referencia y candidato usen los mismos ID.
reviewed.json es estado mutable e ignorado del operador y constituye la ruta principal del taller. El archivo versionado --synthetic-fixture es únicamente una alternativa reproducible para ensayos. No representa una anotación humana ni satisface esta puerta. Ambos modos son mutuamente excluyentes; nunca ejecutes la alternativa sintética después de una promoción auténtica.
La promoción valida el lote completo, el SQL de solo lectura y la procedencia requerida antes de consultar ClickHouse o escribir elementos. Después lee los metadatos existentes y rechaza un ID que colisione con otra procedencia de producción. Si ese preflight autenticado no puede establecer la procedencia con seguridad, se detiene sin escribir. Repetir una promoción auténtica solo es seguro cuando la procedencia que colisiona es idéntica.
Puerta de pruebas 2 — Ejecutar la referencia policy-v1
Primero aprovisiona el juez basado en catálogo para los experimentos. Esto crea su regla online de observaciones en estado deshabilitado:
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--business-policy-experimentsEspera experiment rule enabled=True; online rule enabled=False. Antes de continuar, confirma en Langfuse que la regla online sigue deshabilitada.
Asigna a este intento un sufijo único y ejecuta el modelo y prompt seleccionados sobre el dataset ampliado con la política obsoleta:
export LOOP_RUN_SUFFIX="${LOOP_RUN_SUFFIX:-$(date +%Y%m%d-%H%M%S)}"
export BASELINE_RUN_ID="online-loop-baseline-${LOOP_RUN_SUFFIX}"
export CANDIDATE_RUN_ID="online-loop-candidate-${LOOP_RUN_SUFFIX}"
.venv/bin/python -m eval.harness --run-id "$BASELINE_RUN_ID" \
--policy-version policy-v1 --models "$WINNER_MODEL" --prompts "$WINNER_PROMPT" \
--wait-for-score business-policy-adherenceEl harness añade --policy-v1 al ID de la versión. Espera tres nombres exactos de puntuación en cada traza: correctness, agent-arena-llm-judge y business-policy-adherence. No continúes si se agota el tiempo o falta alguna puntuación.
En Experiments de Langfuse, registra el número de elementos y la corrección agregada de la referencia. En un proyecto limpio debe haber 21 elementos tras la promoción. Los proyectos reutilizados pueden tener más elementos aprobados y las respuestas del proveedor pueden variar, por lo que la puerta de publicación es la comparación emparejada siguiente y no una cifra agregada fija.
Puerta de pruebas 3 — Ejecutar el candidato policy-v2
Sin cambiar el dataset, el modelo, el prompt ni el sufijo, ejecuta el candidato:
.venv/bin/python -m eval.harness --run-id "$CANDIDATE_RUN_ID" \
--policy-version policy-v2 --models "$WINNER_MODEL" --prompts "$WINNER_PROMPT" \
--wait-for-score business-policy-adherenceRegistra el agregado real del candidato, compara ambas ejecuciones en Langfuse y exige:
- ID y recuentos de elementos de dataset idénticos;
- los tres elementos
prod-active-*pasan decorrectness=0conpolicy-v1acorrectness=1conpolicy-v2; - se compara por elemento todo lo anterior a
prod-active-*, sin ninguna regresión decorrectness=1acorrectness=0; y - la corrección agregada del candidato no es inferior a la referencia.
Detente si empeora un elemento preexistente. Un candidato que corrige el incidente rompiendo comportamiento conocido no supera la puerta de publicación.
Puerta de pruebas 4 — Calibrar un juez general de políticas
business-policy-adherence no es un «evaluator de clientes activos». Recibe la pregunta, el SQL generado y el catálogo completo de métricas policy-v2. Determina qué métrica gobernada se aplica y devuelve PASS, FAIL o NOT_APPLICABLE. El mismo diseño comprueba clientes activos, ingresos, conversión y margen bruto sin crear un evaluator por formulación.
Antes de habilitarlo para observaciones de producción, inspecciona estos elementos de experimento:
| Prueba de calibración | Ejecución/elemento | business-policy-adherence requerido |
|---|---|---|
| SQL obsoleto de clientes activos | referencia prod-active-001 | FAIL |
| SQL corregido de clientes activos | candidato prod-active-001 | PASS |
| política de ingresos | candidato q005 | PASS |
| política de conversión de vista a compra | candidato q018 | PASS |
| recuento simple de clientes | candidato q001 | NOT_APPLICABLE |
Repite la comprobación de clientes activos para prod-active-002 y prod-active-003. Lee tanto el razonamiento como la categoría: debe identificar la política aplicable del catálogo y evaluar respecto de ella el SQL generado. Un recuento sencillo debe seguir siendo NOT_APPLICABLE, lo que demuestra que el juez no fuerza todas las preguntas de recuento a la política de clientes activos.
Mantén deshabilitada la regla online si alguna categoría es incorrecta, falta una puntuación, la salida estructurada está mal formada o la comparación de corrección presenta una regresión. La calibración offline viene primero porque permite inspeccionar aprobaciones y fallos falsos sobre ejemplos conocidos antes de que el evaluator influya en la monitorización de producción.
Puerta de pruebas 5 — Habilitar y reproducir en policy-v2
Solo después de superar todas las puertas de calibración, habilita la regla de observaciones:
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--enable-business-policy-onlineEspera el nombre exacto agent-arena-business-policy-online con enabled=True. El comando se cierra ante un fallo si no encuentra una puntuación de experimento business-policy-adherence limitada al dataset; las comprobaciones manuales anteriores siguen siendo la puerta de calidad.
Detén el servidor policy-v1. En el primer terminal, inicia el candidato y déjalo activo:
source .env
AGENT_ARENA_POLICY_VERSION=policy-v2 \
.venv/bin/uvicorn serving.api:app --port 8100En el segundo terminal, define un auxiliar que reciba una pregunta y devuelva el ID de su traza solo después de confirmar una respuesta policy-v2 correcta:
source .env
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"
ask_trace() {
local question="$1"
local body
body=$(.venv/bin/python -c \
'import json,sys; print(json.dumps({"question": sys.argv[1], "config_id": sys.argv[2]}))' \
"$question" "$WINNER_CONFIG_ID")
curl -fsS http://localhost:8100/ask \
-H 'content-type: application/json' -d "$body" | \
.venv/bin/python -c \
'import json,sys; data=json.load(sys.stdin); assert data["policy_version"] == "policy-v2" and data["outcome"] == "ok"; print(data["trace_id"])'
}Formula una vez las preguntas de clientes activos e ingresos. Las puntuaciones online de observaciones usan el nombre de regla agent-arena-business-policy-online, no el nombre de puntuación de los experimentos:
ACTIVE_TRACE=$(ask_trace "How many active customers do we have?")
.venv/bin/python -m scripts.verify_online_scores "$ACTIVE_TRACE" \
sql-execution-success=true agent-arena-business-policy-online=PASS
REVENUE_TRACE=$(ask_trace "What was revenue in the last 30 days?")
.venv/bin/python -m scripts.verify_online_scores "$REVENUE_TRACE" \
sql-execution-success=true agent-arena-business-policy-online=PASSLa pregunta de conversión tiene un límite estocástico verificado. Formúlala una vez y conserva la traza. Si el resultado del servicio no es ok, o faltan o fallan las puntuaciones exactas, reintenta la misma pregunta y configuración como máximo una vez. Este bloque mantiene visibles ambos intentos:
ask_conversion() {
local body
body=$(.venv/bin/python -c \
'import json,sys; print(json.dumps({"question": sys.argv[1], "config_id": sys.argv[2]}))' \
"What is our view-to-purchase conversion rate for the last 7 days?" \
"$WINNER_CONFIG_ID")
curl -fsS http://localhost:8100/ask \
-H 'content-type: application/json' -d "$body" | \
.venv/bin/python -c \
'import json,sys; data=json.load(sys.stdin); assert data["policy_version"] == "policy-v2"; print("\t".join((data["trace_id"], data["outcome"])))'
}
IFS=$'\t' read -r CONVERSION_TRACE_1 CONVERSION_OUTCOME_1 <<< \
"$(ask_conversion)"
if .venv/bin/python -m scripts.verify_online_scores "$CONVERSION_TRACE_1" \
sql-execution-success=true agent-arena-business-policy-online=PASS; then
CONVERSION_SCORES_1=pass
else
CONVERSION_SCORES_1=fail
fi
if [ "$CONVERSION_OUTCOME_1" = ok ] && [ "$CONVERSION_SCORES_1" = pass ]; then
CONVERSION_RESULT_1=pass
else
CONVERSION_RESULT_1=fail
fi
printf 'conversion_attempt=1 trace_id=%s outcome=%s exact_scores=%s result=%s\n' \
"$CONVERSION_TRACE_1" "$CONVERSION_OUTCOME_1" \
"$CONVERSION_SCORES_1" "$CONVERSION_RESULT_1"
CONVERSION_TRACE_2=not-run
CONVERSION_OUTCOME_2=not-run
CONVERSION_SCORES_2=not-run
CONVERSION_RESULT_2=not-run
if [ "$CONVERSION_RESULT_1" != pass ]; then
IFS=$'\t' read -r CONVERSION_TRACE_2 CONVERSION_OUTCOME_2 <<< \
"$(ask_conversion)"
if .venv/bin/python -m scripts.verify_online_scores "$CONVERSION_TRACE_2" \
sql-execution-success=true agent-arena-business-policy-online=PASS; then
CONVERSION_SCORES_2=pass
else
CONVERSION_SCORES_2=fail
fi
if [ "$CONVERSION_OUTCOME_2" = ok ] && [ "$CONVERSION_SCORES_2" = pass ]; then
CONVERSION_RESULT_2=pass
else
CONVERSION_RESULT_2=fail
fi
fi
printf 'conversion_attempt=2 trace_id=%s outcome=%s exact_scores=%s result=%s\n' \
"$CONVERSION_TRACE_2" "$CONVERSION_OUTCOME_2" \
"$CONVERSION_SCORES_2" "$CONVERSION_RESULT_2"
if [ "$CONVERSION_RESULT_1" != pass ] && \
[ "$CONVERSION_RESULT_2" != pass ]; then
printf '%s\n' \
'STOP: conversion failed twice; preserve both traces and investigate.' >&2
false
fiNo reintentes hasta que funcione. Si ambos intentos fallan, conserva las dos trazas, mantén visible el resultado y canaliza las nuevas pruebas mediante anotación humana, mejora de datos dorados y el mismo ciclo de calibración emparejada.
Solo cuando la conversión funcione, formula una vez la pregunta de recuento sencillo:
PRODUCT_TRACE=$(ask_trace "How many products are there?")
.venv/bin/python -m scripts.verify_online_scores "$PRODUCT_TRACE" \
sql-execution-success=true agent-arena-business-policy-online=NOT_APPLICABLEEl evaluator online se ejecuta de forma asíncrona. El verificador consulta durante un máximo de 180 segundos de forma predeterminada; una puntuación aún pendiente no equivale a un fallo.
Mantener el ciclo en funcionamiento
Deja 👍/👎 habilitado después de la publicación. Monitoriza discrepancias como agent-arena-business-policy-online=PASS junto a user-thumbs=false: son candidatas de gran valor para la siguiente cola de anotación. La revisión humana decide si corregir la política, los prompts, los datos o el evaluator. Los casos aprobados vuelven a arena-golden; después, el siguiente candidato repite la misma secuencia de referencia → candidato → calibración → habilitación protegida.
Las reglas del taller muestrean el 100 % de las trazas válidas para que todos vean pruebas. Es un ajuste didáctico, no un valor predeterminado de producción. El muestreo real debe reflejar el tráfico, el coste y la latencia del evaluator, el riesgo y la cobertura de incidentes necesaria.
Pruebas de finalización
- La traza raíz de producción sigue mostrando
sql-execution-success=truey el Booleanuser-thumbs=false. - La tarea humana
production-investigation-<session>está completada con una corrección verificada yapproved-for-golden=true. - Existen los tres elementos dorados con procedencia auténtica
production-feedback, y puedes distinguir la pregunta del usuario de las dos paráfrasis del revisor. - Referencia y candidato usaron el mismo dataset ampliado, modelo y prompt; el candidato corrigió los tres elementos promovidos y no introdujo regresiones de corrección en los existentes.
- La calibración del experimento produjo
FAIL,PASSyNOT_APPLICABLEcon el nombre exactobusiness-policy-adherence. - La regla de observación estuvo deshabilitada durante la calibración y solo se habilitó después de superar las puertas.
- Las trazas de clientes activos, ingresos y conversión tienen
agent-arena-business-policy-online=PASS; el recuento sencillo de productos tieneagent-arena-business-policy-online=NOT_APPLICABLE. - Puedes explicar por qué la evaluación online y el feedback de usuario siguen mejorándose mutuamente después de publicar.