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
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:
| Pipeline | Precisión |
|---|---|
Solo LLM (claude-sonnet-4-6) | 22/30 (73.3%) |
| LLM + prolog-reasoner | 27/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_prologpara ejecución arbitraria de SWI-Prolog, máslist_rule_bases/get_rule_base/save_rule_base/delete_rule_basepara 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_prologpara 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 aprolog_code(en orden). Úsalo para reutilizar reglas de dominio estables entre llamadas sin reenviarlasmax_results— limita el número de soluciones devueltas (por defecto 100)trace— cuandoTrue, adjunta un árbol de prueba estructurado por solución ametadata.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 comoRULEBASE_003. Devuelve{"success": true, "name": ..., "created": bool}dondecreatedestrueen la primera escritura,falseal sobrescribir. Los archivos de más demax_rule_sizese rechazan conRULEBASE_005.list_rule_bases()— devuelve todas las bases de reglas guardadas conname,descriptionytags. 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_):
| Variable | Por defecto | Requerido para |
|---|---|---|
LLM_PROVIDER | openai | biblioteca (openai o anthropic) |
LLM_API_KEY | "" | solo biblioteca — déjalo sin definir para MCP |
LLM_MODEL | gpt-5.4-mini | biblioteca |
LLM_TEMPERATURE | 0.0 | biblioteca |
LLM_TIMEOUT_SECONDS | 30.0 | biblioteca |
SWIPL_PATH | swipl | ambos |
EXECUTION_TIMEOUT_SECONDS | 10.0 | ambos |
RULES_DIR | ~/.prolog-reasoner/rules | ambos (donde viven las bases de reglas guardadas por el usuario) |
BUNDLED_RULES_DIR | sin definir | ambos (opcional — sincronizado en RULES_DIR en el primer arranque para distribuir reglas predeterminadas con un fork) |
MAX_RULE_SIZE | 1048576 (1 MiB) | ambos (límite de guardado por archivo; save_rule_base rechaza contenido mayor con RULEBASE_005) |
MAX_RULE_PROMPT_BYTES | 65536 (64 KiB) | solo biblioteca (presupuesto total para la sección de prompt "Available rule bases"; truncado con un marcador cuando se excede) |
LOG_LEVEL | INFO | ambos |
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:
| Pipeline | Precisión | Latencia media |
|---|---|---|
| Solo LLM | 22/30 (73.3%) | 1.7s |
| LLM + Prolog | 27/30 (90.0%) | 3.8s |
Desglose por categoría:
| Categoría | Solo LLM | LLM + Prolog |
|---|---|---|
| deducción | 6/6 | 6/6 |
| transitivo | 6/6 | 5/6 |
| restricciones | 3/7 | 6/7 |
| contradicción | 4/4 | 3/4 |
| múltiples pasos | 3/7 | 7/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-reasoner | MCP de Prolog con estado | |
|---|---|---|
| Rol de Prolog | Herramienta de razonamiento por llamada | Base de conocimiento a nivel de proyecto |
| Estado | Ejecución sin estado (cada llamada es independiente); bases de reglas nombradas opcionales para reglas estáticas reutilizables, sin memoria de sesión entre llamadas | Sesiones persistentes / KBs en capas |
| Reproducibilidad | Misma entrada (incl. mismas bases de reglas) → misma salida, siempre | Depende del estado acumulado |
| Esfuerzo de integración | Úsalo donde la lógica importa, omítelo donde no | Compromiso arquitectónico |
| Comprobable A/B frente a solo-LLM | Sí (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:
- adamrybinski/prolog-mcp — Trealla WASM con sesiones de guardar/cargar
- umuro/prolog-mcp — KB en capas con persistencia respaldada por archivos
- vpursuit/model-context-lab — SWI-Prolog con sandboxing de seguridad
- dr3d/prolog-reasoning — memoria neuro-simbólica con seguridad en la ruta de escritura
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