Buscar en décadas de correo: primero las palabras clave, después el significado
La búsqueda por IA sobre correo falla con datos sucios. Cómo limpiar, agrupar en hilos e indexar un archivo para que palabras clave y semántica trabajen juntas.

Apuntar un modelo de lenguaje a un buzón y hacerle preguntas suena a proyecto de fin de semana. Después le dice con total confianza la fecha equivocada de un contrato, porque leyó una copia citada de un correo antiguo dentro de una respuesta más reciente, o no encuentra la factura 10442 porque el número no tiene «significado» al que una búsqueda vectorial pueda agarrarse. Una buena búsqueda sobre un archivo de correo casi no va del modelo. Va de los datos que le da y de la forma en que recupera. Así lo abordamos nosotros.
El correo es más sucio de lo que parece
Un archivo de correo que abarca años, varios sistemas de correo y unas cuantas migraciones tiene problemas que arruinan la búsqueda antes de que intervenga ninguna IA:
- Duplicados. El mismo mensaje está en la bandeja de entrada, en una carpeta de proyecto y en una exportación antigua. La búsqueda lo devuelve tres veces; una IA lo cuenta como tres fuentes que coinciden.
- Historial citado. Cada respuesta arrastra el hilo entero debajo. Indexe eso de forma ingenua y el correo más antiguo de un hilo aparece en cada respuesta, fuera de contexto y con la fecha equivocada.
- Codificaciones. El correo antiguo está lleno de juegos de caracteres heredados, cabeceras codificadas y partes mal etiquetadas. Descodifíquelo mal y los nombres y el texto en otros idiomas se convierten en basura.
- Adjuntos. El contrato es el PDF, no el correo que dice «adjunto». Si no extrae el texto de los adjuntos, no puede encontrar los documentos más importantes.
- Borrados. El correo borrado del buzón por razones de retención debe desaparecer también de todos los índices.
Limpie antes de indexar
Deduplique por Message-ID, recurriendo a un hash de las cabeceras clave y el cuerpo cuando falte. Guarde un documento por mensaje y conserve la lista de carpetas en las que apareció como metadatos.
Agrupe en hilos por References e In-Reply-To, no por líneas de asunto. Los asuntos cambian y «Re: factura» no es único. Con una agrupación correcta puede mostrar una conversación entera en orden y dejar que el paso de recuperación reúna un hilo en lugar de fragmentos sueltos.
Separe el texto nuevo del texto citado. Para cada mensaje, indexe el cuerpo completo para la búsqueda por palabras clave, pero genere embeddings solo de la parte que el autor escribió de verdad. Un comienzo sencillo:
import re
from email import policy
from email.parser import BytesParser
QUOTE_MARKERS = [
re.compile(r"^On .+ wrote:$", re.M),
re.compile(r"^-{2,}\s*Original Message\s*-{2,}", re.M | re.I),
re.compile(r"^From: .+\nSent: .+", re.M),
]
def new_text(raw: bytes) -> str:
msg = BytesParser(policy=policy.default).parsebytes(raw)
part = msg.get_body(preferencelist=("plain", "html"))
text = part.get_content() if part else ""
cut = len(text)
for rx in QUOTE_MARKERS:
m = rx.search(text)
if m:
cut = min(cut, m.start())
lines = [l for l in text[:cut].splitlines() if not l.startswith(">")]
return "\n".join(lines).strip()
El correo real necesita más marcadores que estos, en cada idioma en el que escribe su empresa, pero el principio se mantiene: el modelo debería leer lo que dijo cada persona, una vez, con la fecha y el remitente correctos.
Extraiga el texto de los adjuntos con herramientas estándar (pdftotext para los PDF, parsers de biblioteca para los formatos de oficina) e indéxelo como parte del mensaje, manteniendo un enlace al fichero original.
Dos índices, porque responden a preguntas distintas
Un índice de texto completo responde a preguntas con asideros exactos: un remitente, un dominio, un rango de fechas, un número de factura, una frase, «tiene un PDF». Es rápido, preciso y explicable. Use uno con stemming consciente del idioma, para que «facturas» encuentre «factura», y con sinónimos entre los idiomas que usa su empresa, para que una búsqueda en un idioma encuentre correo escrito en el otro.
Un índice vectorial responde a preguntas sobre significado: «¿cuándo empezó el proveedor a poner pegas al calendario de entregas?». Puede que no haya una sola palabra clave para eso. Un modelo de embeddings convierte cada mensaje limpio en un vector, y la búsqueda encuentra los mensajes cercanos en significado a la pregunta.
Ninguno basta por sí solo. Los vectores son débiles con identificadores y números; las palabras clave son débiles con las paráfrasis. Ejecute ambos, y combine:
- Extraiga primero los filtros de la pregunta: personas, dominios, fechas, «con adjunto».
- Aplique esos filtros a las dos búsquedas, para que la búsqueda vectorial nunca se salga del periodo ni de las personas de las que trata la pregunta.
- Fusione las dos listas de resultados. Reciprocal rank fusion es sencillo y funciona bien: puntúe cada mensaje con la suma de
1 / (k + rank)en ambas listas. - Expanda los mejores resultados a sus hilos, para que el modelo lea conversaciones, no fragmentos.
Haga que el modelo enseñe su trabajo
Solo entonces entra el modelo de lenguaje. Dele los mensajes recuperados, cada uno con un ID, fecha y remitente, e indíquele que responda solo a partir de ellos y que cite el ID en cada afirmación. En la interfaz, convierta cada cita en un enlace que abra el correo original.
Las citas hacen dos trabajos. El usuario puede comprobar cada afirmación con un clic, que es la única forma responsable de usar un modelo sobre registros de negocio. Y usted ve de inmediato cuándo falló la recuperación: una respuesta que cita los mensajes equivocados es un problema de recuperación, no del modelo, y le dice dónde mirar.
Diga a los usuarios con claridad que un modelo puede leer mal. Con las fuentes a un clic, ese es un riesgo manejable.
Mantenga los datos en el edificio
Cada componente de este diseño corre con holgura en su propio hardware: el índice de texto completo en servidores normales, la base de datos vectorial al lado, y el modelo de embeddings y el modelo de lenguaje en servidores GPU que usted controla. El correo, las preguntas y las respuestas nunca necesitan salir de su red.
Pruébelo como un sistema, no como una demo
Antes de confiar en él, construya un pequeño conjunto de preguntas con respuestas conocidas: una factura concreta, el primer correo de una disputa conocida, un mensaje escrito en su segundo idioma, un mensaje que se borró el mes pasado (no debería encontrarse). Ejecútelas después de cada cambio en la limpieza, la indexación o los prompts, y guarde los resultados. Si un cambio empeora las preguntas conocidas, lo verá.
Y vigile la propia tubería. Una sincronización que se detuvo en silencio, un índice que no se ha puesto al día o un retraso en los vectores tras un reinicio parecen «la búsqueda funciona» hasta que alguien necesita el correo de la semana pasada. Compruebe toda la cadena de extremo a extremo, de forma programada, y alerte cuando cualquier etapa se quede atrás.
En resumen
- Deduplique, agrupe en hilos, descodifique y elimine el texto citado antes de indexar nada.
- Indexe los adjuntos; ahí es donde están los contratos.
- Ejecute juntas la búsqueda por palabras clave y la vectorial, con los filtros aplicados a ambas.
- Responda solo a partir del correo recuperado, con una cita en cada afirmación.
- Mantenga cada componente en hardware que usted controle, y pruebe con preguntas conocidas.
Construimos archivos de correo privados así, con la IA corriendo en sus propias GPU privadas. Si está planificando uno, con gusto le ayudamos.
Related (rows[1])
Relacionado: Archivo de correo privado con búsqueda por IA local · Managed Nextcloud para documentos de oficina
Publicado el 15 de octubre de 2026.
Todas las notas de campo