Guide
Esperimenti online (test A/B)
Il tuo confronto offline ha detto che la nuova versione è migliore. Un test A/B in produzione pone la stessa domanda a utenti reali. Il tuo router assegna a caso le unità (utenti o sessioni) tra due bracci; oloproof experiment legge i suoi log e risponde con gli stessi quattro stati di ogni altra regola. Il metodo è ammesso da un audit indipendente a condizioni dichiarate: observed_until è la tua parola su quando è stato estratto il log degli esiti, le unità sono state randomizzate in modo indipendente, l'esito di nessuna unità dipende dal braccio di un'altra, gli esiti non sono cambiati mentre cambiava la ripartizione, e ogni sguardo legge l'intero log. Al di fuori di esse la risposta non è coperta.
I log e la dichiarazione
Due file, CSV o JSONL: un log di assegnazione con una riga per unità (la colonna di unità che dichiari, e arm), e un log degli esiti con l'unità e il suo value.
experiment:
name: checkout-v2
unit: user
allocation: {control: 0.5, treatment: 0.5}
control: control
treatment: treatment
assignments: logs/assignments.csv
outcomes: logs/outcomes.csv
kind: rate
rules:
- {id: lift, kind: superiority}
- {id: no-harm, kind: non_inferiority, margin: 0.01}oloproof experiment experiment.yaml --policy release.yaml --jsonkind: mean accetta un range: [low, high] dichiarato; direction: max considera migliore il valore più basso. Un'unità assegnata senza riga di esito è mancante e viene limitata al suo valore peggiore su ciascun lato, a meno che missing_outcome: zero dica che nessuna riga significa zero. Registra una riga per ogni unità, zeri inclusi: un registro di conversioni che contiene solo chi ha convertito legge ogni altra unità come mancante, e la risposta resta INSUFFICIENT_EVIDENCE. Il riempimento con zero presuppone che la finestra di esito di ogni unità contata sia chiusa; un'unità assegnata un'ora fa non ha ancora convertito, e contarla come zero favorisce il braccio che converte più in fretta. Dichiara outcome_window_hours, as_of e observed_until (quando è stato estratto il registro degli esiti), con una colonna assigned_at nel registro di assegnazione, e vengono contate solo le unità la cui finestra si è chiusa entro as_of; missing_outcome: zero è rifiutato senza di essi, qualunque sia il monitoraggio. Scrivi ogni orario con uno scostamento o Z (2026-10-11T00:00:00Z); un orario senza è rifiutato. as_of non può essere successivo a observed_until, e nessuno dei due può essere nel futuro: un esito successivo all'estrazione non è nel registro. La finestra sceglie la domanda: una finestra di 48 ore risponde a «ha convertito entro 48 ore», quindi deve coprire il ritardo dell'esito che intendi. Gli id delle unità sono confrontati come testo dopo aver tolto gli spazi, con 42.0 letto come 42. Il codice di uscita è quello della regola peggiore: 0 PASS, 1 FAIL, 3 INSUFFICIENT_EVIDENCE, 4 MANUAL_REVIEW.
Cosa controlla per primo
- Rapporto del campione. I conteggi di unità dei bracci sono testati contro l'allocazione dichiarata (test binomiale esatto con due bracci, chi quadrato con più, soglia 0,001). Uno scostamento rende ogni regola MANUAL_REVIEW: una suddivisione rotta significa che i bracci non sono più confrontabili, e altri dati dello stesso tipo non la riparano. Trova la causa (un reindirizzamento che perde utenti, un filtro anti-bot applicato a un solo braccio) prima di leggere qualsiasi differenza.
- Un'unità in due bracci. L'assegnazione è trapelata; ogni regola si legge MANUAL_REVIEW.
- Più righe di esito per un'unità. Rifiutato, perché gli eventi di un utente non sono indipendenti. Dichiara aggregate: unit_mean perché la media propria di ogni unità sia il suo esito: la risposta riguarda allora l'utente medio, non l'evento medio.
- Un braccio vuoto. INSUFFICIENT_EVIDENCE finché non ha esiti.
- Righe di esito che non corrispondono a nessuna unità assegnata, con missing_outcome: zero. Gli zeri poggiano sul join, quindi un join rotto leggerebbe unità reali come zeri; ogni regola legge MANUAL_REVIEW finché gli id non concordano. Con il predefinito sono contate e lasciate fuori.
Come decide
La media di ciascun braccio riceve l'intervallo che Oloproof usa già per un'esecuzione (Clopper-Pearson esatto per un tasso, altrimenti la media limitata con scommessa), ciascuno con metà del budget di errore, e la differenza è limitata dagli estremi opposti dei due. Vale a qualsiasi dimensione del campione ed è più largo di un intervallo normale da manuale. Una regola di superiorità passa quando l'intero intervallo è sopra lo zero; una regola di non inferiorità passa quando il suo estremo inferiore è pari o superiore a meno il margine.
monitoring: anytime usa l'intervallo con scommessa per entrambi i bracci, che vale per quante volte tu guardi e ovunque ti fermi. Vale a patto che ogni lettura passi l'intero registro fino a quel momento: non scartare né filtrare mai righe per periodo dopo averle lette. Il predefinito, fixed, si legge una volta alla dimensione del campione pianificata.
Più trattamenti
Dichiara treatments: [b, c] invece di treatment: e assegna a ogni regola il trattamento che confronta (treatment: b). L'intervallo di ogni braccio è preso a 1 - alpha/m per gli m bracci, così ogni trattamento meno il controllo è coperto insieme dal limite dell'unione: una regola per trattamento, un'affermazione ciascuna, e la famiglia regge insieme. Ogni intervallo è più largo che con due bracci, perché ogni braccio paga per gli altri. Con più trattamenti, il report nomina il migliore tra quelli che passano ogni regola che li nomina, e se è separato da ogni altro trattamento che passa: ogni differenza, limitata dagli stessi intervalli simultanei, esclude lo zero. I pareggi mantengono l'ordine dichiarato e non sono mai separati. Il controllo del rapporto del campione legge già ogni braccio. La garanzia congiunta copre i bracci di un solo file di esperimento: dividere i trattamenti su più file vi rinuncia.
Metriche a rapporto
Un tasso di clic è clic su sessioni, e un utente con molte sessioni non è molti utenti indipendenti. Dichiara il rapporto su una riga per unità, con intervalli di valori in cui stanno i valori di ogni unità:
ratio: {numerator: clicks, denominator: sessions, numerator_range: [0, 50], denominator_range: [1, 50]}
aggregate: unit_sum # only if a unit has several rows: they are addedLa stima obiettivo è il numeratore medio di ogni braccio sul suo denominatore medio. Entrambe le medie sono limitate dall'intervallo ammesso, ciascuna con la sua quota del budget di errore, e il rapporto dividendo i loro estremi, così vale a ogni dimensione del campione senza approssimazione normale. Un denominatore il cui intervallo raggiunge lo zero non dà alcun intervallo. Un valore fuori dal suo intervallo di valori è un errore di configurazione che nomina l'unità, mai troncato. L'intervallo del metodo delta, di solito più stretto, è mostrato accanto e non decide mai.
Una covariata da prima del test
Un esito che segue qualcosa misurato prima del test (la spesa del mese scorso, le sessioni della settimana scorsa) varia meno una volta tolto quello. Porta il valore di ogni unità da prima del test e un coefficiente fissato prima che si leggano gli esiti di questo test:
covariate: {values: logs/pre_period.csv, range: [0, 500], theta_from: logs/history.csv}L'esito deciso è Y - theta (X - c). theta è dichiarato (theta:, con un center: facoltativo, per impostazione predefinita il centro dell'intervallo di valori), oppure stimato su un file storico di un periodo prima del test (colonne covariate e outcome), mai sugli esiti di questo stesso test. Poiché X è stato misurato prima dell'assegnazione, e anche quali unità hanno un X era fissato prima dell'assegnazione, la differenza aggiustata ha la stessa stima obiettivo. Un'unità senza valore nel file non viene aggiustata. Un aggiustamento valido può comunque essere più largo di nessun aggiustamento, perché l'intervallo di valori dell'esito aggiustato è più largo, quindi il report mostra entrambi gli intervalli e dice quale è più stretto; non dichiara alcun risparmio.
Novità e interferenza, mostrate
novelty: {exposure_column: exposed_at, time_column: at, days: 7} legge la prima esposizione di ogni unità dal log di assegnazione e il tempo di ogni riga di esito, e mostra la differenza nei primi days di esposizione e dopo. Una differenza iniziale decisa con una successiva non decisa o opposta è chiamata "possibly a novelty effect". La suddivisione non decide mai una regola: le unità senza righe in una fascia ne restano fuori, non limitate al loro peggio. Accetta un esito per riga, non un rapporto né una covariata.
cluster_column: team conta i team che contengono unità di più bracci e chiede se l'esito di un'unità possa dipendere dal braccio di un'altra. Se può, randomizza per team e dichiaralo come unità.
Più trattamenti, rapporti e covariate sono stati ammessi da un audit indipendente e dalla sua conferma il 3 novembre 2026, alle stesse condizioni di due bracci.
Offline e online
Indica il confronto offline e la sua metrica (offline_comparison: <id>, offline_metric: pass_rate) e la risposta riporta se le due direzioni decise concordano. È riportato e non decide mai nulla: un disaccordo è un motivo per guardare la suite, non un verdetto sul rilascio.
Insidie
- Sbirciare. Leggere ogni giorno un test a orizzonte fisso e fermarsi in un giorno buono gonfia le false vittorie. Pianifica la dimensione del campione, oppure dichiara monitoring: anytime.
- Novità e apprendimento. Gli utenti reagiscono a qualsiasi novità, poi si assestano. Un guadagno della prima settimana può svanire; lascia girare abbastanza a lungo da coprire i cicli dei tuoi utenti, e dichiara novelty: per vedere la suddivisione.
- Interferenza. L'intervallo presuppone che l'esito di un'unità non dipenda dal braccio di un'altra. Inventario condiviso, marketplace, funzioni social o una cache scaldata da un braccio lo rompono; in quel caso randomizza per un'unità più grande (una regione, un team), e dichiara cluster_column: per vedere quanto sono misti i cluster.
- Deriva con una suddivisione che cambia. L'intervallo presuppone che l'allocazione sia rimasta costante durante il test, o che gli esiti non siano derivati mentre cambiava. Una rampa dal 10% al 50%, oppure 90/10 e poi 10/90 tra un giorno feriale e un fine settimana, pesa i periodi in modo diverso in ciascun braccio e mescola un effetto del tempo nella differenza; il controllo del rapporto del campione legge conteggi aggregati e non lo vede. Tieni fissa la suddivisione, oppure decidi solo sul periodo dopo la rampa.
- Cambiare l'unità. Randomizza e analizza per la stessa unità. Randomizzare utenti e contare le sessioni come indipendenti restringe l'intervallo in modo falso.
Cosa questo non può dirti
- Se il risultato vale per utenti fuori dalla finestra, dalla regione o dal traffico del test.
- Nulla su una metrica che non hai dichiarato prima del test; una metrica scelta a posteriori è una descrizione.
- Se il tuo router ha davvero randomizzato: il controllo del rapporto del campione individua gli errori grandi, non quelli sottili.
- Quale trattamento sia il migliore tra quelli che non hanno passato: solo i trattamenti che passano sono ordinati. L'allocazione stratificata e i disegni switchback non sono costruiti.