Anleitungen
Tutorial: eine Anwendung hinter einem HTTP-Endpunkt
Evaluieren Sie einen Dienst, den Sie über HTTP erreichen, ohne seinen Code zu importieren: Richten Sie Oloproof auf die URL, führen Sie die Suite aus, finden Sie, was er verpasst, deployen Sie eine Änderung und vergleichen Sie. Ein winziger lokaler Dienst steht stellvertretend für Ihren, sodass alles offline läuft.
Was Sie bauen
Einen Dienst zur Auftragsannahme, der eine Kundennachricht liest und zwei Felder extrahiert: einen intent (where_is_order, cancel, return oder other) und eine order_id (vier Ziffern oder null). Sie prüfen das Format jeder Antwort, wofür keine Referenz nötig ist, und messen, ob jedes Feld stimmt, wofür eine nötig ist. Begriffe wie Fall, Lauf, Metrik und Gate sind in Kernkonzepte definiert.
Voraussetzungen
- Python 3.11 oder neuer und Oloproof in einer virtuellen Umgebung:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- Das Beispielprojekt, das mit dem Paket ausgeliefert wird. Kopieren Sie es in ein neues Verzeichnis und arbeiten Sie dort (httpx, das client.py importiert, wird mit Oloproof installiert):
oloproof init --example http order-intake
cd order-intake- Port 8765 frei auf diesem Rechner. Ist er belegt, wählen Sie einen anderen und ändern Sie ihn sowohl im Serverbefehl als auch in oloproof.yaml.
Nichts hier nutzt einen Provider, einen API-Schlüssel oder das Internet: Der Dienst lauscht auf 127.0.0.1.
Die Dateien
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 casesFühren Sie die Befehle in order-intake/ aus. Starten Sie den Dienst in einem zweiten Terminal und lassen Sie ihn laufen:
python server.py --port 8765intake service (v1) on http://127.0.0.1:8765/extractDer HTTP-Vertrag
Für jeden Fall sendet Oloproof eine Anfrage: den input des Falls als JSON-Body, mit der Methode, die Sie deklarieren (standardmäßig POST). Die Antwort liest es als JSON. Ein Status von 400 oder höher, ein Timeout oder eine abgelehnte Verbindung wird als Ausführungsfehler für diesen Fall erfasst, nie als falsche Antwort.
Anfrage und Antwort für einen Fall:
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 weist Oloproof an, nur result als Ausgabe des Falls zu behalten; ohne diese Angabe ist der ganze Body die Ausgabe. Ein Pfad mit Punkten wie data.answer reicht tiefer.
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_idEin HTTP-System muss eine version deklarieren. Oloproof kann ein Deployment nicht sehen: Es cacht die Ausgabe jedes Falls unter der URL, der Methode, dem Ausgabepfad und dieser Version, also teilen Sie ihm über die Version mit, dass sich der Dienst geändert hat. Vergessen Sie, sie zu ändern, wird ein neues Deployment nie aufgerufen.
Header und Authentifizierung lassen sich auf system.http noch nicht konfigurieren. Der Abschnitt über Tokens weiter unten zeigt den Umweg.
Der Datensatz
{"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 ist genau der Anfrage-Body. expected enthält die Referenz für jedes Feld; null ist ein echter Referenzwert und bedeutet "diese Nachricht enthält keine Auftragsnummer".
Die Evaluatoren wählen
| Kriterium | Evaluator | Braucht expected | Misst |
|---|---|---|---|
| format_valid | json_schema über die Ausgabe | nein | Format: ein bekannter Intent und eine wohlgeformte ID |
| intent_correct | exact_match auf intent | ja | Aufgabenerfolg für das erste Feld |
| order_id_correct | exact_match auf order_id | ja | Aufgabenerfolg für das zweite Feld |
Das Schema ließe {"intent": "other", "order_id": null} für jede Nachricht bestehen: wohlgeformt und nutzlos. Nur die Referenzprüfungen sagen, ob der Dienst seine Arbeit getan hat. Die Felder getrennt zu bewerten zeigt, welches fehlschlägt, was eine kombinierte Prüfung verbergen würde.
release.yaml erlaubt keinen Formatfehler und verlangt, dass jedes Feld in mindestens 70% der Fälle stimmt, beurteilt am 95%-Intervall:
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.70Ausführen
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 missJede Antwort ist wohlgeformt. Jedes Feld stimmt 15 von 20 Mal, aber 20 Fälle lassen ein Intervall bis hinunter zu 50,8%, also ist keine der beiden Untergrenzen von 70% belegt: INSUFFICIENT_EVIDENCE, und das Gate blockiert mit Exit 3.
Die Fehlschläge untersuchen
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: failedLesen Sie die Eingaben daneben (oloproof inspect RUN_ID --case m04), und zwei Fehler werden sichtbar: Das ID-Muster passt nur auf "order 1234", nicht auf "order #5120", "order no. 8123" oder ein bloßes "#1673"; und Formulierungen wie "send back", "stop order" und "where's my parcel" werden keinem Intent zugeordnet. Das ist der nächste Schritt: beide Regeln erweitern.
Eine Änderung deployen
Stoppen Sie den Dienst und starten Sie den Kandidaten, der diese Korrekturen enthält:
python server.py --port 8765 --rules v2Ändern Sie version: rules-v1 in version: rules-v2 in oloproof.yaml, denn die URL hat sich nicht geändert, und Oloproof würde sonst die alten Ausgaben wiederverwenden. Dann:
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 │Beide Untergrenzen bestehen, und der Befehl endet mit 0.
Die beiden Deployments vergleichen
compare.yaml fragt, ob die Intents des Kandidaten besser sind als die der Baseline:
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)Der Kandidat erfüllt seine eigenen Untergrenzen, und doch kann der Vergleich nicht zeigen, dass er besser ist: Fünf Fälle haben sich geändert, und über 20 gepaarte Fälle enthält das Intervall für den Gewinn noch null. Beide Aussagen sind zugleich wahr. "Erfüllt die Anforderung" und "schlägt die Baseline" sind getrennte Fragen, und eine so kleine Suite beantwortet die zweite nur bei großen Effekten. Mehr echte Nachrichten sind die Abhilfe.
Ein Dienst, der ein Token braucht
Starten Sie den Dienst so, dass er ein Bearer-Token verlangt:
INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-tokensystem.http sendet keine eigenen Header, daher wird mit einer neuen version (etwa rules-v2-auth) jeder Aufruf abgelehnt:
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 │und oloproof inspect RUN_ID --failures zeigt bei jedem Fall execution ERROR: TransientError: system returned HTTP 401. Ausführungsfehler sind fehlende Evidenz, keine Fehlschläge: Nichts wurde beobachtet, also ist jede Regel INSUFFICIENT_EVIDENCE.
Der Umweg ist ein Python-Callable, das die Anfrage selbst stellt. client.py fügt den Header aus einer Umgebungsvariable hinzu, sodass das Token nie in oloproof.yaml oder einen gespeicherten Datensatz gelangt:
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"]Ersetzen Sie den http:-Block in oloproof.yaml durch:
system:
name: order-intake
version: rules-v2
callable: client:extractund führen Sie mit dem Token in der Umgebung aus:
INTAKE_TOKEN=s3cret oloproof runAlle 20 Fälle werden wieder beobachtet, und das Gate lässt durch. Der Cache des Callables folgt seinem eigenen Quellcode und seiner version, nicht dem Dienst dahinter, daher gilt dieselbe Regel: Ändern Sie version, wenn Sie deployen.
Fehlerbehebung
| Symptom | Ursache und Abhilfe |
|---|---|
| system connection failed (ConnectError) bei jedem Fall | Der Dienst läuft nicht oder lauscht auf einem anderen Port. |
| system connection failed (RemoteProtocolError) | Etwas anderes antwortet auf diesem Port. Wählen Sie einen freien. |
| KeyError: "missing output path 'results'" | output_path nennt ein Feld, das die Antwort nicht hat. |
| system returned HTTP 401 oder 403 | Der Endpunkt braucht Zugangsdaten: Nutzen Sie den Umweg über ein Callable. |
| Sie haben eine Änderung deployt, und die Cache-Zeile zeigt nur Treffer | Die version hat sich nicht geändert, also wurden die gespeicherten Ausgaben wiederverwendet. |
| an HTTP system needs a declared version | Ergänzen Sie version unter system oder system.http. |
Einschränkungen
- Keine eigenen Header, keine Authentifizierung, keine Query-Parameter und keine Anfrage-Vorlagen auf system.http: Der input des Falls ist der JSON-Body, so wie er ist. Nutzen Sie für alles andere ein Callable.
- Antworten müssen JSON sein. Streaming-Antworten werden nicht als Stream gelesen.
- Oloproof kann ein Deployment nicht erkennen; die deklarierte version ist die gesamte Identität dessen, was geantwortet hat.
- Ihr Dienst verwaltet seinen Zustand selbst: Oloproof sendet Anfragen und erfasst Antworten; es setzt nichts zurück, isoliert nichts und macht nichts rückgängig, was eine Anfrage verändert.