Ir al contenido

¿Por qué evaluar tu RAG?

Cambiaste el chunkSize de 800 a 1200 y le hiciste a tu RAG la pregunta que más te gusta. La respuesta cambió un poco. ¿Está mejor o peor?

Si tu respuesta honesta es “me parece que mejor” — felicitaciones, sos como el 95% de los proyectos de RAG que se ponen “en producción” sin saber si funcionan bien. Y andá despidiéndote del salario que pensaste cobrar.

Para defender una decisión técnica necesitás números reproducibles. Eso es lo que vamos a construir en este nivel.

Cuando una respuesta es mala, hay dos lugares donde puede haber fallado:

  • Retrieval: el retriever no trajo los chunks correctos.
  • Generación: los chunks estaban bien, pero el LLM los usó mal (o no los usó).

Si las medís juntas, no podés saber qué arreglar. Por eso un eval harness decente reporta cada dimensión por separado.

El package 06-evals (ya está en el repo desde el día 1) computa cuatro métricas en cada corrida:

MétricaMideCuándo te avisa
LatencyCuánto tarda query() end-to-endTu RAG es correcto pero lento
Retrieval@k¿Está el chunk correcto en los top-K?Problema de retriever
Faithfulness¿La respuesta usa SOLO los chunks recuperados?El LLM alucina
Answer relevance¿La respuesta responde la pregunta?Respuesta off-topic

Las cuatro juntas te dan un mapa: si retrieval@k está alto pero faithfulness está bajo, el LLM está inventando. Si retrieval@k está bajo, el LLM no tiene chance — no le llegaron los chunks correctos.

Ragas es la librería estándar de evals para RAG. ¿Por qué no la usamos? Tres razones:

  1. Caja transparente vs. caja negra. Cuando un score te sale raro, querés poder leer el código que lo computó. ~200 líneas de TypeScript son más rápidas de leer que una librería con plugins, decoradores, y abstracciones de configuración.
  2. Cero dependencias nuevas. El harness usa el mismo Ollama que ya tenés corriendo. Sin pip, sin uv, sin Python.
  3. Pedagogía. Implementar los 4 evaluators a mano enseña qué mide cada uno. Después podés cambiar a Ragas si querés — ya entendés qué hace por dentro.
packages/06-evals/
├── golden/
│ └── dataset.json # 12 pares Q-A curados a mano
├── reports/ # generados, gitignorados
└── src/
├── index.ts # CLI: parsea args, preflight, runner, reportes
├── runner.ts # framework × pregunta loop con try/catch por celda
├── adapters.ts # importa los 4 query.fn de los packages 01-04
├── preflight.ts # chequea Ollama + Qdrant + collections antes de correr
├── judge.ts # LLM-as-judge: faithfulness + answer-relevance
├── report.ts # renderiza markdown + json
└── metrics/
├── latency.ts
├── retrieval-overlap.ts
├── faithfulness.ts
└── answer-relevance.ts

El harness fue construido pensando en comparar los 4 frameworks (Vercel AI SDK, LangChain.js, LlamaIndex.TS, Mastra) — esa comparación es el contenido del Nivel 5. Pero también podés correrlo sobre uno solo con --only, que es lo que vamos a hacer en este nivel.

Ventana de terminal
pnpm --filter @rag-lab/06-evals run eval -- --only vercel-ai-sdk --limit 3

Lo que hace, paso a paso:

  1. Preflight: verifica que Ollama está corriendo, los modelos están pulleados, Qdrant arriba y la collection rag_vercel ingestada.
  2. Carga el golden set: 12 pares Q-A curados a mano sobre el corpus.
  3. Corre 3 preguntas (por el --limit 3) contra Vercel AI SDK.
  4. Para cada respuesta computa las 4 métricas (la 3 y 4 vía LLM-as-judge — son llamadas adicionales a Ollama).
  5. Escribe un reporte markdown + JSON en packages/06-evals/reports/ y un summary en stdout.

Por diseño. Cada corrida genera un timestamp distinto y los números varían por el LLM-as-judge. No queremos commitear esa basura — solo el código del harness y el dataset son source-truth.

Si una corrida específica te sirve para algo (ej. “antes/después” de un cambio que vas a documentar en un blog post), copiala fuera de reports/ o agregala manualmente a un permanent path.

En el siguiente capítulo abrimos la métrica más interesante: faithfulness. Cómo se implementa con un segundo LLM call, cómo se diseña el judge prompt, y cuál es el bias que trae que el mismo modelo genere y juzgue.