Motor de Raciocínio Evolutivo: Combinando LLM, Grafos e Inferência Simbólica

Imagina uma IA que não só responde perguntas, mas que evolui o que sabe a cada conversa. Que mantém um grafo de conhecimento vivo onde cada informação nova pode reforçar ou contradizer o que já existe. Que tem curiosidade própria e vai buscar respostas para as lacunas que detecta no próprio conhecimento. Parece ficção científica, mas dá para construir isso com Python em algumas centenas de linhas combinando técnicas que normalmente vivem separadas.

Eu venho estudando e implementando esse tipo de sistema há um tempo, e o que mais me fascina é que não precisa de infraestrutura massiva. Um LLM via API, um grafo de conhecimento local, busca vetorial e algumas regras de inferência simbólica. O resultado é uma IA que aprende de verdade, que mantém estado entre conversas, e que sabe quantificar exatamente quão incerta está sobre cada crença que possui.

Guia de tópicos:

  • O Conceito: IA que Evolui em vez de Apenas Responder
  • Os 5 Componentes de um Motor Evolutivo
  • O Ciclo de Vida de uma Mensagem
  • Mundos de Conhecimento: Separando Contextos
  • O LLM como Ferramenta, Não como Cérebro
  • Exemplo Prático em Python
  • Considerações Finais

O Conceito: IA que Evolui em vez de Apenas Responder

A maioria dos chatbots funciona assim: recebe pergunta, gera resposta, esquece tudo. Mesmo os que têm "memória" geralmente só guardam o histórico de mensagens e jogam no contexto do próximo prompt. Não existe aprendizado real, não existe evolução do conhecimento. Cada conversa começa praticamente do zero.

Um motor evolutivo funciona diferente. Cada mensagem que o usuário envia passa por um pipeline completo: o texto é analisado, fatos são extraídos como triplas semânticas (sujeito, verbo, objeto), esses fatos são armazenados em um grafo de conhecimento com distribuições de probabilidade, e o grafo é usado para enriquecer respostas futuras. Se o usuário diz "Python foi criado por Guido van Rossum", o motor extrai a tripla (Python, criado_por, Guido van Rossum) com confiança 0.85 e armazena permanentemente. Na próxima vez que alguém perguntar sobre Python, essa informação está disponível como contexto sem precisar estar no prompt.

Mas vai além disso. O motor detecta contradições automaticamente (se alguém disser que Python foi criado por outra pessoa, ele identifica o conflito e pode investigar). Faz inferências lógicas (se A pertence a B e B está em C, então A está em C). E cria pontes criativas entre domínios diferentes, encontrando conexões que ninguém pediu explicitamente.

Os 5 Componentes de um Motor Evolutivo

Para construir algo assim, você precisa de 5 peças trabalhando juntas:

O orquestrador é o componente central que recebe mensagens, decide o que fazer com elas, coordena os outros módulos e mantém o estado geral do sistema. É ele que decide se precisa buscar na web, se deve criar um mundo novo ou atualizar um existente, e quando disparar o ciclo de curiosidade.

O armazenamento híbrido combina diferentes tecnologias para diferentes necessidades: um grafo para relacionamentos entre entidades, um key-value store para distribuições de probabilidade (que precisam de acesso ultrarrápido), e um índice vetorial para encontrar contextos semanticamente similares em milissegundos.

Os mundos de conhecimento são a forma de organizar informação por domínio. Em vez de um grafo gigante onde "Python linguagem" se mistura com "python cobra", cada tema vive em seu próprio mundo com seu próprio grafo, centroide vetorial e maturidade. Mundos nascem, crescem e podem se fundir quando ficam similares demais.

O módulo de raciocínio inclui extração de triplas (LLM + regex com cross-validation), inferência simbólica (transitividade, herança, modus ponens), detecção de contradições, e geração de pontes criativas entre mundos.

O cliente LLM é usado como ferramenta para tarefas específicas: classificar intenção, extrair fatos estruturados de texto livre, gerar nomes de tópicos, e formular respostas. Ele não é o cérebro do sistema. A lógica de raciocínio, armazenamento e evolução é toda código determinístico.

O Ciclo de Vida de uma Mensagem

Quando o usuário envia "O Brasil é o maior país da América do Sul", o pipeline completo é:

Primeiro, o texto é convertido em um vetor (embedding) usando sentence-transformers. Esse vetor é comparado com os centroides de todos os mundos existentes via busca vetorial para encontrar o mundo mais relevante. Se existe um mundo sobre "Geografia" com similaridade acima do threshold, a mensagem é associada a ele. Se não, um novo mundo é criado.

Segundo, o LLM classifica a intenção: é conversa casual ou precisa de pesquisa web? Se precisa de web, o motor busca informações adicionais e incorpora os resultados.

Terceiro, triplas semânticas são extraídas do texto usando duas fontes: o LLM (que entende semântica complexa) e regex (que pega padrões estruturais). As duas fontes são cruzadas para reduzir alucinações.

Quarto, as triplas são inseridas no grafo do mundo correspondente. Cada tripla não é um fato binário, é uma distribuição de probabilidade que é atualizada bayesianamente.

Quinto, inferência simbólica roda sobre o grafo atualizado, e o módulo de criatividade verifica se existem pontes possíveis entre mundos diferentes.

Tudo isso acontece em background, após a resposta já ter sido enviada ao usuário. O processamento de conhecimento não bloqueia a conversa.

Mundos de Conhecimento: Separando Contextos

O conceito de mundos separados é central. Em vez de ter um único grafo gigante onde tudo se mistura, o conhecimento é organizado em mundos temáticos. Cada mundo tem seu próprio grafo de crenças, seu próprio centroide vetorial (que representa "do que esse mundo fala"), e sua própria maturidade.

Quando um mundo recebe informação nova, seu centroide é atualizado com uma média ponderada: mundos maduros mudam pouco (são "convictos"), mundos jovens mudam mais (ainda estão se definindo). Mundos muito similares são automaticamente fundidos para evitar fragmentação.

O LLM como Ferramenta, Não como Cérebro

Uma decisão arquitetural importante: o LLM não toma decisões. Ele executa tarefas bem definidas com input/output estruturado. A decisão de "quando chamar o LLM" é do código. A validação do output é do código. O armazenamento é do código.

Isso tem vantagens enormes. O comportamento é previsível e debugável. Se uma inferência está errada, você rastreia qual regra simbólica gerou aquele resultado. O sistema funciona mesmo se o LLM alucinar, porque cross-validation e thresholds filtram resultados ruins. E o custo é controlado: poucas chamadas por mensagem.

Exemplo Prático em Python

import numpy as np
from sentence_transformers import SentenceTransformer

class EvolutionaryEngine:
    def __init__(self):
        self.embedder = SentenceTransformer("all-MiniLM-L6-v2")
        self.worlds = {}  # id -> WorldNode

    def process_message(self, text: str) -> str:
        # 1. Embed the message
        vector = self.embedder.encode(text)

        # 2. Find best matching world (cosine similarity)
        best_world = self._find_best_world(vector)

        # 3. Extract facts as semantic triples
        triples = self._extract_triples(text)

        # 4. Update or create world
        if best_world:
            best_world.absorb(vector, triples)
        else:
            world = WorldNode(vector, topic=self._generate_topic(text))
            self.worlds[world.id] = world

        # 5. Run symbolic reasoning on updated world
        self._run_inferences(best_world or world)

        # 6. Generate response using world context
        return self._respond(text, best_world)

    def _find_best_world(self, vector):
        best, highest_sim = None, 0.6  # minimum threshold
        for world in self.worlds.values():
            sim = np.dot(vector, world.centroid) / (
                np.linalg.norm(vector) * np.linalg.norm(world.centroid) + 1e-9)
            # Dynamic threshold: mature worlds require higher similarity
            threshold = 0.60 + world.maturity * 0.10
            if sim > threshold and sim > highest_sim:
                best, highest_sim = world, sim
        return best


class WorldNode:
    def __init__(self, vector, topic=""):
        self.id = str(hash(topic))[:8]
        self.centroid = np.array(vector, dtype=float)
        self.topic = topic
        self.access_count = 1
        self.triples = []

    @property
    def maturity(self):
        if not self.triples:
            return 0.0
        return min(len(self.triples) / 20.0, 1.0)

    def absorb(self, new_vector, new_triples):
        # Alpha decreases as world matures
        alpha = (1.0 / (self.access_count + 1)) * (1.0 - self.maturity * 0.7)
        self.centroid = (1 - alpha) * self.centroid + alpha * np.array(new_vector)
        self.access_count += 1
        self.triples.extend(new_triples)

Considerações Finais

Construir um motor de raciocínio evolutivo é um exercício de combinar técnicas que normalmente vivem separadas: LLMs para compreensão de linguagem, grafos de conhecimento para persistência estruturada, busca vetorial para similaridade semântica, e inferência simbólica para raciocínio lógico. Nenhuma dessas técnicas é nova individualmente, mas a combinação produz algo que é mais que a soma das partes.

O resultado não é perfeito. A extração de triplas ainda erra, as inferências às vezes geram fatos irrelevantes, e a curiosidade pode buscar informações que não agregam. Mas o sistema evolui. Cada interação o torna um pouco mais informado, um pouco mais conectado, um pouco mais capaz de responder com contexto real.


Links indicativos: