Skip to content

Anleitungen

Fortschritt und Nebenläufigkeit

Eine Suite gegen ein produktives Modell dauert Minuten, und die meisten davon vergehen mit Warten auf das Modell. Diese Seite behandelt, wie viele Aufrufe Oloproof gleichzeitig offen hält, was es Ihnen während ihrer Ausführung zeigt und wie Sie herausfinden, wie viel weiteres Ausführen eine Regel entscheiden würde, die nicht entschieden hat.

Nebenläufigkeit

concurrency: in oloproof.yaml begrenzt, wie viele Aufrufe gleichzeitig offen sind:

concurrency:
  system: 4
  judge: 4

system begrenzt Aufrufe an das getestete System und ist standardmäßig 8. judge begrenzt Aufrufe an LLM-Judges und ist standardmäßig 4. Beide sind getrennt, weil die zwei meist hinter unterschiedlichen Rate-Limits stehen.

Einhundertzwanzig Fälle gegen ein System, das pro Aufruf eine Zehntelsekunde braucht:

system:Laufzeit
113,4 s
161,7 s
unverändert, erneut ausgeführt0,4 s

Die letzte Zeile ist der Cache, nicht die Nebenläufigkeit: Jede Ausführung wurde wiederverwendet. Die Nebenläufigkeit ist nicht Teil der Identität eines Systems, daher macht ihre Änderung nie ungültig, was gespeichert ist.

Jeder Fall ist ein eigener Aufruf. Oloproof bündelt Fälle nicht in die Batch-API eines Anbieters, daher ist ein Batch-Rabatt eines Anbieters darüber nicht verfügbar; Nebenläufigkeit und Cache sind es, die einen Lauf schneller machen.

Wiederholungen

Ein Judge-Anbieter oder ein HTTP-System, das mit 429 oder einem 5xx antwortet, in einen Timeout läuft oder die Verbindung abbricht, wird mit Backoff wiederholt, bis zu vier Versuche, und nie früher, als ein Retry-After-Header verlangt. Ein aufrufbares System nimmt daran teil, indem es TransientError aus oloproof mit retryable=True auslöst:

from oloproof import TransientError, system


@system(name="example-support-bot", version="1")
def answer(case):
    ...
    raise TransientError("provider timed out", retryable=True)

retryable ist standardmäßig False. Ohne diese Angabe wird der Fehler am Fall aufgezeichnet, der dann fehlt, statt wiederholt zu werden. Ein System, das bei jedem von dreißig Fällen beim ersten Aufruf so eine Ausnahme auslöste und beim zweiten antwortete:

│ exact_label │ 100.0%   │ [88.4%, 100.0%] │ 30 / 30 observed · 0 missing · 0 excluded │

Dasselbe System ohne retryable=True:

│ exact_label │          │ [0.0%, 100.0%] │ 0 / 0 observed · 30 missing · 0 excluded │

Was ein Lauf während der Ausführung zeigt

In einem Terminal zeichnet oloproof run eine Live-Ansicht auf der Standardfehlerausgabe neu: erledigte Fälle, Cache-Treffer, Fehler und eine vorläufige Schätzung für jede binäre Metrik. Ein Frame:

                    56/120 cases · 0 cached · 0 errors
┏━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Metric      ┃ Estimate ┃ Provisional interval ┃ Cases                   ┃
┡━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ exact_label │ 89.8%    │ [78.2%, 95.6%]       │ 49 observed · 0 missing │
└─────────────┴──────────┴──────────────────────┴─────────────────────────┘
     Provisional Wilson estimates over finished cases; not a decision.

Die Bildunterschrift ist die Regel. Ein vorläufiges Intervall ist ein Wilson-Intervall über das, was bereits fertig ist. Es lohnt sich, es zu beobachten, aber nicht, darauf zu entscheiden: Es wird neu berechnet, während Fälle eintreffen, und ein Intervall, das so lange wiederholt geprüft wird, bis es gut aussieht, ist kein 95-%-Intervall mehr. Nichts beendet einen Lauf deswegen vorzeitig. Die Entscheidung wird einmal getroffen, auf der fertigen Evidenz, mit der Methode, die die Policy nennt.

Außerhalb eines Terminals, etwa in der CI, wird die Live-Ansicht nicht gezeichnet, und die Tabellen werden einmal am Ende ausgegeben.

Der Ereignisstrom

--json schreibt denselben Fortschritt als ein JSON-Objekt pro Zeile auf die Standardausgabe und die Tabellen auf die Standardfehlerausgabe:

oloproof run --json

Ein Lauf mit 120 Fällen, in run.ndjson gespeichert und mit jq -r '.type' run.ndjson | sort | uniq -c gezählt, gibt aus:

 120 case_executed
 120 case_judged
  11 provisional_metrics
   1 run_finished
   2 run_phase_changed
   1 run_started

Jedes Ereignis trägt die Lauf-ID und einen Zeitstempel:

{"run_id":"run_01M3C3ZNXPVQJCDF31DJSBXBHC","timestamp":"2026-09-25T11:07:37.782628Z","type":"run_started","suite_digest":"sha256:c6ae32f25d38ddac175f688c15c40991c1e0ec5348f32bfabd9c493a3f688c28","cases":120}
{"run_id":"run_01M3C3ZNXPVQJCDF31DJSBXBHC","timestamp":"2026-09-25T11:07:37.885640Z","type":"case_executed","scenario_id":"q000","status":"OK","from_cache":false,"latency_ms":102.11420899941004}
{"run_id":"run_01M3C3ZNXPVQJCDF31DJSBXBHC","timestamp":"2026-09-25T11:07:37.885667Z","type":"case_judged","scenario_id":"q000","criterion":"exact_label","status":"OK","passed":true,"score":null,"from_cache":false}
{"run_id":"run_01M3C3ZNXPVQJCDF31DJSBXBHC","timestamp":"2026-09-25T11:07:41.362779Z","type":"run_finished","status":"DECIDED","completeness":"COMPLETE","exit_code":0}

provisional_metrics trägt das laufende Wilson-Intervall für jede binäre Metrik:

jq -c 'select(.type == "provisional_metrics") | [.cases_done, .metrics[0].estimate, .metrics[0].wilson_lower, .metrics[0].wilson_upper]' run.ndjson
[1,1.0,0.20654931411298355,1.0]
[13,0.8461538461538461,0.5776536895684791,0.9567418216820717]
[25,0.88,0.7004420606159933,0.9583318285288502]
[37,0.8918918918918919,0.7529146844205937,0.9571481006263428]

Schließen Sie die Pipe nicht vorzeitig. Ein Leser, der nach den ersten paar Zeilen aufhört, etwa head, beendet den Lauf, bevor dieser seine letzten Fälle speichert, und der Lauf wird als RUN_ERROR/PARTIAL aufgezeichnet.

Wie viel mehr es entscheiden würde

Eine Regel, die INSUFFICIENT_EVIDENCE lautet, ist nicht fehlgeschlagen; die Suite war zu klein, um das Ergebnis vom Schwellenwert zu trennen. oloproof plan sagt, wie viel größer sie sein müsste, beziffert anhand dessen, was der Lauf bereits verbraucht hat. Für das Beispiel examples/support_bot/ mit achtzehn Fällen:

oloproof plan RUN_ID --run
Run run_01M3C3WS0SBTFAG55M7ECM1EZ4
  observed  18 cases

exact-label-floor: about 1614 more cases would decide it, if the observed rate holds (1632 in total)
  time      <1s – 27s
  tokens    none reported by this run's providers
  assuming  the cases to come resemble the 18 already run
            cases run one after another; concurrency divides the time and not the cost

Die Bemessung einer Laufregel ist nur für Bestehensquoten zugelassen. Bei einem Mittelwert sagt der Plan das, statt zu raten:

Run run_01M3C3W5YWJY65X9YM6N02F3W4: no sample size can be computed for a rule that did not decide.
  error-budget: sizing a run rule is admitted for binary rates only, and days_error is a MEAN metric: a run stores its summary, not the per-case values sizing one would need

Mit einer Vergleichs-ID statt einer Lauf-ID plant er die Regeln des Vergleichs und braucht kein Flag.

Wie es weitergeht

denen diese Schätzungen beziffert werden.