La etapa 14 terminó con una medida objetiva y un resultado a favor de la recuperación. Parecía cerrado.
Entonces el dueño del proyecto leyó el montaje y preguntó una cosa:
es que el rag debería ser 50, no?
El dueño del proyecto, leyendo el montaje de la etapa 14Y ahí se cayó el experimento entero.
Una línea de configuración que era el resultado
En translate.py había esto:
RETRIEVE_N = 12Y en pick_core.py, esto:
parser.add_argument("--total", type=int, default=50)Cincuenta frases de núcleo. Doce recuperadas. La etapa 14 comparó «con recuperación» contra «sin recuperación» y presentó el resultado como ¿la recuperación se gana su sitio?
Pero eso no es lo que preguntaba. Preguntaba si doce frases añaden algo por encima de cincuenta. Cuatro a uno en contra. Con esa proporción, la recuperación podía perder por construcción y el informe habría dicho «el RAG no aporta» con toda la solemnidad del mundo.
Porque RETRIEVE_N = 12 tiene pinta de ajuste. Está donde van los ajustes, se lee como los ajustes, y doce es un número que suena a alguien que lo pensó.
No lo pensó nadie. Salió de «doce parecen suficientes» y nunca se midió. El núcleo de 50, en cambio, sí tenía una etapa entera detrás justificándolo — así que uno de los dos lados de la comparación estaba argumentado y el otro era una corazonada, y el informe los trataba igual.
Una constante que fija los términos de una comparación no es configuración: es parte del experimento. Si cambiarla puede dar la vuelta al resultado, hay que barrerla, no suponerla.
Seis configuraciones en vez de una
La forma de arreglarlo no es elegir mejor el número. Es variar los dos lados y mirar la rejilla:
1 núcleo 50 · recupera 0 few-shot con 50 ejemplos — el atajo del día uno
2 núcleo 0 · recupera 50 recuperación pura, mismo presupuesto de ejemplos
3 núcleo 25 · recupera 25 mitad y mitad
4 núcleo 50 · recupera 12 lo que hacía el pipeline
5 núcleo 50 · recupera 50 paridad
6 núcleo 50 · recupera 100 ¿satura?Las dos primeras son la pregunta fundacional del proyecto, por fin planteada de forma justa. Con cincuenta ejemplos delante del modelo, ¿es mejor que los elija una búsqueda para este texto concreto, o que sean siempre los mismos cincuenta?
Ese es el atajo que la etapa 0 rechazó. Rechazarlo costó un corpus, un clasificador, siete horas de Whisper, un segmentador y una base de datos vectorial. Si empatan, todo eso fue decoración.
Tres detalles más del montaje, porque cada uno cambia lo que sale:
Se recupera una vez, a la profundidad máxima, y se corta. Las 12 primeras de un resultado de 100 son exactamente las mismas filas que devolvería un limit 12 — el orden por distancia coseno no depende de dónde cortes. Una consulta a la base de datos por pregunta en vez de seis.
Se genera configuración por configuración, no consulta por consulta. El bloque system de cada configuración es idéntico en sus 48 llamadas, así que si se agrupan, la caché se mantiene caliente. Alternándolas, cada llamada expulsaría la anterior y se pagaría el núcleo entero 288 veces.
Al juez solo se le pasan cuatro duelos. Comparar las quince parejas posibles costaría cuatro veces más y contestaría preguntas que nadie hizo. Los cuatro que deciden algo:
| Duelo | Qué contesta |
|---|---|
núcleo50·rag0 vs núcleo0·rag50 | Few-shot puro contra RAG puro |
núcleo50·rag12 vs núcleo50·rag50 | ¿Era la proporción el problema? |
núcleo50·rag50 vs núcleo50·rag100 | ¿Satura la recuperación? |
núcleo25·rag25 vs núcleo50·rag50 | ¿Cuánto núcleo hace falta? |
Lo que salió
Primero la medida objetiva: cuánto de lo que escribe el modelo sale de las frases recuperadas, con la configuración sin recuperación marcando el azar.
| Configuración | Media | Mediana | Desviación | Sobre el azar |
|---|---|---|---|---|
núcleo50·rag0 | 30,4 % | 30,5 % | 9,4 % | — |
núcleo50·rag12 | 42,0 % | 42,4 % | 12,7 % | +11,7 |
núcleo25·rag25 | 44,8 % | 41,5 % | 16,0 % | +14,4 |
núcleo50·rag50 | 44,7 % | 43,4 % | 14,2 % | +14,4 |
núcleo50·rag100 | 42,8 % | 40,3 % | 14,2 % | +12,4 |
núcleo0·rag50 | 44,9 % | 46,0 % | 11,7 % | +14,5 |
Y como todas las configuraciones contestaron las mismas 48 consultas, se pueden comparar emparejadas: no «¿es mayor la media de A?», sino «¿gana A en la misma consulta?», que quita de en medio la dificultad de cada pregunta.
rag12 sobre rag0: +11,7 pts, gana en 39/48, p = 0,000 -> SÍ
rag25 sobre rag12: +2,8 pts, gana en 29/48, p = 0,193 -> no
rag50 sobre rag12: +2,7 pts, gana en 28/48, p = 0,312 -> no
rag100 sobre rag50: -1,9 pts, gana en 20/48, p = 0,312 -> no
rag50 sin núcleo: +0,2 pts, gana en 24/47, p = 1,000 -> noLa primera línea es el hallazgo del proyecto. Las otras cuatro son el hallazgo de esta etapa.
Y el juez a ciegas, en los cuatro duelos:
| Duelo | Marcador | p |
|---|---|---|
| Few-shot puro vs RAG puro | 24 – 24, 0 empates | 1,000 |
| rag12 vs rag50 | 22 – 24, 2 empates | 0,883 |
| rag50 vs rag100 | 22 – 25, 1 empate | 0,771 |
| núcleo25 vs núcleo50 | 22 – 25, 1 empate | 0,771 |
Cuatro empates técnicos. El juez no distingue ninguna de estas configuraciones de ninguna otra.
Con 48 consultas y un test de signos de dos colas, hace falta 32-16 — un 67 % de victorias — para bajar de p < 0,05.
O sea que este experimento solo era capaz de detectar diferencias enormes. Una configuración un 60 % mejor que otra habría salido de aquí como «empate». «No hay diferencia medible» y «no hay diferencia» no son lo mismo, y confundirlas es exactamente el error que la etapa 14 cometió al revés.
Lo honesto es decir qué se descarta: cualquier diferencia grande. Las pequeñas siguen sin saberse, y con este montaje no se van a saber.
Qué queda contestado y qué no
Contestado, y es lo importante: la recuperación se gana su sitio. +11,7 puntos de reutilización sobre el azar, ganando en 39 de 48 consultas, p < 0,001. Es la única cifra de todo el proyecto que sale del ruido con holgura. El corpus, el clasificador, Whisper y pgvector estaban pagando por algo.
Contestado, y en contra de la hipótesis que motivó la etapa: cuánto recuperes casi da igual. De 12 en adelante, todo es la misma cifra. Subir a 25 o a 50 mueve el punto estimado unos +2,8 puntos que no aguantan la prueba; 100 pierde, y encima cuesta.
Que la crítica fuera correcta y el diagnóstico equivocado es un resultado, no una anécdota: el experimento estaba mal montado y el número que se sospechaba no era el culpable. Las dos cosas a la vez.
Contestado a medias: el núcleo no aparece en esta medida. Quitarlo entero no mueve el solapamiento (+0,2 puntos) y el juez empata 24-24 contra la recuperación pura.
Sería fácil concluir «fuera el núcleo». Sería un error, por dos motivos que la prueba no puede ver:
- Estas 48 consultas no ponen a prueba para qué existe el núcleo. Existe para las peticiones que no tienen vecinos en el corpus — «la cena estaba buenísima» —, y de esas hay ocho aquí. El trabajo del núcleo es ser el suelo cuando la recuperación no tiene nada que ofrecer, y con esta muestra ese caso casi no ocurre.
- El núcleo va antes del corte de caché y la recuperación después. El núcleo se paga una vez; las frases recuperadas, enteras y en cada llamada. Quitar lo barato para quedarse con lo caro es al revés.
La decisión de producción
RETRIEVE_N se queda en 12.
No porque 12 sea óptimo — nadie ha demostrado eso — sino porque la carga de la prueba la tiene el número más caro y no la ha superado. 25 y 50 tienen el punto estimado por encima, y ninguno de los dos aguanta un test. Cuando dos opciones son indistinguibles en calidad y una cuesta cuatro veces más, no hay decisión que tomar.
Que el número acabe siendo el mismo con el que se empezó no significa que la etapa sobrara. Antes era una corazonada que resultó estar bien; ahora es una decisión con un barrido detrás y un motivo por el que se cambiaría: si aparecieran consultas donde el corpus tiene mucho que decir, el punto estimado de 25 dejaría de ser ruido y habría que volver aquí.
Lo que se eligió, y qué fijó cada cosa
| Se eligió | Y con eso quedó fijado |
|---|---|
| Barrer los dos lados y no solo uno | Que la comparación deje de tener un handicap 4:1 metido dentro |
| Volver a elegir el núcleo en cada tamaño | Que «núcleo de 25» mida el tamaño y no el orden de los recursos |
| 48 consultas | El poder de la prueba: solo detecta diferencias de 67 % o más |
| Solapamiento léxico como medida objetiva | Que se mida uso, no calidad. Optimizarlo a ciegas llevaría a copiar y pegar |
| Comparación emparejada | Que la dificultad de cada consulta salga de la comparación |
| Cuatro duelos y no quince | El coste de la prueba, a costa de no saber nada de las once parejas restantes |
| Recuperar 100 una vez y cortar | Una consulta a la base de datos por pregunta en vez de seis |
| Agrupar por configuración al generar | Que la caché del system sobreviva a las 48 llamadas |
Lo que se lleva uno de esta etapa
- Una constante que elegiste es una hipótesis que no probaste. Si fija los términos de una comparación, es parte del experimento; y las que tienen pinta de ajuste son las que sobreviven años sin que nadie las mire.
- Cuando alguien encuentra el fallo de tu montaje, arregla el montaje, no el resultado. Aquí la crítica era correcta y el culpable que señalaba era inocente. Las dos cosas caben.
- Di siempre cuánto podía ver tu prueba. Un empate con poder para detectar solo un 67 % no descarta una diferencia del 60 %. Sin esa frase, «no hay diferencia medible» se lee como «no hay diferencia».
- Que una medida no vea el valor de algo no es prueba de que no lo tenga. El núcleo no aparece en el solapamiento porque estas consultas no son las suyas.
- Cuando dos opciones empatan en calidad, decide el coste. Y mira dónde cae el corte de caché antes de decidir qué es caro: aquí lo barato va delante y lo caro detrás, que es justo lo contrario de lo que parece al leer el prompt.