ガイド
チュートリアル:HTTP エンドポイントの背後にあるアプリケーション
HTTP で到達するサービスを、そのコードをインポートせずに評価します。Oloproof に URL を指定し、スイートを実行し、取りこぼしを見つけ、変更をデプロイして比較します。小さなローカルサービスがあなたのサービスの代わりを務めるので、すべてオフラインで動作します。
作るもの
顧客のメッセージを読み、2 つのフィールドを抽出する注文受付サービスです。intent(where_is_order、cancel、return、other のいずれか)と order_id(4 桁の数字、または null)です。すべての応答の形式を確認し(これには参照は不要です)、各フィールドが正しいかを測定します(これには参照が必要です)。ケース、実行、メトリクス、ゲートなどの用語は 基本概念 で定義しています。
前提条件
- Python 3.11 以降と、仮想環境にインストールした Oloproof:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- パッケージに同梱されているサンプルプロジェクト。新しいディレクトリにコピーし、そこで作業します(client.py がインポートする httpx は Oloproof と一緒にインストールされます):
oloproof init --example http order-intake
cd order-intake- このマシンでポート 8765 が空いていること。使われている場合は別のポートを選び、サーバーのコマンドと oloproof.yaml の両方で変更してください。
ここではプロバイダーも、API キーも、インターネットも使いません。サービスは 127.0.0.1 で待ち受けます。
ファイル
order-intake/
server.py the stand-in service (Python standard library only)
client.py a callable that calls the service with a token (used near the end)
oloproof.yaml the suite: dataset, HTTP system, evaluators
release.yaml rules for a run
compare.yaml a rule for a comparison
data/messages.jsonl 20 casesコマンドは order-intake/ から実行してください。2 つ目のターミナルでサービスを起動し、動かしたままにします:
python server.py --port 8765intake service (v1) on http://127.0.0.1:8765/extractHTTP の契約
各ケースについて、Oloproof はリクエストを 1 つ送ります。ケースの input を JSON ボディとし、宣言したメソッド(既定は POST)を使います。応答は JSON として読みます。400 以上のステータス、タイムアウト、接続拒否は、そのケースの実行エラーとして記録され、誤った回答として記録されることはありません。
1 ケースのリクエストと応答:
POST /extract
{"message": "Where is order 1042? It has not arrived."}
200 OK
{"result": {"intent": "where_is_order", "order_id": "1042"}, "service": {"rules": "v1"}}output_path: result は、ケースの出力として result だけを残すよう Oloproof に指示します。これがなければボディ全体が出力になります。data.answer のようなドット区切りのパスでより深い位置に到達できます。
version: 1
project: order-intake
dataset: data/messages.jsonl
system:
name: order-intake
http:
url: http://127.0.0.1:8765/extract
method: POST
version: rules-v1
output_path: result
timeout_s: 30
evaluators:
- type: json_schema
criterion: format_valid
field: null
schema:
type: object
required: [intent, order_id]
properties:
intent: {enum: [where_is_order, cancel, return, other]}
order_id: {type: [string, "null"], pattern: '^\d{4}$'}
additionalProperties: false
- type: exact_match
criterion: intent_correct
field: intent
- type: exact_match
criterion: order_id_correct
field: order_idHTTP システムは version を宣言しなければなりません。Oloproof はデプロイを見ることができません。各ケースの出力を URL、メソッド、出力パス、そしてそのバージョンをキーとしてキャッシュするので、サービスが変わったことを伝える手段がバージョンです。変更し忘れると、新しいデプロイは決して呼び出されません。
ヘッダーと認証はまだ system.http で設定できません。下のトークンに関する節で回避策を示します。
データセット
{"id":"m04","input":{"message":"Has order #5120 shipped yet?"},"expected":{"intent":"where_is_order","order_id":"5120"}}
{"id":"m07","input":{"message":"Do you ship to Canada?"},"expected":{"intent":"other","order_id":null}}
{"id":"m16","input":{"message":"Please refund and take back the lamp from order #1560."},"expected":{"intent":"return","order_id":"1560"}}input はリクエストボディそのものです。expected は各フィールドの参照を保持します。null は実際の参照値で、「このメッセージには注文 ID がない」ことを意味します。
評価器を選ぶ
| 基準 | 評価器 | expected が必要か | 測るもの |
|---|---|---|---|
| format_valid | 出力に対する json_schema | いいえ | 形式:既知のインテントと正しい形の ID |
| intent_correct | intent に対する exact_match | はい | 1 つ目のフィールドのタスク成功 |
| order_id_correct | order_id に対する exact_match | はい | 2 つ目のフィールドのタスク成功 |
スキーマは、どのメッセージに対しても {"intent": "other", "order_id": null} を合格にしてしまいます。形は正しくても役に立ちません。サービスが仕事をしたかどうかを示すのは参照チェックだけです。フィールドを別々に採点すると、どちらが失敗しているかがわかります。1 つにまとめたチェックではそれが隠れてしまいます。
release.yaml は形式の失敗を一切許さず、各フィールドが少なくとも 70% の割合で正しいことを、95% 区間で判断して求めます:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: valid-format
metric: format_valid
kind: observed_count
max_failures: 0
- id: intent-floor
metric: intent_correct
min: 0.70
- id: order-id-floor
metric: order_id_correct
min: 0.70実行する
oloproof runRun run_01M4... [DECIDED/COMPLETE]
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ PASS │ observed_failures_within_limit │
│ intent-floor │ intent_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ order-id-floor │ order_id_correct │ INSUFFICIENT_EVIDENCE │ interval_overlaps_threshold │
│ format_valid │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ intent_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 75.0% │ [50.8%, 91.4%] │ 15 / 20 observed · 0 missing · 0 excluded │
Cache: execution 0 hit/20 miss; judgment 0 hit/60 missすべての応答は正しい形をしています。各フィールドは 20 回中 15 回正しいですが、20 ケースでは区間が 50.8% まで下がるため、どちらの 70% の下限も示されません。INSUFFICIENT_EVIDENCE となり、ゲートは終了コード 3 でブロックします。
失敗を調べる
oloproof inspect RUN_ID --failures8 of 20 cases failed, errored or did not finish
m04
output: {"intent": "where_is_order", "order_id": null}
order_id_correct: failed
m06
output: {"intent": "other", "order_id": "7011"}
intent_correct: failed
...
m18
output: {"intent": "other", "order_id": null}
intent_correct: failed
order_id_correct: failed横に入力を並べて読むと(oloproof inspect RUN_ID --case m04)、2 つの欠陥が現れます。ID のパターンは「order 1234」にしかマッチせず、「order #5120」「order no. 8123」や単独の「#1673」にはマッチしません。また、「send back」「stop order」「where's my parcel」といった言い回しはどのインテントにも対応しません。次の行動は、両方のルールを広げることです。
変更をデプロイする
サービスを停止し、それらの修正を含む候補を起動します:
python server.py --port 8765 --rules v2oloproof.yaml の version: rules-v1 を version: rules-v2 に変更してください。URL は変わっていないので、そうしないと Oloproof は古い出力を再利用します。続いて:
oloproof runGate: ALLOW (exit 0)
│ intent_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │
│ order_id_correct │ 100.0% │ [83.1%, 100.0%] │ 20 / 20 observed · 0 missing · 0 excluded │両方の下限が合格し、コマンドは 0 で終了します。
2 つのデプロイを比較する
compare.yaml は、候補のインテントがベースラインより良いかを問います:
version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
- id: intent-better
kind: superiority
metric: intent_correctoloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yamlformat_valid: +0.0 points [-23.6, +23.6] · 20 paired · 0 missing · 0 excluded
intent_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
order_id_correct: +25.0 points [-8.5, +58.3] · 20 paired · 0 missing · 0 excluded
Decisions
intent-better intent_correct superiority INSUFFICIENT_EVIDENCE interval_overlaps_zero
Gate: BLOCK (exit 3)候補は自身の下限には合格しますが、比較ではより良いことを示せません。変わったのは 5 ケースで、20 個の対応のあるケースでは、改善幅の区間はまだゼロを含みます。どちらの記述も同時に真です。「要件を満たす」と「ベースラインに勝る」は別の問いであり、これほど小さなスイートが 2 つ目に答えられるのは大きな効果の場合だけです。実際のメッセージを増やすことが対処法です。
トークンを必要とするサービス
ベアラートークンを要求するようにサービスを起動します:
INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-tokensystem.http はカスタムヘッダーを送らないので、新しい version(たとえば rules-v2-auth)では、すべての呼び出しが拒否されます:
Gate: BLOCK (exit 3)
│ valid-format │ format_valid │ INSUFFICIENT_EVIDENCE │ no_observations │
│ intent_correct │ │ [0.0%, 100.0%] │ 0 / 0 observed · 20 missing · 0 excluded │そして oloproof inspect RUN_ID --failures は各ケースで execution ERROR: TransientError: system returned HTTP 401 を表示します。実行エラーは失敗ではなく、欠けたエビデンスです。何も観測されていないので、すべてのルールは INSUFFICIENT_EVIDENCE になります。
回避策は、自らリクエストを行う Python の callable です。client.py は環境変数からヘッダーを付けるので、トークンが oloproof.yaml や保存されるレコードに入ることはありません:
URL = os.environ.get("INTAKE_URL", "http://127.0.0.1:8765/extract")
def extract(case: dict[str, Any]) -> dict[str, Any]:
headers = {"Authorization": f"Bearer {os.environ['INTAKE_TOKEN']}"}
response = httpx.post(URL, json=case, headers=headers, timeout=30)
response.raise_for_status()
return response.json()["result"]oloproof.yaml の http: ブロックを次で置き換えます:
system:
name: order-intake
version: rules-v2
callable: client:extractそして、環境にトークンを入れて実行します:
INTAKE_TOKEN=s3cret oloproof run20 ケースすべてが再び観測され、ゲートは許可します。callable のキャッシュは背後のサービスではなく、callable 自身のソースと version に従うので、同じルールが当てはまります。デプロイしたら version を変更してください。
トラブルシューティング
| 症状 | 原因と対処 |
|---|---|
| すべてのケースで system connection failed (ConnectError) | サービスが動いていないか、別のポートで待ち受けています。 |
| system connection failed (RemoteProtocolError) | そのポートで別の何かが応答しています。空いているポートを選んでください。 |
| KeyError: "missing output path 'results'" | output_path が、応答にないフィールドを指定しています。 |
| system returned HTTP 401 または 403 | エンドポイントが認証情報を必要としています。callable による回避策を使ってください。 |
| 変更をデプロイしたのに、キャッシュの行がすべてヒットになる | version が変わっていないため、保存された出力が再利用されました。 |
| an HTTP system needs a declared version | system または system.http の下に version を追加してください。 |
制限事項
- system.http にはカスタムヘッダー、認証、クエリパラメーター、リクエストのテンプレート化がありません。ケースの input がそのまま JSON ボディになります。それ以外のことには callable を使ってください。
- 応答は JSON でなければなりません。ストリーミング応答はストリームとしては読まれません。
- Oloproof はデプロイを検出できません。宣言された version が、応答したものの同一性のすべてです。
- 状態はサービス自身が管理します。Oloproof はリクエストを送って回答を記録しますが、リクエストが変更したものをリセットも、サンドボックス化も、ロールバックもしません。