Abierto a propuestas
Rodrigo
Torres
Ingeniero de IA
- SUNAT, pasarelas y libros contables
- Diseño la arquitectura y construyo
- años de desarrollo
- 6
- años de IA en producción
- 2
- sistemas propios
- 4
- sistemas en producción
- 20+
Seis años construyendo software. Dos con modelos en producción.
BM25 + búsqueda vectorial // Reciprocal Rank Fusion // servidores MCP // FastAPI // Step Functions // AWS Lambda // CDK // DynamoDB // walk-forward // PostgreSQL // facturación electrónica // pytest
Buscador escrito para esta página: 39 pasajes, BM25 más vectorial fusionados con RRF. No es PrecioVivo, que es el sistema del caso y corre sobre un catálogo de precios.
Escribí un gold set y la fusión perdió una vez
El buscador de arriba estaba descrito como híbrido y calificado por nadie. Veintiséis preguntas, cada una con los pasajes que de verdad la responden, marcados leyendo los pasajes y no leyendo lo que devolvía la búsqueda. Tres configuraciones que se diferencian en una sola cosa cada una.
- Condiciones
- n = 26 · k = 4
- Pasajes
- 39
- Preguntas
- 14 léxicas · 12 semánticas
- Embeddings
- gemini-embedding-001 · 256d
- Medido
- 2026-08-28
Vectores de consulta cacheados en disco, para que dos corridas no difieran por el servicio.
| Configuración | Recall@4 | MRR | NDCG@4 |
|---|---|---|---|
| Solo léxica (BM25) | 0.692[0.50, 0.83] | 0.608 | 0.574 |
| Solo vectorial | 0.885[0.71, 0.96] | 0.835 | 0.800 |
| Fusionadas con RRF● | 0.923[0.76, 0.98] | 0.789 | 0.756 |
La mitad léxica sola falla ocho de veintiséis, y las ocho son preguntas que no comparten ninguna palabra con su pasaje: si construye solo o acompañado, si alguna vez rompió producción, cuánto pretende ganar. Eso es exactamente lo que un índice vectorial existe para resolver.
La fusión recupera una pregunta que ninguna de las dos mitades encuentra por su cuenta, y es la prueba de que fusionar no es decoración: dos señales débiles en listas distintas suman a una posición que ninguna alcanzaba sola.
También paga por ello. El MRR de la fusión (0.789) queda por debajo del de la vectorial sola (0.835): mete la respuesta en los cuatro más veces, y la deja algo más abajo dentro de esos cuatro. Con cuatro pasajes yendo al modelo eso importa poco, y con un solo resultado en pantalla importaría mucho.
En inglés la fusión queda por debajo de la vectorial sola, 0.769 contra 0.846, y pierde las tres preguntas donde discrepan. Con n de 26 el test de signos da p = 0.25, que no distingue eso del azar. Cambiar la configuración por idioma con esa evidencia sería ajustar el sistema al gold set, así que corre la misma en los dos.
El intervalo de Wilson es la parte que no se puede saltar. Un recall de 0.923 sobre 26 casos es 0.923 [0.76, 0.98]: el sistema puede estar en 0.76 y el gold set no lo sabría. Veintiséis casos son un punto de partida, no una respuesta.
Un defecto real salió de aquí. Las dos listas entraban a la fusión con profundidades distintas: la léxica entera, la vectorial cortada en 12. BM25 se queda con todo pasaje que puntúe sobre cero, mediana de 31 de 39, así que un pasaje en el puesto 30 seguía recibiendo voto y ninguna coincidencia vectorial comparable podía responderle. Cortar las dos a la misma profundidad subió el recall en español de 0.885 a 0.923. La corrección se justifica por el argumento; la cifra solo dice cuánto costaba.
Contratar a un ingeniero de IA es contratar cifras que nadie puede verificar.
Casi todo portafolio de IA afirma resultados sin decir cómo se midieron ni dónde falla el sistema. Yo publico la condición de medición junto a cada número, y una sección entera dedicada a lo que se rompe. Si algo no lo puedo enlazar a su fuente, no aparece.
Cómo trabajo ↗Cercanía es la medida
Un índice vectorial no guarda palabras, guarda direcciones. Cada fragmento es un punto y responder una consulta es preguntar qué puntos apuntan hacia el mismo lado. Acerca el cursor y mira lo que hace cada trazo: eso es lo que ocurre 9,281 veces cuando escribes una pregunta.
El campo responde al cursor. El índice responde a la consulta.
Cada cifra con la condición en la que se tomó
Recall de la búsqueda aislada del piso determinista, con el embebedor real. Con el de juguete daba 0.759: la diferencia es lo único que prueba que la mitad semántica no es decoración
Con el embebedor real. Preguntar por papaya, que no está en el catálogo, devuelve fichas de papa: 0.6444 contra 0.6656, dos centésimas. Ningún umbral las separa sin matar las preguntas agregadassin resolver en recuperación
Servidor MCP en producción del sistema RAG
La misma superficie, expuesta por HTTP
435 en pytest, 77 en vitestfalta cobertura
Misma validación walk-forward. Perdió contra AR(1)declarar la métrica
Los límites tienen la misma jerarquía que los resultados.
Papaya devuelve papa, y no lo arreglé en recuperación
Preguntar por un producto que no está en el catálogo devuelve el más parecido por nombre. Papaya puntúa 0.6444 y papa 0.6656: dos centésimas. Probé el umbral obvio y no funciona, porque cualquier corte que las separe mata las preguntas agregadas, que puntúan 0.5151. Lo resolví avisando al modelo desde el filtro léxico, que ya sabía que ninguna palabra coincidía con el catálogo. La evaluación sigue reportando las dos violaciones: no relajé el gold set. ↗
Se rompe a los 510 productos
514 bytes por elemento del Map contra el tope de 256 KB de estado de Step Functions. Sobre ese número hay que paginar el Map o mover la carga a S3. ↗
¿Conversamos?
Respondo rápido y sin plantilla.