关于 opik-compare
opik-compare 用于比较同一 Opik 测试套件上的基线与候选实验。它读取逐项结果,报告指标变化、回归、修复、可比性及并排比较链接,且不修改应用代码。
主要功能
- 01运行或选择基线与候选实验
- 02逐项比较实验结果
- 03报告指标差值和通过率变化
- 04识别回归、修复项和最差结果
- 05检查实验是否可比
- 06生成 Opik 并排比较链接
适用场景
- 01将代码变更与先前运行结果比较
- 02不重新运行而检查两个现有实验
- 03找出测试用例的退步或改善
- 04检查评估运行是否采用一致设置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ comet-ml/ opik-skills --skill opik-compare安装后在 Claude Code 中输入/opik-compare确认已加载。
详细介绍
对比 — 套件中的候选版本与基线
完成定义: 在同一套件上运行两个实验——一个基线和一个候选版本——逐项读取,列出每项指标的变化值、从通过 → 未通过的案例(回归)和从未通过 → 通过的案例(修复)、表现最差的条目、说明两次运行是否可比,以及 Opik 对比视图链接。如果只有一次运行,完成状态为“已创建基线——更改后重新运行”。如果套件无法运行或读取,请在遇到第一个真实阻碍时停止,并且只返回一个下一步操作。仅有汇总分数不算对比;给出结论也不属于此技能的工作。
操作:**以与基线相同的方式运行候选版本,从 Opik 而非运行控制台输出中读取两次运行,指出发生变化的具体案例——不要更改任何应用代码。**此技能唯一写入的文件,是仓库外的临时运行器。
输入
入口为 /opik-compare <suite>(立即运行该套件作为候选版本,并与最近一次先前运行对比)、/opik-compare <experiment-A> <experiment-B>(读取两个已有运行,不启动新运行),或在 /opik-test 后立即运行 /opik-compare(使用该套件)。推断其余信息;以下均为可选覆盖项:
- 套件(默认:
<project>-regressions,或/opik-test刚写入的套件) · 基线(默认:该套件中最近一次较早的实验) · 候选版本名称(默认:candidate-<short git sha>) · 断言评审模型(默认:套件的模型/基线的模型) · 每项运行次数(默认:套件策略)。
只有遇到真实且无法推断的阻碍时才提问(见阻碍)。
激活 — 唯一范围内的工作
1. 确定套件和基线
import opik
client = opik.Opik()
suite = client.get_test_suite(name="<suite>", project_name="<project>")
prior = client.get_test_suite_experiments(name="<suite>", project_name="<project>") # newest first is not guaranteed — sort by created_at yourself
基线 = 该套件中最近一次先前的实验,除非用户指定其他实验。没有先前的实验 → 本次运行即为基线(仍执行第 3 步;状态为 baseline_created)。明确指定了两个实验 → 跳过第 3 步,转到第 4 步。
确认 Opik 可访问:如果 ~/.opik.config 存在或已设置 OPIK_API_KEY,则使用它。否则 → 阻碍。
2. 构建任务适配器(代码候选版本)
套件中的条目说明了要调用什么:/opik-test 写入的每个条目的 description 都以 Entrypoint: <root span name> 结尾。在仓库中搜索该函数,导入并封装:
def task(item: dict) -> dict:
return {"input": item["input"], "output": str(entrypoint(item["input"]))}
将运行器写入仓库外的临时文件(或用户指定的临时路径)——绝不写入代码库,也绝不提交。运行器需要应用的提供方凭据;如果缺少 → 阻碍(应用无法响应这些条目)。绝不要让运行器指向会写入数据、发送内容或产生费用的生产入口。
提示词候选版本(变更的是提示词版本,而非代码):无需适配器——在服务端运行:
client.rest_client.experiments.execute_experiment(
dataset_name=suite.name, dataset_id=suite.id,
prompts=[{"model": "<model>", "messages": [...], "configs": {}, "prompt_versions": [{"id": "<prompt_version_id>"}]}],
project_name="<project>",
) # 202 Accepted; one experiment per prompt variant, processed asynchronously — poll get_experiment_by_id until items fill
3. 运行候选版本
套件、版本、评审模型和每项运行次数均与基线相同——仅改变受测对象。
result = opik.run_tests(
test_suite=suite, # or suite.get_version_view("<baseline's version>") to pin
task=task,
experiment_name="candidate-<sha>",
experiment_tags=["compare", "<sha>"],
model="<same judge model as baseline>",
generate_report=False, # default True writes opik_test_suite_reports/ into cwd — the repo stays untouched
)
candidate_id = result.experiment_id # result.experiment_url is the single-run link
# GUARD: a missing judge credential does NOT raise — every item comes back failed with
# scoring_failed=True and a "Missing credentials" reason, and the experiment is still created.
# Treat that as a Blocker, not as a regression; do not compare against that run.
judge_failed = [
r for ir in result.item_results.values() for t in ir.test_results
for r in t.score_results if getattr(r, "scoring_failed", False)
]
不要读取 result 中的分数就停止——第 4 步会从 Opik 读取两次运行,确保基线和候选版本经过相同流程。
4. 读取两次运行(优先使用 SDK;连接 MCP 时使用 MCP)
base = client.get_experiment_by_id("<baseline_id>")
cand = client.get_experiment_by_id(candidate_id)
b_items = {i.dataset_item_id: i for i in base.get_items()}
c_items = {i.dataset_item_id: i for i in cand.get_items()}
# each item: dataset_item_data, evaluation_task_output, feedback_scores [{name, value, reason}],
# assertion_results [{value, passed, reason}], trace_id
# Side by side, one call, both experiments:
rows = client.get_experiments_client().find_experiment_items_for_dataset(
suite.name, experiment_ids=["<baseline_id>", candidate_id], project_name="<project>"
)
get_experiment_by_name 已弃用——请使用 get_experiments_by_name 按名称查找,并按 id 选取。连接托管 MCP 后,list('experiment', name=…) 会直接显示每项指标的平均值,而 read('experiment', id) 会提供一次运行的摘要——使用它们查找并验证两次运行;上述逐项关联仍通过 SDK 完成。
5. 按顺序回答问题
- 是否可比? 套件版本(
dataset_version/条目数)、评审模型、每个条目的运行次数是否相同。如果不同,请先说明——此后的差值只能作为参考,并非实测结果。 - 哪些案例失败了——基线中通过、候选中未通过的案例。每个案例都要提供其输入(一行)、失败的断言、
reason,以及候选追踪链接。 - 哪些案例修复了——反过来比较。
- 各项指标的净变化——两次运行中每个
feedback_scores名称的均值,以及通过率;报告“基线 → 候选(差值)”。 - 最差的条目——候选中得分最低的条目;它们是否与基线中最差的条目相同?
- 是应用还是评审? 如果某次结果翻转时,
reason引用了输出,那就是应用问题;如果输出未变,但reason摇摆不定,那就是评审问题——请标记出来,并建议该条目使用runs_per_item: 3,而不要只信任一次运行。 - 成本与质量——候选追踪与基线的
total_estimated_cost/时长对比(追踪中包含这些信息时)。 - 不稳定的案例——同一代码的重复运行中结果会翻转的条目(套件的执行策略会公开
runs_passed/runs_total)。 - 趋势——存在两次以上运行时,报告最近几次运行的通过率,为单步差值提供背景。
如实报告数字所示的结果。不要决定是否发布——那是 /opik-verify 的职责,它会根据明确的发布策略评估这些数字。
6. 链接并交接
使用两个 ID 构建比较链接——它是一个经过 URL 编码的 JSON 数组,方便用户直接进入并排视图:
import json, urllib.parse
ids = urllib.parse.quote(json.dumps(["<baseline_id>", candidate_id]))
compare_url = f"{ui_base}/{workspace}/experiments/{suite.id}/compare?experiments={ids}"
ui_base 是 Opik UI 的源地址(配置的 URL 去掉 /api;result.experiment_url 会显示可复用的确切主机和工作区)。然后给出一个后续步骤(见输出)——如果用户问的是是否发布,该步骤就是 /opik-verify。
7. 记录(仅在用户提出要求时)
如果用户希望将发现记录在数据旁边:可通过 client.rest_client.traces.add_trace_comment(trace_id, text=…) 在回归案例的追踪上添加评论,或通过 client.log_traces_feedback_scores([...]) 在评审分数旁边记录人工评分。默认情况下不要记录。
阻塞项
遇到阻塞时,在最早的阻塞项处停止,并且只返回一个后续步骤:
- “运行
opik configure,然后重新运行/opik-compare <suite>。” - “项目
<name>中没有名为<suite>的测试套件——运行/opik-test <trace-id>来创建一个,或指定套件名称。” - “套件条目没有说明要调用哪个函数——传入入口点(
/opik-compare <suite> --entrypoint answer),我会构建运行器。” - “运行器需要提供商凭据——设置
OPENAI_API_KEY(或相应的密钥),然后重新运行。” - “断言评审器没有凭据(每个条目都报告
scoring_failed,‘Missing credentials’)——设置评审器的提供商密钥,然后重新运行;运行<name>不是可比较的候选。” - “实验
<id>还没有条目(服务端运行仍在处理中)——一分钟后重新运行。”
输出
**面向用户:**简短的人类可读消息——可比性说明(如有)、先列回归项(案例、断言、原因),再列修复项,然后是逐项指标表(baseline → candidate (delta))、比较链接,以及唯一的下一步。不提供结论。不要原样倾倒所有条目。
底层(用于内容组织/评估)采用以下一种结构:
status:compared|baseline_created|blockedsuite:name、id、versionbaseline/candidate:experiment_id、name、url、items、pass_ratecomparable:true|false、notedeltas:{metric, baseline, candidate, delta}列表(包含通过率)regressions:{dataset_item_id, input, assertion, reason, trace_url}列表fixes:相同结构attribution:可能归因于某一方时,使用{app: [...ids], judge: [...ids]}compare_urlnext_step:恰好一个verdict:绝不可出现
不变量:compared 必须包含两个实验、非空的 deltas,以及包含两个 id 的 compare_url;baseline_created 只包含 baseline,且 next_step 为 "rerun after the change";blocked 必须且只能包含一个 next_step;任何路径都不得写入代码库;任何路径都不得给出发布/暂缓决定。
示例
已验证修复。 修复 retrieve() 后运行 /opik-compare support-bot-regressions。基线 = 昨天的运行(3/5 通过)。根据 Entrypoint: answer 构建运行器;候选版本 5/5。回读结果:修复 2 项(退款和配送案例),回归 0 项,pass_rate 0.60 → 1.00 (+0.40),套件版本相同,评审模型相同。→ compared;下一步 = "run /opik-online-eval to watch the refund assertion in production" 或 "merge — the decision is yours"。
发现回归。 候选版本修复了退款案例,但“不得提供法律建议”项变为失败;其 reason 引用了新输出中提供法律步骤的内容。归因:应用。→ compared,先列出一项回归,再列出差值表;下一步 = "/opik-explain <candidate trace> for the legal-advice item"。
首次运行。 该套件没有先前的实验。运行一次;报告 3/5,并列出失败项。→ baseline_created:"This is the baseline — rerun /opik-compare after your change."
两个已有运行,无代码。 /opik-compare nightly-2026-09-14 nightly-2026-09-15。通过 get_experiments_by_name 解析、关联条目并报告。→ compared(未构建运行器)。
反模式
从运行器的控制台输出读取分数,而不是从 Opik 读取;比较不同套件版本或评审模型的运行却不说明;只报告汇总数据(“准确率 0.82 → 0.85”),却不指出哪些案例发生了变化;将运行器写入仓库或提交它;更改应用代码以使套件通过;对不稳定项只信任一次运行;给出发布/不发布的结论(按设计不在范围内);使用已弃用的 get_experiment_by_name;只用一个 id 构建比较链接。
参考资料
测试套件、实验和指标的详情见安装在此工具旁边的 opik 技能。直接读取文件——路径相对于此文件:../opik/references/evaluation-test-suites.md(run_tests、TestSuiteResult、执行策略、版本、get_test_suite_experiments)、../opik/references/evaluation-datasets.md(evaluate()、实验、OQL)、../opik/references/production.md(跟踪记录中的成本和延迟字段)。如果宿主以不同方式组织技能,请找到 opik 技能的 references/ 目录。
如果未安装 opik 技能,请在报告中说明,并参考 https://www.comet.com/docs/opik/,不要依赖记忆作答。