Governed CrewAI in 3 Minutes
CrewAI ships with two ideas most agent frameworks don’t: crews of specialist agents and tasks that route between them. A researcher hands off to a writer, who hands off to an editor. It’s elegant.
It’s also exactly the shape that breaks naive governance.
If you put a single API key on your crew, every agent in the crew has access to every tool. The researcher can post to Slack. The writer can read your CRM. And when your compliance officer asks “who did what, through which agent?”, the audit log says crew-service-account for every call.
This is the agent-to-agent governance problem, and it’s what Agentic Control Plane was built for.
The 3-minute setup
pip install acp-crewai. Stack @governed under @tool. Bind the user’s JWT. Call install_crew_hooks(crew) to capture inter-agent handoffs.
import os
from acp_crewai import configure, governed, install_crew_hooks, set_context
from crewai import Agent, Crew, Task
from crewai.tools import tool
configure(base_url="https://api.agenticcontrolplane.com")
@tool("web_search")
@governed("web_search")
def web_search(query: str) -> str:
"""Search the web."""
return my_search(query)
@tool("send_email")
@governed("send_email")
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email."""
return sendmail(to, subject, body)
def run(topic: str, user_jwt: str):
set_context(
user_token=user_jwt,
agent_name="research-crew",
agent_tier="background",
)
researcher = Agent(
role="Researcher",
goal=f"Research {topic}",
tools=[web_search],
llm="gpt-4o-mini",
)
writer = Agent(
role="Writer",
goal="Email a summary to the user",
tools=[send_email],
llm="gpt-4o-mini",
)
crew = Crew(
agents=[researcher, writer],
tasks=[
Task(description=f"Research {topic}.", agent=researcher,
expected_output="Research notes."),
Task(description="Email the summary.", agent=writer,
expected_output="Confirmation that the email was sent."),
],
)
# Captures inter-agent handoffs (researcher → writer) as audit events.
# Safe to call on a single-agent crew — it's a no-op when there's no delegation.
install_crew_hooks(crew)
return str(crew.kickoff())
Every tool call your crew makes flows through ACP’s /govern/tool-use endpoint before the tool runs, and through /govern/tool-output after. Every inter-agent handoff (researcher’s task output feeding writer’s task input, hierarchical “delegate to coworker” calls) becomes a synthetic Agent.Handoff audit event.
What you get for free
Open the Activity dashboard. One row per tool call and one row per handoff, each with:
- Actor — the user from
set_context(user_token=...) - Tool —
web_search,send_email, orAgent.Handofffor delegations - Decision — allow / deny / redact / fail-open + reason
- Session — groups every tool call and handoff from one
crew.kickoff() - Findings — PII or secret patterns detected in tool input or output
CrewAI tool calls sit alongside Claude Code, Cursor, LangGraph, and every other framework wired into ACP — one audit log across every agent surface in your stack.
Why install_crew_hooks matters
CrewAI has two delegation paths that don’t cross a tool boundary:
- Sequential task handoffs — Task N’s output feeds Task N+1’s context.
- Hierarchical delegation — CrewAI’s built-in “Delegate work to coworker” / “Ask question to coworker” tools.
Without install_crew_hooks(crew), @governed only catches your custom tools. The handoffs pass between agents in-memory, leaving no audit trace. With it, the handoffs surface as Agent.Handoff events — same shape as a tool call, with the source agent, destination agent, and message content. PII scanning applies. Audit rows are written. Existing CrewAI callbacks are chained, not overwritten.
If your crew has multiple agents that hand work to each other, install_crew_hooks is the difference between “we know which tools were called” and “we know the full delegation graph.”
What changes when policy enforces
Per-tool denials become tool errors. When ACP denies a call, @governed returns "tool_error: <reason>". CrewAI treats this as the tool’s output; the agent sees the denial and adapts.
Scope narrowing per agent. Register each crew member as an ACP agent profile (researcher.json, writer.json). Set different scopes on each: researcher gets web.*, writer gets email.send. If the writer’s LLM hallucinates a web search call, ACP denies it. The LLM doesn’t decide which tools each agent can use — your policy does.
Inter-agent handoffs are policy-checked too. With install_crew_hooks, a handoff that violates policy (e.g., a low-trust agent trying to delegate sensitive work) can be denied, surfacing the same tool_error shape.
Per-tier policy
set_context(agent_tier="...") controls the policy tier:
interactive— human at the keyboard, permissive.subagent— invoked by another agent, no immediate human.background— autonomous, most restrictive.api— programmatic call from your backend.
A crew running on a schedule (background) typically denies destructive verbs the same crew running interactively (interactive) would allow. Match the tier to actual deployment reality.
Alternative: LLM-layer governance
If you want LLM-call audit, per-agent cost attribution, or budget enforcement at the model layer in addition to tool-layer governance, point CrewAI’s llm at ACP’s OpenAI-compatible proxy:
from langchain_openai import ChatOpenAI
governed_llm = ChatOpenAI(
base_url="https://api.agenticcontrolplane.com/v1",
api_key=os.environ["ACP_API_KEY"],
model="claude-sonnet-4-6",
)
researcher = Agent(role="Researcher", goal="...", tools=[web_search], llm=governed_llm)
Both patterns are valid and composable. Tool-layer (@governed + install_crew_hooks) is the primary recommendation — it gives you per-tool-call interception, output redaction, per-user identity attribution, and inter-agent handoff capture. LLM-layer (OAI proxy) adds budget enforcement and per-agent cost rollups. Use one, the other, or both.
What this unlocks
CrewAI is the first agent framework where multi-agent is the happy path. ACP is the first governance layer where multi-agent is the happy path. Paired, you can run crews in production — in regulated industries, against real data — and still answer the one question governance tools exist to answer:
Who did what, through which agents, and was it authorized?
Three decorators and one install_crew_hooks call. Every crew you ship from now on.
Install ACP free · CrewAI integration guide · CrewAI governance scorecard · Delegation chains deep dive
- 1. Governance for Claude Code in 60 seconds
- 2. Governing the Anthropic Agent SDK
- 3. Governed LangGraph in 3 Minutes
- 4. Governed CrewAI in 3 Minutes · you are here
- 5. Governed Cursor in 3 Minutes
- 6. Governed Codex CLI in 3 Minutes
- 7. Governed OpenAI Agents SDK in 3 Minutes