04 人工调查
将负面用户信号转化为经人工审查的诊断与修正,而不是把反馈误当作真值。
起点
模块 03 针对 How many active customers do we have? 生成了一条权威的 Chat 追踪(trace),其中包含一处有意设置的分歧:
| 证据 | 期望值 |
|---|---|
| root observation | chat_turn |
sql-execution-success | true |
user-thumbs | false |
metadata policyversion | policy-v1 |
请从工作表中保留该 trace 的 ID/URL,以及两个参考计数值。不要使用模块 03 中未评分的 curl 诊断结果。
为什么需要人工调查
一次点踩(thumbs-down)能告诉团队该往哪里看,但不能告诉团队哪里出了问题。用户可能表达的意思有别的含义,请求本身可能有歧义,生成的 SQL 可能无效,或者两个业务定义可能存在差异。 如果把每一条负面信号都直接提升为黄金数据集(golden dataset),就等于把猜测当成了真值。
在本模块中,审查者首先记录可观察到的事实,然后测试各种可能的解释,最后才记录诊断结论和修正方案。这个经过审查的决定——而不是那次点踩——才是交给模块 05 的真值。
目标
针对模块 03 的 root chat_turn,完成一个 production-investigation-<session> 标注任务。完成后的任务必须包含一条观察记录、一个失败类别、准确的修正 SQL、对黄金数据集的批准,以及生产环境的溯源信息。
步骤 1 — 找到确切的反馈事件
在 Langfuse 中打开 Tracing,按布尔型分数 user-thumbs = false 进行过滤。打开同时满足以下所有条件的 trace:
- 名称/root observation 为
chat_turn; - 问题为
How many active customers do we have?; - 与模块 03 中记录的获胜
config_id和 trace ID 一致; - metadata
policyversion=policy-v1;以及 - 分数
sql-execution-success=true且user-thumbs=false。
服务端发出的是 policy_version,但 OpenTelemetry 适配器会去掉下划线,因此 Langfuse 中的 metadata 键名是 policyversion。
要标注的是 root chat_turn,而不是其子节点 llm_call 这条 generation。root 中包含端到端的问题以及结构化输出——SQL、列、行、错误和结果——这些正是调查所需要的。子节点只包含模型的对话记录和生成的 SQL,并不是权威的反馈事件。
步骤 2 — 创建三个审查分数配置(score config)
这一环节被有意设计为仅通过界面完成的人工审查步骤。工作坊仓库中不包含任何用于创建或完成此标注任务的命令。
在创建队列之前,先打开 Settings → Scores → Create,创建以下配置:
| 名称 | 数据类型 | 允许的值 / 用途 |
|---|---|---|
observed-issue | TEXT | 只描述在 trace 和对比中可见的证据。 |
failure-category | CATEGORICAL | stale-business-policy、incorrect-sql、ambiguous-request、not-actionable |
approved-for-golden | BOOLEAN | 只有在修正内容经过验证之后才批准。 |
请严格按照上面显示的名称和连字符写法使用这些配置。先创建配置是最安全的做法,因为一个队列所附加的分数配置 ID 集合在队列创建时就已固定。如果某个配置在那次附加时被遗漏,就需要用一个新的后缀创建新队列。分数配置本身是可变的:受支持的名称、schema 或类别修改必须作为一次经审计的分数配置更新来进行,且这类修改不会改写已经存在的分数记录。
步骤 3 — 创建队列并将目标指向逻辑 root
打开 Annotations → Queues → Create,然后:
- 将队列命名为
production-investigation-<session>,把<session>替换为一个简短且唯一的工作坊标识符。 - 附加步骤 2 中的全部三个分数配置。
- 创建该队列。
- 返回模块 03 的 trace,选中其 root
chat_turnobservation,打开 Annotate 下拉菜单,选择这个队列。 - 打开新生成的任务,确认其目标是
chat_turn,而不是llm_call。
队列创建后无法更改其附加的分数配置 ID 集合。只有当该附加集合确实有误时才需要重新创建队列;对于已附加配置的受支持修改,应使用经审计的分数配置更新。
步骤 4 — 记录你能观察到的事实
模块 03 有意公开了预先设置的场景。在本次调查中,请先搁置这段先前的工作坊知识,练习审查者面对未知事件时会采用的工作方式:在给出结论之前,先检查问题、生成的 SQL、返回的计数、模型/提示词,以及两个分数。在 observed-issue 中输入一条只陈述证据的说明,例如:
The answer returned a count and its SQL executed. The observed count differs from the
second reference count recorded in Module 03. The generated query uses a 90-day
customer signup window, and the trace metadata reports policy-v1.这段措辞尚未断言是模型、SQL 引擎、用户还是策略出了问题。这种区分可以防止预先设置的诊断结论在证据被核实之前就悄悄混入审查过程。
步骤 5 — 检查完整的 trace 证据
仍在 root chat_turn 上,核实以下各项:
- metadata
policyversion=policy-v1; - 生成的 SQL 使用了
v_customers以及基于signup_date的 90 天窗口; - 结构化结果中包含模块 03 记录的观测计数;
- 运营分数为布尔值
sql-execution-success=true;以及 - 用户信号为布尔值
user-thumbs=false。
生成的 SQL 与发布版本所提供的 policy-v1 指令是一致的。执行成功这一分数在其有意设定的狭窄范围内也是正确的。此时,这两项事实都无法确定该已部署的策略是否与当前受治理的定义一致。
步骤 6 — 并排测试两个策略定义
在 ClickHouse_Demos/workshops/agent_arena 目录下,在同一环境中执行两个只读定义:
source .env
.venv/bin/python - <<'PY'
from arena.config import load_config
from agents.chclient import ROClickHouseClient
queries = {
"policy-v1": """SELECT count() FROM v_customers
WHERE signup_date >= today() - INTERVAL 90 DAY""",
"policy-v2": """SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')""",
}
client = ROClickHouseClient(load_config().clickhouse)
for version, sql in queries.items():
result = client.query(sql)
print(f"{version}: {result.rows[0][0]}")
PY这两个计数值必须与工作表中记录的数值一致,并且相互之间不同。至此,你已经有足够的证据诊断出问题在于一个已过时的部署业务定义:该 trace 声明使用的是 policy-v1,其 SQL 遵循的正是这一策略,而经过验证的当前查询实现的是 policy-v2。
步骤 7 — 标注、修正、批准并完成
返回标注任务,记录以下内容:
| 字段 | 值 |
|---|---|
observed-issue | 保留以证据为先的说明;补充经过验证的策略对比结果。 |
failure-category | stale-business-policy |
| Corrected Output | 下方的准确 SQL |
approved-for-golden | true |
将 Corrected Output 切换为纯文本模式,然后输入以下这段准确的原始 SQL:
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')Langfuse 只会记录这条修正内容,并不会执行这段 SQL。在批准之前,步骤 6 中的只读 ClickHouse 客户端必须已经成功执行过这段完全一致的文本。如果你修改了这条修正内容,请用同一个客户端重新执行该文本。然后选择 Complete(或 Complete + next)。任何格式错误、无法执行或未经验证的修正都不得作为黄金真值被批准。
步骤 8 — 记录模块 05 所需的溯源信息
将以下值复制到你的工作表中。请让相关 ID 仅在工作坊项目内部使用,不要公开。
| 溯源字段 | 需要记录的值 |
|---|---|
source | production-feedback |
source_trace_id | 模块 03 中那条权威 Chat trace 的 ID |
failure_category | stale-business-policy |
source_policy_version | policy-v1 |
annotation_id | 已完成标注任务的 ID(如有) |
| reviewed correction | 上方准确的当前策略 SQL |
对于一条源自生产环境的黄金记录,source_trace_id、failure_category 和 source_policy_version 是必需的。annotation_id 在运行时是可选的,但只要界面中能看到它,就应该记录下来,以保持该决定可被审计。
如何验证你已完成
- 你调查了模块 03 中唯一一条
user-thumbs=false的 Chat trace。 - 标注的目标是 root
chat_turn,而不是子节点llm_call。 - 队列名为
production-investigation-<session>,并包含全部三个类型正确的分数配置。 observed-issue中记录的是在诊断之前观察到的行为。- 你并排运行了旧策略和当前策略的 SQL,并确认两者计数不同。
- 已完成的任务中记录了
stale-business-policy、准确的修正 SQL,以及approved-for-golden=true。 - 你的工作表为模块 05 保留了生产环境的溯源信息,且没有公开真实的 trace ID 或项目 URL。
- 你能够解释为什么一次点踩会提升人工审查的优先级,但其本身并不能成为真值。
继续阅读 模块 05 — 闭环,以推广经审查的修正结果、比较策略版本,并在线上防止同类失败再次发生。