Las ventanas de contexto más grandes no reparan RAG, así que construí un sistema que sí lo hace

TL;DR

Creé un sistema de preguntas y respuestas con un conjunto de datos y confié en una respuesta de RAG que era menos de la mitad correcta. Medí esto en 7 tipos de consultas y 5 tamaños de contexto en 100.000 filas. consultas fuera de RAG por completo.

Confié en el número equivocado

El mes pasado estuve construyendo una nueva función para EmiTechLogic. Los alumnos ahora pueden cargar sus propios archivos CSV desordenados y hacer preguntas en inglés sencillo sobre sus datos. Sonaba perfecto para RAG, así que hice todo lo posible: incrustaciones, recuperación, respuestas atractivas.

Las primeras demostraciones parecían increíbles. Tablas limpias, números seguros, formato profesional. De hecho, comencé a confiar en el sistema en nuestras pruebas internas.

Luego elegí un número para volver a verificar.

Gasto real en comestibles en el conjunto de datos: 1.140.033,24 dólares.

La modelo me dio un hermoso desglose por categorías. Parecía legítimo. Sumé los números que devolvió.

Era menos de la mitad.

Me quedé sentado mirando la pantalla pensando "esto no puede estar bien". Entonces hice lo que haría cualquier ingeniero. Aumenté la ventana de contexto. 4k… 16k… 32k… 128k fichas. Cada vez la respuesta era más larga, más detallada y más equivocada.

Fue entonces cuando finalmente hizo clic. Este no fue un problema de recuperación. Le estaba pidiendo a un sistema de recuperación que realizara cálculos pesados ​​sobre datos que solo había visto parcialmente. Y en lugar de decir que no estaba seguro o que faltaba información, el modelo estaba produciendo respuestas pulidas y estructuradas que parecían correctas.

Por qué RAG no puede agregar

El proceso de RAG no comprende realmente los datos estructurados. Todo lo que hace es tomar cada fila CSV y aplanarla hasta convertirla en texto sin formato. Eso es todo. Para el modelo, una fila se parece a esto:

"2019-01-01 comestibles_pos 107.23 F NC Jennifer Banks …"

Para una consulta como "¿Cuál es el gasto total por categoría?", el canal RAG hace lo siguiente:

1. Tokenizar: ["total", "gasto", "categoría"] 2. Calificar las 100.000 filas por superposición de palabras clave 3. Devolver las N filas principales como texto sin formato serializado 4. Pídale al LLM que sume y agrupe a partir de ese texto

El paso 4 es donde falla el sistema. El LLM no ejecuta un SUM. Se trata de hacer coincidir patrones de números de un blob de texto y generar una respuesta que imita una agregación.

Los modelos luchan con la precisión numérica a escala[1], pero el verdadero problema es la presentación. El modelo le ofrece un desglose detallado de todas las categorías. Esta es una trampa clásica. El resultado parece profesional. Imita tan bien la estructura de un informe real que su cerebro asume que el contenido es válido. No tienes forma de verificar que falta el 92% de tus datos.

RAG es una herramienta de recuperación. No es un motor de cálculo. La recuperación encuentra fragmentos relevantes. El cálculo requiere un análisis completo del conjunto de datos. Cuando usas RAG para matemáticas, obtienes una respuesta incorrecta que parece autorizada. Esa distinción es crítica. Una respuesta parcial indica que faltan datos. Una respuesta aparentemente incorrecta simplemente indica una confianza falsa.

Código completo: https://github.com/Emmimal/context-window-engine/

El punto de referencia: dos canalizaciones, la misma consulta

Para medir esto con precisión, creé un punto de referencia que ejecuta dos canales uno al lado del otro para cada consulta.

El primer oleoducto es una simulación RAG. Modela lo que un canal vectorial ingenuo pasa a un LLM en cinco tamaños de contexto. Probé cinco tamaños de contexto, desde 5 filas hasta 8000. Eso varía de 325 tokens a 500.000. Para cada tamaño, realicé un seguimiento de tres métricas: cuántos datos ve el LLM, qué suma calcula a partir de ese segmento específico y si un lector realmente podría detectar el error.

El segundo canal es un motor semántico que ejecuta la misma consulta como un escaneo completo determinista en las 100.000 filas y devuelve la respuesta correcta exacta.

Comparación arquitectónica de los flujos de trabajo de procesamiento de consultas, contrastando la recuperación de simulación RAG basada en texto con la agregación de datos estructurados en un motor semántico. Imagen del autor.

La simulación no reproduce resultados exactos del LLM. Lo que conserva es la propiedad estructural clave: una porción parcial de datos ingresada en un sistema que devuelve una respuesta completa. Esa es la propiedad que causa el problema y eso es lo que mide el punto de referencia.

Elegí siete tipos de consultas para cubrir todos los patrones de agregación que probablemente encontrará un sistema de datos estructurados:

QueryOperationPor qué rompe RAGGasto total por categoríaSUM + GROUP BYRequiere sumar todas las filas en 14 gruposTransacción promedio más alta por categoríaAVG + GROUP BYCambios promedio con cada fila faltanteTotal gastado en comestibles_posSUM + filtro categóricoEl filtro requiere ver todas las filas coincidentesCuántas clientas realizaron transaccionesCOUNT + filterCount no tiene sentido en un escaneo parcialGasto total donde monto > $500SUM + comparación numéricaLógica de umbral requiere datos completosEstado con el gasto total más bajoMIN + GROUP BY en 50 gruposEl mínimo solo se puede encontrar con todos los grupos presentesPorcentaje de transacciones fraudulentasCOUNT + ratioLa relación no está definida en un denominador parcial

Estas consultas no son únicas ni complejas. Son las preguntas estándar que hace cualquier analista cuando analiza un nuevo conjunto de datos. Precisamente por eso este fracaso es tan crítico.

Colapso de observabilidad de errores

Aquí está el resultado comparativo completo de la consulta que inició todo esto. Lo muestro completo porque los números hacen que el problema sea imposible de descartar.

GROUND TRUTH (motor semántico) SUMA(amt) GRUPO POR categoría → 14 grupos #1 comestibles_pos 1,140,033.24 #2 shopping_net 773,527.93 #3 shopping_pos 725,766.14 #4 gas_transport 648,804.24 #5 home 556,526.53 Latencia: 100,47 ms | Filas escaneadas: 100.000 SIMULACIÓN DE RAG: lo que recibe el LLM en cada tamaño de contexto Cobertura de filas de contexto Suma parcial ¿Error detectable? diminuto (~325 tokens) 5 0,0050% 197,73 FÁCIL pequeño (~3K tokens) 50 0,0500% 2.003,56 MODERADO mediano (~32K tokens) 500 0,5000% 31.023,21 DIFÍCIL grande (~130K tokens) 2000 2,0000% 140.093,16 MUY DIFÍCIL xlarge (~520.000 tokens) 8.000 8,0000% 569.368,22 CASI IMPOSIBLE

Me quedé mirando estos resultados por un tiempo. La parte más preocupante no fue sólo que las respuestas fueran incorrectas, sino que se volvió mucho más difícil detectar los errores a medida que crecía la ventana de contexto.

En 8000 filas, el error todavía superaba el 50%, pero la respuesta parecía un informe profesional. Tendrías que verificar manualmente los números para notar que algo andaba mal. Eso es lo que comencé a llamar colapso de la observabilidad de errores. Cuanto más contexto le daba al modelo, más convincente (pero no más preciso) se volvía el resultado.

La columna "Suma parcial" muestra el total si el LLM agregó todos los valores de monto en las filas que realmente recuperó. El mensaje "¿Error detectable?" La columna califica la probabilidad de que un lector humano detecte un error.

Con 5 filas, la suma parcial es 197,73. El total correcto es 1.140.033,24. Es obvio. El resultado es breve, los números son incorrectos y los datos que faltan son claros. El error es instantáneo.

En 8.000 filas, la suma parcial llega a 569.368,22. El LLM ahora ha visto las 14 categorías. Genera un informe de 1.500 palabras con cifras específicas y un lenguaje seguro. El error es del 50%, pero está oculto dentro de una prosa autorizada y bien estructurada. Sin una referencia externa, el lector no tiene forma de captarlo.

Este es el patrón que se mantuvo en las siete consultas:

Ventana de contextoFilasCobertura del conjunto de datosLongitud de respuesta¿Error detectable?~325 tokens50,005%~50 palabrasSÍ, obviamente una suposición~3K tokens500,050%~150 palabrasQUIZÁS~32K tokens5000,500%~400 palabrasDIFÍCIL~130K tokens20002,000%~800 palabrasMUY DIFÍCIL ~ 520 000 tokens 80008,000 % ~ 1500 palabras CASI IMPOSIBLE Motor semántico 100 000 100 % <200 ms N/A — exacto

A esto lo llamé Colapso de observabilidad de errores. A medida que crece el contexto, la confianza aumenta con él. La corrección no.

Diagrama de flujo y líneas de tendencia que demuestran los efectos del aumento del tamaño del contexto en los LLM. El gráfico muestra que un mayor contexto conduce a una mayor confianza y una menor detectabilidad de errores, mientras que la precisión general permanece estable.
La ilusión del contexto: cómo las ventanas de contexto más grandes en los sistemas RAG y LLM aumentan la confianza del usuario y disminuyen la detectabilidad de errores sin mejorar la precisión real. Imagen del autor.

Los modos de fallo son asimétricos, lo que los hace peligrosos:

Una respuesta RAG incorrecta parece correcta. Está formateado, es específico y seguro. Un cálculo fallido genera un error explícito. Es visible.

Un fracaso guarda silencio. El otro es ruidoso. A medida que las ventanas de contexto alcanzan millones de tokens, la falla silenciosa se vuelve más difícil de detectar[4]. El sistema no se vuelve más seguro a medida que crece. Simplemente se vuelve más convincente.

El motor semántico: prueba de que la respuesta correcta es rápida

Antes de comprender completamente el problema, ya había creado un motor semántico simple por frustración. Sólo quería la respuesta correcta al menos una vez.

El enfoque resultó ser simple: analizar la consulta en operaciones adecuadas y ejecutar una sola pasada sobre todo el conjunto de datos. Sin incrustaciones, sin recuperación, sin conjeturas.

Así es como se ve eso en la práctica:

La lógica es simple. Realice una consulta como "¿Cuál es el gasto total por categoría?". El motor asigna esto a una operación directa: SUM(amt) GROUP BY categoría. Procesa el conjunto completo de 100.000 filas en una sola pasada. Acumula totales agrupados. No hay recuperación. Ninguna inferencia. Sin escaneo parcial. Visita cada fila una vez y devuelve el resultado exacto.

Esto demuestra que la respuesta correcta no es cara. Las consultas de referencia finalizaron en menos de 200 ms. Tamaño de la muestra: 100.000 filas. La agregación es trivial. La falla ocurre cuando dirige esas consultas a un sistema diseñado para malinterpretarlas.

desde context_window_engine importar compute_ground_truth, load_csv filas = load_csv("data/credit_card_transactions.csv", max_rows=100_000) gt = compute_ground_truth( query_label = "total por categoría", filas = filas, agg_func = "suma", agg_col = "amt", group_col = "categoría",) # gt.answer → [(grocery_pos, 1140033.24), (shopping_net, 773527.93), …] # gt.latency_ms → 100.47

El motor admite SUM, AVG, COUNT, MIN, MAX. Maneja filtros categóricos y numéricos. Incluye GROUP BY y ratios. Cero dependencias externas. Cada operación se ejecuta como una función determinista sobre la lista completa.

El motor en sí no es el producto. Esta es la prueba: la respuesta correcta se puede alcanzar en menos de un segundo. No se requiere inferencia. El verdadero desafío es enrutar las consultas allí de manera confiable.

La solución no es una mejor recuperación

Deja de intentar mejorar la recuperación. Si una consulta necesita el 100% de los datos, una muestra del 8% falla. La solución es eliminar la recuperación del bucle.

Necesitamos una capa de clasificación. Se encuentra delante de la tubería y realiza una llamada binaria: ¿cálculo o búsqueda?

La diferencia es clara. El “gasto total por categoría” exige un análisis completo. "Buscar transacciones de Jennifer Banks" es una búsqueda sencilla. El RAG estándar obliga a ambos a seguir el mismo camino. Ese es el defecto de diseño.

Un QueryRouter soluciona este problema. Inspecciona cada consulta entrante y la dirige a la ruta correcta antes de que comience una única recuperación.

Diagrama de flujo arquitectónico que ilustra un QueryRouter que clasifica las consultas entrantes. El enrutador divide las cargas de trabajo en una ruta de Computación codificada en azul para consultas analíticas manejadas por un SemanticEngine y una ruta de Recuperación codificada en verde para consultas de búsqueda manejadas por una canalización RAG.
Arquitectura de enrutamiento de consultas basada en intenciones, que separa las intenciones de cálculo analítico de los canales de recuperación de información semántica. Imagen de Autor.

El clasificador utiliza tres niveles de señales, priorizados. Nivel 1: verbos de agregación: total, cuántos, promedio, mínimo, porcentaje. Estos exigen el cálculo de un conjunto de datos completo. Nivel 2: comparación numérica: más de 500, por encima de $1000, al menos. Esto implica filtrar y luego agregar, algo imposible para RAG. Nivel 3: señales de recuperación: buscar, mostrarme, enumerar, buscar. Estos indican búsquedas donde funciona la similitud semántica.

TierSignalEjemplosRuta1Verbo de agregacióntotal, cuántos, promedio, más bajo, porcentajeCOMPUTATION2Comparación numérica mayor que 500, por encima de $1,000, al menosCOMPUTATION3Señal de recuperaciónbuscar, mostrarme, enumerar, recuperarRETRIEVAL0No hay coincidenciaambiguoCOMPUTATION: valor predeterminado más seguro

El valor predeterminado es COMPUTACIÓN si ningún nivel coincide. Esto es deliberado. Los modos de falla son asimétricos: una respuesta RAG incorrecta en una agregación es silenciosamente incorrecta. Un motor de cálculo que no puede analizar una consulta genera un error. En caso de duda, falle en voz alta.

from query_router import QueryRouter router = QueryRouter(rows) result = router.route("¿Cuál es el gasto total por categoría?") # result.routed_to → "COMPUTATION" # result.answer.answer → [(grocery_pos, 1140033.24), …] # result.total_latency → ~250ms — clasificar + ejecutar resultado combinado = router.route("Buscar transacciones de Jennifer Banks") # result.routed_to → "RETRIEVAL" # result.answer.safe → True — RAG es apropiado

Enrutamiento del punto de referencia completo

Ejecuté nueve consultas a través del enrutador para verificar el rendimiento en ambos tipos: siete consultas de agregación destinadas al motor semántico y dos consultas de búsqueda para RAG.

Cada ruta era correcta. Las siete consultas de agregación llegaron al motor de escaneo completo y arrojaron resultados exactos. Las dos consultas de búsqueda activaron correctamente la ruta RAG. Observe el resultado: puntuaciones de confianza altas, coincidencia de patrones correcta y latencia inferior a 130 ms, incluso con el escaneo de 100 000 filas.

[1]✓ CÁLCULO "¿Cuál es el gasto total por categoría?" Nivel 1 | coincidente='total' | confianza=0,97 #1 comestibles_pos 1.140.033,24 (102,57 ms | 100.000 filas | exacto)[2]✓ CÁLCULO "¿Qué categoría tiene el monto promedio de transacción más alto?" Nivel 1 | coincidente = 'más alto' | confianza=0,97 71,91 (119,47 ms | 100.000 filas | exacto)[3]✓ CÁLCULO "¿Cuál es el monto total gastado en comestibles_pos?" Nivel 1 | coincidente='total' | confianza=0,97 1.140.033,24 (49,96 ms | 100.000 filas | exacto)[4]✓ CÁLCULO "¿Cuántas transacciones realizaron las clientas?" Nivel 1 | coincidente = '¿Cuántos?' | confianza=0,97 54.641,00 (90,45 ms | 100.000 filas | exacto)[5]✓ CÁLCULO "¿Cuál es el gasto total cuando el monto es superior a 500?" Nivel 1 | coincidente='total' | confianza=0,97 1.274.269,60 (91,65 ms | 100.000 filas | exacto)[6]✓ CÁLCULO "¿Qué estado tiene el gasto total más bajo?" Nivel 1 | coincidente = 'más bajo' | confianza = 0,97 RI más bajo 2.125,60 (109,05 ms | 100.000 filas | exacto)[7]✓ CÁLCULO "¿Qué porcentaje de transacciones son fraudulentas?" Nivel 1 | coincidente='porcentaje' | confianza=0,97 0,9900% (87,35 ms | 100.000 filas | exacto)[8]✓ RECUPERACIÓN "Buscar transacciones de Jennifer Banks" Nivel 3 | coincidente = 'Buscar' | confianza = 0,85 RAG es apropiado; no se requiere agregación[9]✓ RECUPERACIÓN "Muéstrame una transacción de muestra de Texas" Nivel 3 | matched='Muéstrame' | confianza = 0,85 RAG es apropiado; no se requiere agregación Precisión de enrutamiento: 9/9

9/9 correcto. El colapso de la observabilidad de errores es imposible si las consultas de agregación nunca llegan a RAG.

El conjunto de pruebas

El punto de referencia verifica nueve consultas específicas. El conjunto de pruebas garantiza la confiabilidad en una gama más amplia: casos extremos, entradas con formato incorrecto, datos faltantes y puntos de falla de producción comunes.

El conjunto de motores tiene 87 pruebas en 10 clases. Cubre el análisis flotante con signos de dólar, comas y notación científica; las cinco funciones de agregación en condiciones normales y con entradas vacías; los cinco operadores de filtros numéricos; agregación GROUP BY completa con filtros categóricos y numéricos combinados; Métricas de cobertura de simulación RAG en cada tamaño de contexto; y casos extremos que incluyen conjuntos de datos vacíos, filas con valores de columna faltantes y entradas de una sola fila.

El conjunto de enrutadores tiene 72 pruebas en 5 clases. Cubre los patrones de tres niveles, incluidos casos extremos como consultas en mayúsculas y consultas muy largas; análisis de operaciones de lenguaje natural a escritura para cada formulario de consulta admitido; corrección de enrutamiento y ejecución frente a las siete consultas de referencia; y un conjunto de contrastes que verifica que las respuestas del enrutador coincidan con el cálculo independiente de la verdad del terreno, lo que garantiza que el enrutador no introduzca ninguna desviación de la salida del propio motor.

Ejecute las pruebas del motor escribiendo python space -m space unittest space test_engine space -v. Esto ejecuta las 87 pruebas de la suite.

Ejecute las pruebas del enrutador escribiendo python space -m space unittest space test_router space -v. Esto ejecuta las 72 pruebas de la suite.

Los 159 pasan por Python 3.9+ sin dependencias externas.

Limitaciones honestas

Esta solución no es perfecta. Por el momento, solo funciona con archivos CSV individuales. Los conjuntos de datos de producción reales suelen ser complicados y tienen varias tablas que deben unirse. Deliberadamente mantuve el alcance pequeño porque quería algo que realmente funcionara de un extremo a otro primero.

El enrutador también es bastante básico (basado en expresiones regulares). Probé un pequeño clasificador basado en LLM desde el principio, pero era inconsistente y agregaba latencia, así que volví al enfoque simple. A veces gana la solución aburrida.

También simulé las respuestas de RAG en lugar de utilizar API reales para el punto de referencia. Los patrones se mantienen, pero su kilometraje con GPT-4o o Claude 3.5 variará ligeramente.

Se requiere formato CSV. El motor carga datos estructurados directamente desde archivos CSV. En este momento no se admiten conexiones de bases de datos, archivos Parquet y otros formatos tabulares.

Qué cambia esto

Agregar una capa de enrutamiento no cuesta casi nada. Clasificar una consulta según 65 patrones de expresiones regulares lleva solo microsegundos. El motor semántico agrega menos de 200 ms para escanear un conjunto de datos de 100.000 filas. La sobrecarga total es menor que la de una única llamada de incorporación.

Lo que obtiene a cambio es una respuesta determinista para cada consulta de agregación. Cada total, cada recuento y cada porcentaje ahora provienen de un análisis completo en lugar de una aproximación segura basada en el 8 por ciento de los datos. RAG sigue manejando aquello en lo que realmente es bueno: recuperar registros específicos, sacar a la luz pasajes relevantes y responder preguntas de búsqueda donde la similitud semántica es la herramienta adecuada para el trabajo.

El trapo no está roto. Simplemente se le pide que calcule y no puede hacerlo.
Lo peligroso no es que falle. Es que falla de manera convincente. Y ningún contexto cambia eso.

Puedes intentar escribirlo así:

Para comenzar, clone el repositorio usando git clone seguido de la URL https://github.com/Emmimal/context-window-engine/. Una vez que termine, vaya al directorio escribiendo cd context-window-engine. Finalmente, inicie el proyecto ejecutando python demo.py en su terminal.

Referencias

[1]Levy, M., Jacoby, A. y Goldberg, Y. (2024). Misma tarea, más tokens: el impacto de la longitud de la entrada en el rendimiento del razonamiento de modelos de lenguaje grandes. En Actas de la 62ª Reunión Anual de la Asociación de Lingüística Computacional (Volumen 1: Artículos extensos), páginas 15339–15353, Bangkok, Tailandia. Asociación de Lingüística Computacional.
https://doi.org/10.18653/v1/2024.acl-long.818

[2]Lewis, P., Perez, E., Piktus, A., Petroni, F., Karpukhin, V., Goyal, N., Küttler, H., Lewis, M., Yih, W.-t., Rocktäschel, T., Riedel, S. y Kiela, D. (2020). Generación aumentada de recuperación para tareas de PNL intensivas en conocimiento. Avances en los sistemas de procesamiento de información neuronal, 33, 9459–9474. https://doi.org/10.48550/arXiv.2005.11401

[3]Gao, Y., Xiong, Y., Gao, X., Jia, K., Pan, J., Bi, Y., Dai, Y., Sun, J.,
Guo, Q., Wang, M. y Wang, H. (2023). Generación de recuperación aumentada para modelos de lenguaje grandes: una encuesta. Preimpresión de arXiv arXiv:2312.10997.
https://doi.org/10.48550/arXiv.2312.10997

[4]Liu, NF, Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F. y Liang, P. (2023). Perdido en el medio: cómo los modelos de lenguaje utilizan contextos largos. Transacciones de la Asociación de Lingüística Computacional, 12, 157-173. https://doi.org/10.1162/tacl_a_00638

[5]Koshorek, O., Granot, N., Alloni, A., Admati, S., Hendel, R., Weiss, I., Arazi, A., Cohen, S.-N. y Belinkov, Y. (2025). RAG estructurado para responder preguntas agregativas. Preimpresión de arXiv arXiv:2511.08505.
https://doi.org/10.48550/arXiv.2511.08505

Divulgación

Todos los números de referencia provienen de ejecuciones reales en Python 3.12.6, Windows 11, solo CPU, sin GPU. El punto de referencia utiliza el conjunto de datos de detección de fraude en transacciones con tarjetas de crédito (Kartik Gajjar, Kaggle, 2020), un conjunto de datos sintético generado utilizando el simulador de transacciones Sparkov creado por Brandon Harris, con licencia CC0 (dominio público), disponible en kaggle.com/datasets/kartik2112/fraud-detection. La línea de base de RAG simula la recuperación y modela señales de confianza: no se realizan llamadas reales a la API de LLM. No se requieren claves API externas para reproducir ningún resultado en este artículo. Todo el código descrito aquí fue escrito y probado por mí.