Como a recuperação funciona
O pipeline híbrido, o modelo de memória e a maquinaria de confiança: uma página, sem enrolação.
Este é o modelo mental sobre o qual a API e as ferramentas MCP (catálogo gerado) se apoiam. Tudo aqui descreve o que o engine realmente faz; o contrato mecânico completo está nas páginas de referência geradas (REST e schemas MCP).
O pipeline de recuperação
Recuperação híbrida: BM25 (português e inglês) e vetores densos (LFM2.5-Embedding-350M, 1024 dimensões) fundidos por RRF, depois reordenação por cross-encoder (BGE-Reranker-v2-m3) e MMR para diversidade. Um LLM só expande a pergunta; nunca ranqueia. O PageRank personalizado existe atrás de uma flag e está desligado na produção.
┌─ BM25 (pt + en, pgturbohybrid) ─────────────────┐
query ─▶ LLM expansion ──┤ ├─ RRF ─▶ cross-encoder (BGE-Reranker-v2-m3) ─▶ MMR ─▶ top-K
└─ dense vectors (LFM2.5-Embedding-350M, 1024d) ──┘- Expansão da pergunta (LLM) — um LLM reescreve/expande a pergunta para gerar variantes de busca. É o único ponto com modelo generativo; ele nunca ordena resultados.
- Léxico (BM25, português e inglês) — o
pgturbohybridmantém BM25 e denso em um único índice; otenant_hashviaja noINCLUDEdo índice, então o filtro de tenant acontece dentro do index scan, não como pós-filtro. Num índice multi-tenant, essa é a diferença entre um ANN que funciona e um ANN que silenciosamente passa fome nos tenants pequenos. - Denso — embeddings LFM2.5-Embedding-350M de 1024 dimensões em
content_vectors(deduplicados por hash de conteúdo), HNSW comef_search=1000. - Fusão RRF — a reciprocal-rank fusion mescla as duas pernas sem precisar de scores comparáveis.
- Rerank de cross-encoder — o BGE-Reranker-v2-m3 pontua pares pergunta-documento diretamente.
- MMR — o filtro de diversidade (
diverse: true, padrão) impede que o top-K seja dez paráfrases de um documento só.
O mode em /search e memory_retrieve (auto, keyword, semantic, causal, hybrid) polariza o roteamento da pergunta dentro desse mesmo pipeline; o auto decide por você.
Ranking é o último suspeito, não o primeiro. Quando a recuperação falha, a ordem honesta de investigação é: pool de candidatos (o documento foi indexado?) → hidratação (o corpo foi carregado?) → renderização de snippet/janela (a resposta estava dentro do trecho entregue?) → ranking. O histórico de debugging do próprio engine é uma cadeia de nove "correções" de ranking que não mudaram nada enquanto o documento estava sendo cortado na renderização.
O modelo de memória
| Camada | O que é | Comportamento-chave |
|---|---|---|
| Documentos | O corpus. Identidade collection + path, conteúdo deduplicado por hash (entre coleções também) | Embedding assíncrono (embed_state); visibilidade tenant/private |
| Observações consolidadas | Síntese de ordem superior dos documentos | Worker de consolidação, gate de confiança ≥ 0.65 |
| Keyed facts | Snapshots (fact_key, as_of) com nível de autoridade | Supersedem a prosa. Um documento que contradiz um keyed fact perde |
| Tríplas de entidades (KG) | Fatos SPO, temporais, por (tenant_id, entity_id) | Pre-check SHACL; rejeitadas vão para quarentena, nunca entram em silêncio |
| Episódios | Sumários delimitados de diálogo | Recuperação episódica; memory_prepare com escopo |
| Decisões | Registros de primeira classe: categoria, cenário, raciocínio, resultado, confiança | Trilha de auditoria com cadeia de hashes; links causais (caused/influenced/precedent_for) |
| Provenance | Linhagem W3C PROV-O, cadeia de hashes SHA-256 | Toda escrita de trípla é rastreada; à prova de adulteração por construção |
| Conflitos de fatos | Contradições explícitas de valor/tempo/relação | Detectadas nos ticks de consolidação e sob demanda; resolvidas por estratégia |
O caminho de escrita espelha isso: documentos crus entram, a extração de entidades (GLiNER zero-shot) enriquece, a consolidação sintetiza observações, os hooks do decision-extractor promovem decisões, e toda escrita de trípla chega com provenance.
Tenancy e identidade
- Toda tabela com escopo de tenant é protegida por RLS com uma policy
tenant_id; o runtime conecta como um role de app semBYPASSRLS. - A identidade vem do token — nunca adivinhada, nunca hardcoded. Zero particularidades de tenant/usuário/empresa no código-fonte.
- A identidade de uma entidade é
(tenant_id, entity_id); o vault não isola nada por si só — quem isola é o tenant.
Consolidação e decaimento
Memória que nunca decai é um lixão. Três mecanismos a mantêm honesta:
- Políticas de lifecycle arquivam documentos velhos (itens pinados ficam isentos); o
lifecycle_sweepas executa (dry-run por padrão). - Curation —
pin(+0.3 de boost permanente),snooze(esconde por N dias) — é a alavanca manual, movida pelo loop de feedback domemory_used. - O ledger de uso registra cada operação por canal; o
value_dailypré-agrega tudo. O ranking aprende do uso declarado, não de palpites.
O contrato de uma chamada
POST /api/v1/memory/prepare (REST) e memory_prepare (MCP) montam um bundle completo de contexto — recall + fatos do vault + foresights + funil (episódios, lições, keyed facts, top documents) — numa única chamada, com um orçamento de tokens. Este é o ponto de integração pretendido para prompt hooks; as ferramentas de leitura servem para aprofundar, não para você montar seu próprio bundle.