Engineering Brief · Neo4j Aura + neo4j-graphrag

GraphRAG Agêntico com Autocorreção de Dados e Resiliência a OOM

Sistema de produção que evoluiu de um retriever para um curador autônomo do grafo. Dados reais, medidos em execução.

00 O que este sistema faz e para que serve

Antes da engenharia, o propósito.

PerguntaResposta
O problemaMarcas perdem vendas sem entender o porquê. A decisão de compra acontece fora do alcance delas: o consumidor desconfia, compara, valida e decide em conversas, avaliações e fóruns que a marca não enxerga.
O que o sistema fazEscuta conversas reais de consumidores, modela o caminho de decisão como um grafo (Ciclo de Decisão™: Desconfiança → Comparação → Validação → Decisão), conecta cada objeção a fatos verificados e fontes com URL, e responde perguntas com evidência ancorada no grafo.
Para que serveDiagnosticar por que uma marca é escolhida ou descartada (qual momento do ciclo ela perde) e orientar o que fazer em cada momento. O grafo é o ativo acumulado: cada ciclo de operação adiciona fatos, fontes e padrões à Biblioteca de Decisão™.
Por que grafosA decisão é um caminho, não um documento. A pergunta "por que o consumidor não comprou?" exige navegar de objeção em objeção até a fonte que a sustenta. Um grafo modela isso nativamente; uma tabela ou um texto não.
22
Objeções reais de consumidor no grafo
20
Fatos verificados sustentando objeções
33
Fontes com URL (imprensa, fóruns, Reclame AQUI, órgãos)
4
Momentos do Ciclo de Decisão™ modelados

Exemplo real de uso: a pergunta "quais as principais objeções de preço na fase de Validação?" retorna as objeções do grafo, cada uma com o fato que a sustenta e as fontes com URL (Reclame AQUI, Forbes, Mundo do Marketing). Sem fonte, o sistema recusa a afirmação: prefere declarar um gap a fabricar um dado. É o comportamento demonstrado na seção 05.

01 O Grafo de Decisão™ ao vivo

98 nós, 108 arestas. Objeções de consumidor conectadas a fatos verificados e fontes com URL. Navegável: arraste, clique, passe o mouse.

Cadeia típica: (:Objeção) ← SUSTENTA ← (:Fato) ← CITA ← (:Fonte). Cada Fato carrega URL real; cada Fonte tem nome, tipo e link verificados.

02 Métricas de operação (medidas, não estimadas)

0.54s
Latência de consulta RAG (era 10.7s)
25
Nós ingeridos e indexados sem intervenção manual
98
Nós Embeddable com text + embedding (384 dims)
≤5min
Recuperação do daemon via watchdog (cron)
ComponenteAntesDepoisTécnica
RAG (embedding + busca híbrida)10.7s0.92sDaemon persistente ONNX (modelo carregado 1x)
Consulta textual do grafo3.7s0.85sDriver Bolt persistente (sem reconexão por chamada)
Pre-flight completo (Memória + Grafo + RAG)10.7s0.54sHTTP local + fallback gracioso p/ subprocess

03 Arquitetura

Um daemon local segura o modelo e o driver; o pre-flight tenta HTTP e cai para subprocess se o daemon estiver fora. O watchdog (cron 5min) restaura em OOM.

pre_flight_memoria.py
   │  tenta HTTP local (0.2-0.9s)
   ▼
embedding_daemon.py  :9120   ──►  Neo4j Aura (driver persistente)
   • /health                     vector index 384d (cosine)
   • /perguntar (RAG híbrido)    fulltext index
   • /grafo   (consulta textual) + neighbor
   ▲
   │  se fora do ar
   ▼
subprocess neo4j_rag.py  (fallback gracioso, ~10s)

watchdog_embedding_daemon.py  (cron a cada 5min)
   • pgrep padrão específico → kill órfãos
   • Popen start_new_session (filho órfão sobrevive ao OOM)
   • confirma no próximo ciclo (não segura memória no boot)

O filho órfão (start_new_session) sobrevive ao OOM killer durante o boot do modelo (~700MB RSS). O watchdog morre, o daemon fica. No pior caso, o fallback subprocess mantém a consulta viva até a restauração.

04 Autocorreção Agêntica de Dados

O agente detectou gaps estruturais, gerou Cypher, executou e verificou — com aprovação humana do plano.

Gap detectadoAção executadaResultado
2 objeções com campo momento nulo (quebrava filtros por fase)SET o.momento = 'Desconfiança'Fases íntegras
Objeções de Validação sem fatos ("dinheiro de volta", "budget pro board")MERGE Fato + SUSTENTA + CITA (Reclame AQUI, Forbes, Mundo do Marketing)9 nós
Objeções de Desconfiança sem fatos (franquia, agência, eletrólito, skincare)MERGE Fato + SUSTENTA + CITA (Reddit, soar.sh, Reclame AQUI)16 nós
Reindexação dos nós novosneo4j_rag.py indexar98/98 com embedding
# Exemplo do padrão de carga (verificado em produção)
MERGE (f:Fato {titulo: $titulo})
SET f.descricao = $descricao, f.data = $data, f.url = $url
WITH f
MATCH (o:Objeção {titulo: $obj})
MERGE (f)-[:SUSTENTA]->(o)
WITH f
UNWIND $fontes AS ft
MERGE (fonte:Fonte {nome: ft.nome})
SET fonte.tipo = ft.tipo, fonte.url = ft.url
MERGE (f)-[:CITA]->(fonte)

05 Anti-Alucinação via Cadeia de Custódia

Regra rígida: afirmação factual só existe se ancorada em nó do grafo com fonte. Caso real, em produção:

PerguntaEntidade inexistente ("EcoSysTechhx")
Fontes consultadasMemória™ · Grafo Neo4j (query + RAG vetorial) · Arquivos em disco · Sessões passadas (FTS5)
Resultado0 resultados em todas as fontes
Resposta do agente"Não tenho nenhum dado sobre a EcoSysTechhx no sistema Tulejur" + tabela de fontes consultadas
ComportamentoDeclara o gap em vez de fabricar uma aresta

06 Pitfalls descobertos no Aura 5.27 (feedback direto)

Descobertas reais de operação, documentadas para o time de engenharia.

#PitfallSintomaWorkaround
1Acesso dinâmico de propriedade no planner do Aura 5.27any(k IN keys(n)) ... n[k] IS STRING → CypherTypeError hasALabelOrType()Campos explícitos por label (toLower(coalesce(n.campo,'')) CONTAINS $t)
2db.index.vector.queryNodes deprecadoWarning de deprecação no Aura 5.27Migrar para SEARCH (neo4j-graphrag ainda emite o warning)
3Namespace-package shadowingimport neo4j resolvia para diretório doc local sem __init__.pyRemover diretório neo4j/ sem pacote; path injection do venv
4OOM em box de 2GB durante reindexaçãoSIGKILL no watchdog durante boot do modelo ONNX (~700MB RSS)Popen órfão (start_new_session) + fallback subprocess + watchdog
5Vetor multi-label conflita com índice mono-labelCREATE VECTOR INDEX FOR (n:A|B) vs índice existenteLabel comum :Embeddable + IF NOT EXISTS

07 Stack técnica

CamadaTecnologia
Banco de grafosNeo4j Aura (5.27), driver neo4j 6.3.0
RAGneo4j-graphrag 1.19.0 (HybridRetriever: vector + fulltext)
Embeddingsfastembed 0.8.0 · paraphrase-multilingual-MiniLM-L12-v2 (ONNX, 384d, cosine)
OrquestraçãoHermes Agent (Nous Research) + Python 3.13
Visualizaçãopyvis/vis-network 9.1.2 (este grafo ao vivo)