Skip to content

Plain Python

No framework needed. Decorate tools and call them, or dispatch by name from your own agent loop:

from agentguard import Guard, GuardDenied

guard = Guard("policy.yaml", audit="agentguard.jsonl")

@guard.tool(capability="filesystem.read")
def read_file(path: str) -> str:
    """Read a UTF-8 text file."""
    ...

# Your agent loop: the model returns a tool name and JSON arguments.
for step in model_steps():
    try:
        result = guard.call(step.tool, step.arguments)
    except GuardDenied as exc:
        result = {"error": "denied", "reasons": list(exc.decision.reasons)}
    send_to_model(result)

await guard.acall(name, arguments) is the async equivalent. guard.tools lists registered names for the model's tool list.

GuardMiddleware wraps the same calls for code that expects an invoke/ainvoke object:

from agentguard.adapters.python import GuardMiddleware

middleware = GuardMiddleware(guard)
middleware.invoke("read_file", {"path": "notes.txt"})

The Gemma demo agent (agentguard/demo/gemma_agent.py) is a complete example of a small agent loop built this way, including a schema that restricts the model to guard.tools.