VORIM
We use cookies

We use cookies to analyze site traffic and improve your experience. You can choose to accept all cookies or only essential ones. See our Privacy Policy.

Tutorial · 8 min read

Adding audit logs to a CrewAI agent in 12 lines

Every tool call signed, logged, and exportable as a tamper-evident bundle. With vorim[crewai], this is a decorator on each tool — not a refactor of your crew.

Updated 17 May 2026 · Python 3.11+ · CrewAI 0.55+ · Vorim SDK 2.x

The problem

CrewAI is great for orchestrating multiple agents around a task. What it doesn't give you out of the box is a question your security team will absolutely ask: which agent called which tool, with what arguments, and was it authorized?

You can get part of the way with logging.info() in each tool, but that gives you a flat log file with no identity binding, no permission outcomes, no tamper evidence, and no export format a compliance reviewer will accept. The goal here is to bind every tool call to a verifiable agent identity, gate it through a permission check, and stream the result into an append-only audit trail you can export with a signed manifest.

What we're building

A two-member CrewAI crew — a researcher and a writer. Each member gets a distinct Ed25519 identity. Each tool call goes through a permission check. Every call (allowed or denied) lands in the Vorim audit log. At the end, we export a SHA-256-signed bundle for the last 24 hours of activity.

1. Install

pip install "vorim[crewai]" crewai

2. Register the crew (4 lines that matter)

Each member is registered with a role, capabilities, and a list of permission scopes. Vorim returns an Ed25519 keypair per member — the agent identity.

from vorim import Vorim
from vorim.integrations.crewai import register_crew, vorim_tool

vorim = Vorim(api_key="agid_sk_live_...")

crew = register_crew(vorim, {
    "crew_name": "content-pipeline",
    "members": [
        {
            "role": "researcher",
            "name": "crew-researcher",
            "capabilities": ["web_search"],
            "scopes": ["agent:read", "agent:execute"],
        },
        {
            "role": "writer",
            "name": "crew-writer",
            "capabilities": ["file_write"],
            "scopes": ["agent:read", "agent:write"],
            "allow_delegation": True,
        },
    ],
})

researcher = crew.get_member("researcher")
writer = crew.get_member("writer")

3. Wrap each tool with the audit decorator

@vorim_tool takes the Vorim client, the agent_id, and the required permission scope. It runs a permission check before every call and emits an audit event after — for allowed, denied, and error outcomes. The decorated function still works as a CrewAI tool.

@vorim_tool(vorim, agent_id=researcher.agent_id, permission="agent:read")
def search_docs(query: str) -> str:
    """Search internal documents for relevant material."""
    return do_search(query)  # your real implementation

@vorim_tool(vorim, agent_id=writer.agent_id, permission="agent:write")
def save_draft(filename: str, content: str) -> str:
    """Persist a draft article to disk."""
    with open(filename, "w") as f:
        f.write(content)
    return f"Saved {len(content)} bytes to {filename}"

4. Wire the tools into CrewAI normally

From CrewAI's perspective these are regular tools. The audit and identity behavior happens at decoration time, so the crew definition stays clean.

from crewai import Agent, Task, Crew

researcher_agent = Agent(
    role="Researcher",
    goal="Find the three most relevant facts on a topic",
    backstory="Methodical, evidence-driven.",
    tools=[search_docs],
)

writer_agent = Agent(
    role="Writer",
    goal="Turn research into a 200-word draft",
    backstory="Concise. Doesn't bury the lede.",
    tools=[save_draft],
)

result = Crew(
    agents=[researcher_agent, writer_agent],
    tasks=[
        Task(description="Research: agent identity in 2026", agent=researcher_agent),
        Task(description="Write a 200-word brief, save as draft.md", agent=writer_agent),
    ],
).kickoff()

5. What's in the audit log

Every call lands in Vorim's audit store with the agent id, action, permission scope, latency, outcome, and any error. Denials are logged just as carefully as successes — that asymmetry is what makes the log a useful compliance artefact.

{
  "agent_id":  "agid_crew_researcher_a1b2c3",
  "event_type":"tool_call",
  "action":    "search_docs",
  "permission":"agent:read",
  "result":    "allowed",
  "latency_ms":47,
  "timestamp": "2026-05-17T09:42:11Z",
  "metadata":  { "framework": "crewai" }
}

6. Export a signed bundle

When you need to hand the trail to an auditor, security review, or post-market surveillance request, export the last N hours as a signed bundle. The bundle contains the events, a SHA-256 manifest, and an Ed25519 signature over the manifest. Recompute the hash, verify the signature, confirm integrity.

bundle = vorim.audit.export(
    hours=24,
    agent_ids=crew.agent_ids(),
    format="bundle",  # also: "json", "csv", "pdf"
)

with open("crew-audit-2026-05-17.zip", "wb") as f:
    f.write(bundle.content)

print(f"signed manifest: {bundle.manifest_sha256}")
print(f"signature:       {bundle.signature[:32]}...")

What the 12 lines actually buy you

  • Each crew member has a verifiable Ed25519 identity, not a shared API key.
  • Every tool call goes through a permission check before the side effect.
  • Denials are logged with reasons — debuggable, not silent.
  • Errors are logged with the exception type — visible in the audit trail.
  • Bundles are independently verifiable — anyone can recompute the hash.
  • No CrewAI internals were monkey-patched. If you remove the decorators, your crew still runs.

What this doesn't solve

This pattern audits tool calls. It does not audit raw LLM completions — if you need a record of every model invocation, wrap your LLM client too (Vorim has helpers for the OpenAI and Anthropic SDKs). It also doesn't sandbox the tool implementation — a permission check guards whether a tool runs; it doesn't limit what the function body can do once it executes. Pair this with the usual code-level guardrails.

Why bother?

Two reasons. First, debugging. Multi-agent systems fail in subtle ways — silent denials, role confusion, the wrong agent picking up the wrong tool. A clean audit log makes those failures legible. Second, compliance. The EU AI Act's Article 12 requires high-risk AI systems to log events over their lifetime. SOC 2 evidence collection for AI-driven workflows looks the same. The work to do it later is much higher than the work to do it now.

References

Try this in your own CrewAI project

Free tier, no credit card. The SDK is open source and the API key takes 30 seconds.