03 Publier et détecter
Publiez l'agent sélectionné avec un angle mort connu, puis utilisez l'évaluation opérationnelle et le feedback réel pour le détecter.
Point de départ
Le Module 02 est terminé. Notez le config_id gagnant de l'exécution réelle, puis exportez-le depuis la racine du laboratoire (ClickHouse_Demos/workshops/agent_arena) :
source .env
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"L'exécution vérifiée a choisi qwen3.7-flash__P2_fewshot ; conservez le gagnant de votre salle s'il diffère. Vous utiliserez le même modèle et prompt que ceux mesurés, pas un agent spécial conçu pour échouer.
Si Qwen a gagné, OpenRouter Settings → Privacy → Data Policies → Zero Data Retention → Non-frontier doit être désactivé. La route Alibaba est refusée lorsque le ZDR non-frontier est imposé. Examinez vos exigences de confidentialité avant ce changement sur de vraies données.
Pourquoi un evaluator qui réussit peut manquer la valeur utilisateur
Un evaluator en ligne ne mesure que la dimension pour laquelle il a été conçu. Ici, sql-execution-success répond à une question opérationnelle importante : l'agent a-t-il produit un SQL exécutable par ClickHouse ? Il ignore si ce SQL suit la définition métier actuelle d'un client actif.
Cela crée un angle mort réaliste :
| Signal | Question | Valeur attendue dans cet incident |
|---|---|---|
sql-execution-success | Le SQL généré s'est-il exécuté ? | true |
user-thumbs | La réponse répond-elle au besoin de cet utilisateur ? | false |
L'évaluation opérationnelle détecte SQL cassé, timeouts et erreurs. L'évaluation sémantique ou utilisateur vérifie l'utilité et l'alignement métier. Aucune ne remplace l'autre. Un 👎 est un signal de priorité, pas une vérité de référence ; un humain l'examinera au Module 04.
Objectif
Créer une vraie trace chat_turn où l'evaluator opérationnel réussit mais l'utilisateur rejette la réponse. Consignez la trace et les deux décomptes contradictoires pour l'enquête humaine.
Étape 1 — Prouver que l'incident préparé est reproductible
Exécutez le preflight depuis la racine avant le serveur :
source .env
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"
.venv/bin/python -m schema.gen_schema_context
.venv/bin/python -m scripts.check_online_eval_scenario \
--config-id "$WINNER_CONFIG_ID"La commande exécute les deux définitions et pose trois paraphrases à la configuration. Elle doit afficher des stale_count et current_count différents, trois lignes classification_N=policy-v1, puis :
Si une paraphrase renvoie ok/unknown, le preflight ne réessaie qu'une fois cette même paraphrase et configuration. Il ne réessaie ni policy-v2 ni les échecs de fournisseur, modèle ou agent ; un second ok/unknown bloque toujours.
OK: seeded online-evaluation incident is reproducibleSi les décomptes sont identiques ou une classification diffère de policy-v1, arrêtez : le contraste ne serait pas visible.
La définition métier actuelle correspond exactement à :
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')L'ancienne définition policy-v1 compte les clients inscrits dans les 90 derniers jours. Le SQL généré est donc valide selon les instructions policy-v1. Le problème n'est pas l'intelligence du modèle : le contexte publié est obsolète et l'evaluator d'exécution trop étroit pour le remarquer.
Étape 2 — Provisionner l'evaluator opérationnel
Le provisionnement est idempotent :
source .env
.venv/bin/python -m scripts.provision_online_evaluators --operationalLa sortie doit nommer l'evaluator sql-execution-success et la règle activée agent-arena-sql-execution-online.
Étape 3 — Démarrer la publication obsolète préparée
Dans le premier terminal, démarrez le serveur avec l'ancienne politique explicite :
source .env
AGENT_ARENA_POLICY_VERSION=policy-v1 \
.venv/bin/uvicorn serving.api:app --port 8100N'omettez pas AGENT_ARENA_POLICY_VERSION. Sinon, le service emploie policy-v2, qui exclut correctement les commandes annulées et retournées.
Étape 4 — Interroger et noter dans Chat
Dans un autre terminal, démarrez le dashboard si nécessaire :
scripts/arena.sh serveOuvrez http://localhost:5174, sélectionnez Chat et $WINNER_CONFIG_ID, puis demandez :
How many active customers do we have?Lisez le SQL et le résultat. Ils doivent suivre la définition policy-v1 des inscriptions sur 90 jours. Cliquez sur 👎 et attendez feedback sent.
Cette trace racine Chat est l'unique incident de référence transmis au Module 04.
Étape 5 — Trouver et vérifier la trace Chat
Dans Langfuse, ouvrez Tracing et filtrez user-thumbs = false. Ouvrez la racine chat_turn la plus récente dont la question est How many active customers do we have?, dont la configuration correspond à $WINNER_CONFIG_ID et dont les métadonnées affichent policyversion=policy-v1. Copiez l'ID et l'URL, puis définissez :
export CHAT_TRACE_ID="<paste the Chat trace ID>"
.venv/bin/python -m scripts.verify_online_scores "$CHAT_TRACE_ID" \
sql-execution-success=true user-thumbs=falseVérifiez que user-thumbs est un Boolean false, pas un score numérique ou texte. Le code appelle le champ policy_version ; OpenTelemetry le nettoie en policyversion dans Langfuse.
Étape 6 — Reproduire avec un curl non noté
L'appel API brut est un diagnostic obligatoire. Il crée une trace séparée, pas l'incident de feedback, et ne doit pas être noté :
source .env
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"
ASK_BODY=$(.venv/bin/python -c \
'import json,sys; print(json.dumps({"question": sys.argv[1], "config_id": sys.argv[2]}))' \
"How many active customers do we have?" "$WINNER_CONFIG_ID")
CURL_RESPONSE=$(curl -fsS http://localhost:8100/ask \
-H 'content-type: application/json' -d "$ASK_BODY")
printf '%s\n' "$CURL_RESPONSE" | .venv/bin/python -m json.tool
CURL_TRACE_ID=$(printf '%s\n' "$CURL_RESPONSE" | .venv/bin/python -c \
'import json,sys; data=json.load(sys.stdin); assert data.get("policy_version") == "policy-v1"; assert data.get("outcome") == "ok"; trace_id=data.get("trace_id"); assert isinstance(trace_id, str) and trace_id; print(trace_id)')La réponse doit avoir outcome: "ok", un trace_id non vide et policy_version: "policy-v1".
Attendez l'evaluator asynchrone et vérifiez uniquement son score opérationnel :
.venv/bin/python -m scripts.verify_online_scores "$CURL_TRACE_ID" \
sql-execution-success=trueN'appelez pas /feedback pour CURL_TRACE_ID et ne l'inscrivez pas dans la feuille. Ce n'est qu'un diagnostic ; CHAT_TRACE_ID reste la trace transmise.
Étape 7 — Comparer avec la politique actuelle
Exécutez le SQL actuel avec le même client ClickHouse en lecture seule, puis comparez son résultat à celui de CURL_RESPONSE :
.venv/bin/python - <<'PY'
from arena.config import load_config
from agents.chclient import ROClickHouseClient
sql = """SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')"""
result = ROClickHouseClient(load_config().clickhouse).query(sql)
print(result.rows[0][0])
PYVoici la requête exacte exécutée :
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')Le décompte différent est l'échec visible. Le SQL a fonctionné, mais a répondu à la mauvaise définition métier. Notez ce décompte avec les preuves Chat de référence.
Feuille d'enquête
Conservez pour le Module 04 :
| Preuve | Votre valeur |
|---|---|
config_id gagnant | |
| ID de la trace Chat de référence | |
| URL de la trace Chat de référence | |
Décompte policy-v1 obsolète | |
Décompte policy-v2 actuel | |
sql-execution-success | true |
user-thumbs | false |
Comment vérifier que vous avez terminé
- Le preflight a montré des décomptes différents et trois classifications
policy-v1. - Le service tournait avec
AGENT_ARENA_POLICY_VERSION=policy-v1et/aska renvoyéoutcome: "ok". - La trace Chat de référence a
sql-execution-success=trueetuser-thumbs=false. - Le diagnostic curl a renvoyé
outcome: "ok", produit unCURL_TRACE_IDdistinct et n'a pas été noté ni transmis. - Vous avez consigné l'ID, l'URL et les deux décomptes sans partager d'identifiants.
- Vous pouvez expliquer pourquoi un SQL exécutable ne prouve pas la correction sémantique.
Passez au Module 04 — Enquêter pour transformer ce signal en diagnostic revu par un humain.
02 Mesurer hors ligne
Utilisez les evaluators, datasets et traces Langfuse pour mesurer réellement le gagnant, question par question et niveau par niveau.
04 Enquêter
Transformez un signal utilisateur négatif en diagnostic et correction revus par un humain sans confondre feedback et vérité de référence.