Developers · CrewAI
Govern a CrewAI crew
CrewAI's tool() decorator builds its argument schema from your function's signature, the same mechanism the OpenAI Agents SDK uses — so the same rule applies: guard on the inside, decorate on the outside. Governance isn't a Task guardrail either — a guardrail inspects what an agent's output SAID; the gate decides an action before it happens.
01
How it works
Wrap the consequential tool with guard_tool() from the Python reference client, THEN pass the result to CrewAI's tool(). Reverse that order and the schema CrewAI builds for the agent loses its fields — guard_tool uses functools.wraps precisely so this can't happen. Every other agent in the crew, and every read-only tool, stays exactly as it was.
02
What to notice
- Only the consequential agent's tool needs a gate — a researcher agent with a read-only tool stays ungoverned
- Guard on the inside, decorate on the outside — the same order the OpenAI Agents SDK example needs, and for the same reason
- Task's guardrail is a different feature: it validates an agent's OUTPUT; the gate decides an action — an amount, a merchant, a scope — signed by the agent's key against a mandate its owner issued
Example
See it in code
crewai_agent.py
from crewai import Agent, Task, Crew
from crewai.tools import tool
from metamynd_client import MetaMyndClient, guard_tool, GovernanceBlocked
client = MetaMyndClient.from_env()
# Your tool, unchanged and unaware it is governed.
def book_flight(airline: str, amount: float, route: str) -> dict:
"""Book a flight and charge the corporate card."""
...
# Guard on the INSIDE, decorate on the OUTSIDE.
governed_book_flight = guard_tool(
client, "flight-purchase", book_flight,
lambda airline, amount, route: {"amount": amount, "merchant": airline},
)
booker = Agent(
role="Flight booker",
goal="Book the requested flight within policy",
backstory="If a booking is refused, explain why rather than trying another way.",
tools=[tool(governed_book_flight)],
)