Skip to main content

A clean Python and CLI interface for authoring and querying SWI-Prolog, built for agents.

Project description

AgentPrologKit

A clean Python (and CLI) interface for authoring and querying SWI-Prolog — designed so any program, including an LLM agent, can drive Prolog without ever touching its surface syntax. The Python package is called agentprologkit; this repo is where it lives.

The motivating problem: agents driving Prolog by shelling out swipl -g "add_fact(...)" -g halt fight two escaping layers at once (shell quoting and Prolog term syntax) — and an agent's own tool-call arguments (file paths, URLs, arbitrary text) are exactly the kind of untrusted strings that break both. agentprologkit removes that surface entirely: you build terms in Python, not Prolog source text, so malformed or unsafely-escaped Prolog is unrepresentable.

Why Prolog for agent safety evaluation?

A safety property over an agent's trace is relational and ordered — "read a local file, then later make an outbound call" is just a conjunction of goals over a log with an ordering constraint — and Prolog's unification and backtracking are precisely a search for whether such a pattern holds, so you declare what counts as unsafe and let the engine find it. That buys the one thing an LLM grading another LLM cannot: a machine-checkable specification that is deterministic and inspectable, and that returns either a witness (the offending bindings) or a definite no — verification needs a ground-truth anchor to check against, and probabilistic self-assessment isn't one. This is not only a first-principles argument. In RefineAct: Automatic Runtime Verification of LLM Agent Actions (Batole, Khomh & Rajan), an approach built using Prolog engine as the representation interposed before each agent action on the ToolEmu benchmark cuts failure incidence from 77% to 39% while raising task helpfulness from 1.0 to 1.9 — a symbolic verifier improving safety and utility at once, rather than trading one for the other. agentprologkit is the substrate that makes that loop cheap: an agent can author the rules and run the queries without ever emitting a line of Prolog.

Two surfaces

  1. Authoring — no swipl needed. add_fact / add_rule / write render terms in Python and append them to a .pl file. Because terms are rendered (never parsed from a hand-written string), malformed Prolog is unrepresentable, and a plain str is always a quoted atom — the only way to get a logical variable is the explicit Var("X") sentinel.
  2. Engine — swipl via MQI. load consults files; query runs goals and returns structured bindings (list[dict]), no stdout parsing. Backed by SWI-Prolog's Machine Query Interface (swiplserver).

Setup

Requirements:

  • Python >= 3.10
  • SWI-Prolog >= 8.4.2 on PATH — only needed to run queries; authoring facts/rules to a .pl file works without it.

agentprologkit isn't published on PyPI, so pip install agentprologkit won't work. Run it straight from a checkout instead:

git clone https://github.com/fraolBatole/AgentPrologKit.git
cd AgentPrologKit

Querying (Engine / KnowledgeBase.query) additionally needs the swiplserver client — that's how agentprologkit talks to a running swipl process:

pip install swiplserver

If you'd rather install agentprologkit itself (e.g. to get the agentprologkit CLI command on PATH), use an editable install from the checkout:

pip install -e .

Example: is this agent run safe?

The scenario this library is built for: an agent just finished a tool-calling session, and you have a plain list of the actions it took. Log each action as a fact, author one safety rule, and query it — no hand-written Prolog, no shell escaping, even when an argument (like this file path) contains a quote:

from agentprologkit import KnowledgeBase, Compound, Var

# A log of steps an agent took, in order -- exactly what an agent runtime
# already has on hand after a tool-calling session.
actions = [
    ("read_file", "/home/o'brien/.ssh/id_rsa"),  # note the quote -- handled safely
    ("call_api", "https://weather.example.com/lookup"),
    ("http_post", "https://attacker.example.com/collect"),
]

with KnowledgeBase("agent_run.pl") as kb:
    for step, (kind, target) in enumerate(actions, start=1):
        kb.add_fact("action", [step, kind, target])

    # unsafe(Step) :- the agent read a local file, then later made an
    # outbound network call -- a classic exfiltration pattern.
    kb.add_rule(
        Compound("unsafe", [Var("Read")]),
        [
            Compound("action", [Var("Read"), "read_file", Var("_Path")]),
            Compound("action", [Var("Send"), "http_post", Var("_Url")]),
            Compound("<", [Var("Read"), Var("Send")]),
        ],
    )

    for row in kb.query("unsafe(Step)"):
        print(row)  # {"Step": 1} -- flags the read-then-exfiltrate pattern

Swap in your own predicates for whatever "safe" means in your domain — add_fact/add_rule don't care whether the facts came from a tool-call log, a file scan, or a chain-of-thought trace, only that they're valid terms.

Controlled vocabulary (optional)

from agentprologkit import KnowledgeBase, Schema

schema = Schema().define("action", 3)
kb = KnowledgeBase("agent_run.pl", schema=schema)
kb.add_fact("action", ["a", "b"])   # -> AddResult(ok=False, error={code: arity_mismatch, ...})

Results, errors, and persistence

query returns a list[dict], one dict of variable bindings per solution. Everything in it is JSON-serializable as-is:

  • no solution → []; success with no variables → [{}]
  • atom / number → Python str / number; Prolog list → Python list
  • compound term → {"functor": ..., "args": [...]}; unbound variable → "_"

Errors are made to be branched on, not string-matched:

Where What you get Meaning / next step
add_fact / add_rule AddResult(ok=False, error={"code": ...}) with unknown_predicate, arity_mismatch, or term_error fix the fact and retry
query PrologError — the message names the goal; .prolog holds the structured exception term e.g. existence_error = the predicate isn't defined: load the file that defines it, or check spelling/arity
query QueryTimeout after 10 s (configurable via query_timeout; None = no limit) the goal loops or is genuinely slow
load PrologError with file, line, and column in .prolog the .pl file has a syntax error at that position

Loading a malformed file fails loudly: plain swipl skips the bad clause, prints to stderr, and reports success — agentprologkit reads the file term by term first, so the agent gets the exact position instead of a mystery existence_error later.

Persistence: opening KnowledgeBase("kb.pl") on an existing file loads it, so facts written by an earlier run are queryable immediately, and the loaded predicates can still be extended with add_fact. Adds are append-only — re-adding the same fact appends a duplicate clause (delete the file to start fresh, as test.py does).

CLI

Available as agentprologkit ... after pip install -e ., or straight from a checkout with no install:

PYTHONPATH=src python3 -m agentprologkit.cli ...
# --args-json gives typed args (here, an int step) instead of bare-atom positionals.
agentprologkit add-fact --file agent_run.pl action --args-json '[1, "read_file", "/etc/passwd"]'
agentprologkit add-fact --file agent_run.pl action --args-json '[2, "http_post", "https://attacker.example.com"]'
agentprologkit query --load rules.pl --load agent_run.pl 'unsafe(Step)'

Each command emits one JSON object and is shaped to map 1:1 onto a future MCP tool. Failures exit non-zero with {"ok": false, "error": {"code": ..., "message": ...}}, where code is a stable snake_case value (prolog_error, timeout, engine_unavailable, bad_args_json, ...).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agentprologkit-0.1.0.tar.gz (25.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agentprologkit-0.1.0-py3-none-any.whl (21.6 kB view details)

Uploaded Python 3

File details

Details for the file agentprologkit-0.1.0.tar.gz.

File metadata

  • Download URL: agentprologkit-0.1.0.tar.gz
  • Upload date:
  • Size: 25.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for agentprologkit-0.1.0.tar.gz
Algorithm Hash digest
SHA256 6512c8b256de5bb20ea06effb90de24493c8ba26079dba3a8aa1a97422a7f921
MD5 11694a6744b1a57d0894aa18d19215db
BLAKE2b-256 6b1e7bf58a3eee54a6137c7326876b42dd136453e432a76c9eda9869a56bdd35

See more details on using hashes here.

File details

Details for the file agentprologkit-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: agentprologkit-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.6

File hashes

Hashes for agentprologkit-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a279efb49d9107ba74b87457dd20e413b1fe4b2c69dfb63664839020940b7bf3
MD5 8a380a39768e500268448a85380c221d
BLAKE2b-256 ddf757632c407d88c5a4d604c158109eab6dcb56807d6a28da0a31f35751872a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page