opik-compare について
opik-compareは、同じOpikテストスイート上のベースライン実験と候補実験を比較するスキルです。項目ごとの結果から指標の変化、リグレッション、修正、比較可能性を報告し、アプリケーションコードは変更しません。
主な機能
- 01ベースラインと候補実験の実行または選択
- 02実験結果の項目別比較
- 03指標差分と合格率の変化を報告
- 04リグレッション、修正、最低スコア項目の特定
- 05実験の比較可能性を確認
- 06Opikの比較ビューリンクを作成
活用シーン
- 01コード変更を以前の実行結果と比較
- 02再実行せずに既存の2実験を確認
- 03テストケースの悪化や改善を特定
- 04評価実行の設定が一致しているか確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ comet-ml/ opik-skills --skill opik-compareインストール後、Claude Code で/opik-compareを入力して読み込まれたか確認します。
詳細
比較 — スイート全体で候補とベースラインを比較
完了条件: 同じスイートで実施した2つの実験、つまりベースラインと候補を、項目ごとに読み込み、メトリクスごとの差分、合格 → 不合格(リグレッション)になったケースと不合格 → 合格(修正)になったケース、最悪の行、実行を比較可能かどうかの注記、Opik の比較ビューへのリンクを提示します。実行が1つしかない場合、完了状態は「ベースラインを作成済み — 変更後に再実行」です。スイートを実行または読み込めない場合は、最初の実際のブロッカーで停止し、次のステップをちょうど1つ返します。集計スコアだけでは比較になりません。判定を下すことは、このスキルの役割ではありません。
操作:ベースラインと同じ方法で候補を実行し、実行時のコンソール出力ではなく Opik から両方の実行結果を読み込み、変更された具体的なケースを挙げます — アプリケーションコードは変更しません。 このスキルが書き込むファイルは、リポジトリ外に置く使い捨てのランナーだけです。
入力
エントリポイントは /opik-compare <suite>(今すぐスイートを候補として実行し、最新の以前の実行と比較)、/opik-compare <experiment-A> <experiment-B>(新しい実行はせず、既存の2つの実行結果を読み込み)、または /opik-test の直後に実行する /opik-compare(そのスイート)です。それ以外は推定します。以下は任意の上書き設定として扱います。
- suite(デフォルト:
<project>-regressions、または/opik-testが直前に書き込んだスイート) · baseline(デフォルト:そのスイートで最も最近の以前の実験) · candidate name(デフォルト:candidate-<short git sha>) · アサーション用の judge model(デフォルト:スイートまたはベースラインのもの) · 項目ごとの runs(デフォルト:スイートのポリシー)。
実際に推定できないブロッカーがある場合にのみ確認します(ブロッカーを参照)。
有効化 — 対象となる作業はこれだけです
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)。実験が2つ明示的に指定されている場合は、ステップ3をスキップしてステップ4に進みます。
Opik に接続できることを確認します。~/.opik.config が存在するか、OPIK_API_KEY が設定されていれば、それを使用します。どちらもない場合 → ブロッカー。
2. タスクアダプターを構築する(コード候補の場合)
スイートの項目には、呼び出す対象が記載されています。/opik-test が作成した各項目の description は、Entrypoint: <root span name> で終わっています。リポジトリ内でその関数を grep し、インポートしてラップします。
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. 候補を実行する
ベースラインと同じスイート、同じバージョン、同じ judge model、項目ごとの同じ runs 数で実行します。変更するのはテスト対象だけです。
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 を使用)
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) では1回分の実行の概要を取得できます。これらを使って2つの実行を特定し、妥当性を確認してください。項目レベルの結合には、引き続き上記の SDK を使用します。
5. 質問に以下の順序で回答します
- 比較可能ですか? スイートのバージョン(
dataset_version/項目数)、判定モデル、項目ごとの実行回数が同じか確認します。異なる場合は、まずその旨を伝えます。その場合、以下の差分は測定値ではなく参考値です。 - 失敗したケース — ベースラインでは
passedだったものの、候補ではそうでなかったケースです。それぞれについて、入力(1 行)、失敗したアサーション、そのreason、候補のトレースリンクを示します。 - 修正されたケース — その逆です。
- メトリクスごとの正味の影響 — 両方の実行における各
feedback_scores名の平均値と合格率を示します。baseline → candidate (delta)の形式で報告します。 - 最も低いスコアの行 — 候補のスコアが最も低い行です。ベースラインで最も低かった行と同じかどうかも示します。
- アプリ側ですか、判定側ですか?
reasonが出力を挙げている反転はアプリ側です。出力が変わっていないのにreasonが揺らいでいる反転は判定側です。その場合はその旨を示し、1 回の実行を信頼せず、その項目のruns_per_item: 3を提案します。 - コストと品質の比較 — トレースに記録されている場合、候補のトレースとベースラインの
total_estimated_cost/実行時間を比較します。 - 不安定なケース — 同じコードでの繰り返し実行間で結果が反転した項目です(スイートの実行ポリシーで
runs_passed/runs_totalが公開されています)。 - 傾向 — 3 回以上の実行がある場合は、直近数回の合格率を示し、1 回分の差分を文脈に沿って捉えられるようにします。
数値が示す内容を報告します。リリース可否は判断しません。それは、これらの数値に明示的なリリースポリシーを適用する /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 で確認できます)。次に行う手順を 1 つ案内します(出力を参照)。ユーザーがリリースすべきかどうかを尋ねている場合、その手順は /opik-verify です。
7. 記録する(依頼があった場合のみ)
ユーザーが結果をデータのそばに記録したい場合は、後退したケースのトレースに client.rest_client.traces.add_trace_comment(trace_id, text=…) でコメントを追加するか、判定結果のそばに client.log_traces_feedback_scores([...]) で人間のスコアを記録します。既定では記録しません。
ブロッカー
最初に発生したブロッカーで停止し、次の手順を正確に 1 つだけ返します。
- "Run
opik configure, then rerun/opik-compare <suite>." - "No test suite named
<suite>in project<name>— run/opik-test <trace-id>to create one, or name the suite." - "The suite's items don't say which function to call — pass the entrypoint (
/opik-compare <suite> --entrypoint answer) and I'll build the runner." - "The runner needs a provider credential — set
OPENAI_API_KEY(or the relevant key) and rerun." - "The assertion judge has no credential (every item reports
scoring_failed, 'Missing credentials') — set the judge's provider key and rerun; the run<name>is not a comparable candidate." - "Experiment
<id>has no items yet (server-side run still processing) — rerun in a minute."
出力
ユーザー向け: 簡潔なメッセージです。比較可能性に関する注記(あれば)、回帰(ケース、アサーション、理由の順)、修正、指標ごとの表(baseline → candidate (delta))、比較リンク、次のステップを1つだけ記載します。判定はしません。すべての項目をそのまま列挙するのではありません。
内部形式(構成/評価用)は、次の1つの形にします。
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:1つだけverdict:記載しない
不変条件:compared には両方の実験、空でない deltas、両方のIDを含む compare_url を記載します。baseline_created には baseline のみを記載し、next_step は "rerun after the change" とします。blocked には next_step を1つだけ記載します。どの経路でもコードベースに書き込まず、出荷可否の判断も出しません。
例
修正を検証済み。 retrieve() の修正後に /opik-compare support-bot-regressions を実行します。ベースラインは昨日の実行(5件中3件合格)です。Entrypoint: answer からランナーを構築し、候補は5件中5件合格しました。結果を読み戻すと、修正は2件(返金と配送の項目)、回帰は0件、pass_rate 0.60 → 1.00 (+0.40) で、スイートのバージョンとジャッジは同じです。→ compared;次のステップ = "本番環境で返金アサーションを監視するために /opik-online-eval を実行する"、または "マージする — 判断はあなたに委ねられています"。
回帰を検出。 候補では返金のケースが修正されますが、「法律上の助言をしない」という項目が失敗に変わり、reason には法律上の手順を提案する新しい出力が引用されています。原因:アプリ。→ compared とし、回帰を1件、最初に記載して、その後に差分表を続けます。次のステップ = "法律上の助言に関する項目の /opik-explain <candidate trace>"。
初回実行。 スイートに過去の実験がありません。1回実行し、失敗した項目とともに5件中3件合格と報告します。→ baseline_created:"これがベースラインです — 変更後に /opik-compare を再実行してください。"
既存の実行が2件、コード変更なし。 /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を1つだけ使って比較リンクを構築すること。
参考資料
テストスイート、実験、指標の詳細は、このスキルと一緒にインストールされる 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/ を参照してください。