Publicado el 7 min de lectura

Cómo evaluar la recuperación RAG con Recall@k y MRR

En esta página

Un asistente de documentación da una respuesta incompleta sobre cómo compartir una cuenta. Antes de reescribir su prompt, comprueba si la búsqueda recuperó tanto la guía de permisos como la página de límites. Si falta una, el modelo nunca recibió la evidencia que esperabas que utilizara.

Este tutorial crea una prueba pequeña de recuperación con identificadores de documentos etiquetados, Recall@k y rango recíproco medio. El ejemplo de Python funciona localmente sin modelo, base de datos vectorial ni clave de API. Evalúa listas de resultados proporcionadas; no ejecuta un buscador ni mide un sistema RAG de producción.

Una lupa examina tarjetas de documentos recuperados, con resultados relevantes destacados en verde azulado junto a una marca de verificación.

1. Define qué cuenta como evidencia útil

Elige una colección, como la documentación de tu producto, y fija una versión para la prueba. Asigna un identificador estable a cada elemento recuperable. Decide si cada elemento será un documento completo o un fragmento; utiliza la misma unidad en las etiquetas y en los resultados.

Para un primer conjunto de desarrollo, escribe unas pocas decenas de preguntas basadas en el trabajo que debería realizar el asistente. Es un punto de partida editorial, no un tamaño de muestra justificado estadísticamente. Revisa la evidencia manualmente e incluye preguntas que necesiten varias fuentes.

Este conjunto mínimo es ilustrativo:

ID de consulta Pregunta IDs de documentos relevantes
q_export ¿Cómo exporto un CSV? export_csv
q_reset ¿Cómo restablezco el acceso y qué restricciones existen? reset_steps, reset_limits
q_sharing ¿Quién puede compartir una cuenta y qué límites se aplican? share_roles, share_limits

Anota por qué es relevante cada elemento y qué versión de la colección revisó la persona responsable. Una respuesta que solo repite la pregunta no es evidencia. No permitas que el recuperador evaluado defina sus propias respuestas de referencia.

Si cambias los límites de los fragmentos, revisa las etiquetas antes de comparar puntuaciones. Un esquema nuevo de identificadores puede invalidar una evaluación que antes funcionaba.

2. Interpreta Recall@k y MRR juntos

Aquí, k es el número máximo de elementos ordenados que se examinan por consulta. Elige un corte que coincida con la cantidad de elementos que la aplicación realmente puede enviar a la siguiente etapa.

Recall@k es el número de elementos relevantes encontrados entre los primeros k resultados dividido por el total de elementos etiquetados como relevantes. Sigue la definición de recall de Introduction to Information Retrieval, de Stanford.

Para q_sharing, recuperar share_roles pero no share_limits produce un recall de 1/2, o 0,5. Encontrar un resultado relevante es un acierto, pero no supone recuperar toda la evidencia si hay dos elementos relevantes.

El rango recíproco con corte k es 1 dividido por la posición del primer resultado relevante, o cero si no aparece ninguno dentro del corte. MRR@k promedia esos valores entre las consultas. El evaluador de recuperación de Sentence Transformers también calcula MRR y Recall@k.

Un resultado en primera posición aporta 1,0; en segunda, 0,5. MRR premia encontrar pronto un resultado útil, pero no comprueba si los resultados posteriores aportan el resto de la evidencia. Acompáñalo de recall cuando una pregunta necesite varias fuentes.

Nuestro ejemplo utiliza relevancia binaria y da el mismo peso a cada consulta al promediar recall. Ninguna de las dos métricas comprueba si la respuesta generada es correcta.

3. Ejecuta un evaluador con la biblioteca estándar de Python

Guarda lo siguiente como evaluate_retrieval.py. Los identificadores y las listas son ejemplos escritos a mano, no resultados de un benchmark.

def evaluate(rankings, labels, k):
    if k < 1 or not labels:
        raise ValueError("Use k >= 1 and at least one labeled query")
    rows = {}
    for query_id, relevant in sorted(labels.items()):
        if not relevant:
            raise ValueError("Evaluate no-answer queries separately")
        ranking = rankings.get(query_id, [])
        if len(ranking) != len(set(ranking)):
            raise ValueError("Duplicate document IDs in ranking")
        top = ranking[:k]
        recall = len(set(top) & relevant) / len(relevant)
        rr = next(
            (1 / rank for rank, doc in enumerate(top, 1)
             if doc in relevant),
            0.0,
        )
        rows[query_id] = (recall, rr)
    count = len(rows)
    mean_recall = sum(row[0] for row in rows.values()) / count
    mrr = sum(row[1] for row in rows.values()) / count
    return rows, mean_recall, mrr


labels = {
    "q_export": {"export_csv"},
    "q_reset": {"reset_steps", "reset_limits"},
    "q_sharing": {"share_roles", "share_limits"},
}
rankings = {
    "q_export": ["export_csv", "billing", "profile"],
    "q_reset": ["billing", "reset_steps", "reset_limits"],
    "q_sharing": ["profile", "share_roles", "billing"],
}

if __name__ == "__main__":
    k = 3
    rows, mean_recall, mrr = evaluate(rankings, labels, k)
    for query_id, (recall, rr) in rows.items():
        print(f"{query_id} recall@{k}={recall:.3f} rr@{k}={rr:.3f}")
    print(f"Mean recall@{k}={mean_recall:.3f}")
    print(f"MRR@{k}={mrr:.3f}")

Ejecútalo con Python 3:

python3 evaluate_retrieval.py

Salida esperada:

q_export recall@3=1.000 rr@3=1.000
q_reset recall@3=1.000 rr@3=0.500
q_sharing recall@3=0.500 rr@3=0.500
Mean recall@3=0.833
MRR@3=0.667

La consulta sobre restablecimiento encuentra los dos elementos necesarios, pero el primero aparece en segunda posición. La consulta sobre compartir cuentas solo encuentra uno de sus dos elementos relevantes. Sus rangos recíprocos coinciden, mientras que recall muestra la diferencia.

La función cuenta una lista ausente como fallo de recuperación, rechaza identificadores duplicados y no acepta etiquetas de relevancia vacías. Las preguntas sin una fuente relevante necesitan otra prueba de abstención o solicitud de aclaraciones; asignarles recall cero mezclaría tareas distintas.

4. Sustituye los ejemplos por resultados de búsqueda capturados

Mantén labels como referencia revisada. Sustituye rankings por los identificadores ordenados que devuelve tu servicio de búsqueda. Conserva el orden de la canalización y todas las consultas etiquetadas, aunque su lista de resultados esté vacía.

Evalúa la etapa que importa. Si el generador recibe tres fragmentos después de reordenar y filtrar, mide esos fragmentos finales, no solo los candidatos iniciales. También puedes evaluar por separado la etapa inicial para descubrir dónde desaparece la evidencia.

Registra el contexto necesario para reproducir cada ejecución:

  • Versión de la colección y esquema de identificadores.
  • Conjunto de consultas y revisión de las etiquetas de relevancia.
  • Modelo de embeddings o configuración de búsqueda léxica.
  • Número de candidatos, configuración del reordenador, filtros y corte final.
  • Resultados por consulta, tiempo de búsqueda y tamaño del contexto.

Aplica los mismos permisos de usuario en la prueba que en la aplicación. Un documento relevante fuera de la colección autorizada para ese usuario no debe contar como recuperación correcta.

5. Compara un cambio cada vez

Ejecuta el recuperador actual y una propuesta de cambio con las mismas preguntas y etiquetas fijadas. Puedes comparar búsqueda léxica con un método que combine búsqueda léxica y vectorial, o añadir un reordenador manteniendo los mismos candidatos iniciales.

Revisa las diferencias por consulta antes de celebrar una media. Mejorar las preguntas sobre exportación puede ocultar una regresión en las de permisos. Separa los resultados por tipo de tarea e idioma cuando esas diferencias importen a tus usuarios.

Define las reglas de aceptación antes de ajustar el sistema. Para este flujo ilustrativo de documentación, exige que ambas fuentes de q_sharing aparezcan entre los tres primeros resultados, que no empeoren los casos de regresión obligatorios y que la latencia respete el presupuesto de la aplicación. Son reglas de ejemplo, no umbrales universales de despliegue.

En una evaluación mayor, reserva preguntas revisadas que no utilizarás durante los ajustes. Investiga los fallos y los desacuerdos de etiquetado; tres preguntas de ejemplo no permiten establecer la calidad general de un recuperador.

6. Investiga el fallo, no solo la puntuación

Observación Qué revisar después
Recall es bajo en todos los cortes probados Documentos ausentes, fallos de indexación, redacción o filtros restrictivos
Recall mejora al aumentar k Calidad del orden y coste de enviar más contexto
MRR es alto pero recall es bajo Evidencia secundaria ausente en preguntas con varias fuentes
Las puntuaciones cambian mucho al modificar los fragmentos Correspondencia de etiquetas y unidad recuperable
La recuperación parece buena pero las respuestas siguen mal Recorte del contexto, fuentes contradictorias y respaldo de las respuestas

Recuperar más elementos no mejora automáticamente el asistente completo. Registra la longitud del contexto y la calidad de las respuestas junto con las métricas de recuperación, utilizando el paso real de ensamblado del contexto.

Como introducción, consulta cómo RAG utiliza los datos empresariales y recall frente a precisión.

Conclusión

Empieza con preguntas revisadas, identificadores estables y los elementos ordenados que recibe realmente el asistente. Utiliza Recall@k para detectar evidencia ausente y MRR@k para examinar el primer resultado útil; después evalúa por separado la calidad de las respuestas. Conserva estos ejemplos para comprobar el evaluador, no como prueba de que un recuperador está listo para producción.

Fuentes comprobadas el 29 de octubre de 2026. El conjunto de ejemplo y las reglas de aceptación son ilustrativos.

Artículos relacionados

Últimos artículos