minions-ai-agents/docs/ANALISE_VETOR_GRAFO.md

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:

  1. Banco Vetorial (Qdrant):

    • Armazena embeddings de memórias e conhecimento
    • Busca por similaridade semântica via SearchMemoryTool
    • Integrado via Mem0 (src/memory/wrapper.py)
  2. 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
  3. 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 neo4j driver

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

  1. 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
  1. 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:

  1. Curto Prazo (1-2 semanas):

    • Implementar Opção 2 (Qdrant Metadata) para validar conceito
    • Adicionar campos related_memory_ids e entity_type aos metadados
    • Modificar SearchMemoryTool para considerar relacionamentos
  2. 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
  3. 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


Documento criado em: 2025-01-XX
Autor: Análise Técnica - Sistema Antigravity Brain
Status: Proposta para Revisão