AACsearch
Relevance Studio

Learning to Rank

La pipeline LTR en Relevance Studio — feedback de clics, corrección de sesgo de posición, entrenamiento, versionado de modelos, tests A/B y activación. Cómo interpretar la significancia del z-test y elegir un ganador.

Learning to Rank (LTR) es el bucle cerrado de feedback que convierte clics reales de usuarios en un modelo de ranking aprendido. Studio entrega los cuatro paneles que mapean a las cuatro etapas: Feedback de clics, Ejecuciones de entrenamiento, Modelos y Tests A/B.

Pipeline de un vistazo

clics (SearchUsageEvent)

   ▼   corrección de sesgo de posición (debias)

   ▼   entrenamiento (shim de LightGBM hoy; LightGBM/XGBoost nativos diferidos)

   ▼   artefacto de modelo + métricas (NDCG, MRR, AUC)

   ▼   test A/B: split de tráfico, significancia por z-test

   ▼   activar ganador → ranking en el camino de lectura

Cada etapa es reversible. Un modelo malo está a un clic de ser desactivado — el modelo activo anterior siempre se conserva.

1 · Feedback de clics

El panel Feedback de clics resume la señal cruda que alimenta al entrenador. Tres números por índice:

  • clics-con-contexto — cuántos eventos click llegaron con un queryId y position válidos. Sin queryId no se puede atar un clic a una búsqueda.
  • corrección de sesgo de posición — la curva de debias vigente. El sesgo de posición es el fenómeno conocido de que la posición 1 recibe clics independientemente de la relevancia. El score corregido es, aproximadamente, observed_ctr / propensity[position], donde propensity se ajusta por índice.
  • filas de entrenamiento utilizables — tras el debias y filtrar resultados vacíos, cuántas filas son elegibles para alimentar al entrenador.

Si las filas utilizables están bajo ~5k, Training se niega a lanzar el run y muestra un banner "señal insuficiente". Bajo ese umbral el modelo aprendido sobreajusta y rinde peor que el ranker estático.

2 · Ejecuciones de entrenamiento

Una ejecución produce un artefacto de modelo a partir de una ventana elegida de feedback de clics. En Studio elige:

  • Ventana — últimos 7, 30 o 90 días.
  • Algoritmo — actualmente shim (única opción en releases enviados). LightGBM y XGBoost nativos están diferidos — ver nota abajo.
  • Features — set predefinido (relevancia textual, recencia, precio, popularidad, cohorte de categoría). Las features personalizadas están en el roadmap.

El run muestra progreso y, al completar, tres métricas:

MétricaSignificado
NDCG@10Normalized discounted cumulative gain en top 10 — métrica primaria
MRRMean reciprocal rank del primer resultado clicado
AUCÁrea bajo la curva clic vs no-clic

Estado actual del shim (mayo 2026)

El entrenador LTR enviado es un algoritmo shim: un combinador lineal simplificado que ajusta unos pocos pesos sobre las features pre-calculadas. Es intencionalmente conservador y va detrás del feature flag ltr.shim.

Los entrenadores LightGBM y XGBoost nativos completos están diferidos. Están implementados detrás del flag ltr.native pero no están en GA — requieren más pruebas de carga y un roll-out del registry de modelos. Por ahora, considere al shim como el único algoritmo soportado, y espere un lift de NDCG@10 del 5–15% sobre la baseline no-rankeada, no el 25–40% de algunos papers. Clientes que necesiten LightGBM nativo hoy pueden contactar con soporte para una preview privada.

3 · Modelos

Cada run exitoso produce un modelo en el panel Modelos. Los modelos son artefactos inmutables y llevan:

  • ID versionado (mdl_<random>), timestamp de creación, run de origen,
  • la tupla de métricas (NDCG@10, MRR, AUC),
  • el feature set y la ventana sobre la que se entrenó,
  • el estado de despliegue: draft, in_ab_test, active o archived.

Un modelo en active es lo que consulta el camino de lectura en cada búsqueda para ese índice. Hay exactamente un modelo activo por índice en cualquier momento.

4 · Tests A/B

No se promueve un modelo de draft directamente a active. En su lugar, se lanza un test A/B que parte el tráfico en vivo entre el modelo candidato (brazo B) y el modelo activo actual (brazo A, control).

Lanzar un test A/B

Desde Studio → LTR → Tests A/B → Nuevo test:

// Lo que Studio hace por dentro — también invocable directamente
import { client } from "@repo/api/client";

const test = await client.ltr.abTests.create.call({
  indexSlug: "products",
  controlModelId: "mdl_active_now",
  variantModelId: "mdl_candidate",
  trafficSplit: 0.10, // 10% del tráfico en vivo ve el brazo B
  primaryMetric: "ctr", // ctr | cvr | ndcg10
  minSampleSize: 50_000, // búsquedas por brazo antes de calcular significancia
});

El split de tráfico es por petición, no por usuario — así que el mismo usuario puede caer en brazos distintos entre sesiones. El modelo variante se aplica al brazo B; el brazo A sigue usando el modelo activo existente.

Leer los resultados

Mientras el test corre, el panel muestra contadores en vivo por brazo:

  • n — búsquedas asignadas al brazo.
  • clicks, ctr, cvr — métricas primarias, refrescadas cada 5 minutos.
  • z-score — diferencia estandarizada entre brazo B y A sobre la métrica primaria.
  • p-value — bilateral.
  • decisiónrunning, significant_win, significant_loss, ±borderline o no_effect.

Cómo interpretar la significancia

El entrenador usa un z-test de dos proporciones estándar, tratando cada búsqueda como un ensayo Bernoulli independiente sobre la métrica primaria. Umbrales:

DecisiónRango de zSignificado
significant_winz ≥ +1.96 (p ≤ 0.05)Candidato bate al control con 95% de confianza.
significant_lossz ≤ −1.96Candidato pierde con 95% de confianza — parar el test.
±borderline1.65 ≤ |z| < 1.9690–95% de confianza — esperar más muestras o decidir antes.
no_effect|z| < 1.65 y n ≥ minSampleSizeSin diferencia detectable a esta muestra.
runningn < minSampleSizeAún no hay datos suficientes.

La banda ±borderline se expone como decisión propia porque la mayoría de equipos de producto tienen una regla pre-test como "ship al 90% si delta es positivo, hold al 95% si es negativo". Studio no promueve auto en borderline — un humano debe clicar Activar ganador.

Guía de tamaño de muestra

minSampleSize por defecto es 50,000 búsquedas por brazo. Con split del 10%, un índice con 50k búsquedas/día alcanza significancia en ~11 días para un efecto de 2 puntos porcentuales en CTR. Para índices más pequeños, espere 3–4 semanas. El panel de Studio muestra un ETA en vivo.

Activar un ganador

Cuando la decisión es significant_win (o decidió ship en ±borderline), clique Activar ganador en el panel A/B. Esto:

  1. Pone el modelo variante en active.
  2. Degrada el modelo activo anterior a archived (se puede reactivar de un clic si el nuevo regresa en producción).
  3. Cierra el A/B con informe final.
  4. Enruta el 100% del tráfico en vivo por el nuevo modelo en ~60 segundos (TTL del LRU policy-cache).
await client.ltr.abTests.activateWinner.call({
  testId: test.id,
  // safety check opcional — rechaza si la decisión live divergió de
  // `significant_win` entre el fetch y la llamada
  expectedDecision: "significant_win",
});

Si necesita rollback tras activar, el panel Modelos mantiene el modelo activo anterior como archived y expone Reactivar de un clic.

Mejores prácticas

  • Siempre lance un A/B test. Hasta un modelo que gana en NDCG offline puede regresar en CTR live — sesgo de posición y efectos de presentación no aparecen en las métricas offline.
  • Elija la métrica que mapea al negocio. CTR es el default; catálogos conversion-driven ponen primaryMetric: "cvr".
  • No "peek-and-stop". El z-test asume tamaño de muestra fijo — mirar antes de minSampleSize y parar inflará la tasa de falsos positivos. Fije la muestra, váyase y decida después.
  • Archive agresivamente. Mantenga a lo sumo 5–10 modelos archivados por índice. El registry es barato pero el audit log es más legible con menos filas.

Relacionado

On this page