Pesquisar décadas de e-mail: primeiro as palavras-chave, depois o significado
A pesquisa com IA em e-mail falha com dados sujos. Como limpar, agrupar em threads e indexar um arquivo de e-mail para que a pesquisa por palavras-chave e a pesquisa semântica funcionem juntas, com fontes.

Apontar um modelo de linguagem a uma caixa de correio e fazer-lhe perguntas soa a projeto de fim de semana. Depois diz-lhe com toda a confiança a data errada de um contrato, porque leu uma cópia citada de um e-mail antigo dentro de uma resposta mais recente, ou não consegue encontrar a fatura 10442 porque o número não tem «significado» a que uma pesquisa vetorial se possa agarrar. Uma boa pesquisa num arquivo de e-mail não tem sobretudo a ver com o modelo. Tem a ver com os dados que lhe dá e com a forma como recupera. É assim que a abordamos.
O e-mail é mais confuso do que parece
Um arquivo de e-mail que abrange anos, vários sistemas de correio e algumas migrações tem problemas que arruínam a pesquisa antes de qualquer IA entrar em cena:
- Duplicados. A mesma mensagem está na Caixa de entrada, numa pasta de projeto e numa exportação antiga. A pesquisa devolve-a três vezes; uma IA conta-a como três fontes a concordar.
- Histórico citado. Cada resposta transporta todo o thread por baixo. Indexe isso de forma ingénua e o e-mail mais antigo de um thread aparece em todas as respostas, fora de contexto e com a data errada.
- Codificações. O correio mais antigo está cheio de conjuntos de caracteres legados, cabeçalhos codificados e partes mal etiquetadas. Descodifique-o mal e os nomes e o texto não inglês transformam-se em lixo.
- Anexos. O contrato é o PDF, não o e-mail que diz «em anexo». Se não extrair o texto dos anexos, não consegue encontrar os documentos mais importantes.
- Eliminações. O correio eliminado da caixa por razões de retenção tem de desaparecer também de todos os índices.
Limpe antes de indexar
Elimine duplicados pelo Message-ID, recorrendo a um hash dos cabeçalhos principais e do corpo quando ele falta. Guarde um documento por mensagem e mantenha como metadados a lista de pastas em que apareceu.
Agrupe em threads pelos References e In-Reply-To, não pelos assuntos. Os assuntos mudam e «Re: fatura» não é único. Com threading correto pode mostrar uma conversa inteira por ordem e deixar o passo de recuperação puxar um thread completo em vez de fragmentos isolados.
Separe o texto novo do texto citado. Para cada mensagem, indexe o corpo completo para a pesquisa por palavras-chave, mas faça embedding apenas da parte que o autor realmente escreveu. Um começo simples:
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()
O correio real precisa de mais marcadores do que estes, em todas as línguas em que a sua empresa escreve, mas o princípio mantém-se: o modelo deve ler o que cada pessoa disse, uma vez, com a data e o remetente certos.
Extraia o texto dos anexos com ferramentas padrão (pdftotext para PDF, parsers de bibliotecas para formatos de escritório) e indexe-o como parte da mensagem, mantendo uma ligação ao ficheiro original.
Dois índices, porque respondem a perguntas diferentes
Um índice de texto integral responde a perguntas com pegas exatas: um remetente, um domínio, um intervalo de datas, um número de fatura, uma frase, «tem um PDF». É rápido, preciso e explicável. Use um com stemming que conheça a língua, para que «faturas» encontre «fatura», e com sinónimos entre as línguas que a sua empresa usa, para que uma pesquisa numa língua encontre correio escrito na outra.
Um índice vetorial responde a perguntas de significado: «quando é que o fornecedor contestou pela primeira vez o calendário de entrega?» Pode não haver uma única palavra-chave para isso. Um modelo de embedding transforma cada mensagem limpa num vetor, e a pesquisa encontra mensagens próximas em significado da pergunta.
Nenhum chega por si só. Os vetores são fracos em identificadores e números; as palavras-chave são fracas em paráfrases. Corra ambos, e combine:
- Extraia primeiro filtros da pergunta: pessoas, domínios, datas, «com anexo».
- Aplique esses filtros às duas pesquisas, para que a pesquisa vetorial nunca saia do período ou das pessoas a que a pergunta diz respeito.
- Funda as duas listas de resultados. O reciprocal rank fusion é simples e funciona bem: pontue cada mensagem pela soma de
1 / (k + rank)nas duas listas. - Expanda os melhores resultados aos seus threads, para que o modelo leia conversas, não fragmentos.
Faça o modelo mostrar o seu trabalho
Só então entra o modelo de linguagem. Dê-lhe as mensagens recuperadas, cada uma com ID, data e remetente, e instrua-o a responder apenas a partir delas e a citar o ID em cada afirmação. Na interface, transforme cada citação numa ligação que abre o e-mail original.
As citações fazem dois trabalhos. O utilizador pode verificar cada afirmação com um clique, que é a única forma responsável de usar um modelo sobre registos de negócio. E vê-se de imediato quando a recuperação falhou: uma resposta que cita as mensagens erradas é um problema de recuperação, não um problema do modelo, e diz-lhe onde procurar.
Diga claramente aos utilizadores que um modelo pode interpretar mal. Com as fontes a um clique de distância, esse é um risco gerível.
Mantenha os dados dentro do edifício
Todos os componentes deste desenho correm confortavelmente no seu próprio hardware: o índice de texto integral em servidores comuns, a base de dados vetorial ao lado, e o modelo de embedding e o modelo de linguagem em GPU servers que a sua empresa controla. O correio, as perguntas e as respostas nunca precisam de sair da sua rede.
Teste-o como um sistema, não como uma demonstração
Antes de confiar nele, construa um pequeno conjunto de perguntas com respostas conhecidas: uma fatura específica, o primeiro e-mail de um litígio conhecido, uma mensagem escrita na sua segunda língua, uma mensagem eliminada no mês passado (não deve ser encontrada). Corra-as depois de cada alteração à limpeza, à indexação ou aos prompts, e guarde os resultados. Se uma alteração piorar perguntas conhecidas, vai vê-lo.
E vigie o próprio pipeline. Uma sincronização que parou em silêncio, um índice que não recuperou o atraso, ou uma fila de vetores depois de um reinício parecem todos «a pesquisa funciona» até alguém precisar do correio da semana passada. Verifique toda a cadeia de ponta a ponta, com regularidade, e alerte quando qualquer etapa ficar para trás.
Em suma
- Elimine duplicados, agrupe em threads, descodifique e retire o texto citado antes de indexar o que quer que seja.
- Indexe os anexos; é lá que estão os contratos.
- Corra a pesquisa por palavras-chave e a pesquisa vetorial juntas, com filtros aplicados a ambas.
- Responda apenas a partir do correio recuperado, com uma citação em cada afirmação.
- Mantenha todos os componentes em hardware que controla, e teste com perguntas conhecidas.
Construímos arquivos de e-mail privados assim, com a IA a correr nas suas próprias GPU privadas. Se está a planear um, teremos todo o gosto em ajudar.
Relacionados: Arquivo de e-mail privado com pesquisa por IA local · Managed Nextcloud para documentos de escritório
Publicado em 15 de outubro de 2026.
Todas as notas de campo