Handleidingen
Tutorial: een applicatie achter een HTTP-endpoint
Evalueer een dienst die je via HTTP bereikt, zonder zijn code te importeren: richt Oloproof op de URL, draai de suite, vind wat hij mist, deploy een wijziging en vergelijk. Een kleine lokale dienst staat in voor de jouwe, dus alles draait offline.
Wat je gaat bouwen
Een dienst voor orderintake die een klantbericht leest en twee velden extraheert: een intent (where_is_order, cancel, return of other) en een order_id (vier cijfers, of null). Je controleert het formaat van elk antwoord, waarvoor geen referentie nodig is, en meet of elk veld juist is, waarvoor wel. Termen als case, run, metriek en gate worden gedefinieerd in Kernbegrippen.
Vereisten
- Python 3.11 of nieuwer, en Oloproof in een virtuele omgeving:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof- Het voorbeeldproject, dat met het pakket meekomt. Kopieer het naar een nieuwe map en werk daar (httpx, dat client.py importeert, wordt met Oloproof geïnstalleerd):
oloproof init --example http order-intake
cd order-intake- Poort 8765 vrij op deze machine. Is die bezet, kies dan een andere en wijzig die zowel in het servercommando als in oloproof.yaml.
Niets hier gebruikt een provider, een API-sleutel of het internet: de dienst luistert op 127.0.0.1.
De bestanden
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 casesVoer de commando's uit vanuit order-intake/. Start de dienst in een tweede terminal en laat hem draaien:
python server.py --port 8765intake service (v1) on http://127.0.0.1:8765/extractHet HTTP-contract
Voor elke case stuurt Oloproof één verzoek: de input van de case als JSON-body, met de methode die je declareert (standaard POST). Het leest het antwoord als JSON. Een status van 400 of hoger, een time-out of een geweigerde verbinding wordt voor die case vastgelegd als uitvoeringsfout, nooit als fout antwoord.
Verzoek en antwoord voor één case:
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 vertelt Oloproof om alleen result als uitvoer van de case te houden; zonder dat is de hele body de uitvoer. Een pad met punten zoals data.answer gaat dieper.
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_idEen HTTP-systeem moet een version declareren. Oloproof kan een deployment niet zien: het cachet de uitvoer van elke case onder de URL, de methode, het uitvoerpad en die versie, dus de versie is hoe je het vertelt dat de dienst veranderde. Vergeet je die te wijzigen, dan wordt een nieuwe deployment nooit aangeroepen.
Headers en authenticatie kunnen nog niet op system.http worden ingesteld. Het deel over tokens hieronder toont de omweg.
De dataset
{"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 is precies de body van het verzoek. expected bevat de referentie voor elk veld; null is een echte referentiewaarde, die betekent "er staat geen ordernummer in dit bericht".
De evaluators kiezen
| Criterium | Evaluator | Heeft expected nodig | Meet |
|---|---|---|---|
| format_valid | json_schema over de uitvoer | nee | formaat: een bekende intent en een goedgevormd id |
| intent_correct | exact_match op intent | ja | taaksucces voor het eerste veld |
| order_id_correct | exact_match op order_id | ja | taaksucces voor het tweede veld |
Het schema zou {"intent": "other", "order_id": null} voor elk bericht laten slagen: goedgevormd en nutteloos. Alleen de referentiecontroles zeggen of de dienst zijn werk deed. De velden apart scoren laat zien welk veld faalt, wat één gecombineerde controle zou verbergen.
release.yaml staat geen formaatfout toe en vraagt dat elk veld in minstens 70% van de gevallen juist is, beoordeeld op het 95%-interval:
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.70Draaien
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 missElk antwoord is goedgevormd. Elk veld is 15 van de 20 keer juist, maar 20 cases laten een interval tot 50.8% over, dus geen van beide ondergrenzen van 70% is aangetoond: INSUFFICIENT_EVIDENCE, en de gate blokkeert met exit 3.
De mislukkingen bekijken
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: failedLees de inputs ernaast (oloproof inspect RUN_ID --case m04) en er verschijnen twee fouten: het id-patroon past alleen op "order 1234", niet op "order #5120", "order no. 8123" of een kale "#1673"; en formuleringen als "send back", "stop order" en "where's my parcel" passen bij geen intent. Dat is de volgende stap: verbreed beide regels.
Een wijziging deployen
Stop de dienst en start de kandidaat, die die oplossingen bevat:
python server.py --port 8765 --rules v2Wijzig version: rules-v1 in version: rules-v2 in oloproof.yaml, omdat de URL niet veranderde en Oloproof anders de oude uitvoer zou hergebruiken. Daarna:
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 ondergrenzen slagen en het commando eindigt met 0.
De twee deployments vergelijken
compare.yaml vraagt of de intents van de kandidaat beter zijn dan die van de 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)De kandidaat haalt zijn eigen ondergrenzen, maar de vergelijking kan niet aantonen dat hij beter is: vijf cases veranderden, en over 20 gepaarde cases bevat het interval voor de winst nog steeds nul. Beide uitspraken zijn tegelijk waar. "Voldoet aan de eis" en "verslaat de baseline" zijn aparte vragen, en een suite zo klein beantwoordt de tweede alleen voor grote effecten. Meer echte berichten is de remedie.
Een dienst die een token nodig heeft
Start de dienst zo dat hij een bearer-token eist:
INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-tokensystem.http stuurt geen eigen headers, dus met een nieuwe version (zeg rules-v2-auth) wordt elke aanroep geweigerd:
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 │en oloproof inspect RUN_ID --failures toont execution ERROR: TransientError: system returned HTTP 401 bij elke case. Uitvoeringsfouten zijn ontbrekend bewijs, geen mislukkingen: er werd niets waargenomen, dus elke regel is INSUFFICIENT_EVIDENCE.
De omweg is een Python-callable die het verzoek zelf doet. client.py voegt de header toe vanuit een omgevingsvariabele, zodat het token nooit in oloproof.yaml of een opgeslagen record komt:
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"]Vervang het http:-blok in oloproof.yaml door:
system:
name: order-intake
version: rules-v2
callable: client:extracten draai met het token in de omgeving:
INTAKE_TOKEN=s3cret oloproof runAlle 20 cases worden weer waargenomen en de gate laat door. De cache van de callable volgt zijn eigen broncode en version, niet de dienst erachter, dus dezelfde regel geldt: wijzig version wanneer je deployt.
Problemen oplossen
| Symptoom | Oorzaak en oplossing |
|---|---|
| system connection failed (ConnectError) bij elke case | De dienst draait niet, of luistert op een andere poort. |
| system connection failed (RemoteProtocolError) | Iets anders antwoordt op die poort. Kies een vrije. |
| KeyError: "missing output path 'results'" | output_path noemt een veld dat het antwoord niet heeft. |
| system returned HTTP 401 of 403 | Het endpoint heeft inloggegevens nodig: gebruik de omweg met de callable. |
| Je deployde een wijziging en de cacheregel toont alleen hits | De version veranderde niet, dus de opgeslagen uitvoer werd hergebruikt. |
| an HTTP system needs a declared version | Voeg version toe onder system of system.http. |
Beperkingen
- Geen eigen headers, authenticatie, queryparameters of verzoeksjablonen op system.http: de input van de case is de JSON-body zoals hij is. Gebruik voor al het andere een callable.
- Antwoorden moeten JSON zijn. Streamende antwoorden worden niet als stream gelezen.
- Oloproof kan een deployment niet detecteren; de gedeclareerde version is de hele identiteit van wat antwoordde.
- Je dienst beheert zijn eigen toestand: Oloproof stuurt verzoeken en legt antwoorden vast; het reset, sandboxt of draait niets terug dat een verzoek wijzigt.