Esta nota cuenta cómo construimos Chatty, un asistente conversacional que responde sobre un corpus de más de 10.000 documentos que se actualiza solo.
El recorrido va desde los primeros intentos fallidos con datos estructurados hasta una arquitectura RAG (Retrieval-Augmented Generation) con un algoritmo propio de ponderación vectorial para resolver un problema que rara vez se discute: la alucinación temporal.
El problema
El objetivo era simple de enunciar y bastante difícil de implementar: una interfaz en lenguaje natural sobre una base de conocimiento que crece todos los días.
Lo difícil no era el volumen
Diez mil archivos no son nada para una base vectorial moderna. El problema era otro: la información cambia con el tiempo. Las cifras de 2024 no son las de 2025, aunque hablen exactamente del mismo tema y se parezcan mucho a nivel semántico.
Fase 1: la trampa de creer que el LLM iba a calcular
Ingesta directa de datos crudos
Le pasamos al modelo Excel y CSV con varias columnas y le pedimos que respondiera consultas de negocio. Primeras iteraciones a inicios de 2024.
No funcionó
Los LLM no razonan aritméticamente sobre matrices grandes. En cuanto pedías comparar dos años, el modelo inventaba cifras o cruzaba columnas equivocadas. El error no era ocasional, era estructural.
De ahí salió la decisión que cambió el proyecto: el valor no estaba en el dato crudo, sino en el dato ya interpretado. Cambiamos la fuente. En vez de tablas, ingestamos los documentos donde alguien ya había analizado esos números: informes, presentaciones, PDFs. Chatty no tenía que calcular nada; tenía que recuperar la interpretación.
Backend: por qué nos salimos de Pinecone
Pinecone
Base vectorial gestionada. Cómoda para empezar, pero limitada cuando uno quiere meterle lógica propia a la recuperación.
PostgreSQL + pgvector
Permite mezclar operaciones sobre vectores y metadatos en la misma consulta SQL. Esa flexibilidad terminó siendo la base del algoritmo que vamos a describir más abajo.
Pipeline de ingesta
Hay un proceso nocturno de crawling que hace tres cosas:
Extracción
Una API conecta con el CMS y baja documentos nuevos o actualizados.
Chunking
Los documentos se parten en fragmentos lógicos, cuidando que cada bloque mantenga sentido por sí mismo.
Embeddings
Cada fragmento se convierte en vector usando modelos de OpenAI (text-embedding-3-small en este caso).
Vectores separados, no un blob único
Una decisión que vale la pena explicar: en vez de vectorizar el documento completo como un solo bloque, indexamos por separado el título, el contenido y guardamos los metadatos temporales como columna aparte.
Vector del título
Captura la intención temática del documento.
Vector del contenido
Captura los detalles y los datos específicos.
Metadatos temporales
Fecha de publicación, usada después para ponderar relevancia.
El problema temporal: cuando dos respuestas se parecen y solo una sirve
El bug más interesante del proyecto fue este. Lo bautizamos internamente como "ambigüedad semántica temporal", que suena rimbombante pero describe algo concreto.
Escenario: el usuario pregunta "¿Cómo está la venta de tarjetas gráficas?"
Informe A · Enero 2020
"Las ventas de tarjetas gráficas subieron."
Informe B · Octubre 2025
"Las ventas de tarjetas gráficas cayeron."
Para una similitud de coseno estándar, los dos documentos son casi idénticos respecto a la pregunta. Peor: el de 2020 podía obtener un score levemente superior y la respuesta terminaba siendo la versión obsoleta.
Solución: ponderación vectorial dinámica
Lo que hicimos fue agregar una capa lógica sobre pgvector que no se queda en la similitud, sino que calcula un índice combinando similitud y antigüedad.
Fórmula
Rfinal = (α · Scontenido) + (β · Stítulo) + (γ · Ptemporal)
Scontenido
Distancia vectorial del cuerpo del texto. Menor distancia, mayor similitud semántica.
Stítulo
Distancia vectorial del título. Sirve para reforzar la intención temática.
Ptemporal
Penalización por antigüedad. Días desde la fecha actual, normalizados.
α, β, γ
Pesos. Se ajustan empíricamente según el dominio del corpus; no hay receta universal.
Cómo se ve esto en debugging
| Documento | Similitud | Factor fecha | Score ponderado | Resultado |
|---|---|---|---|---|
| Doc_2025 | 0.15 | 1.0 | 0.86 | Selecciona |
| Doc_2020 | 0.12 | 1.5 | 1.24 | Descarta |
Un score por debajo de 1.0 indica una coincidencia de muy alta calidad.
Continuidad conversacional
Vector híbrido
El sistema detecta cuando una pregunta es continuación de la anterior. Si hay correlación, se arma un vector híbrido que combina ambas para refinar la búsqueda. Es la forma de evitar que el hilo de la conversación se corte cada vez que el usuario hace una pregunta de seguimiento.
La capa LLM
Cuando Postgres devuelve los diez fragmentos mejor rankeados, recién ahí entra el LLM (OpenAI, en esta implementación).
Una distinción que vale la pena marcar
El LLM no sabe la respuesta. La redacta a partir de lo que el algoritmo ya seleccionó. Si el retrieval falla, el LLM también; no hay magia que compense una recuperación mala.
Lo que recibe el modelo
System prompt: reglas estrictas sobre cómo comportarse y qué no hacer.
Contexto: los diez fragmentos filtrados por el algoritmo.
Pregunta: la consulta del usuario en lenguaje natural.
La instrucción es deliberadamente cerrada: "Usando exclusivamente la información proporcionada a continuación, responde a la pregunta del usuario humanizando los datos." Sin esa cláusula, el modelo se permite improvisar.
Lo que viene: agentes de enrutamiento
La conclusión práctica de este proyecto es que para RAG sobre datos históricos la búsqueda vectorial pura no alcanza. La ponderación temporal sobre pgvector fue lo que nos permitió responder con la verdad actual y no solo con la más parecida.
El próximo paso es agregar agentes de enrutamiento: un modelo liviano que clasifique la intención de la pregunta (por ejemplo, ventas vs. marketing) y dirija la consulta a particiones específicas de la base. La meta es bajar latencia y costo manteniendo precisión.
¿Estás armando un RAG y te suenan estos problemas?
Si estás peleando con alucinaciones, datos viejos o relevancia, conversemos. En Wovi diseñamos arquitecturas de IA pensadas para que la respuesta sea útil, no solo parecida.
Hablemos por WhatsApp