volver al blog

Late interaction: por qué ColBERT recupera mejor que un solo vector

ragembeddingsrecuperación

Un vector no puede con todo

La recuperación densa clásica funciona así: metes la consulta y el documento por el mismo codificador, sacas un vector por cada uno y comparas con coseno. Rápido, elegante y suficiente la mayoría de las veces.

El problema aparece cuando el documento es largo o mezcla temas. Estás pidiéndole a un único vector de 768 o 1024 dimensiones que resuma un párrafo entero. Toda la riqueza léxica (el nombre de un producto, un número de versión, un término raro que solo aparece una vez) se promedia hasta desaparecer. Si tu consulta depende justo de esa palabra, el vector denso te falla y no sabes por qué.

Los cross-encoders resuelven esa pérdida metiendo consulta y documento juntos en el modelo, pero cobran caro: no puedes precalcular nada, así que reordenar 100 candidatos son 100 pasadas de inferencia. Sirven para el último empujón, no para buscar en millones de documentos.

La interacción tardía (late interaction), popularizada por ColBERT, se queda en el medio. Y ese medio es más interesante de lo que parece.

Qué cambia con la interacción tardía

La idea es sencilla de enunciar: no comprimas el documento en un solo vector. Guarda un vector por cada token.

Un documento de 200 tokens deja de ser un punto en el espacio y pasa a ser una nube de 200 puntos. La consulta hace lo mismo: cada uno de sus tokens tiene su propio vector. La relevancia ya no es un producto escalar entre dos resúmenes, sino una comparación fina entre las dos nubes.

El nombre “tardía” viene de cuándo interaccionan consulta y documento. En el denso, nunca: cada uno se codifica por su cuenta y solo se tocan al final con un coseno. En el cross-encoder, desde el primer momento: van juntos por todas las capas de atención. ColBERT codifica por separado (como el denso, así que puedes indexar offline) pero deja la interacción real para el último paso, con una operación barata. De ahí que sea tardía pero exista.

Eso te da lo mejor de dos mundos parciales: la precisión gana porque cada palabra de la consulta busca su mejor pareja en el documento, y la escalabilidad se mantiene porque los vectores del documento se calculan una sola vez, al indexar.

MaxSim, la operación que lo sostiene

El corazón de ColBERT es una función que suena peor de lo que es: MaxSim. Para cada token de la consulta, busca el token del documento con el que más se parece y se queda con esa similitud. Luego suma esos máximos. Fin.

import torch

def maxsim_score(query_emb, doc_emb):
    """
    query_emb: (Nq, D) un vector por token de la consulta
    doc_emb:   (Nd, D) un vector por token del documento
    Ambos normalizados a norma 1.
    """
    # Similitud de cada token de consulta con cada token del documento
    sim = query_emb @ doc_emb.T          # (Nq, Nd)

    # Para cada token de consulta, su mejor pareja en el documento
    best_per_query_token = sim.max(dim=1).values  # (Nq,)

    # La puntuación es la suma de esos máximos
    return best_per_query_token.sum().item()

Lo que hace interesante a MaxSim es lo que implica. Si buscas “error de despliegue en Astro”, el token Astro encontrará su equivalente exacto en un documento que hable de Astro, y aportará una similitud alta aunque el resto de la frase no case perfecto. Un vector denso habría diluido Astro entre las otras palabras. Aquí cada término defiende su parte de la puntuación por separado, y por eso la recuperación conserva el detalle léxico que el denso pierde.

Denso, cross-encoder e interacción tardía

Puesto uno al lado del otro, el cuadro se aclara:

CriterioDenso (un vector)Cross-encoderLate interaction (ColBERT)
Vectores por documento10 (no precalcula)1 por token
¿Se indexa offline?No
Coste al consultarMuy bajoAlto (una pasada por candidato)Bajo-medio
Precisión léxicaMediaAltaAlta
AlmacenamientoBajoNuloAlto
Uso típicoPrimer filtro a gran escalaReordenar el top-NRecuperar o reordenar con detalle

No es una escalera donde ColBERT gana siempre. Es un triángulo de compromisos: la interacción tardía compra precisión con espacio en disco, no con latencia de consulta.

El coste que nadie te cuenta: el disco

Aquí está la letra pequeña. Un vector por token multiplica el índice. Un corpus que ocupaba 2 GB en denso se te puede ir a 20 o 30 GB en ColBERT ingenuo. Multiplica eso por millones de documentos y el ahorro de un buen rerank se lo come la factura de almacenamiento.

Por eso ColBERTv2 no guarda los vectores en crudo. Los comprime con cuantización de producto y agrupa por centroides, y baja el índice a algo manejable sin perder apenas calidad. Si vas a montar interacción tardía en serio, esa compresión no es un extra: es la diferencia entre viable e inviable.

La otra trampa es tratarlo como un reemplazo total del denso. Rara vez lo es. El patrón que funciona en producción es por etapas: un recuperador denso o BM25 trae unos cientos de candidatos baratos, y ColBERT reordena ese conjunto con su MaxSim. Pagas el coste de los vectores por token solo sobre lo que ya has acotado.

Cuándo merece la pena

Reglas de campo, no dogma:

  • Búsqueda con jerga o identificadores (nombres de producto, códigos, versiones, términos médicos o legales): la interacción tardía brilla porque conserva el token raro. Aquí es donde el denso más falla.
  • Consultas largas y específicas: cuando la consulta tiene varias condiciones, MaxSim deja que cada una puntúe por su lado.
  • Corpus mediano y de alto valor: documentación, base de conocimiento interna, soporte. Miles o cientos de miles de documentos, no miles de millones.
  • Si tu métrica de recuperación se ha estancado con denso + rerank y sospechas que pierdes por vocabulario, prueba ColBERT antes de tocar el modelo generador.

Y cuándo no: si tu denso ya te da un recall que satisface al usuario, o si el corpus es enorme y el presupuesto de almacenamiento es ajustado, no compliques. La interacción tardía es una herramienta de precisión, no un interruptor de “mejor por defecto”.

El punto que me llevo de haber peleado con esto: la mayoría de fallos de RAG que la gente atribuye al modelo de lenguaje son, en realidad, fallos de recuperación. Y muchos de esos fallos son un vector denso tragándose una palabra que importaba. Antes de subir de modelo, mira si el problema es que estás comprimiendo demasiado. ColBERT es una de las formas de dejar de hacerlo.