CrewAI organizes work into Agent, Task, and Crew objects: agents have roles and tools, tasks describe work, crews coordinate execution and hand context between agents. It's a clean model for building multi-agent workflows — but there's no point in that model where anyone asks "is this agent allowed to do this?"
A Task object isn't a good place to intercept that question either — tasks describe intent, not execution, and a Crew's kickoff() doesn't expose a hook per tool call. The right interception boundary is the tool itself: wrap the tool, and every _run() call goes through policy before the underlying tool executes.
Install
pip install agf-sdk[crewai]
export AGF_API_KEY=agfk_your_key_here
Requires Python ≥ 3.10 and crewai ≥ 0.28.
Quick authorization check
Before wiring up tool guards, AgentGovernance.authorize() is the simplest way to check policy directly:
import os
from agf import AgentGovernance
agf = AgentGovernance(
api_key=os.environ["AGF_API_KEY"],
org_id="org_acme",
)
result = agf.authorize(
agent_id="did:agf:agt_01abc",
action="read:data",
resource="customer-records",
)
if result.allowed:
print("Authorized")
else:
print(f"Blocked: {result.reason}")
authorize() always returns an AuthResult — it never raises on DENY. Check result.allowed before proceeding.
Guarding a tool
AGFCrewAITool wraps any CrewAI BaseTool (or a LangChain BaseTool, since older CrewAI versions re-export it) and intercepts every _run() call:
import os
from agf import AGFClient
from agf.crewai import AGFCrewAITool
from crewai_tools import FileReadTool
client = AGFClient(api_key=os.environ["AGF_API_KEY"])
# Wraps a CrewAI BaseTool — every _run() call goes through the PDP
guarded_file_tool = AGFCrewAITool(
tool=FileReadTool(),
client=client,
agent_id="did:agf:agt_01abc",
action_type="read:file",
resource="filesystem",
)
If the PDP returns DENY, the tool raises before the wrapped tool's code runs — the file is never read.
Single-agent crew
from crewai import Agent, Task, Crew
researcher = Agent(
role="Research Analyst",
goal="Gather and analyse customer data",
backstory="Expert data analyst with access to customer records.",
tools=[guarded_file_tool],
verbose=True,
)
task = Task(
description="Read last month's churn report and summarise key findings.",
agent=researcher,
expected_output="A 3-bullet summary of churn drivers.",
)
crew = Crew(agents=[researcher], tasks=[task])
crew.kickoff()
Every time the researcher calls FileReadTool, the PDP is consulted first — whether the call came from the model's own reasoning or from a prompt injected into the task description.
Multi-agent crews and delegation
Crews where agents pass context between tasks (via Task(context=[...])) need more than isolated per-tool checks — the PDP should see the full authority chain, not just the immediate caller:
from crewai import Agent, Task, Crew
writer = Agent(
role="Report Writer",
goal="Write a report based on research findings",
backstory="Senior technical writer.",
tools=[guarded_file_tool, guarded_write_tool],
)
task1 = Task(description="Gather customer churn data.", agent=researcher, expected_output="Raw data.")
task2 = Task(description="Write a report from the data.", agent=writer, expected_output="Draft report.", context=[task1])
crew = Crew(agents=[researcher, writer], tasks=[task1, task2])
crew.kickoff()
Register each agent separately in AGF, and pass the full delegation chain (chain: [orchestrator_id, specialist_id]) via AGFClient.decide() directly when a downstream agent's authority derives from an upstream one. This lets the PDP evaluate delegated trust rather than treating each agent as independently authorized.
What you get
Every PDP call — whether via authorize() or AGFCrewAITool — produces a signed audit artifact stored in your org's audit log, exportable from the dashboard or GET /v1/audit. That's the same audit trail described in the LangChain integration post — the artifact shape doesn't change based on which framework triggered it.
Next steps
agf-sdk is open source. PyPI →

