prolog-reasoner

Ejecución de SWI-Prolog para LLMs con CLP(FD) y recursión — mejora la precisión lógica/de restricciones del 73% al 90% en un benchmark de 30 problemas.

Documentación

prolog-reasoner

PyPI version Python versions CI License: MIT

SWI-Prolog como una "calculadora lógica" para LLMs — disponible como servidor MCP y biblioteca Python. Elimina la caja negra del razonamiento lógico de los LLM.

Los LLM sobresalen en lenguaje natural pero tienen dificultades con la lógica formal. Prolog sobresale en razonamiento lógico pero no puede procesar lenguaje natural. prolog-reasoner cierra esta brecha exponiendo la ejecución de SWI-Prolog a los LLM.

¿Ayuda?

En el benchmark de lógica integrado de 30 problemas:

PipelinePrecisión
Solo LLM (claude-sonnet-4-6)22/30 (73.3%)
LLM + prolog-reasoner27/30 (90.0%)

La brecha se concentra en la satisfacción de restricciones y el razonamiento de múltiples pasos — el territorio combinatorio donde los LLM son débiles y Prolog es fuerte. Desglose completo abajo.

Por qué funciona

Los LLM hacen coincidencia de patrones; Prolog realmente busca y resuelve. Cuando el LLM escribe su problema como Prolog, dos cosas ocurren a la vez:

  • Prolog maneja el trabajo combinatorio en el que los LLM son débiles — satisfacción de restricciones, inferencia de múltiples pasos, búsqueda exhaustiva.
  • El razonamiento existe como código que puedes leer, re-ejecutar y depurar. Cuando falla, ves el Prolog exacto que falló y por qué.

Dos formas de usarlo

  • Servidor MCP — Claude (o cualquier cliente MCP) lo llama como solucionador lógico durante la conversación. Las bases de reglas permiten al LLM guardar reglas de dominio estables una vez y referenciarlas por nombre en cada llamada.
  • Biblioteca Python — pipeline completo NL→Prolog con autocorrección. Requiere OpenAI o Anthropic.

Características

  • Herramientas MCP: execute_prolog para ejecución arbitraria de SWI-Prolog, más list_rule_bases / get_rule_base / save_rule_base / delete_rule_base para bases de reglas nombradas reutilizables (v14)
  • Bases de reglas: guarda reglas Prolog estables una vez (p. ej. reglas de movimientos de ajedrez, axiomas legales) y refiérelas por nombre desde execute_prolog para que el LLM solo escriba los hechos específicos de la situación en cada llamada
  • Representación intermedia transparente: el código Prolog es la pista de auditoría — inspecciona, modifica o verifica antes de la ejecución
  • Soporte CLP(FD): programación lógica con restricciones para planificación y optimización
  • Negación por fallo, recursión, todas las características estándar de SWI-Prolog
  • Modo biblioteca: traducción NL→Prolog con bucle de autocorrección (OpenAI / Anthropic)

Requisitos

  • Python ≥ 3.10
  • SWI-Prolog instalado y en PATH (≥ 9.0)
  • Clave API para OpenAI o Anthropic — solo para el modo biblioteca, no para el servidor MCP

Instalación

# 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]

Configuración del Servidor MCP

El servidor MCP expone cinco herramientas — execute_prolog ejecuta código Prolog escrito por el LLM conectado, y cuatro herramientas de bases de reglas gestionan módulos Prolog nombrados y reutilizables. No llama a ninguna API de LLM externa, por lo que no se requiere clave API.

Claude Desktop / Claude Code

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

O, si prolog-reasoner está instalado directamente:

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

Docker (SWI-Prolog incluido)

Usa Docker si no quieres instalar SWI-Prolog localmente:

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

Referencia de herramientas

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

  • prolog_code — hechos y reglas Prolog (cadena)
  • query — consulta Prolog a ejecutar, p. ej. "mortal(X)" (cadena)
  • rule_bases — lista opcional de nombres de bases de reglas guardadas para anteponer a prolog_code (en orden). Úsalo para reutilizar reglas de dominio estables entre llamadas sin reenviarlas
  • max_results — limita el número de soluciones devueltas (por defecto 100)
  • trace — cuando True, adjunta un árbol de prueba estructurado por solución a metadata.proof_trace. Subcaracterística opcional; tiene sobrecarga de rendimiento y no soporta CLP(FD), predicados de orden superior, ni assert/retract.

Devuelve un objeto JSON con success, output, query, error y metadata.

En caso de éxito, metadata incluye execution_time_ms, result_count, truncated y rule_bases_used. Cuando se solicitaron bases de reglas, rule_base_load_ms también se adjunta (tiempos de E/S de disco). En caso de fallo, metadata también incluye error_category (uno de syntax_error, undefined_predicate, unbound_variable, type_error, domain_error, evaluation_error, permission_error, timeout, trace_mechanism_error, unknown) y error_explanation — una pista en lenguaje natural para que el LLM conectado (o un humano) decida cómo corregir el código Prolog.

Herramientas de bases de reglas — gestionan módulos Prolog nombrados y reutilizables bajo PROLOG_REASONER_RULES_DIR (por defecto ~/.prolog-reasoner/rules/). Los nombres están restringidos a [a-z0-9_-], longitud 1–64.

  • save_rule_base(name, content) — escribe o sobrescribe una base de reglas. El contenido se valida sintácticamente (solo análisis) antes de la escritura; los fallos aparecen como RULEBASE_003. Devuelve {"success": true, "name": ..., "created": bool} donde created es true en la primera escritura, false al sobrescribir. Los archivos de más de max_rule_size se rechazan con RULEBASE_005.
  • list_rule_bases() — devuelve todas las bases de reglas guardadas con name, description y tags. Los metadatos se extraen de los comentarios iniciales % description: / % tags: de cada archivo.
  • get_rule_base(name) — devuelve el código fuente Prolog sin procesar de una base de reglas guardada.
  • delete_rule_base(name) — elimina una base de reglas guardada.

Para errores de nombre/tamaño/existencia, las herramientas devuelven {"success": false, "error": "...", "error_code": "RULEBASE_001"|"RULEBASE_002"|"RULEBASE_003"|"RULEBASE_005"} en lugar de lanzar excepciones. Los fallos de E/S (RULEBASE_004) se propagan como errores de infraestructura.

Convenciones de bases de reglas — comienza cada archivo de base de reglas con comentarios iniciales que sirven como metadatos list_rule_bases:

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

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

Luego refiérela desde execute_prolog:

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

Las bases de reglas también sirven como base para forks especializados por dominio: distribuye un conjunto curado (axiomas legales, reglas de juegos, escenarios fiscales, etc.) empaquetado vía BUNDLED_RULES_DIR como un paquete de razonamiento listo para usar.

Uso de la biblioteca

La biblioteca expone PrologExecutor (solo Prolog, sin LLM) y PrologReasoner (pipeline NL→Prolog, necesita una clave API de LLM).

Ejecutar Prolog directamente (sin 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 (requiere clave 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())

Configuración

Todos los ajustes mediante variables de entorno (prefijo PROLOG_REASONER_):

VariablePor defectoRequerido para
LLM_PROVIDERopenaibiblioteca (openai o anthropic)
LLM_API_KEY""solo biblioteca — déjalo sin 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 (donde viven las bases de reglas guardadas por el usuario)
BUNDLED_RULES_DIRsin definirambos (opcional — sincronizado en RULES_DIR en el primer arranque para distribuir reglas predeterminadas con un fork)
MAX_RULE_SIZE1048576 (1 MiB)ambos (límite de guardado por archivo; save_rule_base rechaza contenido mayor con RULEBASE_005)
MAX_RULE_PROMPT_BYTES65536 (64 KiB)solo biblioteca (presupuesto total para la sección de prompt "Available rule bases"; truncado con un marcador cuando se excede)
LOG_LEVELINFOambos

Benchmark

benchmarks/ contiene 30 problemas de lógica en 5 categorías (deducción, transitivo, restricciones, contradicción, múltiples pasos) para comparar el razonamiento solo-LLM frente al razonamiento LLM+Prolog. El benchmark ejercita la ruta de biblioteca (traductor + ejecutor), ya que requiere el paso NL→Prolog.

Resultados

Medido en anthropic/claude-sonnet-4-6, una sola ejecución sobre 30 problemas:

PipelinePrecisiónLatencia media
Solo LLM22/30 (73.3%)1.7s
LLM + Prolog27/30 (90.0%)3.8s

Desglose por categoría:

CategoríaSolo LLMLLM + Prolog
deducción6/66/6
transitivo6/65/6
restricciones3/76/7
contradicción4/43/4
múltiples pasos3/77/7

La brecha se concentra en restricciones (SEND+MORE, 6 reinas, mochila, coloreado K4, Einstein-lite) y múltiples pasos (teoría de juegos de Nim, caballeros-y-escuderos de 3 personas, TSP-4, acertijo de la cebra) — exactamente el territorio combinatorio/de búsqueda intensiva donde los solucionadores simbólicos superan a la finalización de patrones. En preguntas puramente deductivas o transitivas el LLM ya es fuerte y Prolog añade latencia sin ganancias de precisión.

Los 3 fallos de LLM+Prolog fueron errores de ejecución de Prolog por código malformado generado por el LLM (definiciones de predicados faltantes, variables CLP(FD) sin enlazar) más que errores de razonamiento — abordables mediante ajuste de prompts. Notablemente, cada fallo es inspeccionable: puedes ver el Prolog exacto que falló y por qué, en lugar de una respuesta incorrecta en lenguaje natural sin explicación.

Ejecutarlo tú mismo

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

Los resultados se guardan en benchmarks/results.json.

Comparación con otros MCP de Prolog

Existen varios servidores MCP de Prolog, cada uno con diferentes decisiones de diseño. prolog-reasoner es intencionalmente sin estado y de uso puntual — Prolog es una calculadora a la que llamas cuando la lógica importa, no la columna vertebral de la memoria de tu agente.

prolog-reasonerMCP de Prolog con estado
Rol de PrologHerramienta de razonamiento por llamadaBase de conocimiento a nivel de proyecto
EstadoEjecución sin estado (cada llamada es independiente); bases de reglas nombradas opcionales para reglas estáticas reutilizables, sin memoria de sesión entre llamadasSesiones persistentes / KBs en capas
ReproducibilidadMisma entrada (incl. mismas bases de reglas) → misma salida, siempreDepende del estado acumulado
Esfuerzo de integraciónÚsalo donde la lógica importa, omítelo donde noCompromiso arquitectónico
Comprobable A/B frente a solo-LLMSí (cada llamada es un experimento controlado)Estructuralmente no comparable

Esta es también la razón por la que los benchmarks de precisión se publican aquí y no en otro lugar: la ausencia de estado es lo que hace posible una comparación lado a lado.

Si necesitas memoria persistente de agente, almacenamiento de hechos protegido contra alucinaciones, o un sustrato neuro-simbólico completo, otros proyectos pueden encajar mejor:

Somos la opción de uso puntual.

Desarrollo

# 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

Licencia

MIT