CrewAI Integration

This page describes the qarai-agent-guard-crewai package. The package adds Agent Guard protection to CrewAI crews.

It contains the enable_guard function. The function registers security hooks against the CrewAI hook system. It does not change your agent or task definitions.

Installation

Install the packages:

pip install qarai-agent-guard qarai-agent-guard-crewai

The package requires Python 3.11 or newer. It requires a compatible crewai version. It does not install CrewAI itself.

Quick start

Build a guard and register it globally:

from qarai_agent_guard import AgentGuard, Detector, default_policy
from qarai_agent_guard_crewai import enable_guard

guard = AgentGuard(
    detectors=[Detector(name="prompt_injection", default_rules="prompt_injection")],
    policy=default_policy(),
)

enable_guard(guard)

# Define your agents, tasks, and crew as usual.
# crew = Crew(agents=[...], tasks=[...])

enable_guard() registers all four hooks by default. The guard then evaluates every message and tool interaction in the crew.

Registered hooks

enable_guard() registers these hooks:

Hook Content Key Operation
before_llm_call Outgoing messages model_input input
after_llm_call Model responses model_output output
before_tool_call Tool arguments tool_input:<tool name> tool_input
after_tool_call Tool results tool_result:<tool name> tool_result

The flow of a guarded crew:

User message
     |
     v
before_llm_call
     |
     v
     LLM
     |
     v
after_llm_call
     |
     v
Agent decides to use a tool
     |
     v
before_tool_call
     |
     v
     Tool
     |
     v
after_tool_call

At each stage, the guard evaluates the content against the policy.

The executor supports five actions:

Action Behavior
ALLOW Continue execution.
WARN Emit a warning and continue.
REDACT Redact the content and write the result back to the context.
BLOCK Stop execution.
QUARANTINE Send the content to the quarantine handler and stop execution.

Parameters

Call enable_guard() with keyword parameters:

executor = enable_guard(
    guard,
    hooks=None,
    fail_open=True,
    on_error=None,
    on_violation=None,
    on_warn=None,
    quarantine_handler=None,
    raise_on_violation=True,
    emit_events=True,
    scan_all_messages=False,
)
Parameter Type Default Role
guard AgentGuard Required The guard instance for security checks.
hooks Iterable[str] \| None None (all hooks) The subset of hooks to register.
fail_open bool True When True, unexpected errors are logged and ignored. When False, they raise.
on_error Callable \| None None Called with (hook, error, context) on unexpected hook errors.
on_violation Callable \| None None Called with (source, decision, content) on a violation.
on_warn Callable \| None None Called with (source, decision, content) on a warn action.
quarantine_handler Callable \| None None Called with (source, content, decision) for quarantine actions.
raise_on_violation bool True Raise AgentGuardViolation on block or quarantine.
emit_events bool True Record telemetry events through the guard.
scan_all_messages bool False When True, scan the full conversation. When False, scan only the latest message.

The function raises ValueError when guard is None. It raises ValueError when hooks contains an unknown hook name.

Selecting hooks

Register a subset of hooks when you need only partial coverage:

enable_guard(
    guard,
    hooks=[
        "before_llm_call",
        "before_tool_call",
    ],
)

Available hook names:

["after_llm_call", "after_tool_call", "before_llm_call", "before_tool_call"]

Fail-open vs. fail-closed

fail_open defines the behavior when an unexpected technical error occurs inside a hook. Policy decisions are intentional and do not follow fail_open.

Fail-open (default). An unexpected error is logged and ignored. The crew continues:

enable_guard(guard, fail_open=True)

Fail-closed. An unexpected error raises AgentGuardHookError. Execution stops:

enable_guard(guard, fail_open=False)

BLOCK and QUARANTINE decisions always raise AgentGuardViolation. fail_open does not change this behavior.

Callbacks

Error callback

The callback runs on unexpected hook errors:

def handle_error(*, hook, error, context):
    print(f"Agent Guard error in {hook}: {error}")

enable_guard(guard, on_error=handle_error)

The callback receives hook, error, and context. Errors raised inside the callback are isolated. They never bypass enforcement.

Violation callback

The callback runs when content is blocked or quarantined:

def handle_violation(*, source, decision, content):
    print(f"Violation detected: {source} -> {decision.action}")

enable_guard(guard, on_violation=handle_violation)

The callback receives source, decision, and content. It is informational only. It does not alter enforcement outcomes.

Warn callback

The callback runs when the policy returns WARN:

def handle_warn(*, source, decision, content):
    print(f"Warning from {source}: {decision.action}")

enable_guard(guard, on_warn=handle_warn)

The callback receives source, decision, and content. It is informational only. It does not alter enforcement outcomes.

Quarantine handler

The handler processes quarantined content:

def handle_quarantine(*, source, content, decision):
    print(f"Quarantined content from: {source}")

enable_guard(guard, quarantine_handler=handle_quarantine)

The handler receives source, content, and decision. A successful handler returns a blocked result without raising. When no handler is configured, the executor raises AgentGuardViolation.

Conversation scanning

By default, the guard inspects only the latest message. This avoids re-scanning a growing conversation:

enable_guard(guard, scan_all_messages=False)

Set scan_all_messages to True to inspect the entire conversation on every call. This gives stronger coverage but higher latency:

enable_guard(guard, scan_all_messages=True)

Redaction

When the policy returns REDACT, the integration writes the sanitized value back into the relevant CrewAI context:

Before: "My IBAN is GB29NWBK60161331926819"
After:  "My IBAN is [REDACTED:iban]"

Exceptions

The package defines AgentGuardHookError for unexpected errors under fail_open=False.

from qarai_agent_guard_crewai import AgentGuardViolation, AgentGuardHookError

BLOCK and QUARANTINE decisions raise AgentGuardViolation:

from qarai_agent_guard_crewai import AgentGuardViolation

try:
    result = crew.kickoff()
except AgentGuardViolation as exc:
    print(f"Execution blocked: {exc}")

Set raise_on_violation to False to return a result with blocked=True instead.

See Exceptions for the complete exception reference.

Full workflow example

from crewai import Agent, Crew, Task

from qarai_agent_guard import AgentGuard, Detector
from qarai_agent_guard_crewai import enable_guard, AgentGuardViolation

guard = AgentGuard(
    detectors=[
        Detector(name="prompt_injection", default_rules="prompt_injection"),
        Detector(name="pii", default_rules="pii"),
    ],
)

def on_error(*, hook, error, context):
    print(f"[AgentGuard] Unexpected error in {hook}: {error}")

def on_violation(*, source, decision, content):
    print(f"[AgentGuard] Policy violation: {source} -> {decision.action}")

enable_guard(
    guard,
    fail_open=False,
    on_error=on_error,
    on_violation=on_violation,
)

researcher = Agent(
    role="Researcher",
    goal="Research the requested topic",
    backstory="You are a careful research assistant.",
)

task = Task(
    description="Research the requested topic.",
    expected_output="A concise research summary.",
    agent=researcher,
)

crew = Crew(agents=[researcher], tasks=[task])

try:
    result = crew.kickoff()
    print(result)
except AgentGuardViolation as exc:
    print(f"[AgentGuard] Crew execution blocked: {exc}")