在 AI Agent 的评测里,用户反馈往往是最容易被低估的一环。很多人会把注意力放在模型本身、提示词、工具调用链上,却忘了:一个 Agent 到底好不好,最终还是要看它在真实交互里有没有解决问题。用户说"这个回答不错",或者用户默默点了个差评,背后都藏着可以拿来改进系统的信息。
Opik 提供了一套比较完整的反馈记录和评分能力。它不只是让你在后台写一条"好"或"坏",而是把用户反馈、人工标注、在线评估、手动评估这些环节串起来。你可以把定性反馈和定量评分记录到具体的 trace、span,甚至整段对话线程上。时间一长,这些数据会变成非常具体的改进依据。
这篇文章会从第三方视角,把 Opik 里记录用户反馈的几种方式讲清楚:SDK 怎么用、UI 怎么标注、在线评估和手动评估分别适合什么场景,以及接下来还能往哪些方向走。内容尽量贴近实际使用,不堆概念,方便你直接对照自己的项目。
为什么用户反馈值得认真记录
记录用户反馈、给 trace 打分,是评估和改进 Agent 的关键动作。这里的"反馈"不一定非得是用户主动填的问卷,也可以是产品团队打的标签、专家给出的评分、LLM as a Judge 自动跑出来的结果,甚至是客服在工单里写的一句"这个回答不准确"。只要它能反映某次交互的质量,就值得被系统化地记录下来。
当你把定性或定量反馈记录到具体交互,或者整段对话流上,可以得到几个很实际的好处:
- 跟踪性能变化
今天的好评率、平均分、差评原因,和上周相比是变好了还是变差了?没有持续记录,就只能靠感觉。有了 trace 级别的反馈,性能趋势会变得可观察。 - 找到改进方向
是检索不准,还是工具调用失败,还是回答太啰嗦?如果反馈只停留在"用户不满意",很难定位。把评分和具体 trace、span 绑定后,问题会更容易暴露。 - 比较不同模型版本或提示词
同一个任务,换了模型、改了 prompt、调整了工具描述,效果到底有没有提升?用同一套反馈指标去对比,比单纯看几个案例靠谱得多。 - 为微调或再训练收集数据
高质量反馈本身就是数据。哪些输入输出被专家打了高分,哪些被标记为错误,都可以成为后续优化、微调、构建评测集的素材。 - 给利益相关者提供具体指标
产品、运营、管理层不一定关心技术细节,但他们关心"系统到底有没有效果"。好评率、平均分、差评分布、人工复核结果,这些都能变成沟通材料。
说白了,用户反馈不是评测算法的附属品,而是 Agent 迭代闭环里非常核心的一环。Opik 要解决的就是:让这些反馈有地方放、能关联到具体调用、还能被后续流程复用。
用 SDK 记录用户反馈和评分
Opik 的 SDK 可以直接用来记录用户反馈,也可以给 trace 打分。它支持 TypeScript 和 Python,下面分别看一下。
TypeScript SDK
在 TypeScript 里,你可以先创建一个 Opik 客户端,然后创建 trace 和 span。等交互结束后,再把反馈分数写到已有的 trace 或 span 上。
typescript
import { Opik } from "opik";
const client = new Opik();
// Create a new trace with a span
const trace = client.trace({
name: "my_trace",
input: { input: "Hi!" },
output: { output: "Hello!" },
});
const span = trace.span({
name: "processing",
input: { step: 1 },
});
span.update({ output: { result: "processed" } });
span.end();
trace.end();
// Log feedback scores to existing traces
client.logTracesFeedbackScores([
{ id: trace.data.id, name: "overall_quality", value: 0.9, reason: "Good answer" },
{ id: trace.data.id, name: "coherence", value: 0.8 }
]);
// Log feedback scores to existing spans
client.logSpansFeedbackScores([
{ id: span.data.id, name: "accuracy", value: 0.95 }
]);
// Flush to ensure all data is sent
await client.flush();
这段代码里有两个关键点。第一,trace 和 span 是有 ID 的,反馈分数通过 ID 关联到具体对象上。第二,reason 是可选的,但很建议填。比如 overall_quality 给了 0.9,原因是"Good answer",后续回看时就知道这个分数不是随手打的。
最后 await client.flush() 也很重要。它确保所有数据都发送出去,避免程序退出太快导致反馈丢失。实际项目里,如果你在服务端记录反馈,记得在合适的位置 flush。
Python 函数装饰器
如果你用 Python,而且代码里已经用 @opik.track 装饰了函数,那么可以在函数内部直接更新当前 trace 的反馈分数。
python
import opik
from opik import opik_context
@opik.track
def my_function():
opik_context.update_current_trace(
feedback_scores=[
{
"name": "user_feedback",
"value": 1,
"reason": "Good answer" # Optional
}
]
)
return "Hello, world!"
这种方式很适合把反馈记录嵌进业务流程。比如某个函数负责生成回答,生成完之后根据用户点击、人工复核结果,或者下游规则,直接给当前 trace 写一个分数。代码不复杂,但能把反馈和调用链自然绑在一起。
Python SDK
如果你不想用装饰器,也可以直接用 Python SDK。它既能给已有 trace 记录反馈,也能在创建新 trace 时直接带上反馈分数。
python
import opik
client = opik.Opik()
# Log feedback scores to an existing trace
client.log_traces_feedback_scores(
scores=[
{"id": "trace_id", "name": "user_feedback", "value": 1, "project_name": "my-project"},
{"id": "trace_id", "name": "accuracy", "value": 1, "reason": "Good answer", "project_name": "my-project"} # Optional reason score
]
)
# Log feedback score to a new trace
client.trace(
name="my_trace",
input={"input": "Hi!"},
output={"output": "Hello!"},
feedback_scores=[
{"name": "user_feedback", "value": 1, "reason": "Good answer"}
]
)
这里可以看到,log_traces_feedback_scores 接收一个 scores 列表,每条包含 trace ID、指标名称、分数值,还可以带 reason 和 project_name。而 client.trace(...) 在创建 trace 时就能直接写入反馈分数。对于"先有结果,后补反馈"和"结果与反馈一起产生"这两种场景,Opik 都留了入口。
从工程角度看,这种设计比较灵活。你可以让前端把用户评分传给后端,后端再调用 SDK 写入;也可以在离线分析脚本里,把人工标注结果批量回填到已有 trace 上。
通过 UI 给 Trace 做标注
不是所有反馈都来自代码。很多团队里,产品经理、领域专家、标注人员更习惯在界面上直接看 trace,然后手动打分。Opik 的 UI 也支持这种方式。
要标注 trace,可以先进入 traces 页面,找到你想标注的那条 trace,然后点击 Annotate 按钮。点击后会打开一个侧边栏,你可以在里面添加标注。
你不仅能标注 trace,也能标注 span。关键是要在侧边栏里选对 span。因为一次 trace 可能包含多个步骤,比如检索、工具调用、生成回答,每个步骤的质量可能不一样。如果只看最终输出,可能会漏掉中间环节的问题。
界面示意如下:


当你给了一个反馈分数之后,还可以补充一个 reason,解释为什么给这个分。这一点在实际协作里很有用。比如同样是 0.6 分,有人是因为"事实错误",有人是因为"表达不够清楚"。有了原因,后续分析时就不会只看到一堆数字,而能理解分数背后的含义。
如果多个团队成员都在标注同一条 trace,UI 的 Feedback scores 区域会显示每个人的标注。平均分会显示在 trace 和 trace 级别上。对于需要多人复核的场景,这个设计能减少"谁说了算"的争议:每个人都可以独立打分,最后看分布和平均值。
如果你想要一个更专门的标注界面,可以使用 Annotation Queues 功能。它更适合组织一批 trace,让专家按队列进行审核和标注,而不是在 traces 页面里一条条找。
在线评估:让 LLM as a Judge 自动跑分
手动标注很准,但也很贵。尤其是生产环境里,每天可能产生成千上万条 trace,不可能每条都让人看。这时候就可以用 Opik 的在线评估功能。
在线评估的核心思路是:定义 LLM as a Judge 指标,让模型自动给全部或一部分生产 trace 打分。你可以设置采样率,也可以只让某些规则生效。这样,不需要手动标注每一条 trace,也能持续测量 Agent 的表现。

它适合什么场景?比如你想长期监控回答的事实一致性、格式合规性、语气是否合适、是否包含敏感信息。只要能把判断标准写成 LLM as a Judge 规则,就可以让它在生产流量里自动跑。跑出来的结果会作为反馈分数出现在 trace 上,后续可以和人工标注、用户反馈一起分析。
在线评估的好处是覆盖广、可持续。缺点是它依赖 Judge 的质量,而且不是所有问题都能靠自动规则发现。所以它通常和人工标注配合使用:自动评估负责大范围筛查,人工标注负责校准和深入分析。
手动评估:把控制权拿回来
在线评估会根据采样率和启用的规则自动给 trace 打分。手动评估则不一样,它让你完全控制哪些 trace 或 thread 在什么时候被评估。这两者不是替代关系,而是互补关系。
手动评估特别适合这些情况:
- 你想评估某些失败或需要仔细检查的特定 trace 或 thread;
- 你想把评估规则应用到历史数据上,而这些数据没有被采样到;
- 你想在启用自动评分之前,先用选中的样本测试新规则;
- 你想用更新或修改后的规则重新评估 trace。
手动评估是怎么工作的
手动评估允许你从 UI 直接把已有的评估规则应用到选中的 trace 或 thread 上,绕过采样率和规则启用状态。也就是说,哪怕某条规则当前是禁用状态,只要你手动触发,它也会执行。
你可以从两个地方触发手动评估:
- Traces 页面 :选择一个或多个 trace,点击
Evaluate,应用 trace 级别的规则; - Threads 页面 :选择一个或多个 thread,点击
Evaluate,应用 thread 级别的规则。
这里有一个重要提醒:trace 级别的规则只能应用到 trace 上,thread 级别的规则只能应用到 thread 上。选择实体时,要确保规则类型和对象类型匹配。
当你触发手动评估后,会发生几件事:
- 所有选中的 trace 或 thread 都会进入评估队列,不受采样率影响;
- 你可以一次应用多个规则;
- 即使规则当前处于禁用状态,也会执行;
- 评估结果会作为反馈分数出现在被评估的 trace 或 thread 上;
- 评估是异步处理的,所以可能需要等几秒钟,或者刷新页面才能看到结果。
这套机制的好处是灵活。你不需要为了评估几条关键 trace 去改在线评估配置,也不需要等下一次采样。看到可疑案例,选中、点击、评估,结果就会回到 trace 上。对于排查问题、验证新规则、补跑历史数据,都很实用。
下一步可以做什么
记录用户反馈只是第一步。Opik 的文档里也给了几个继续深入的方向:
- 在生产环境中给 Agent 打分,持续跟踪并捕捉具体问题;
- 使用 Annotation Queues,把 trace 组织起来,交给专家团队审核和标注;
- 查看 LLM as a Judge 指标,了解如何用模型自动评估质量。
如果把这几个环节连起来看,会发现一条比较清晰的路径:SDK 和 UI 负责收集反馈,在线评估负责扩大覆盖,手动评估负责关键抽查,Annotation Queues 负责团队协作,LLM as a Judge 指标负责自动化判断。最终目的只有一个:让 Agent 的每次改进都有数据支撑,而不是靠拍脑袋。
对于任何认真做 AI Agent 的团队来说,用户反馈都不应该停留在聊天记录里。把它记下来、关联到 trace、变成分数和原因,再用于比较、排查、微调和汇报,才算真正把反馈用起来了。Opik 提供的这些能力,本质上就是帮团队把这条闭环搭起来。