12 KiB
🔗 Análise: Bancos Vetoriais + Grafos de Relacionamento
📋 Resumo Executivo
Este documento analisa a implementação atual do sistema de memória e propõe a integração de grafos de relacionamento com o banco de dados vetorial (Qdrant) existente para melhorar significativamente a capacidade dos agentes de IA em encontrar, classificar e contextualizar informações.
🔍 Situação Atual
Arquitetura Existente
O sistema Antigravity Brain atualmente utiliza:
-
Banco Vetorial (Qdrant):
- Armazena embeddings de memórias e conhecimento
- Busca por similaridade semântica via
SearchMemoryTool - Integrado via Mem0 (
src/memory/wrapper.py)
-
Metadados Básicos:
source: origem da memória (agent_execution, telegram_chat)type: tipo de informação (insight, learned_insight)chat_id: ID do chat (quando aplicável)user_id: escopo do projeto
-
Limitações Identificadas:
- ❌ Não captura relacionamentos entre entidades
- ❌ Não permite navegação entre memórias relacionadas
- ❌ Busca apenas por similaridade semântica (pode perder contexto estrutural)
- ❌ Dificuldade em rastrear cadeias causais (problema → solução → agente)
🎯 Benefícios da Integração Vetor + Grafo
1. Busca Híbrida Mais Poderosa
Cenário Atual:
Usuário: "Mostre problemas relacionados ao Zabbix"
→ Busca vetorial encontra memórias sobre "Zabbix"
→ Mas não mostra relacionamentos: qual problema levou a qual solução, qual agente resolveu, etc.
Com Grafo:
Usuário: "Mostre problemas relacionados ao Zabbix"
→ Busca vetorial inicial encontra memórias sobre "Zabbix"
→ Navegação de grafo expande: Problema → Solução → Agente → Outros problemas similares
→ Retorna contexto completo com relacionamentos
2. Redução de Alucinações
Grafos fornecem estrutura explícita que reduz a necessidade de o LLM "inventar" conexões:
- ✅ Relacionamentos documentados explicitamente
- ✅ Contexto estruturado disponível para o LLM
- ✅ Validação de conexões antes de inferências
3. Rastreabilidade e Auditoria
Com grafos, é possível:
- Rastrear cadeias de decisões: "Como chegamos nesta solução?"
- Identificar padrões: "Problemas similares foram resolvidos da mesma forma?"
- Mapear responsabilidades: "Qual agente resolveu problemas de tipo X?"
4. Classificação Melhorada
Agentes podem usar grafos para:
- Classificar com base em relacionamentos existentes
- Identificar novos padrões ao conectar memórias desconectadas
- Criar taxonomias dinâmicas baseadas em estrutura
🏗️ Arquitetura Proposta
Opção 1: Neo4j + Qdrant (Recomendado)
┌─────────────────┐
│ Qdrant (Vector)│ ← Busca Semântica
│ Embeddings │
└────────┬─────────┘
│
│ Sync IDs
▼
┌─────────────────┐
│ Neo4j (Graph) │ ← Relacionamentos
│ Nodes & Edges │
└─────────────────┘
Vantagens:
- ✅ Neo4j tem suporte nativo para vector search (Neo4j 5.x+)
- ✅ Cypher é poderoso para queries de relacionamento
- ✅ Ecossistema maduro e bem documentado
- ✅ Integração Python via
neo4jdriver
Estrutura do Grafo:
// Exemplo de estrutura
(:Memory)-[:RELATES_TO]->(:Memory)
(:Memory)-[:SOLVES]->(:Problem)
(:Memory)-[:CREATED_BY]->(:Agent)
(:Problem)-[:LEADS_TO]->(:Solution)
(:Agent)-[:RESOLVED]->(:Problem)
Opção 2: Qdrant com Metadata Relacional (Mais Simples)
Manter Qdrant, mas enriquecer metadados com referências:
metadata = {
"source": "agent_execution",
"type": "insight",
"related_memory_ids": ["mem_123", "mem_456"], # IDs relacionados
"entity_type": "solution", # solution, problem, rule, etc.
"entity_relationships": {
"solves": "prob_zabbix_001",
"created_by": "arthur_mendes"
}
}
Vantagens:
- ✅ Sem nova infraestrutura
- ✅ Implementação mais rápida
- ✅ Mantém tudo no Qdrant
Desvantagens:
- ❌ Queries de relacionamento são limitadas
- ❌ Não aproveita otimizações de grafos para navegação
Opção 3: PostgreSQL com pgvector + Relações (Híbrido)
PostgreSQL com extensão pgvector + tabelas relacionais:
CREATE TABLE memories (
id UUID PRIMARY KEY,
content TEXT,
embedding vector(1536),
metadata JSONB
);
CREATE TABLE memory_relationships (
from_memory_id UUID REFERENCES memories(id),
to_memory_id UUID REFERENCES memories(id),
relationship_type VARCHAR(50),
created_at TIMESTAMP
);
Vantagens:
- ✅ Tudo em um banco
- ✅ PostgreSQL já é preferido no projeto (ver
database_standards.md) - ✅ Suporte a JSONB para flexibilidade
💡 Casos de Uso Específicos
Caso 1: Rastreamento de Problemas
Problema Atual:
Agente busca: "problemas de Zabbix"
→ Encontra memórias isoladas
→ Não consegue conectar: problema A → solução B → implementação C
Com Grafo:
MATCH (p:Problem {category: "Zabbix"})-[:SOLVED_BY]->(s:Solution)
-[:IMPLEMENTED_BY]->(a:Agent)
RETURN p, s, a, collect(s.related_problems) as related
Caso 2: Classificação Inteligente
Problema Atual:
Router precisa classificar: "Precisamos melhorar a performance do Zabbix"
→ Classifica como "Infra Engineering" (correto)
→ Mas não sabe que existem 3 problemas similares já resolvidos
Com Grafo:
1. Busca vetorial encontra memórias sobre "Zabbix performance"
2. Grafo identifica: problemas similares → crew que resolveu → padrões
3. Router classifica com contexto: "Infra Engineering, histórico: 3 casos similares resolvidos"
Caso 3: Aprendizado Incremental
Problema Atual:
Novo insight salvo: "Solução X funciona para problemas de tipo Y"
→ Fica isolado, não conecta com problemas anteriores
Com Grafo:
1. Ao salvar novo insight, LLM extrai entidades
2. Sistema cria nós: Problem, Solution, Agent
3. Conecta com nós existentes por similaridade vetorial
4. Agora: buscar "problemas tipo Y" retorna solução X automaticamente
🔧 Implementação Recomendada (Neo4j)
Fase 1: Setup Inicial
- Adicionar Neo4j ao docker-compose.yml:
neo4j:
image: neo4j:5.15
container_name: antigravity_neo4j
ports:
- "7474:7474" # Browser
- "7687:7687" # Bolt
environment:
- NEO4J_AUTH=neo4j/password
- NEO4J_PLUGINS=["apoc", "graph-data-science"]
volumes:
- neo4j_data:/data
networks:
- antigravity_net
- Atualizar requirements.txt:
neo4j
neo4j-vector-search # Se usar Neo4j vector search nativo
Fase 2: Criar Graph Wrapper
Criar src/memory/graph_wrapper.py:
from neo4j import GraphDatabase
import logging
class GraphWrapper:
"""Gerencia relacionamentos entre memórias no Neo4j"""
def __init__(self):
self.driver = GraphDatabase.driver(
os.getenv("NEO4J_URI", "bolt://localhost:7687"),
auth=(os.getenv("NEO4J_USER", "neo4j"),
os.getenv("NEO4J_PASSWORD", "password"))
)
def create_memory_node(self, memory_id: str, content: str, metadata: dict):
"""Cria nó de memória"""
with self.driver.session() as session:
session.run("""
CREATE (m:Memory {
id: $memory_id,
content: $content,
type: $type,
source: $source,
created_at: datetime()
})
""", memory_id=memory_id, content=content,
type=metadata.get("type"), source=metadata.get("source"))
def link_memories(self, from_id: str, to_id: str, relationship: str):
"""Cria relacionamento entre memórias"""
with self.driver.session() as session:
session.run("""
MATCH (a:Memory {id: $from_id})
MATCH (b:Memory {id: $to_id})
CREATE (a)-[r:RELATES_TO {type: $rel_type}]->(b)
""", from_id=from_id, to_id=to_id, rel_type=relationship)
def find_related_memories(self, memory_id: str, depth: int = 2):
"""Encontra memórias relacionadas via grafo"""
with self.driver.session() as session:
result = session.run("""
MATCH path = (m:Memory {id: $id})-[*1..2]-(related:Memory)
RETURN related, length(path) as distance
ORDER BY distance
LIMIT 10
""", id=memory_id)
return [record["related"] for record in result]
Fase 3: Integrar com SearchMemoryTool
Modificar SearchMemoryTool para busca híbrida:
def _run(self, query: str) -> str:
# 1. Busca vetorial inicial (como antes)
vector_results = client.search(query, limit=5)
# 2. Para cada resultado, buscar relacionamentos no grafo
enriched_results = []
graph = GraphWrapper()
for result in vector_results:
memory_id = result['id']
related = graph.find_related_memories(memory_id, depth=2)
enriched_results.append({
'memory': result,
'related': related,
'context': f"Found {len(related)} related memories"
})
return format_results(enriched_results)
Fase 4: Extração Automática de Relacionamentos
Ao salvar memória, usar LLM para extrair entidades e relacionamentos:
def _run(self, fact: str) -> str:
# Salvar no Mem0 (como antes)
memory_id = client.add(fact, metadata={...})
# Extrair relacionamentos com LLM
extraction_prompt = f"""
Analise este fato e extraia:
1. Tipo de entidade (Problem, Solution, Rule, Agent, etc.)
2. Relacionamentos com outras memórias conhecidas
3. Entidades mencionadas
Fato: {fact}
Retorne JSON:
{{
"entity_type": "...",
"relationships": [
{{"type": "solves", "target": "problema_zabbix_001"}},
{{"type": "created_by", "target": "arthur_mendes"}}
]
}}
"""
# Usar LLM para extrair
relationships = llm_call(extraction_prompt)
# Criar nós e relacionamentos no Neo4j
graph.create_memory_node(memory_id, fact, metadata)
for rel in relationships:
graph.link_memories(memory_id, rel['target'], rel['type'])
return "Successfully saved to memory with relationships."
📊 Comparação de Opções
| Critério | Neo4j | Qdrant Metadata | PostgreSQL |
|---|---|---|---|
| Busca Vetorial | Nativa (5.x+) | Nativa | pgvector |
| Navegação de Grafo | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐ |
| Complexidade Setup | Média | Baixa | Baixa |
| Performance Queries | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| Custo Infra | +1 container | 0 | 0 |
| Manutenibilidade | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
🎯 Recomendação Final
Implementação Gradual:
-
Curto Prazo (1-2 semanas):
- Implementar Opção 2 (Qdrant Metadata) para validar conceito
- Adicionar campos
related_memory_idseentity_typeaos metadados - Modificar
SearchMemoryToolpara considerar relacionamentos
-
Médio Prazo (1 mês):
- Avaliar necessidade de upgrade para Neo4j (se relacionamentos ficarem complexos)
- Implementar extração automática de relacionamentos com LLM
-
Longo Prazo (2-3 meses):
- Migrar para Neo4j se métricas mostrarem benefício
- Criar dashboard de visualização de relacionamentos
- Implementar queries avançadas (pattern matching, path finding)
🔗 Referências
- Neo4j Vector Search Documentation
- IBM: Vector Databases
- Microsoft: Azure Cosmos DB AI Graph
- InforChannel: Neo4j Pesquisa Vetorial
Documento criado em: 2025-01-XX
Autor: Análise Técnica - Sistema Antigravity Brain
Status: Proposta para Revisão