Pular para o conteúdo

Guias

Experimentos online (testes A/B)

Sua comparação offline disse que a nova versão é melhor. Um teste A/B em produção faz a mesma pergunta a usuários reais. Seu próprio roteador distribui aleatoriamente unidades (usuários ou sessões) entre dois braços; oloproof experiment lê os logs dele e responde com os mesmos quatro estados de qualquer outra regra. O método é admitido por uma auditoria independente em termos declarados: observed_until é a sua palavra sobre quando o log de resultados foi extraído, as unidades foram aleatorizadas de forma independente, o resultado de nenhuma unidade depende do braço de outra, os resultados não derivaram enquanto a divisão mudava, e cada olhada lê o log inteiro. Fora deles, a resposta não está coberta.

Os logs e a declaração

Dois arquivos, CSV ou JSONL: um log de atribuição com uma linha por unidade (a coluna de unidade que você declara, e arm), e um log de resultados com a unidade e seu 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 --json

kind: mean recebe um range: [low, high] declarado; direction: max lê o menor como melhor. Uma unidade atribuída sem linha de resultado está ausente e é limitada ao seu pior valor em cada lado, a menos que missing_outcome: zero diga que nenhuma linha significa zero. Registre uma linha para cada unidade, zeros incluídos: um log de conversões que só contém quem converteu lê todas as outras unidades como ausentes, e a resposta fica em INSUFFICIENT_EVIDENCE. Preencher com zero supõe que a janela de resultado de cada unidade contada se fechou; uma unidade atribuída há uma hora ainda não converteu, e contá-la como zero favorece o braço que converte mais rápido. Declare outcome_window_hours, as_of e observed_until (quando o log de resultados foi extraído), com uma coluna assigned_at no log de atribuição, e só são contadas as unidades cuja janela se fechou até as_of; missing_outcome: zero é recusado sem eles, seja qual for o monitoramento. Escreva todo horário com um deslocamento ou Z (2026-10-11T00:00:00Z); um horário sem nenhum dos dois é recusado. as_of não pode ser posterior a observed_until, e nenhum dos dois pode estar no futuro: um resultado após a extração não está no log. A janela escolhe a pergunta: uma janela de 48 horas responde "converteu em até 48 horas", então ela deve cobrir o atraso do resultado que você quer dizer. Os ids de unidade são comparados como texto após remover espaços, com 42.0 lido como 42. O código de saída é o da pior regra: 0 PASS, 1 FAIL, 3 INSUFFICIENT_EVIDENCE, 4 MANUAL_REVIEW.

O que ele verifica primeiro

  • Proporção da amostra. As contagens de unidades dos braços são testadas contra a alocação declarada (teste binomial exato para dois braços, qui-quadrado para mais, limiar 0,001). Um descompasso torna toda regra MANUAL_REVIEW: uma divisão quebrada significa que os braços não são mais comparáveis, e mais dados do mesmo tipo não a consertam. Encontre a causa (um redirecionamento que perde usuários, um filtro de bots aplicado a um só braço) antes de ler qualquer diferença.
  • Uma unidade em dois braços. A atribuição vazou; toda regra lê MANUAL_REVIEW.
  • Várias linhas de resultado para uma unidade. Recusado, porque eventos de um mesmo usuário não são independentes. Declare aggregate: unit_mean para que a média própria de cada unidade seja seu resultado: a resposta passa a ser sobre o usuário médio, não sobre o evento médio.
  • Um braço vazio. INSUFFICIENT_EVIDENCE até que tenha resultados.
  • Linhas de resultado que não correspondem a nenhuma unidade atribuída, sob missing_outcome: zero. Os zeros dependem da junção, então uma junção quebrada leria unidades reais como zeros; toda regra lê MANUAL_REVIEW até que os ids concordem. No padrão, elas são contadas e deixadas de fora.

Como ele decide

A média de cada braço recebe o intervalo que o Oloproof já usa para uma execução (Clopper-Pearson exato para uma taxa, a média limitada por apostas nos outros casos), cada um com metade do orçamento de erro, e a diferença é limitada pelas extremidades opostas dos dois. Vale em qualquer tamanho de amostra e é mais largo que um intervalo normal de livro-texto. Uma regra de superioridade passa quando todo o intervalo está acima de zero; uma regra de não inferioridade passa quando sua extremidade inferior está em ou acima de menos a margem.

monitoring: anytime usa o intervalo por apostas nos dois braços, que vale não importa quantas vezes você olhe nem quando pare. Isso vale desde que cada olhada passe o log inteiro até aquele momento: nunca descarte nem filtre linhas por período depois de lê-las. O padrão, fixed, é lido uma vez no tamanho de amostra que você planejou.

Vários tratamentos

Declare treatments: [b, c] em vez de treatment: e dê a cada regra o tratamento que ela compara (treatment: b). O intervalo de cada braço é tomado em 1 - alpha/m para os m braços, então cada tratamento menos o controle é coberto ao mesmo tempo pela cota da união: uma regra por tratamento, uma afirmação cada, e a família se sustenta junta. Cada intervalo é mais largo do que com dois braços, porque cada braço paga pelos outros. Com vários tratamentos, o relatório nomeia o melhor entre os que passam em todas as regras que os nomeiam, e se ele está separado de cada outro tratamento que passa: cada diferença, limitada pelos mesmos intervalos simultâneos, exclui o zero. Empates mantêm a ordem declarada e nunca são separados. A verificação da proporção da amostra já lê todos os braços. A garantia conjunta cobre os braços de um único arquivo de experimento: dividir os tratamentos entre vários arquivos abre mão dela.

Métricas de razão

Uma taxa de cliques é cliques sobre sessões, e um usuário com muitas sessões não é muitos usuários independentes. Declare a razão sobre uma linha por unidade, com faixas em que os valores de toda unidade estão:

  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 added

O estimando é o numerador médio de cada braço sobre seu denominador médio. As duas médias são limitadas pelo intervalo admitido, cada uma na sua parte do orçamento de erro, e a razão dividindo suas extremidades, então ela vale em qualquer tamanho de amostra, sem aproximação normal. Um denominador cujo intervalo chega a zero não dá intervalo. Um valor fora da sua faixa é um erro de configuração que nomeia a unidade, nunca cortado. O intervalo do método delta, geralmente mais estreito, é mostrado ao lado e nunca decide.

Uma covariável de antes do teste

Um resultado que acompanha algo medido antes do teste (o gasto do mês passado, as sessões da semana passada) varia menos quando isso é descontado. Traga o valor de cada unidade de antes do teste e um coeficiente fixado antes de os resultados deste teste serem lidos:

  covariate: {values: logs/pre_period.csv, range: [0, 500], theta_from: logs/history.csv}

O resultado decidido é Y - theta (X - c). theta é declarado (theta:, com um center: opcional, o meio da faixa por padrão), ou ajustado em um arquivo de histórico de um período antes do teste (colunas covariate e outcome), nunca nos próprios resultados deste teste. Como X foi medido antes da atribuição, e quais unidades têm um X também foi fixado antes da atribuição, a diferença ajustada tem o mesmo estimando. Uma unidade sem valor no arquivo não é ajustada. Um ajuste válido ainda pode ser mais largo do que nenhum, porque a faixa do resultado ajustado é mais larga, então o relatório mostra os dois intervalos e diz qual é mais estreito; não afirma nenhuma economia.

Novidade e interferência, mostradas

novelty: {exposure_column: exposed_at, time_column: at, days: 7} lê a primeira exposição de cada unidade no log de atribuição e o horário de cada linha de resultado, e mostra a diferença nos primeiros days dias de exposição e depois. Uma diferença inicial decidida com uma posterior não decidida ou oposta é chamada de "possivelmente um efeito de novidade". A divisão nunca decide uma regra: unidades sem linhas em uma faixa ficam de fora dela, não limitadas no seu pior. Ela aceita um resultado por linha, não uma razão nem uma covariável.

cluster_column: team conta as equipes que têm unidades de vários braços e pergunta se o resultado de uma unidade pode depender do braço de outra. Se puder, aleatorize pela equipe e declare-a como a unidade.

Vários tratamentos, razões e covariáveis foram admitidos por uma auditoria independente e sua confirmação em 3 de novembro de 2026, nos mesmos termos que dois braços.

Offline e online

Nomeie a comparação offline e sua métrica (offline_comparison: <id>, offline_metric: pass_rate) e a resposta informa se as duas direções decididas concordam. Isso é relatado e nunca decide nada: uma discordância é um motivo para olhar a suíte, não um veredito sobre a versão.

Armadilhas

  • Espiar. Ler todo dia um teste de horizonte fixo e parar num dia bom infla vitórias falsas. Planeje o tamanho da amostra, ou declare monitoring: anytime.
  • Novidade e aprendizado. Usuários reagem a qualquer coisa nova e depois se acomodam. Um ganho da primeira semana pode sumir; rode por tempo suficiente para cobrir os ciclos dos seus usuários, e declare novelty: para ver a divisão.
  • Interferência. O intervalo supõe que o resultado de uma unidade não depende do braço de outra. Estoque compartilhado, marketplaces, recursos sociais ou um cache aquecido por um braço quebram isso; nesse caso, aleatorize por uma unidade maior (uma região, uma equipe), e declare cluster_column: para ver quão misturados estão os clusters.
  • Deriva sob uma divisão que muda. O intervalo supõe que a alocação ficou constante durante o teste, ou que os resultados não derivaram enquanto ela mudava. Uma rampa de 10% para 50%, ou 90/10 e depois 10/90 entre um dia útil e um fim de semana, pondera os períodos de forma diferente em cada braço e mistura um efeito do tempo na diferença; a verificação da proporção da amostra lê contagens agregadas e não vê isso. Mantenha a divisão fixa, ou decida só sobre o período depois da rampa.
  • Mudar a unidade. Aleatorize e analise pela mesma unidade. Aleatorizar usuários e contar sessões como independentes estreita o intervalo falsamente.

O que isto não pode dizer

  • Se o resultado vale para usuários fora da janela, da região ou do tráfego do teste.
  • Nada sobre uma métrica que você não declarou antes do teste; uma métrica escolhida depois do fato é uma descrição.
  • Se o seu roteador realmente aleatorizou: a verificação da proporção da amostra pega falhas grandes, não sutis.
  • Qual tratamento é o melhor entre os que não passaram: só os tratamentos que passam são ordenados. A alocação estratificada e os desenhos switchback não estão construídos.