ガイド
チュートリアル:分類器または構造化出力
サポートの質問にラベルを付ける Python 関数を評価し、リリースがブロックされる理由を読み、取りこぼしを直し、修正版を元の版と比較します。すべてあなたのマシン上で、アカウントもネットワークもモデルも使わずに行います。
作るもの
answer と label(refund、account、other のいずれか)を持つ JSON オブジェクトを返すサポートボットです。これに 3 つの要件を課します。ラベルが十分な頻度で正しいこと、出力が常に正しい形をしていること、そしてどの回答も米国の社会保障番号のように見えるものを漏らさないことです。このうち 2 つは参照回答を必要としない形式チェックで、1 つは参照ラベルに対してタスクの成功を測ります。この違いは重要であり、このページでは両者を分けて扱います。
以下で使う用語(ケース、実行、メトリクス、区間、ルール、ゲート)は 基本概念 で定義しています。
前提条件
- Python 3.11 以降。
- 仮想環境にインストールした Oloproof:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- パッケージに同梱されているサンプルプロジェクトと候補の変更。両方を新しいディレクトリにコピーし、1 つ目で作業します。すべてのファイルは下にも載せているので、手で入力することもできます:
oloproof init --example support_bot support-classifier
oloproof init --example classification support-change
cd support-classifierこのページでは、API キー、プロバイダーのアカウント、ネットワークアクセスは一切使いません。
ファイル
support-classifier/
app.py the application under test (a Python callable)
oloproof.yaml the suite: dataset, system, evaluators
release.yaml the release policy: rules the run is decided against
data/support.jsonl 18 cases, one JSON object per line
rubrics/helpful.md a judge rubric, unused hereすべてのコマンドは support-classifier/ ディレクトリから実行してください。Oloproof はそこの .oloproof/ にストアを保持します。最初からやり直すには、そのディレクトリを削除してください。
アプリケーションとそのアダプター
アプリケーションにはアダプターを通じて到達します。Python アプリケーションの場合、アダプターは関数そのものです。Oloproof はそれをインポートし、ケースごとにそのケースの input を渡して 1 回呼び出し、返された辞書をそのケースの出力として記録します。
# app.py
from typing import Any
from oloproof import system
@system(name="support-bot", version="slice-a-example")
def answer(case: dict[str, Any]) -> dict[str, str]:
question = str(case["question"]).lower()
if "refund" in question:
return {"answer": "Refunds are available within 30 days when the order is eligible.",
"label": "refund"}
if "password" in question or "login" in question:
return {"answer": "Use password reset, then contact support if the login still fails.",
"label": "account"}
return {"answer": "A support specialist will follow up with the next step.", "label": "other"}自分の分類器を評価するには、そのコードはそのままにして、それを呼び出して辞書を返す、このような薄い関数を書いてください。関数は async でもかまいません。Oloproof はそれを呼び出しますが、アプリケーションをホストも、サンドボックス化も、リセットもしません。そのため、アプリケーションが呼び出し間で保持する状態はあなたが管理します。
oloproof.yaml はその関数と評価器を指定します:
version: 1
project: support-bot-example
dataset: data/support.jsonl
system:
name: support-bot
version: slice-a-example
callable: app:answer
timeout_s: 30
evaluators:
- type: exact_match
criterion: exact_label
field: label
- type: json_schema
criterion: format_valid
field: null
schema:
type: object
required: [answer, label]
properties:
answer: {type: string}
label: {type: string}
additionalProperties: false
- type: regex
criterion: pii_free
field: answer
pattern: '\b\d{3}-\d{2}-\d{4}\b'
pass_if: no_match出力は、関数のソース、宣言された version、config をキーとしてキャッシュされます。関数が他のファイル(プロンプト、ルール表)を読む場合は、それらを system.code_paths に列挙してください。そうすれば、それらを編集したときにシステムが再実行されます。
データセット
1 行に 1 ケースです。input は関数が case として受け取るものそのもので、expected は exact_match 評価器が比較する参照です:
{"id":"refund_00","input":{"question":"Can I get a refund for yesterday's order?"},"expected":{"label":"refund"}}
{"id":"account_04","input":{"question":"I can't sign in on my new phone."},"expected":{"label":"account"}}
{"id":"other_04","input":{"question":"I don't want a refund, I just need a copy of my receipt."},"expected":{"label":"other"}}関数は各ケースに対して、{"answer": "Use password reset, ...", "label": "account"} のようなオブジェクトを返します。
評価器を選ぶ
| 基準 | 評価器 | expected が必要か | 測るもの |
|---|---|---|---|
| exact_label | label に対する exact_match | はい | タスクの成功:ラベルが正しいこと |
| format_valid | 出力全体に対する json_schema | いいえ | 形式:オブジェクトがちょうど 2 つの文字列フィールドを持つこと |
| pii_free | answer に対する regex、pass_if: no_match | いいえ | テキストの安全性に関する性質 |
形式チェックは、形は正しいが誤った回答も合格にするため、タスクの成功の代わりには決してなりません。タスクチェックはすべてのケースに参照を必要とし、参照のないケースは exact_match で採点できません。決定的な評価器は人による検証を必要としません。2 回実行しても同じ判定になります。
ポリシー
release.yaml は実行の判断基準です:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
warn_on: []
rules:
- id: exact-label-floor
metric: exact_label
min: 0.70
- id: valid-format
metric: format_valid
kind: observed_count
max_failures: 0
- id: pii-free
metric: pii_free
kind: observed_count
max_failures: 0exact-label-floor は、ラベルが少なくとも 70% の割合で正しくなければならないと定め、95% 区間全体が 0.70 以上のときにのみ合格します。2 つの observed_count ルールは、実行したケースで失敗を一切許しません。これらはこのケースについて述べるもので、ユーザーが尋ねるすべての質問について述べるものではありません。
実行する
oloproof run実際の出力(一部省略):
Run run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ exact-label-floor │ exact_label │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ valid-format │ format_valid │ PASS │ observed_failures_within_limit │
│ pii-free │ pii_free │ PASS │ observed_failures_within_limit │
│ exact_label │ 72.2% │ [46.5%, 90.4%] │ 13 / 18 observed · 0 missing · 0 excluded │
│ format_valid │ 100.0% │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
│ pii_free │ 100.0% │ [81.4%, 100.0%] │ 18 / 18 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/18 miss; judgment 0 hit/54 miss読み方:
- 18 個中 13 個のラベルが正しく、72.2% です。これは 0.70 を上回りますが、区間は 46.5% まで下がります。18 ケースでは、真の割合が少なくとも 0.70 であることを示せません。そのためルールは INSUFFICIENT_EVIDENCE であり、PASS でも FAIL でもありません。
- すべての出力が正しい形をしており、SSN のような番号を含むものもないため、両方の形式ルールは合格します。
- block_on に INSUFFICIENT_EVIDENCE が含まれているので、ゲートはブロックし、コマンドは 3 で終了します。終了コード 0 は、ポリシーがブロックするものが何もないことを意味します。すべてのコードは CI のゲート に載っています。
もう一度実行すると、キャッシュの行は execution 18 hit/0 miss になります。何も変わっていないので、関数は呼び出されません。
失敗を調べる
oloproof inspect RUN_ID --failures
oloproof inspect RUN_ID --case refund_04RUN_ID は、実行の出力の最初の行にある ID です。
5 of 18 cases failed, errored or did not finish
refund_04
output: {"answer": "A support specialist will follow up with the next step.", "label": "other"}
exact_label: failed
...
other_04
output: {"answer": "Refunds are available within 30 days when the order is eligible.", "label": "refund"}
exact_label: failedcase refund_04
input: {
"question": "I was charged twice this month and want my money back."
}
expected: {
"label": "refund"
}
execution: OK, 1 ms
output: {
"answer": "A support specialist will follow up with the next step.",
"label": "other"
}
judgments:
exact_label: failed
format_valid: passed
pii_free: passed入力を読めばパターンは明らかです。「money back」「reverse the payment」「sign in」「two-factor」はキーワードリストになく、other_04 は「I don't want a refund」と言っているのに、「refund」という語にマッチしてしまいます。refund_04 は誤っているのに両方の形式チェックに合格している点に注意してください。これが、形式の確認と成功の測定の差です。
ここで意味のある次の行動は 2 つあります。取りこぼしを直す(下記)か、ケースを追加するかです。同じ正解率でケースが増えれば区間は狭まり、oloproof plan RUN_ID --run が何件必要かを見積もります。
実際に変更する
../support-change/app.py を app.py に上書きコピーします。取りこぼした言い回しが追加されています:
REFUND_WORDS = ("refund", "money back", "reverse the payment")
ACCOUNT_WORDS = ("password", "login", "sign in", "two-factor")
@system(name="support-bot", version="keywords-v2")
def answer(case: dict[str, Any]) -> dict[str, str]:
question = str(case["question"]).lower()
if any(word in question for word in REFUND_WORDS):
...そして oloproof.yaml の system の下で version: keywords-v2 を設定し、実行が新しいバージョンとして記録されるようにします。続いて:
oloproof runGate: ALLOW (exit 0)
│ exact-label-floor │ exact_label │ PASS │ lower_bound_meets_minimum │
│ exact_label │ 94.4% │ [72.7%, 99.9%] │ 17 / 18 observed · 0 missing · 0 excluded │18 個中 17 個が正しく、区間の下限 72.7% が 0.70 を上回るので、ルールは合格し、コマンドは 0 で終了します。other_04 はまだ失敗します。修正は否定表現に手を付けていません。
候補をベースラインと比較する
実行ルールが問うのは、候補が下限を満たすかどうかです。比較が問うのは、候補がベースラインとケースごとにどう違うかです。../support-change/compare.yaml をプロジェクトにコピーしてください。比較ルールが 1 つ入っています:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: label-no-regression
kind: non_inferiority
metric: exact_label
margin: 0.10oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlComparison sha256:de76... of run_01M4...TJAD against run_01M4...ECVEF · 18 paired cases
exact_label: +22.2 points [-12.9, +57.0] · 18 paired · 0 missing · 0 excluded
format_valid: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
pii_free: +0.0 points [-25.8, +25.8] · 18 paired · 0 missing · 0 excluded
Decisions
label-no-regression exact_label non-inferiority, margin 10.0 points INSUFFICIENT_EVIDENCE interval_overlaps_margin
about 3 more paired cases would decide it, if the difference holds (21 in total at 22% discordance)
Gate: BLOCK (exit 3)候補は 4 ケースを直し、1 つも壊さず、推定で 22 ポイントの改善です。しかし変わったのは 4 ケースだけで、18 個の対応のあるケースでは、区間は 12.9 ポイント悪化から 57 ポイント改善までとなり、10 ポイントのマージンをまたぎます。比較は、候補が許容範囲を超えて悪いという可能性をまだ否定できないため、INSUFFICIENT_EVIDENCE となり 3 で終了します。その下の行はサイズの見積もりです。--policy を付けない場合、compare は差を表示し、プロジェクトの release.yaml が比較ルールを宣言していないことを伝え、何も判断されていないので 0 で終了します。
候補をベースラインと比較する で、マージンとその他のルールの種類を説明しています。
トラブルシューティング
| 症状 | 原因と対処 |
|---|---|
| app について ModuleNotFoundError | app.py があるディレクトリから実行するか、そこからインポート可能なモジュールパスを callable に指定してください。 |
| ルールが、どの評価器も生成しないメトリクスを指定している | ルールの metric は評価器の criterion と一致する必要があります。エラーには存在するメトリクスが列挙されます。 |
| 分類器を編集したのに、実行がすべての出力を再利用した | キャッシュは callable のソースに従います。それが読むヘルパーファイルは system.code_paths に列挙する必要があります。 |
| exact_label がケースを欠損として報告する | それらの実行は例外を送出したかタイムアウトしました。oloproof inspect RUN_ID --failures で各エラーを確認できます。 |
| 推定値が高いのに実行が 3 で終了する | 判断するのは推定値ではなく区間です。ケースを追加するか、実行前に決めたうえで低い下限を受け入れてください。 |