Skip to content

Guias

Progresso e concorrência

Uma suíte contra um modelo real leva minutos, e a maior parte deles é gasta esperando o modelo. Esta página trata de quantas chamadas o Oloproof mantém em andamento, do que ele mostra enquanto elas rodam e de como descobrir quanto mais de execução resolveria uma regra que não decidiu.

Concorrência

concurrency: em oloproof.yaml limita quantas chamadas ficam em andamento ao mesmo tempo:

concurrency:
  system: 4
  judge: 4

system limita as chamadas ao sistema sob teste e tem 8 como padrão. judge limita as chamadas a juízes LLM e tem 4 como padrão. Eles são separados porque os dois normalmente ficam atrás de limites de taxa diferentes.

Cento e vinte casos contra um sistema que leva um décimo de segundo por chamada:

system:Tempo decorrido
113,4 s
161,7 s
sem alteração, reexecutado0,4 s

A última linha é o cache, não a concorrência: todas as execuções foram reaproveitadas. A concorrência não faz parte da identidade de um sistema, então alterá-la nunca invalida o que está armazenado.

Cada caso é uma chamada própria. O Oloproof não agrupa casos na API de lote de um provedor, então o desconto de lote de um provedor não está disponível por meio dele; a concorrência e o cache são o que tornam uma execução mais rápida.

Novas tentativas

Um provedor de juiz ou um sistema HTTP que responde 429 ou um 5xx, estoura o tempo limite ou derruba a conexão é tentado de novo com backoff, até quatro tentativas, e nunca antes do que um cabeçalho Retry-After pede. Um sistema callable adere a isso levantando TransientError de oloproof com retryable=True:

from oloproof import TransientError, system


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

retryable tem False como padrão. Sem ele, o erro é registrado no caso, que então fica ausente em vez de ser tentado de novo. Um sistema que levantou a exceção dessa forma na primeira chamada de cada um de trinta casos, e respondeu na segunda:

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

O mesmo sistema com retryable=True removido:

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

O que uma execução mostra enquanto roda

Em um terminal, oloproof run redesenha uma visualização ao vivo na saída de erro padrão: casos concluídos, acertos de cache, erros e uma estimativa provisória para cada métrica binária. Um quadro:

                    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.

A legenda é a regra. Um intervalo provisório é um intervalo de Wilson sobre o que já terminou, o que é útil de acompanhar e não algo com base no qual decidir: ele é recalculado à medida que os casos chegam, e um intervalo verificado repetidamente até parecer bom deixa de ser um intervalo de 95%. Nada interrompe uma execução antecipadamente com base nele. A decisão é tomada uma única vez, sobre a evidência concluída, com o método que a política nomeia.

Fora de um terminal, como no CI, a visualização ao vivo não é desenhada e as tabelas são impressas uma única vez, no final.

O fluxo de eventos

--json grava o mesmo progresso como um objeto JSON por linha na saída padrão, e as tabelas na saída de erro padrão:

oloproof run --json

Uma execução de 120 casos, salva em run.ndjson e contada com jq -r '.type' run.ndjson | sort | uniq -c, emite:

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

Cada um traz o id da execução e um timestamp:

{"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 traz o intervalo de Wilson corrente para cada métrica binária:

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]

Não feche o pipe antes da hora. Um leitor que para depois das primeiras linhas, como head, encerra a execução antes que ela armazene os seus últimos casos, e a execução é registrada como RUN_ERROR/PARTIAL.

Quanto mais seria necessário para decidir

Uma regra que mostra INSUFFICIENT_EVIDENCE não falhou; a suíte era pequena demais para separar o resultado do limiar. oloproof plan diz o quanto maior ela precisaria ser, com o preço calculado a partir do que a execução já gastou. Para o examples/support_bot/ de dezoito casos:

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

O dimensionamento de uma regra de execução só é admitido para taxas de aprovação/reprovação. Em uma média, o plano diz isso em vez de chutar:

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

Se receber o id de uma comparação, ele planeja as regras da comparação e não precisa de flag.

Próximos passos

dos quais essas estimativas são calculadas.