prolog-reasoner

Execução SWI-Prolog para LLMs com CLP(FD) e recursão — aumenta a precisão lógica/de restrições de 73% para 90% em um benchmark de 30 problemas.

Documentação

prolog-reasoner

PyPI version Python versions CI License: MIT

SWI-Prolog como uma "calculadora lógica" para LLMs — disponível como servidor MCP e biblioteca Python. Elimine a caixa-preta do raciocínio lógico de LLMs.

LLMs são excelentes em linguagem natural, mas têm dificuldade com lógica formal. Prolog é excelente em raciocínio lógico, mas não processa linguagem natural. prolog-reasoner preenche essa lacuna expondo a execução de SWI-Prolog para LLMs.

Isso ajuda?

No benchmark de lógica integrado com 30 problemas:

PipelinePrecisão
Somente LLM (claude-sonnet-4-6)22/30 (73,3%)
LLM + prolog-reasoner27/30 (90,0%)

A lacuna se concentra em satisfação de restrições e raciocínio de múltiplas etapas — o território combinatório em que LLMs são fracos e Prolog é forte. Detalhamento completo abaixo.

Por que funciona

LLMs fazem correspondência de padrões; Prolog realmente busca e resolve. Quando o LLM escreve seu problema em Prolog, duas coisas acontecem ao mesmo tempo:

  • Prolog lida com o trabalho combinatório em que LLMs são fracos — satisfação de restrições, inferência de múltiplas etapas, busca exaustiva.
  • O raciocínio existe como código que você pode ler, reexecutar e depurar. Quando algo dá errado, você vê exatamente o Prolog que falhou e por quê.

Duas formas de usar

  • Servidor MCP — Claude (ou qualquer cliente MCP) o chama como solucionador lógico durante a conversa. Bases de regras permitem que o LLM salve regras de domínio estáveis uma vez e as referencie por nome a cada chamada.
  • Biblioteca Python — pipeline completo de NL→Prolog com autocorreção. Requer OpenAI ou Anthropic.

Recursos

  • Ferramentas MCP: execute_prolog para execução arbitrária de SWI-Prolog, além de list_rule_bases / get_rule_base / save_rule_base / delete_rule_base para bases de regras nomeadas reutilizáveis (v14)
  • Bases de regras: salve regras Prolog estáveis uma vez (ex.: regras de movimento de xadrez, axiomas legais) e referencie-as por nome a partir de execute_prolog para que o LLM escreva apenas os fatos específicos da situação a cada chamada
  • Representação intermediária transparente: o código Prolog é a trilha de auditoria — inspecione, modifique ou verifique antes da execução
  • Suporte a CLP(FD): programação com restrições para agendamento e otimização
  • Negação por falha, recursão, todos os recursos padrão do SWI-Prolog
  • Modo biblioteca: tradução NL→Prolog com loop de autocorreção (OpenAI / Anthropic)

Requisitos

  • Python ≥ 3.10
  • SWI-Prolog instalado e no PATH (≥ 9.0)
  • Chave de API para OpenAI ou Anthropic — apenas para o modo biblioteca, não para o servidor MCP

Instalação

# MCP server only (no LLM dependencies)
pip install prolog-reasoner

# Library with OpenAI
pip install prolog-reasoner[openai]

# Library with Anthropic
pip install prolog-reasoner[anthropic]

# Both providers
pip install prolog-reasoner[all]

Configuração do Servidor MCP

O servidor MCP expõe cinco ferramentas — execute_prolog executa código Prolog escrito pelo LLM conectado, e quatro ferramentas de base de regras gerenciam módulos Prolog nomeados e reutilizáveis. Ele não chama nenhuma API externa de LLM, portanto nenhuma chave de API é necessária.

Claude Desktop / Claude Code

{
  "mcpServers": {
    "prolog-reasoner": {
      "command": "uvx",
      "args": ["prolog-reasoner"]
    }
  }
}

Ou, se prolog-reasoner estiver instalado diretamente:

{
  "mcpServers": {
    "prolog-reasoner": {
      "command": "prolog-reasoner"
    }
  }
}

Docker (SWI-Prolog incluído)

Use Docker se você não quiser instalar SWI-Prolog localmente:

docker build -f docker/Dockerfile -t prolog-reasoner .
{
  "mcpServers": {
    "prolog-reasoner": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "prolog-reasoner"]
    }
  }
}

Referência de ferramentas

execute_prolog(prolog_code, query, rule_bases=None, max_results=100, trace=False)

  • prolog_code — fatos e regras Prolog (string)
  • query — consulta Prolog a executar, ex.: "mortal(X)" (string)
  • rule_bases — lista opcional de nomes de bases de regras salvas para antepor a prolog_code (em ordem). Use para reutilizar regras de domínio estáveis entre chamadas sem reenviá-las
  • max_results — limita o número de soluções retornadas (padrão 100)
  • trace — quando True, anexa uma árvore de prova estruturada por solução a metadata.proof_trace. Sub-recurso opcional; tem custo de desempenho e não suporta CLP(FD), predicados de ordem superior ou assert/retract.

Retorna um objeto JSON com success, output, query, error e metadata.

Em caso de sucesso, metadata inclui execution_time_ms, result_count, truncated e rule_bases_used. Quando bases de regras foram solicitadas, rule_base_load_ms também é anexado (tempo de I/O de disco). Em caso de falha, metadata também inclui error_category (um de syntax_error, undefined_predicate, unbound_variable, type_error, domain_error, evaluation_error, permission_error, timeout, trace_mechanism_error, unknown) e error_explanation — uma dica em linguagem natural para o LLM conectado (ou humano) decidir como corrigir o código Prolog.

Ferramentas de base de regras — gerenciam módulos Prolog nomeados e reutilizáveis em PROLOG_REASONER_RULES_DIR (padrão: ~/.prolog-reasoner/rules/). Os nomes são restritos a [a-z0-9_-], com 1–64 caracteres.

  • save_rule_base(name, content) — grava ou sobrescreve uma base de regras. O conteúdo é validado sintaticamente (somente análise) antes da gravação; falhas aparecem como RULEBASE_003. Retorna {"success": true, "name": ..., "created": bool}, onde created é true na primeira gravação e false em sobrescrita. Arquivos acima de max_rule_size são rejeitados com RULEBASE_005.
  • list_rule_bases() — retorna todas as bases de regras salvas com name, description e tags. Os metadados são extraídos dos comentários iniciais % description: / % tags: de cada arquivo.
  • get_rule_base(name) — retorna o código-fonte Prolog bruto de uma base de regras salva.
  • delete_rule_base(name) — remove uma base de regras salva.

Para erros de nome/tamanho/inexistência, as ferramentas retornam {"success": false, "error": "...", "error_code": "RULEBASE_001"|"RULEBASE_002"|"RULEBASE_003"|"RULEBASE_005"} em vez de lançar exceções. Falhas de I/O (RULEBASE_004) são propagadas como erros de infraestrutura.

Convenções de base de regras — inicie cada arquivo de base de regras com comentários iniciais que funcionam como metadados de list_rule_bases:

% description: Chess piece movement rules
% tags: chess, games

piece_move(knight, (X1,Y1), (X2,Y2)) :- ...

Depois referencie a partir de execute_prolog:

{
  "rule_bases": ["chess_moves"],
  "prolog_code": "position(knight, (4,4)).",
  "query": "piece_move(knight, (4,4), Target)"
}

As bases de regras também servem como base para forks especializados por domínio: envie um conjunto curado (axiomas legais, regras de jogos, cenários fiscais, etc.) empacotado via BUNDLED_RULES_DIR como um pacote de raciocínio pronto para uso.

Uso da Biblioteca

A biblioteca expõe PrologExecutor (somente Prolog, sem LLM) e PrologReasoner (pipeline NL→Prolog, precisa de chave de API de LLM).

Executar Prolog diretamente (sem LLM)

import asyncio
from prolog_reasoner.config import Settings
from prolog_reasoner.executor import PrologExecutor

async def main():
    settings = Settings()  # no API key needed
    executor = PrologExecutor(settings)
    result = await executor.execute(
        prolog_code="human(socrates). mortal(X) :- human(X).",
        query="mortal(X)",
    )
    print(result.output)  # mortal(socrates)

asyncio.run(main())

Pipeline completo NL→Prolog (requer chave de API de LLM)

import asyncio
from prolog_reasoner import PrologReasoner, TranslationRequest, ExecutionRequest
from prolog_reasoner.config import Settings
from prolog_reasoner.executor import PrologExecutor
from prolog_reasoner.translator import PrologTranslator
from prolog_reasoner.llm_client import LLMClient

async def main():
    settings = Settings(llm_api_key="sk-...")  # from env or explicit
    llm = LLMClient(
        provider=settings.llm_provider,
        api_key=settings.llm_api_key,
        model=settings.llm_model,
        timeout_seconds=settings.llm_timeout_seconds,
    )
    reasoner = PrologReasoner(
        translator=PrologTranslator(llm, settings),
        executor=PrologExecutor(settings),
    )
    translation = await reasoner.translate(
        TranslationRequest(query="Socrates is human. All humans are mortal. Is Socrates mortal?")
    )
    print(translation.prolog_code)
    result = await reasoner.execute(
        ExecutionRequest(prolog_code=translation.prolog_code, query=translation.suggested_query)
    )
    print(result.output)

asyncio.run(main())

Configuração

Todas as configurações via variáveis de ambiente (prefixo PROLOG_REASONER_):

VariávelPadrãoNecessário para
LLM_PROVIDERopenaibiblioteca (openai ou anthropic)
LLM_API_KEY""somente biblioteca — deixe sem definir para MCP
LLM_MODELgpt-5.4-minibiblioteca
LLM_TEMPERATURE0.0biblioteca
LLM_TIMEOUT_SECONDS30.0biblioteca
SWIPL_PATHswiplambos
EXECUTION_TIMEOUT_SECONDS10.0ambos
RULES_DIR~/.prolog-reasoner/rulesambos (onde ficam as bases de regras salvas pelo usuário)
BUNDLED_RULES_DIRnão definidoambos (opcional — sincronizado em RULES_DIR na primeira inicialização para enviar regras padrão com um fork)
MAX_RULE_SIZE1048576 (1 MiB)ambos (limite de gravação por arquivo; save_rule_base rejeita conteúdo maior com RULEBASE_005)
MAX_RULE_PROMPT_BYTES65536 (64 KiB)somente biblioteca (orçamento total para a seção de prompt "Bases de regras disponíveis"; truncado com um marcador quando excedido)
LOG_LEVELINFOambos

Benchmark

benchmarks/ contém 30 problemas de lógica em 5 categorias (dedução, transitivo, restrição, contradição, múltiplas etapas) para comparar o raciocínio somente-LLM com o raciocínio LLM+Prolog. O benchmark exercita o caminho da biblioteca (tradutor + executor), pois requer a etapa NL→Prolog.

Resultados

Medido em anthropic/claude-sonnet-4-6, execução única sobre 30 problemas:

PipelinePrecisãoLatência média
Somente LLM22/30 (73,3%)1,7s
LLM + Prolog27/30 (90,0%)3,8s

Detalhamento por categoria:

CategoriaSomente LLMLLM + Prolog
dedução6/66/6
transitivo6/65/6
restrição3/76/7
contradição4/43/4
múltiplas etapas3/77/7

A lacuna se concentra em restrição (SEND+MORE, 6 rainhas, mochila, coloração K4, Einstein-lite) e múltiplas etapas (teoria de jogos Nim, cavaleiros-e-mentirosos com 3 pessoas, TSP-4, quebra-cabeça do zebra) — exatamente o território combinatório/pesado em busca onde solucionadores simbólicos superam a conclusão de padrões. Em questões puramente dedutivas ou transitivas, o LLM já é forte e Prolog adiciona latência sem ganhos de precisão.

Todas as 3 falhas de LLM+Prolog foram erros de execução Prolog provenientes de código malformado gerado por LLM (definições de predicados ausentes, variáveis CLP(FD) não vinculadas) em vez de erros de raciocínio — resolvíveis via ajuste de prompt. Notavelmente, toda falha é inspecionável: você pode ver exatamente o Prolog que falhou e por quê, em vez de uma resposta errada em linguagem natural sem explicação.

Executando você mesmo

docker run --rm -e PROLOG_REASONER_LLM_API_KEY=sk-... \
    prolog-reasoner-dev python benchmarks/run_benchmark.py

Os resultados são salvos em benchmarks/results.json.

Comparação com outros MCPs de Prolog

Vários servidores MCP de Prolog existem, cada um com escolhas de design diferentes. prolog-reasoner é intencionalmente sem estado e de uso pontual — Prolog é uma calculadora que você chama quando a lógica importa, não a espinha dorsal da memória do seu agente.

prolog-reasonerMCPs de Prolog com estado
Papel do PrologFerramenta de raciocínio por chamadaBase de conhecimento em todo o projeto
EstadoExecução sem estado (cada chamada independente); bases de regras nomeadas opcionais para regras estáticas reutilizáveis, sem memória de sessão entre chamadasSessões persistentes / KBs em camadas
ReprodutibilidadeMesma entrada (incl. mesmas bases de regras) → mesma saída, sempreDepende do estado acumulado
Esforço de integraçãoUse onde a lógica importa, pule onde não importaCompromisso arquitetural
Testável A/B vs somente-LLMSim (cada chamada é um experimento controlado)Estruturalmente não comparável

É também por isso que benchmarks de precisão são publicados aqui e não em outros lugares: a ausência de estado é o que torna possível uma comparação lado a lado.

Se você precisa de memória persistente de agente, armazenamento de fatos protegido contra alucinações ou um substrato neuro-simbólico completo, outros projetos podem se adequar melhor:

Somos a opção de uso pontual.

Desenvolvimento

# Build dev image
docker build -f docker/Dockerfile -t prolog-reasoner-dev .

# Run tests (no API key needed — LLM calls are mocked)
docker run --rm prolog-reasoner-dev

# With coverage
docker run --rm prolog-reasoner-dev pytest tests/ -v --cov=prolog_reasoner

# Or via docker compose
docker compose -f docker/docker-compose.yml run --rm test

Licença

MIT