Golden sets: ground truth manual
Para medir retrieval, necesitás saber qué está bien
Sección titulada «Para medir retrieval, necesitás saber qué está bien»Faithfulness no necesita ground truth — el judge mira la respuesta y los chunks y compara. Pero retrieval@k sí necesita: para decir “el chunk correcto está en el top-K”, tenés que saber cuál ES el correcto.
Eso es un golden set: un conjunto de tuplas (pregunta, archivos esperados, respuesta esperada) curadas a mano. Es trabajoso, no escala, y es el único ground truth real que vas a tener.
Por qué a mano y no auto-generado
Sección titulada «Por qué a mano y no auto-generado»La tentación es obvia: que el LLM mire los chunks y arme el dataset por vos. Lo vamos a hacer en el próximo capítulo (datasets sintéticos) — pero no reemplaza al golden set.
El problema es la circularidad: si usás el LLM para generar el ground truth y después usás otro LLM (o el mismo) para evaluar, estás midiendo “¿el LLM evaluador está de acuerdo con el LLM generador?”. Eso no es ground truth, es coherencia entre LLMs.
Para tener señal real de cuán bien anda tu RAG, necesitás un humano que haya leído los documentos y haya escrito las preguntas con la respuesta correcta en mente.
El dataset del curso
Sección titulada «El dataset del curso»packages/06-evals/golden/dataset.json tiene 12 entries — 3 por cada uno de los 4 markdowns en data/. Cada entry sigue este schema:
{ "id": "clean-arch-01", "question": "¿Qué establece la regla de dependencia en Clean Architecture?", "expected_answer": "La regla de dependencia establece que las dependencias del código fuente sólo pueden apuntar hacia adentro...", "expected_filenames": ["clean-architecture.md"], "tags": ["clean-architecture", "definición"]}Cuatro campos importan:
id: identificador único, formato<topic>-<NN>. Aparece en el reporte para que sepas qué pregunta estás mirando.question: tal cual la haría un usuario. En el mismo idioma del corpus (acá español —nomic-embed-textes monolingual y mezclar idiomas degrada retrieval).expected_filenames: la lista de archivos donde está la respuesta. Acá vive el ground truth de retrieval.expected_answer: la respuesta correcta, escrita por vos. Aparece en los reportes para que un revisor humano pueda comparar contra la respuesta del LLM. No la usan los evaluators automáticos.
La métrica que habilita: retrieval@k
Sección titulada «La métrica que habilita: retrieval@k»packages/06-evals/src/metrics/retrieval-overlap.ts computa esto:
function computeRetrievalAtK(returned, expectedFilenames) { const expectedSet = new Set(expectedFilenames.map((f) => f.toLowerCase())); const returnedSet = new Set(returned.map((c) => c.filename.toLowerCase())); let intersection = 0; for (const f of expectedSet) if (returnedSet.has(f)) intersection++; return expectedSet.size === 0 ? 0 : intersection / expectedSet.size;}Léelo así: del conjunto de archivos que TE digo que tienen la respuesta, ¿qué fracción está en los top-K que devolvió el retriever?.
- Score 1.0 — todos los archivos esperados aparecen en el top-K. Retrieval perfecto.
- Score 0.5 — la mitad de los archivos esperados están. Caso típico: pregunta multi-doc donde el retriever encontró 1 de 2.
- Score 0.0 — el retriever no trajo ningún archivo correcto. Algo está roto (mal idioma, mal modelo, corpus sin la respuesta).
Qué hace bueno a un golden set
Sección titulada «Qué hace bueno a un golden set»Tres reglas que aprendí pagando con tiempo:
-
Cobertura del corpus. Para cada documento del corpus, al menos 2-3 preguntas. Si tenés 4 docs, mínimo 8-12 entries. Sin esto, el promedio te miente: si solo medís preguntas sobre 1 doc, no sabés qué pasa con los otros 3.
-
Dificultad variada. Mezclá preguntas obvias (“¿qué es X?”) con preguntas que requieren combinar info de varios chunks (“¿cómo se diferencia X de Y?”, “¿cuál es el anti-patrón típico al hacer Z?”). Si todas son fáciles, el RAG va a sacarte 1.0 en todo y no vas a poder discriminar entre configs.
-
Casos borde explícitos. Una pregunta cuya respuesta NO está en el corpus (out-of-corpus). El retrieval@k esperado es 0 — pero faithfulness debería ser alto, porque el LLM debería decir “no tengo info en el contexto” en vez de inventar. Esa pregunta es el test más duro de tu prompt anti-alucinación.
Cómo extender el dataset
Sección titulada «Cómo extender el dataset»Editá directamente packages/06-evals/golden/dataset.json. Agregá una entry con un id único y los 4 campos obligatorios. No hace falta script ni CLI — es un JSON, abrilo y editalo.
Lo que sí cuidá:
idúnico. Si pisás un ID existente, el harness va a tirar resultados duplicados confusos.expected_filenamesapunta a un archivo que existe endata/. Sin esto, retrieval@k siempre te va a dar 0 para esa entry y vas a creer que tu RAG está mal cuando en realidad el ground truth está mal.- Idioma consistente. Si tu corpus es en español, escribí preguntas y respuestas en español.
Lab: agregá 2 preguntas al dataset
Sección titulada «Lab: agregá 2 preguntas al dataset»Buscá un tema del corpus que las 12 entries existentes no cubren bien. Posibles candidatos:
- Detalle puntual: “¿cuál es la diferencia entre driving port y driven port?” (existe en
hex-arch-03, pero podés agregar una variante más específica). - Combinación multi-doc: “¿qué SOLID principle se relaciona más directo con la regla de dependencia?” (relaciona
clean-architecture.mdysolid-principles.md). - Out-of-corpus: “¿quién inventó GraphQL?” (no está en el corpus — el RAG debería decir “no sé”).
expected_filenames: []yexpected_answervacío o “no contestar”.
Editá el JSON, agregá tus 2 entries con IDs nuevos, y volvé a correr el harness:
pnpm --filter @rag-lab/06-evals run eval -- --only vercel-ai-sdkAhora corre las 14 preguntas. Mirá el reporte y prestá atención a tus 2 entries nuevas — ¿retrieval@k está alto? ¿faithfulness está alto?
Si alguna te tira un score bajo, ese es contenido para mejorar tu RAG: o el retriever no llegó al chunk, o el LLM lo usó mal. Es exactamente la señal que el harness existe para darte.
Lo que viene
Sección titulada «Lo que viene»12 entries no escalan. Si querés 100, ¿las inventás vos a mano? Hay otra opción — que el LLM las invente. Pero (spoiler) los datasets sintéticos vienen con sesgos importantes. En el siguiente capítulo los vemos en detalle.