03 リリースして検知する
既知のポリシーの盲点を抱えたまま選ばれたエージェントをリリースし、運用評価と実際のユーザーフィードバックでそれを検知します。
出発点
Module 02 が完了していること。実際の Arena run で選ばれた勝者の config_id を書き留め、
ラボのルート (ClickHouse_Demos/workshops/agent_arena) からエクスポートします:
source .env
export WINNER_CONFIG_ID="${WINNER_CONFIG_ID:-qwen3.7-flash__P2_fewshot}"検証済みのワークショップ run では qwen3.7-flash__P2_fewshot が選ばれました。教室の勝者が
異なる場合はそちらを使ってください。使うのは、あなたが測ったのと同じモデルとプロンプトです。
失敗専用の特別なエージェントではありません。
勝者が Qwen の場合、OpenRouter の Settings → Privacy → Data Policies → Zero Data Retention → Non-frontier を無効にしておく必要があります。non-frontier の ZDR が強制されて いると、利用可能な Alibaba のルートが拒否されます。実データに対してこの設定を変更する前に、 自分たちのプライバシー要件を確認してください。
合格した evaluator でもユーザー価値を見落とせる理由
オンライン evaluator は、それが測るように設計された次元だけを測ります。ここでの
sql-execution-success は重要な運用上の問いに答えます。エージェントは ClickHouse が実行できる
SQL を生成したか? しかしその SQL が アクティブ顧客 の現在のビジネス定義に従っているかは
知りません。
そこに現実的な監視のギャップが生まれます:
| シグナル | 答える問い | このインシデントで期待される値 |
|---|---|---|
sql-execution-success | 生成された SQL は正常に実行できたか? | true |
user-thumbs | この回答はこのユーザーのニーズを満たしたか? | false |
運用評価は壊れた SQL、タイムアウト、実行エラーを捕まえます。意味的な評価やユーザーによる評価は、 実行できた回答が有用でビジネス上の意味と揃っているかを問います。どちらも他方の代わりには なりません。👎 は優先順位付けのシグナルであって、正解データ (ground truth) ではありません。 Module 04 で人がそれを調査します。
ゴール
運用 evaluator は合格するのに、ユーザーはその回答に低い評価を付ける、という実際の chat_turn
trace を 1 つ作ります。その trace と食い違う 2 つのカウントを、人による調査のために記録します。
ステップ 1 — 仕込まれたインシデントが再現することを確かめる
デモサーバーを起動する前に、ラボのルートからプリフライトを実行します:
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"このコマンドは両方の定義を実行し、選ばれた構成に 3 つの言い換えを尋ねます。異なる
stale_count と current_count の値、3 行の classification_N=policy-v1、そして次を
出力しなければなりません:
言い換えの結果が ok/unknown だった場合、プリフライトが再試行するのは、その同じ
言い換えと構成を 1 回だけです。policy-v2、またはプロバイダー/モデル/エージェントの
失敗は再試行しません。2 回目も ok/unknown なら、そのままブロックされます。
OK: seeded online-evaluation incident is reproducibleカウントが等しい場合、あるいはいずれかの分類が policy-v1 でない場合は、そこで止めてください。
このデータ/モデルの run ではコントラストが見えません。
現在のビジネス定義はまさにこの SQL です:
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')古い policy-v1 の定義は、代わりに過去 90 日間にサインアップした顧客を数えます。したがって、
このモジュールでモデルが生成する SQL は、明示的に与えられた policy-v1 の指示に対しては
妥当です。ポイントはモデルが賢くないということではありません。デプロイされたポリシーの文脈が
古びている一方で、SQL 実行の evaluator はそれに気づけるほど広くないのです。
ステップ 2 — 運用 evaluator をプロビジョニングする
プロビジョニングは冪等なので、再実行しても安全です:
source .env
.venv/bin/python -m scripts.provision_online_evaluators --operational出力に evaluator sql-execution-success と有効化されたルール
agent-arena-sql-execution-online の名前が出ることを確認します。
ステップ 3 — 仕込まれた古いリリースを起動する
1 つ目のターミナルで、ラボのルートから、古いポリシーを明示的に選んでサーバーを起動し、 そのまま動かし続けます:
source .env
AGENT_ARENA_POLICY_VERSION=policy-v1 \
.venv/bin/uvicorn serving.api:app --port 8100AGENT_ARENA_POLICY_VERSION を省略しないでください。省略するとサービスは現在の policy-v2 を
既定で使い、それはキャンセル済み・返品済みの注文を正しく除外します。
ステップ 4 — Chat で尋ねて評価する
別のターミナルで、まだ動いていなければダッシュボードを起動します:
scripts/arena.sh servehttp://localhost:5174 を開き、Chat タブと $WINNER_CONFIG_ID を選んで、こう尋ねます:
How many active customers do we have?生成された SQL と結果を読んでください。policy-v1 の、仕込まれた 90 日サインアップ定義に
従っているはずです。この回答に 👎 をクリックし、UI に feedback sent と出るまで待ちます。
この Chat のルート trace が、あなたが採点して Module 04 に引き渡す唯一の正典となるインシデントです。
ステップ 5 — Chat の trace を見つけて検証する
Langfuse で Tracing を開き、user-thumbs = false でフィルタします。質問が
How many active customers do we have? で、構成が $WINNER_CONFIG_ID に一致し、メタデータが
policyversion=policy-v1 を示している、最も新しいルートの chat_turn を開きます。その trace ID と
trace URL をコピーし、ローカルに ID を設定します:
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=falseuser-thumbs が数値やテキストの score ではなく、Boolean の false であることを確認してください。
serving のソースはこのメタデータフィールドを policy_version と呼びますが、OpenTelemetry の
アダプターがそれをサニタイズして、Langfuse に送られるキーは policyversion になります。
ステップ 6 — 評価を付けない curl で再現する
素の API 呼び出しは、必須のコマンドレベルの再現と診断です。別の trace を作りますが、それは フィードバックのインシデントでは ありません。評価を付けてはいけません:
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)')レスポンスは outcome: "ok"、空でない trace_id、そして policy_version: "policy-v1" を
持たなければなりません。
非同期の evaluator を待ち、その運用 score だけを検証します:
.venv/bin/python -m scripts.verify_online_scores "$CURL_TRACE_ID" \
sql-execution-success=trueCURL_TRACE_ID に対して /feedback を呼ばないでください。またワークシートにも載せないでください。
これは再現可能な API 診断にすぎず、引き渡す trace は CHAT_TRACE_ID のままです。
ステップ 7 — 現在のポリシーと比較する
エージェントと同じ読み取り専用の ClickHouse クライアント経由で現在のポリシーの SQL を実行し、
その単一の結果を 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])
PYいま実行したのはまさにこのクエリです:
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')カウントが違うことが、ユーザーの目に見える失敗です。SQL は動きました。ただ、答えたのが 間違ったビジネス定義だったのです。そのカウントを、正典となる Chat trace のエビデンスの そばに記録してください。
調査ワークシート
この引き渡し内容を Module 04 のために保管します:
| エビデンス | あなたの値 |
|---|---|
勝者の config_id | |
| 正典となる Chat trace の ID | |
| 正典となる Chat trace の URL | |
古い policy-v1 のカウント | |
現在の policy-v2 のカウント | |
sql-execution-success | true |
user-thumbs | false |
完了したかどうかの確認
- プリフライトが異なる古い/現在のカウントを示し、仕込まれた 3 つの質問すべてが
policy-v1に 分類された。 - サービスが
AGENT_ARENA_POLICY_VERSION=policy-v1で動き、/askがoutcome: "ok"を返した。 - 正典となる Chat trace が Langfuse 上で
sql-execution-success=trueとuser-thumbs=falseを 持っている。 - 必須の curl 診断が
outcome: "ok"を返し、別個のCURL_TRACE_IDを生み、評価も引き渡しも されなかった。 - 認証情報を共有せずに、正典となる Chat trace の ID/URL と両方のカウントを記録した。
- SQL の実行が成功したことが意味的な正しさを証明しなかった理由を説明できる。
このシグナルを人がレビューした診断へと変えるために、 Module 04 — 人が調査する に進んでください。