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
- Authoring — no
swiplneeded.add_fact/add_rule/writerender terms in Python and append them to a.plfile. Because terms are rendered (never parsed from a hand-written string), malformed Prolog is unrepresentable, and a plainstris always a quoted atom — the only way to get a logical variable is the explicitVar("X")sentinel. - Engine —
swiplvia MQI.loadconsults files;queryruns 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.plfile 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, ...).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agentprologkit-0.1.1.tar.gz.
File metadata
- Download URL: agentprologkit-0.1.1.tar.gz
- Upload date:
- Size: 24.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a732f320bf1585cbd7aabc5818ff77fc647b9ac86cc624082750ea49a895c3c
|
|
| MD5 |
836d0e05b78dc7d2206f5edc398f39d8
|
|
| BLAKE2b-256 |
3fe11f3f7b29fab4dffee80158cf3a9c239cc234a2fedbacb34411256a20cbd6
|
File details
Details for the file agentprologkit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: agentprologkit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d0fd12f5eb18ae35e424202e399479df91a9a3360dcccdd878dfc93926b12fdf
|
|
| MD5 |
730da40bc9dfa516c9895c7bedf3453a
|
|
| BLAKE2b-256 |
43c1a08158b15246173a37c9b7ace537b31419d58a6d478adade0c15f134ee4a
|