05 루프 완성하기
검토된 프로덕션 실패 하나를 골든 데이터, 보정된 비즈니스 정책 평가자, 그리고 향후 트래픽에 대한 보호 장치로 전환합니다.
시작점
모듈 04는 권위 있는 모듈 03 chat_turn에 대한
사람의 검수 주석(annotation)을 완료한 상태로 끝났습니다. 수정된 SQL과 출처(provenance)
워크시트를 계속 사용할 수 있게 준비해 두세요.
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>원본 프로덕션 트레이스는 sql-execution-success=true와 user-thumbs=false를 갖습니다.
싫어요(thumbs-down)가 검토할 가치가 있는 트레이스를 찾아냈고, 완료된 주석이 진단과
수정된 정답(ground truth)을 제공했습니다.
지속적인 평가 및 개선 루프
이 모듈은 루프의 한 턴을 완성합니다.
- 사용자 피드백이 현재 온라인 평가자의 사각지대를 드러낸다.
- 사람이 조사하고 수정 사항을 승인한다.
- 그 검토된 사건이 골든 데이터셋을 확장한다.
- 베이스라인과 후보 릴리스가 동일하게 확장된 데이터셋에 대해 실행된다.
- 일반 평가자가 온라인으로 활성화되기 전에 오프라인에서 보정(calibrate)된다.
- 이후 트래픽은 평가자 점수와 사용자 피드백을 계속 수집한다.
마지막 단계가 중요합니다. 더 나은 평가자를 배포한다고 해서 사용자 피드백이 끝나는 것은 아닙니다. 평가자는 자신의 정책 카탈로그와 프롬프트에 표현된 차원만 측정할 수 있습니다. 앞으로의 👎는 또 다른 누락된 정책, 모호한 요청, 또는 실패 모드를 드러낼 수 있고, 동일한 루프를 다시 시작하게 만듭니다.
목표
다섯 개의 증거 게이트를 통과하세요: 승격(promote), 베이스라인, 후보, 보정, 그리고 마지막으로 활성화 및 재실행(replay). 두 실험 모두에 모듈 02의 우승 조합을 사용해서, 정책 버전이 유일한 의도된 처리(treatment) 변경 사항이 되도록 하세요.
아래 모든 명령은 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}"기본값은 검증된 워크숍 우승 조합입니다. 여러분의 방에서 다른 config_id를 선택했다면,
세 값을 모두 해당 모델/프롬프트로 설정하고 모든 게이트를 거치는 동안 변경하지 마세요.
증거 게이트 1 — 검토된 사건 승격하기
랩 루트에 다음 세 개의 레코드로 reviewed.json을 생성하세요. 승격을 실행하기 전에 모든
곳에서 두 개의 자리표시자(placeholder) 값을 교체하세요. Langfuse가 주석 작업(annotation
task) ID를 제공하지 않는다면, 세 레코드 모두에서 자리표시자를 남기는 대신 annotation_id를
제거하세요. 이 필드는 선택 사항이지만, 다른 프로덕션 출처 필드들은 필수입니다.
[
{
"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>"
}
]prod-active-001만이 사용자 피드백 트레이스에서 나온 정확한 질문입니다.
prod-active-002와 prod-active-003은 같은 조사된 사건에서 검토자가 작성한 바꿔쓰기
(paraphrase)입니다. 이들은 감사 추적(auditability)을 위해 동일한 소스 트레이스와 완료된
주석을 사용합니다. 즉 두 개의 추가 프로덕션 피드백 트레이스가 아닙니다. 이 세 입력은 모두
의도적으로 거버넌스 대상인 활성 고객(active-customer) 지표를 호출하므로, 베이스라인이
관련 없는 카운트를 테스트해서 정상처럼 보일 수 없습니다.
검토된 배치를 승격하세요.
source .env
.venv/bin/python -m scripts.promote_to_golden reviewed.json세 개의 prepared prod-active-* 줄이 나온 다음 아래가 표시되어야 합니다.
promoted 3 question(s) into the 'arena-golden' datasetLangfuse → Datasets → arena-golden을 열고 새로 추가된 각 항목의 메타데이터를
확인하세요. source=production-feedback, 동일한 실제 source_trace_id,
failure_category=stale-business-policy, source_policy_version=policy-v1을
확인하세요.
저장소의 원본 코퍼스에는 YAML 질문이 20개 있습니다. q019와 q020은 few-shot
프롬프트용 holdout이므로, 깨끗한 project의 arena-golden은 Experiment 항목 18개로
시작합니다. 이 세 항목을 승격하면 깨끗한 데이터셋은 21개가 됩니다. 재사용 project에
다른 승인 항목이 있다면 개수를 맞추려고 삭제하지 말고 provenance를 기록하며, 베이스라인과
후보가 동일한 항목 ID를 사용하도록 요구하세요.
reviewed.json은 Git에 커밋되지 않는(ignored) 가변적인 운영자 상태이며 이 워크숍의 주된
경로입니다. Git에 포함된(tracked) --synthetic-fixture는 재현 가능한 리허설용 대체 수단일
뿐입니다. 이는 사람의 주석을 대표하지 않으며 이 모듈의 증거 게이트를 만족시킬 수 없습니다.
두 모드는 상호 배타적입니다. 실제 승격을 실행한 뒤에는 절대로 synthetic 대체 수단을
실행하지 마세요.
승격은 ClickHouse를 조회하거나 데이터셋 항목을 작성하기 전에 전체 배치, 읽기 전용 (read-only) SQL, 필수 출처 정보를 검증합니다. 그런 다음 기존 데이터셋 메타데이터를 읽고, 프로덕션 출처가 다른 상태로 ID가 충돌하면 거부합니다. 인증된 사전 점검(preflight)이 출처를 안전하게 확립할 수 없는 경우, 쓰기 작업을 수행하지 않고 중단합니다. 동일한 승격을 반복하는 것은 충돌하는 프로덕션 출처가 완전히 동일할 때만 안전합니다.
증거 게이트 2 — policy-v1 베이스라인 실행하기
먼저 실험을 위한 카탈로그 기반 심사자(judge)를 프로비저닝하세요. 이는 온라인 관찰 규칙을 비활성화 상태로 생성합니다.
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--business-policy-experimentsexperiment rule enabled=True; online rule enabled=False가 표시되어야 합니다. 계속하기
전에 Langfuse에서 온라인 규칙이 여전히 비활성화되어 있는지 확인하세요.
이번 워크숍 시도에 고유한 접미사(suffix)를 부여한 다음, 오래된 정책으로 확장된 데이터셋에 대해 선택된 모델과 프롬프트를 실행하세요.
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-adherence하니스(harness)는 릴리스 ID에 --policy-v1을 덧붙입니다. 이는 모든 트레이스에서 세 개의
정확한 Experiment 점수 이름을 기다립니다: correctness, agent-arena-llm-judge,
business-policy-adherence. 실행이 타임아웃되거나 점수가 하나라도 누락되면 진행하지
마세요.
Langfuse Experiments에서 베이스라인의 데이터셋 항목 수와 집계된 correctness를 기록하세요. 승격 후 깨끗한 project라면 21개 항목을 기대합니다. 재사용 project에는 승인된 항목이 더 있을 수 있고 제공자 응답도 달라질 수 있으므로, 릴리스 게이트는 고정 집계 점수가 아니라 아래의 짝지어진(paired) 비교입니다.
증거 게이트 3 — policy-v2 후보 실행하기
데이터셋, 모델, 프롬프트, 실행 접미사를 변경하지 않은 채로 후보를 실행하세요.
.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-adherence후보의 실제 집계값을 기록한 뒤 Langfuse에서 두 실행을 비교하고 다음을 요구하세요.
- 동일한 데이터셋 항목 ID와 항목 수;
- 세
prod-active-*항목 모두가policy-v1에서correctness=0이었다가policy-v2에서correctness=1로 이동; prod-active-*보다 앞서 존재하던 모든 항목은 항목 단위로 비교되며,correctness=1에서correctness=0으로의 회귀(regression)가 없음; 그리고- 후보의 집계 correctness가 베이스라인의 correctness보다 낮지 않음.
기존 항목이 회귀하면 중단하세요. 기존 동작을 깨뜨리면서 사건을 해결하는 후보는 릴리스 게이트를 통과한 것이 아닙니다.
증거 게이트 4 — 하나의 일반 정책 심사자 보정하기
business-policy-adherence는 "활성 고객 평가자"가 아닙니다. 이것은 질문, 생성된 SQL,
그리고 완전한 policy-v2 지표 카탈로그를 받습니다. 어떤 거버넌스 대상 지표가 적용되는지
결정하고 PASS, FAIL, 또는 NOT_APPLICABLE을 반환합니다. 같은 설계로 질문 문구별로
평가자를 하나씩 만들지 않고도 활성 고객, 매출, 전환율, 매출 총이익을 확인할 수 있습니다.
프로덕션 관찰용으로 활성화하기 전에, 다음 Experiment 항목들을 검사하세요.
| 보정 프로브 | 실행/항목 | 요구되는 business-policy-adherence |
|---|---|---|
| 오래된 활성 고객 SQL | 베이스라인 prod-active-001 | FAIL |
| 수정된 활성 고객 SQL | 후보 prod-active-001 | PASS |
| 매출 정책 | 후보 q005 | PASS |
| 조회-구매 전환 정책 | 후보 q018 | PASS |
| 단순 고객 수 | 후보 q001 | NOT_APPLICABLE |
prod-active-002와 prod-active-003에 대해서도 활성 고객 확인을 반복하세요. 카테고리뿐만
아니라 심사자의 논리(reasoning)도 읽어보세요. 적용 가능한 카탈로그 정책의 이름을 명시하고
생성된 SQL을 그 정책에 대해 평가해야 합니다. 단순 카운트는 반드시 NOT_APPLICABLE로 남아야
하며, 이는 심사자가 모든 카운트 질문을 활성 고객 정책으로 강제하지 않음을 보여줍니다.
카테고리가 잘못되었거나, 필수 점수가 누락되었거나, 구조화된 출력이 형식에 맞지 않거나, correctness 비교가 회귀했다면 온라인 규칙을 비활성화 상태로 유지하세요. 오프라인 실험 보정이 먼저인 이유는, 평가자가 프로덕션 모니터링에 영향을 미치기 전에 알려진 예시들에 대한 거짓 통과(false pass)와 거짓 실패(false failure)를 검사할 수 있게 해주기 때문입니다.
증거 게이트 5 — policy-v2에서 활성화 및 재실행(replay)하기
모든 보정 게이트를 통과한 후에만, 관찰 규칙을 활성화하세요.
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--enable-business-policy-online정확한 규칙 이름 agent-arena-business-policy-online이 enabled=True와 함께
표시되어야 합니다. 이 명령은 business-policy-adherence라는 이름의 데이터셋 범위
(dataset-scoped) Experiment 점수를 찾지 못하면 페일 클로즈드(fail closed)로 동작합니다.
위에서 수행한 수동 보정 확인이 여전히 품질 게이트로 남습니다.
policy-v1 서버를 중지하세요. 첫 번째 터미널에서 후보를 시작하고 계속 실행 상태로
두세요.
source .env
AGENT_ARENA_POLICY_VERSION=policy-v2 \
.venv/bin/uvicorn serving.api:app --port 8100두 번째 터미널에서, 질문을 받아 policy-v2 응답이 성공했음을 확인한 후에만 트레이스 ID를
반환하는 헬퍼를 정의하세요.
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"])'
}활성 고객 질문과 매출 질문을 한 번씩 물어보세요. 온라인 관찰 점수는 Experiment 점수
이름이 아니라 규칙 이름 agent-arena-business-policy-online을 사용합니다.
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=PASS전환율 질문은 검증된 확률적(stochastic) 경계선을 갖습니다. 이를 한 번 물어보고 그 트레이스를
보존하세요. 서빙 결과(outcome)가 ok가 아니거나, 정확히 요구되는 점수가 누락되었거나
실패했다면, 같은 질문과 설정으로 최대 한 번만 재시도하세요. 아래 블록은 두 번의 시도
모두를 표시된 상태로 유지합니다.
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
fi초록불(green)이 나올 때까지 재시도하지 마세요. 두 번의 시도가 모두 실패하면, 두 트레이스를 모두 보관하고, 결과를 표시된 상태로 유지하며, 새로운 증거를 사람의 주석, 골든 데이터 개선, 그리고 동일한 짝지어진 보정 루프로 보내세요.
전환율이 통과한 후에만, 단순 카운트 질문을 한 번 물어보세요.
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_APPLICABLE온라인 평가자는 비동기(asynchronously)로 실행됩니다. 검증기는 기본적으로 최대 180초까지 폴링(poll)합니다. 아직 대기 중(pending)인 점수는 실패한 점수와 같지 않습니다.
루프를 계속 돌리기
배포 이후에도 👍/👎를 계속 활성화해 두세요. agent-arena-business-policy-online=PASS가
user-thumbs=false와 나란히 나타나는 것과 같은 불일치(disagreement)를 모니터링하세요.
이는 다음 주석 대기열(annotation queue)에 넣을 가치가 높은 후보입니다. 사람의 검토가
정책, 프롬프트, 데이터, 또는 평가자 중 무엇을 수정할지 결정합니다. 승인된 사례는
arena-golden으로 돌아가고, 그 다음 후보는 동일한 베이스라인 → 후보 → 보정 → 안전장치를
갖춘 활성화 순서를 반복합니다.
이 워크숍의 규칙은 모든 학습자가 증거를 볼 수 있도록 적격 트레이스의 100%를 샘플링합니다. 이는 교육용 설정이며 프로덕션 기본값이 아닙니다. 실제 샘플링은 트래픽, 평가자 비용, 지연 시간(latency), 위험, 그리고 필요한 사건 커버리지를 반영해야 합니다.
완료 증거
- 프로덕션 루트 트레이스는 여전히
sql-execution-success=true와 불(Boolean)user-thumbs=false를 보여줍니다. production-investigation-<session>사람 주석 작업(human-annotation task)이 검증된 수정 사항과approved-for-golden=true로 완료되었습니다.- 세 개의 골든 항목이 모두 진짜
production-feedback출처와 함께 존재합니다. 여러분은 하나의 사용자 질문과 두 개의 검토자 작성 바꿔쓰기(paraphrase)를 구분할 수 있습니다. - 베이스라인과 후보는 동일하게 확장된 데이터셋, 모델, 프롬프트를 사용했습니다. 후보는 승격된 세 항목 모두를 수정했고 기존 correctness 회귀를 초래하지 않았습니다.
- Experiment 보정은 정확한 점수 이름
business-policy-adherence로FAIL,PASS,NOT_APPLICABLE을 생성했습니다. - 관찰 규칙은 보정 중에 비활성화되어 있다가 게이트가 통과한 후에만 활성화되었습니다.
- 활성 고객, 매출, 전환율 트레이스는
agent-arena-business-policy-online=PASS를 가지며, 단순 상품 수 트레이스는agent-arena-business-policy-online=NOT_APPLICABLE을 가집니다. - 배포 이후에도 온라인 평가와 사용자 피드백이 서로를 계속 개선시키는 이유를 설명할 수 있습니다.