opik-test について
opik-test は、Opik のトレースで確認した失敗を、検証済みのテストスイート項目に変換するスキルです。回帰を追跡するチーム向けに、記録された入力と判定可能なアサーションを Opik に保存し、アプリケーションコードは変更しません。
主な機能
- 01Opik のトレースからケースを抽出
- 02失敗に対応する二値アサーションを作成
- 03プロジェクトのテストスイートを作成または再利用
- 04トレース ID で重複を確認
- 05項目を読み戻して保存を検証
- 06アプリケーションコードを変更しない
活用シーン
- 01トレースの失敗を回帰ケースとして保存
- 02失敗の説明からテストを作成
- 03同じトレースの重複登録を防止
MCP 対応
スイートや項目の操作には、Opik のホスト型 MCP 書き込みツールまたは Opik SDK を使用できます。MCP は必須ではありません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ comet-ml/ opik-skills --skill opik-testインストール後、Claude Code で/opik-testを入力して読み込まれたか確認します。
詳細
Test — 失敗ケースを再現可能なチェックとして記録する
完了の定義: Opik にテストスイートの項目が 1 つ存在し、失敗を再現します。その data には失敗した入力(既知の場合は期待される出力も)が含まれ、正しいトレースなら通過し、問題のあるトレースでは失敗する二値アサーションが 1 つまたは 2 つ設定されています。この項目はプロジェクトを対象とした名前付きスイートに含まれ、読み戻して内容を確認済みで、/opik-compare を実行する準備が整っています。ケースを記録できない場合は、実際に発生した最初のブロッカーで停止し、次のステップをちょうど 1 つ返してください。pytest ファイルを書いたり、テストを文章で説明したりするだけでは成功ではありません。
作業内容:実際のトレースデータからケースを抽出し、判定者がはい/いいえで判断できるアサーションを書き、/opik-compare が見つけられる場所に保存します。アプリケーションコードは変更しません。 このスキルは Opik に書き込み、リポジトリには書き込みません。
入力
エントリーポイントは /opik-test <trace-id>(そのトレースを記録)、/opik-test <describe the failure>(トレースを見つけるか、説明だけから記録)、または /opik-explain の直後に実行する /opik-test(直前に説明したトレースを記録)です。それ以外は推測してください。次の項目は任意の上書きとして扱います。
- スイート名(デフォルト:
<project>-regressions)・プロジェクト(デフォルト:トレースのプロジェクト、なければ設定済みのプロジェクト)・期待される出力(デフォルト:なし — 期待値はアサーションで表します)・実行ポリシー(デフォルト:スイートの設定。断続的な失敗の場合に限りruns_per_item: 3, pass_threshold: 2を使用します)。
推測できない実際のブロッカーがある場合に限り質問してください(Blockers を参照)。
有効化 — 対象範囲内の作業はこれだけです
1. ケースを特定する
- トレース ID(UUID 形式):そのトレースがケースです。
- 説明(「返金の回答が間違っている」「配送時間を幻覚した」):プロジェクトに対して
search_tracesを実行し、一致するトレース(エラー、低スコア、またはフリーテキストの一致)を検索して、最も適切なものを選びます。一致するものがなければ、説明だけから記録します — ユーザーの入力と期待値を項目にします。 - Opik に接続できることを確認します。
~/.opik.configが存在するか、OPIK_API_KEYが設定されていれば、それを使用します。どちらもない場合 → Blocker(「opik configureを実行してから、再実行してください」)。
2. トレースを読み取る(SDK を優先)
import opik
client = opik.Opik()
tid = "<trace_id>"
trace = client.get_trace_content(tid) # TracePublic: .input, .output, .project_id — NOT .project_name (accessing it raises)
project = client.rest_client.projects.get_project_by_id(trace.project_id).name
spans = client.search_spans(project_name=project, trace_id=tid) # root span (no parent_span_id) names the entrypoint the compare skill will call
# Pass project_name: without it search_spans looks in the configured default project and returns nothing.
トレースに記録された入力(ルートスパン/トレースの input)、実際の出力(問題が起きた内容)、およびルートスパン名(エントリーポイント)を取得します。MCP が接続されている場合は、read('trace', id) も同等の方法です — 便利ですが、必須ではありません。
3. アサーションを書く
アサーションは、項目の data と候補の出力から LLM 判定者が合否を判断する、平易な英語の文です。ルール:
- 1 つのアサーションにつき、失敗モードは 1 つ。 品質の羅列ではなく、正しい出力が何をするかを述べます。
- 問題のあるトレースなら失敗すること。 直前に読み取った実際の出力と照らし合わせます。合格してしまうなら、そのアサーションは適切ではありません。
- 出力だけで判定できること(
expected_outputがある場合はそれも含む)。「役に立つ」などの表現や、リッカート尺度は使いません。 - 最大 2 つまで: 肯定的な期待値を 1 つと、問題のある出力が具体的に誤ったことを述べている場合に限り、否定的なアサーションを 1 つ(「…と主張しない」)。否定形より肯定形を優先してください。小規模な判定モデルは否定文を誤読します(実例:「24 時間以内に返金すると約束しない」というアサーションが、そのとおりに約束していた出力に対して真と判定されました)。否定が重要なら、肯定形に含めてください(「24 時間ではなく、5~7 営業日と伝えている」)。
正解が厳密に決まっている場合は、判定者より決定的なチェックを優先してください。data.expected_output にも設定すれば、/opik-compare でヒューリスティック指標を使ってスコアリングできます。
4. ケースを保存する(作成または追加)
スイート名は、ユーザーが指定しない限り<project>-regressionsにします。既存のスイートを再利用し、同じプロジェクト用に2つ目のスイートを作成しないでください。
suite = client.get_or_create_test_suite(
name="<project>-regressions",
project_name=project,
tags=["regression"],
)
# Dedupe on the source trace before inserting.
existing = suite.get_items(filter_string=f'data.source_trace_id = "{tid}"')
if not existing:
suite.insert([{
"data": {
"input": trace.input, # verbatim from the trace
"source_trace_id": tid,
# "expected_output": "...", # only when the correct answer is exact
},
"assertions": ["<positive assertion>", "<optional negative assertion>"],
"description": "Regression from trace <tid>: <one-line failure>. Entrypoint: <root span name>",
}])
descriptionにはエントリーポイント(ルートスパン名)を格納します。これは、項目を実行する関数を/opik-compareが把握するために必要です。source_trace_idキーを使うことで、同じトレースに対して2回目の/opik-testを実行しても、重複登録を防げます。
ホステッドMCPに接続されている場合、writeツールのtest_suite.createとtest_suite_item.upsert操作でも同じことができます。正確なエンベロープについてはschema("test_suite_item.upsert")を呼び出してください。どちらのトランスポートでも同じスイートに保存されます。SDKの方法ではMCPは不要です。
5. 検証する
項目を読み戻します。suite.get_items(filter_string=…)は項目をidとともに返します。また、スイートの現在のバージョン(suite.get_current_version_name())を記録します。挿入呼び出しだけを根拠に成功を報告しないでください。
6. 報告する
スイート、項目、アサーション、および次のステップを1つ返します(出力を参照)。自然な次のステップは、「修正後に/opik-compare <suite>を実行する」です。このスキルはキャプチャを行います。スイートの実行は行いません(それは/opik-compareの役割です)。また、コードは変更しません。
ブロッカー
最初に見つかったブロッカーで停止し、次のステップを1つだけ返します。
- 「
opik configureを実行してから、/opik-test <trace-id>を再実行してください。」 - 「プロジェクト
<name>に<id>のトレースが見つかりません。IDとプロジェクトを確認するか、失敗の内容を説明してください。そこからキャプチャします。」 - 「トレースに記録済みの入力がありません。入力と期待される動作を渡してください。そこからキャプチャします。」
- 「ここでどの動作が誤っていますか?出力がどうあるべきかを教えてください。アサーションを作成します。」
出力
ユーザー向け:短いメッセージで、スイート名とバージョン、項目(入力は1行、アサーションは原文のまま)、項目が新規か既存か、および次のステップを1つ示します。JSONやpytestファイルにはしません。
内部形式(構成/評価用)では、次の形式を1つ使います。
status:captured|exists|blockedsuite:name、project、versionitem:id、source_trace_id、input(保存された値)、assertions(リスト)、expected_output(設定されている場合)、entrypointsource:sdk|mcpnext_step:1つだけ(通常は「修正後に/opik-compare <suite>を実行する」)
不変条件:capturedとexistsには、空でないassertionsリストとsuiteを含むitemを持たせます。各アサーションでは、1つの失敗モードを指定します。existsは、重複排除処理によってトレースがすでにキャプチャ済みだと判定され、何も挿入されていないことを意味します。blockedにはnext_stepを1つだけ含めます。どの経路でもコードベースは変更されません。
例
説明済みのトレースから。 /opik-explainで、retrieveツールが何も返さず、回答が24時間以内の返金を約束していることが判明しました。/opik-test 019fd8a7-…:入力 = "what is your refund window?"、ルートスパン = answer。アサーション:「回答に返金処理には5~7営業日かかると記載されている」「回答で24時間以内の返金を約束していない」。スイートsupport-bot-regressionsを作成し、項目を挿入して読み戻すとv1でした。→ captured;次のステップ = 「retrieve()を修正したら/opik-compare support-bot-regressionsを実行する」
すでにキャプチャ済み。 同じトレースをもう一度指定します。source_trace_idフィルターで項目が見つかります。→ exists:「すでにsupport-bot-regressionsにあります(項目…)— 準備ができたら/opik-compareを実行してください。」
トレースなしで、説明から。 /opik-test the bot should refuse to give legal advice。一致するトレースがないため、ユーザーの入力と期待が項目になります。入力 = 説明されたプロンプト、アサーション = 「回答は法律上の助言を断り、専門家への相談を促している」。→ captured(source_trace_idなし)。
ブロック — 設定なし。 ~/.opik.configもOPIK_API_KEYもありません。→ blocked:「opik configureを実行してから再実行してください。」(コードには触れません。)
アンチパターン
スイート項目ではなく pytest/vitest ファイルを書くこと(/opik-compare が実行し、UI に表示するのはスイートです)。全体的なアサーション(「レスポンスは良好です」「レスポンスは正確で役に立ちます」)。失敗したトレースでもパスしていたはずのアサーション。1 つの失敗に対して 5 つのアサーションを設けること。トレースごとに新しいスイートを作ること。入力をそのまま保存せず、言い換えること。ここでスイートを実行すること(それは /opik-compare の役割です)。アプリケーションコードを編集すること。副次的にバグを修正すること。
参考資料
テストスイートとデータセットの詳細は、これと同じ場所にインストールされている opik スキルにあります。ファイルを直接読んでください。パスはこのファイルからの相対パスです。../opik/references/evaluation-test-suites.md(スイート、項目、アサーション、実行ポリシー、バージョン)、../opik/references/evaluation-datasets.md(OQL フィルター構文、トレースからのデータセット作成)、../opik/references/production.md(search_traces)。審査者が判定できるアサーションの書き方については、../opik-evaluate/references/write-judge-prompt.md を参照してください。ホストのスキルの配置方法が異なる場合は、opik スキルの references/ ディレクトリを探してください。
opik スキルがインストールされていない場合は、レポートにその旨を記載し、記憶を頼りにせず https://www.comet.com/docs/opik/ を使用してください。