Agno¶
Integrates Agent Assembly with Agno (formerly Phidata) to enforce governance policy on tool calls before they execute.
What this example demonstrates¶
- Initializing Agent Assembly with
init_assembly()in offlinesdk-onlymode. - Installing Agno tool-level governance via the native Agno adapter —
AgnoPatchpatchesagno.tools.function.FunctionCall.execute, the single chokepoint every Agno function-tool call runs through. - Running an allowed tool call (
get_weather), another allowed tool call (summarize_docs), and a denied tool call (execute_sql, blocked by policy ruledeny_arbitrary_execution). - How a denied tool short-circuits entirely — its body never runs — and returns a failure
FunctionExecutionResultinstead of a result. - Driving genuine Agno
@toolfunctions exactly as an AgnoAgentdoes —FunctionCall(...).execute()— with no gateway, API key, or live LLM.
The framework / library¶
Agno (formerly Phidata) is the agent framework governed in this example. Agno has a native Agent Assembly adapter (agent_assembly.adapters.agno), so governance is wired by patching Agno's own tool-execution chokepoint rather than wrapping individual tools.
Version pins (from pyproject.toml):
| Dependency | Version |
|---|---|
agno |
>=2.0.0 |
agent-assembly |
>=0.0.1rc1 (the release that ships the Agno adapter) |
| Python | >=3.12 |
How it works¶
init_assembly() is opened as a context manager in offline sdk-only mode with the agent id agno-demo-agent:
with init_assembly(
gateway_url=gateway_url,
api_key=api_key,
agent_id="agno-demo-agent",
mode="sdk-only",
) as ctx:
...
Hook point. The Agno adapter governs Agno by patching agno.tools.function.FunctionCall.execute (and its async counterpart aexecute) — the single chokepoint every Agno function-tool call runs through. Every tool an Agno agent invokes therefore passes through policy before its body executes.
Offline note. In production, init_assembly() auto-detects Agno and wires the live runtime as the interceptor automatically. In this offline sdk-only demo there is no live runtime, so init_assembly() installs a no-op hook; the example reverts it and re-applies the hook wired to a local LocalPolicyEngine so the demo shows real allow/deny decisions without a gateway (the patch is idempotent, so the no-op hook is reverted first):
The adapter calls check_tool_start on LocalPolicyEngine (src/policy.py), which returns a decision dict in the gateway wire format {"status": "allow"} or {"status": "deny", "reason": ...}. execute_sql and run_shell_command are denied (arbitrary execution); everything else is allowed. The engine runs fail-closed, so an unknown verdict denies rather than allows.
Deny behavior. When execute_sql is denied, FunctionCall.execute() returns a FunctionExecutionResult with status == "failure" whose error carries the [BLOCKED by governance policy] marker and the deny_arbitrary_execution rule name. The tool's body never runs — the example proves this with a side-effect sink (SQL_EXECUTIONS) that stays empty for a denied call, the negative control that would fail if governance were a no-op.
Prerequisites & running it¶
See Preparing the runtime environment for the shared prerequisites.
Then run the example (offline — no API key and no running gateway required):
Expected output¶
==============================================================
Agent Assembly — Agno Tool Policy Demo
==============================================================
Initializing Agent Assembly (gateway: http://localhost:8080, sdk-only mode)...
Agent: agno-demo-agent
Gateway: http://localhost:8080
Mode: sdk-only (offline demo)
Policy rules (local simulation of gateway policy):
DENY — execute_sql, run_shell_command (arbitrary execution)
ALLOW — everything else
Agno governance hook installed on FunctionCall.execute.
Tools governed: get_weather, summarize_docs, execute_sql
Running governed tool calls:
--------------------------------------------
→ get_weather({'city': 'London'})
✅ ALLOWED — Weather in London: sunny, 22C (mock)
→ summarize_docs({'topic': 'policy enforcement'})
✅ ALLOWED — Summary for 'policy enforcement': Agent Assembly provides governance... (mock)
→ execute_sql({'sql': 'DROP TABLE users; --'})
❌ BLOCKED — [BLOCKED by governance policy] Tool 'execute_sql' is blocked by policy rule 'deny_arbitrary_execution'.. Please choose a different approach to accomplish this task.
Assembly context shut down.
Run the smoke tests¶
The example ships offline smoke tests that drive real Agno @tool functions through the real AgnoPatch and assert genuine governance — an allowed tool runs and returns its output, a denied tool's body is short-circuited before it runs (the negative control):