LlamaIndex
Governing LlamaIndex agents in Python, zero-code.
Every real LlamaIndex agent step dispatches a tool through one method:
llama_index.core.tools.FunctionTool.call (and its async twin .acall). raxit's auto-patcher
wraps exactly that method, so every tool call resolves to permit / defer / deny
before it executes, with no governance code in the agent. Python only — no TS variant
(LlamaIndex.TS is not in raxit's TS autopatch set).
Run it yourself
For your own LlamaIndex agent, install the SDK and launch it under raxit run:
pip install raxit
raxit run -- python your_agent.pyRun the same file without raxit run and it runs ungoverned — governance is a deploy-time
choice, not a code change.
How it works
Tools are ordinary FunctionTool instances with no governance code. The only "wiring" is each
tool's name, the policy resource it's governed as:
# imports NOTHING from raxit
from llama_index.core.tools import FunctionTool
def db_write(payload: str) -> str: ...
tool = FunctionTool.from_defaults(fn=db_write, name="db.write")
tool.call("file a ticket")apiVersion: raxit.security/v1
agent: llamaindex-bot
default: block
rules:
- effect: allow
resource: fetch_docs
- effect: review # unconditional review == defer.policy
resource: db.write
# shell.exec is intentionally UNLISTED -> default-deny (deny.default_or_forbid).A denied or deferred call raises raxit.autopatch.RaxitDenied from inside FunctionTool.call,
carrying the reason code — the tool body never executes. Catch it like any other exception, or
let it propagate to your own agent loop. raxit explain <call_id> shows the full decision
record.
Real captured output from a run:
[PASS] autopatch: install() governed the LlamaIndex FunctionTool.call dispatch — patched={'llamaindex': ['call', 'acall']}
fetch_docs -> permit -> ran: fetch_docs OK — top hit for 'refund policy': 'Refunds within 30 days...' (stub)
db.write -> defer -> RaxitDenied raised: reason=defer.policy
shell.exec -> deny -> RaxitDenied raised: reason=deny.default_or_forbid
audit.verify -> ok=True signatures_verified=True signed_count=3 count=3
all checks PASSScope: no MODIFY here
FunctionTool.call receives its input as a single positional scalar (e.g. tool.call("file a ticket")), not the positional-dict/tool_input shape raxit's MODIFY support spreads for
field-level redaction — so argument-rewriting verdicts aren't exercised here. This is the same
scope note our test suite documents for this framework.
What got denied and why
defer.policy—db.writematched thereviewrule; it's suspended for human approval (see Approvals). The demo runs withRAXIT_DEFER_TIMEOUT=0, so the defer surfaces as a raisedRaxitDeniedrather than blocking inline.deny.default_or_forbid—shell.execappears nowhere in the policy, so it falls todefault: block. The tool body never ran, confirmed by the demo's own call log.
Every decision — the permit, the defer, and the deny — lands in one hash-chained,
Ed25519-signed Decision Provenance log, verified green with client.audit.verify(signatures=True).
See Audit and Decisions for the full
permit / defer / deny vocabulary and reason-code catalog.
Related
- LangGraph (Python) — same zero-code autopatch story at a different interception point.
- pydantic-ai — same story for pydantic-ai's
FunctionToolset.call_toolinterception point.

