05 闭合改进循环
将一次经过审查的生产失败案例转化为黄金数据、一个经过校准的业务策略评估器,并保护未来的流量。
起点
模块 04 结束时,已针对权威的模块 03
chat_turn 完成了一次人工标注。请保留其修正后的 SQL 和溯源工作表:
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>原始生产 trace 的 sql-execution-success=true、user-thumbs=false。这个点踩
找到了一条值得审查的 trace;已完成的标注则给出了诊断结果和修正后的标准答案。
持续评估与改进循环
本模块闭合了这个循环中的一轮:
- 用户反馈暴露了当前在线评估器的一个盲点;
- 人工进行调查并批准一项修正;
- 该经过审查的事件扩充了黄金数据集;
- 基线版本和候选版本在同一个扩充后的数据集上运行;
- 一个通用评估器在启用为在线评估之前先经过离线校准;并且
- 未来的流量会持续收集评估器分数和用户反馈。
最后一步很关键:部署一个更好的评估器并不意味着用户反馈就此结束。评估器只能衡量 其策略目录和提示词中已体现的维度。未来的一次 👎 仍可能揭示另一个缺失的策略、 一个含糊的请求,或一种新的失败模式,并重新开启同一个循环。
目标
通过五个证据关卡:提升(promote)、基线(baseline)、候选(candidate)、校准 (calibrate),然后启用并重放(enable and replay)。两个实验都使用模块 02 的 获胜配置,这样策略版本就是唯一有意的处理变量。
以下所有命令均应在 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,包含以下三条记录。在运行提升操作之前,请将
所有出现的两个占位值替换掉。如果 Langfuse 没有暴露标注任务 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 是来自用户反馈 trace 的原始问题。prod-active-002 和
prod-active-003 是审查者根据同一个已调查事件撰写的改写版本。为了便于审计,它们
使用相同的来源 trace 和相同的已完成标注;它们并不是另外两条独立的生产反馈
trace。这三条输入都特意调用了受治理的活跃客户指标,这样基线版本就无法通过测试
不相关的计数而看起来一切正常。
提升这批经审查的记录:
source .env
.venv/bin/python -m scripts.promote_to_golden reviewed.json预期会看到三行 prepared prod-active-*,随后是:
promoted 3 question(s) into the 'arena-golden' dataset打开 Langfuse → Datasets → arena-golden,检查每个新条目的元数据。确认
source=production-feedback、相同的真实 source_trace_id、
failure_category=stale-business-policy 以及 source_policy_version=policy-v1。
仓库的源语料共有 20 个 YAML 问题。q019 与 q020 是 few-shot 保留样例,
所以干净项目中的 arena-golden 从 18 个 Experiment 条目开始;提升这三条记录后,
应当有 21 个条目。如果你复用了已有项目,条目数可能更高:保留并核对溯源元数据,
不要为了凑数而删除旧条目。基线与候选实验必须使用完全相同的条目 ID。
reviewed.json 是被忽略的可变操作者状态,是工作坊的主要路径。已纳入版本控制的
--synthetic-fixture 只是一个可复现的排练后备方案。它不代表人工标注,也不能
满足本模块的证据关卡要求。这两种模式是互斥的;在完成真实提升之后,绝不要再运行
合成后备方案。
提升操作会在查询 ClickHouse 或写入数据集条目之前,先验证整批数据的完整性、只读 SQL,以及必需的溯源信息。随后它会读取已有的数据集元数据,并拒绝在生产溯源不同的 情况下写入一个发生 ID 冲突的条目。如果这个经过身份验证的预检无法安全地确认 溯源,它会停止并不进行写入。只有当发生冲突的生产溯源完全一致时,重复执行一次 真实的提升操作才是安全的。
证据关卡 2 —— 运行 policy-v1 基线
首先为实验配置目录驱动的评判器。这会创建其在线观测规则,并使其处于禁用状态:
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--business-policy-experiments预期看到 experiment rule enabled=True; online rule enabled=False。在继续之前,
确认在线规则在 Langfuse 中仍处于禁用状态。
为这次工作坊尝试指定一个唯一的后缀,然后使用陈旧策略,在选定的模型和提示词上, 针对扩充后的数据集运行:
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该测试工具会在发布 ID 后追加 --policy-v1。它会等待每条 trace 上出现三个确切
的 Experiment 分数名称:correctness、agent-arena-llm-judge 和
business-policy-adherence。如果运行超时或任何分数缺失,不要继续。
在 Langfuse Experiments 中,记录基线版本的数据集条目数量和汇总正确率。干净项目 提升后应有 21 个条目;复用项目可能更多。不同的模型服务商响应可能有所不同,因此 发布关卡依据的是下面的配对比较,而不是要求一个固定的正确数。
证据关卡 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的情况;并且 - 候选版本的汇总正确率不低于基线版本的正确率。
如果任何一个既有条目发生退化,就停止。一个通过破坏已知行为来修复该事件的候选 版本,并没有通过发布关卡。
证据关卡 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 重复相同的活跃客户检查。除了分类结果
之外,也要阅读评判器的推理过程:它应当指出适用的目录策略,并据此评估生成的 SQL。
一个普通计数问题必须保持 NOT_APPLICABLE,这说明该评判器不会把每一个计数问题都
强行归入活跃客户策略。
如果任何分类结果有误、任何必需的分数缺失、结构化输出格式不正确,或者正确率比较 出现退化,就保持在线规则处于禁用状态。离线的 Experiment 校准要先行,因为它能让 你在评估器影响生产监控之前,针对已知样例检查误通过和误失败的情况。
证据关卡 5 —— 启用并在 policy-v2 上重放
只有在所有校准关卡都通过之后,才启用观测规则:
source .env
.venv/bin/python -m scripts.provision_online_evaluators \
--enable-business-policy-online预期会看到确切的规则名称 agent-arena-business-policy-online,且
enabled=True。当该命令找不到一个数据集范围内、名为
business-policy-adherence 的 Experiment 分数时,会以失败关闭(fail closed);
你上面所做的人工校准检查依然是质量关卡。
停止 policy-v1 服务器。在第一个终端中,启动候选版本并让它保持运行:
source .env
AGENT_ARENA_POLICY_VERSION=policy-v2 \
.venv/bin/uvicorn serving.api:app --port 8100在第二个终端中,定义一个辅助函数,它接受一个问题,并在确认收到成功的
policy-v2 响应之后才返回其 trace 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"])'
}将活跃客户问题和营收问题各问一次。在线观测分数使用的是规则名称
agent-arena-business-policy-online,而不是 Experiment 分数名称:
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转化率问题存在一个经过验证的随机边界情况。将其问一次并保留该 trace。如果服务
返回的 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不要重试直到通过为止。如果两次尝试都失败,保留两条 trace,让结果保持可见,并将 这项新证据纳入人工标注、黄金数据改进,以及同一套配对校准循环。
只有在转化率检查通过之后,才将普通计数问题问一次:
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在线评估器是异步运行的。校验器默认最多轮询 180 秒;分数仍处于待定状态并不等同于 一个失败的分数。
让循环持续运转
上线之后继续保留 👍/👎。留意诸如
agent-arena-business-policy-online=PASS 与 user-thumbs=false 并存这样的
分歧情况:它们是下一轮标注队列中的高价值候选项。人工审查将决定要修正的是策略、
提示词、数据,还是评估器本身。被批准的案例会回流到 arena-golden,然后下一个
候选版本重复同样的基线 → 候选 → 校准 → 受控启用流程。
工作坊的规则对所有符合条件的 trace 采样 100%,以便每个学员都能看到证据。这是一 个教学设置,而不是生产环境的默认做法。真实的采样比例应当反映流量、评估器成本、 延迟、风险,以及你所需要覆盖的事件类型。
完成证据
- 生产根 trace 仍显示
sql-execution-success=true以及布尔值user-thumbs=false。 production-investigation-<session>人工标注任务已完成,带有一个经过验证的 修正结果和approved-for-golden=true。- 全部三个黄金条目都存在,并带有真实的
production-feedback溯源信息;你能够 区分出那一个用户提出的问题和另外两个审查者撰写的改写版本。 - 基线版本和候选版本使用了同一个扩充后的数据集、同一个模型和同一套提示词;候选 版本修复了全部三个已提升的条目,且没有引入任何既有正确率的退化。
- Experiment 校准产生了
FAIL、PASS和NOT_APPLICABLE,分数名称确切为business-policy-adherence。 - 观测规则在校准期间保持禁用,只有在所有关卡通过之后才被启用。
- 活跃客户、营收和转化率的 trace 都得到了
agent-arena-business-policy-online=PASS;普通商品计数得到了agent-arena-business-policy-online=NOT_APPLICABLE。 - 你能够解释为什么在部署之后,在线评估和用户反馈仍会持续地相互改进。