Skip to main content

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.
pip install agentprologkit

This installs everything needed, including swiplserver (the client behind Engine / KnowledgeBase.query) and the agentprologkit CLI.

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 agentprologkit, 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, ...).

Metadata

Release files for agentprologkit 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agentprologkit 0.1.1
File Size Uploaded Provenance
agentprologkit-0.1.1.tar.gz 24.8 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for agentprologkit 0.1.1
File Interpreter ABI Platform
agentprologkit-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 46.2 kB

Release files / agentprologkit-0.1.1.tar.gz

Download URL agentprologkit-0.1.1.tar.gz
Size 24.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3a732f320bf1585cbd7aabc5818ff77fc647b9ac86cc624082750ea49a895c3c
BLAKE2b-256 checksum
How to use checksums
3fe11f3f7b29fab4dffee80158cf3a9c239cc234a2fedbacb34411256a20cbd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.6

Release files / agentprologkit-0.1.1-py3-none-any.whl

Download URL agentprologkit-0.1.1-py3-none-any.whl
Size 21.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d0fd12f5eb18ae35e424202e399479df91a9a3360dcccdd878dfc93926b12fdf
BLAKE2b-256 checksum
How to use checksums
43c1a08158b15246173a37c9b7ace537b31419d58a6d478adade0c15f134ee4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.6

Security for agentprologkit 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page