Przejdź do treści

Przewodniki

Samouczek: aplikacja za punktem końcowym HTTP

Oceń usługę, do której docierasz przez HTTP, bez importowania jej kodu: skieruj Oloproof na URL, uruchom zestaw, znajdź, czego usługa nie trafia, wdróż zmianę i porównaj. Malutka lokalna usługa zastępuje Twoją, więc wszystko działa offline.

Co zbudujesz

Usługę przyjmowania zamówień, która czyta wiadomość klienta i wyodrębnia dwa pola: intent (where_is_order, cancel, return lub other) oraz order_id (cztery cyfry albo null). Sprawdzisz format każdej odpowiedzi, co nie wymaga wzorca, i zmierzysz, czy każde pole jest poprawne, co go wymaga. Terminy takie jak przypadek, przebieg, metryka i bramka są zdefiniowane w Pojęciach.

Wymagania wstępne

  • Python 3.11 lub nowszy oraz Oloproof w środowisku wirtualnym:
python3 -m venv .venv
. .venv/bin/activate
pip install oloproof
  • Przykładowy projekt, dostarczany z pakietem. Skopiuj go do nowego katalogu i pracuj tam (httpx, które importuje client.py, instaluje się razem z Oloproof):
oloproof init --example http order-intake
cd order-intake
  • Wolny port 8765 na tej maszynie. Jeśli jest zajęty, wybierz inny i zmień go zarówno w poleceniu serwera, jak i w oloproof.yaml.

Nic tutaj nie korzysta z dostawcy, klucza API ani internetu: usługa nasłuchuje na 127.0.0.1.

Pliki

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

Uruchamiaj polecenia z order-intake/. Uruchom usługę w drugim terminalu i zostaw ją włączoną:

python server.py --port 8765
intake service (v1) on http://127.0.0.1:8765/extract

Kontrakt HTTP

Dla każdego przypadku Oloproof wysyła jedno żądanie: input przypadku jako treść JSON, z metodą, którą zadeklarujesz (domyślnie POST). Odpowiedź czyta jako JSON. Status 400 lub wyższy, przekroczenie limitu czasu lub odrzucone połączenie jest zapisywane jako błąd wykonania dla tego przypadku, nigdy jako błędna odpowiedź.

Żądanie i odpowiedź dla jednego przypadku:

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 mówi Oloproof, by jako wynik przypadku zachował tylko result; bez tego wynikiem jest cała treść. Ścieżka z kropkami, taka jak data.answer, sięga głębiej.

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_id

System HTTP musi zadeklarować version. Oloproof nie widzi wdrożenia: cache'uje wynik każdego przypadku według URL, metody, ścieżki wyniku i tej wersji, więc wersja to sposób, w jaki mówisz mu, że usługa się zmieniła. Zapomnij ją zmienić, a nowe wdrożenie nigdy nie zostanie wywołane.

Nagłówków i uwierzytelniania nie da się jeszcze skonfigurować w system.http. Sekcja o tokenach poniżej pokazuje obejście.

Zbiór danych

{"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 to dokładnie treść żądania. expected zawiera wzorzec dla każdego pola; null to prawdziwa wartość wzorcowa, oznaczająca "w tej wiadomości nie ma numeru zamówienia".

Wybór ewaluatorów

KryteriumEwaluatorWymaga expectedMierzy
format_validjson_schema na wynikunieformat: znana intencja i poprawnie zbudowany identyfikator
intent_correctexact_match na intenttaksukces zadania dla pierwszego pola
order_id_correctexact_match na order_idtaksukces zadania dla drugiego pola

Schemat przepuściłby {"intent": "other", "order_id": null} dla każdej wiadomości: poprawnie zbudowane i bezużyteczne. Tylko sprawdzenia względem wzorca mówią, czy usługa wykonała swoje zadanie. Ocenianie pól osobno pokazuje, które z nich zawodzi, co jedno łączne sprawdzenie by ukryło.

release.yaml nie dopuszcza żadnego błędu formatu i wymaga, by każde pole było poprawne w co najmniej 70% przypadków, ocenianych na przedziale 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

Uruchom

oloproof run
Run 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

Każda odpowiedź jest poprawnie zbudowana. Każde pole jest poprawne 15 razy na 20, ale 20 przypadków zostawia przedział sięgający w dół do 50,8%, więc żaden z progów 70% nie jest wykazany: INSUFFICIENT_EVIDENCE, a bramka blokuje z kodem 3.

Przejrzyj niepowodzenia

oloproof inspect RUN_ID --failures
8 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

Przeczytaj obok nich wejścia (oloproof inspect RUN_ID --case m04), a pojawią się dwa błędy: wzorzec identyfikatora dopasowuje tylko "order 1234", a nie "order #5120", "order no. 8123" ani samo "#1673"; a sformułowania takie jak "send back", "stop order" i "where's my parcel" nie odpowiadają żadnej intencji. To jest następne działanie: poszerz obie reguły.

Wdróż zmianę

Zatrzymaj usługę i uruchom kandydata, który zawiera te poprawki:

python server.py --port 8765 --rules v2

Zmień version: rules-v1 na version: rules-v2 w oloproof.yaml, ponieważ URL się nie zmienił, a Oloproof w przeciwnym razie użyłby ponownie starych wyników. Następnie:

oloproof run
Gate: 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 │

Oba progi przechodzą, a polecenie kończy się kodem 0.

Porównaj dwa wdrożenia

compare.yaml pyta, czy intencje kandydata są lepsze niż intencje punktu odniesienia:

version: 1
confidence_level: 0.95
block_on: [FAIL, INSUFFICIENT_EVIDENCE, MANUAL_REVIEW]
rules:
  - id: intent-better
    kind: superiority
    metric: intent_correct
oloproof compare CANDIDATE_RUN_ID BASELINE_RUN_ID --policy compare.yaml
format_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)

Kandydat spełnia własne progi, a jednak porównanie nie może wykazać, że jest lepszy: zmieniło się pięć przypadków, a na 20 sparowanych przypadkach przedział zysku nadal obejmuje zero. Oba stwierdzenia są prawdziwe jednocześnie. "Spełnia wymaganie" i "pokonuje punkt odniesienia" to osobne pytania, a tak mały zestaw odpowiada na drugie tylko przy dużych efektach. Lekarstwem jest więcej prawdziwych wiadomości.

Usługa, która wymaga tokena

Uruchom usługę tak, aby żądała tokena bearer:

INTAKE_TOKEN=s3cret python server.py --port 8765 --rules v2 --require-token

system.http nie wysyła własnych nagłówków, więc przy nowej version (na przykład rules-v2-auth) każde wywołanie jest odrzucane:

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 │

a oloproof inspect RUN_ID --failures pokazuje execution ERROR: TransientError: system returned HTTP 401 przy każdym przypadku. Błędy wykonania to brakujące dowody, a nie niepowodzenia: niczego nie zaobserwowano, więc każda reguła ma stan INSUFFICIENT_EVIDENCE.

Obejściem jest funkcja wywoływalna w Pythonie, która sama wykonuje żądanie. client.py dodaje nagłówek ze zmiennej środowiskowej, więc token nigdy nie trafia do oloproof.yaml ani do żadnego zapisanego rekordu:

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"]

Zastąp blok http: w oloproof.yaml tym:

system:
  name: order-intake
  version: rules-v2
  callable: client:extract

i uruchom z tokenem w środowisku:

INTAKE_TOKEN=s3cret oloproof run

Wszystkie 20 przypadków jest znowu obserwowanych, a bramka przepuszcza. Cache funkcji podąża za jej własnym kodem źródłowym i version, a nie za usługą za nią, więc obowiązuje ta sama zasada: zmień version, gdy wdrażasz.

Rozwiązywanie problemów

ObjawPrzyczyna i rozwiązanie
system connection failed (ConnectError) przy każdym przypadkuUsługa nie działa albo nasłuchuje na innym porcie.
system connection failed (RemoteProtocolError)Na tym porcie odpowiada coś innego. Wybierz wolny.
KeyError: "missing output path 'results'"output_path wskazuje pole, którego odpowiedź nie ma.
system returned HTTP 401 lub 403Punkt końcowy wymaga poświadczeń: użyj obejścia z funkcją wywoływalną.
Wdrożyłeś zmianę, a wiersz cache pokazuje same trafieniaversion się nie zmieniła, więc użyto ponownie zapisanych wyników.
an HTTP system needs a declared versionDodaj version pod system lub system.http.

Ograniczenia

  • Brak własnych nagłówków, uwierzytelniania, parametrów zapytania i szablonów żądań w system.http: input przypadku jest treścią JSON w obecnej postaci. Do wszystkiego innego użyj funkcji wywoływalnej.
  • Odpowiedzi muszą być w JSON. Odpowiedzi strumieniowe nie są czytane jako strumień.
  • Oloproof nie potrafi wykryć wdrożenia; zadeklarowana version to cała tożsamość tego, co odpowiedziało.
  • Twoja usługa jest właścicielem swojego stanu: Oloproof wysyła żądania i zapisuje odpowiedzi; nie resetuje, nie izoluje ani nie wycofuje niczego, co zmienia żądanie.