Framework Guide

EU AI Act Compliance for Google ADK Agents

Wrap your Google ADK tool functions with @guard. You get a hash-chained audit log, side-effect compensation, loop detection, and per-run token/step/time limits. Your Agent configuration stays the same.

Your role under the EU AI Act

Google is the Provider of the Gemini models. When you build with ADK, you are the Deployer of the AI system you assemble on top. Deployer obligations focus on how the system is used, not how the model was trained. If you fine-tune Gemini or place the resulting system on the EU market under your name, you may also become a Provider — see deployer vs provider.

Fuze is a component supplier under Article 25(4). Fuze tooling does not certify your AI system. Compliance determination is yours; see Terms §6.

1. Install

pip install fuze-ai

fuze-ai ships as a Python package. No ADK-specific adapter is required — @guard is framework-agnostic and wraps any async function ADK calls as a tool.

2. Wrap your tools

from fuze_ai import guard
from google.adk import Agent
from google.adk.tools import FunctionTool

# Wrap your async tool functions with @guard.
# guard() is framework-agnostic — no ADK-specific adapter required.
@guard(
    resource_limits={
        "max_tokens_per_run": 200_000,
        "max_steps": 25,
        "max_wall_clock_ms": 60_000,
    },
)
async def search_documents(query: str) -> dict:
    return await vertex_search.query(query)

# Mark irreversible writes as side effects with a compensation function.
# If the run is killed mid-step, Fuze runs the compensate callable.
@guard(
    side_effect=True,
    compensate=lambda record_id, data: firestore.revert(record_id),
)
async def update_record(record_id: str, data: dict) -> dict:
    return await firestore.update(record_id, data)

# Hand the wrapped functions to ADK as FunctionTools. ADK code unchanged.
agent = Agent(
    model="gemini-2.0-flash",
    name="document_assistant",
    instruction="Process document requests…",
    tools=[
        FunctionTool(func=search_documents),
        FunctionTool(func=update_record),
    ],
)

Wrap, then hand to FunctionToolas you would any Python coroutine. ADK doesn't know it's instrumented.

3. Verify the audit trail

Every wrapped call writes one JSONL record to ~/.fuze/audit.jsonl. Records are HMAC-SHA256 hash-chained: each record carries the previous record's hash plus its own signature. Tampering with one record breaks every record after it.

from fuze_ai import verify_chain, read_audit

# Read the JSONL audit trail produced by @guard.
# Default location: ~/.fuze/audit.jsonl
records = read_audit()

# Verify the HMAC-SHA256 hash chain.
# Returns ok=True if untouched, with first_mismatch_index on failure.
result = verify_chain(records)
print(result.ok, result.first_mismatch_index)

# Each record carries: step_id, run_id, step_number, tool_name,
# args_hash (sha256, first 16 chars), tokens_in, tokens_out,
# latency_ms, error, prev_hash, hash, signature, sequence.

Arguments are SHA-256 hashed (first 16 chars stored), not raw — so logs don't leak PII even when wrapped tools process personal data. Set log_pii=True per call if you need raw arguments and have a lawful basis.

What Fuze actually does, and what it doesn't

Fuze sees the tool-call boundary. Honest scope:

  • Tool-call audit log — every @guard call recorded with stepId, runId, toolName, argsHash, tokensIn, tokensOut, latencyMs, error.
  • 3-layer loop detection— iteration cap, repeated tool calls, no-progress detection. Detects loops at the tool-call level. ADK's internal multi-agent state is not introspected.
  • Per-run resource limits — tokens, steps, wall-clock. Enforced shared across all @guard calls in the run.
  • Side-effect compensation — the compensate callable runs if the run is killed mid-step.
  • Kill switch — the daemon can halt the next step; the wrapped function refuses to run and logs a guard event.

Fuze does not track cost in dollars ( deliberately — see the budget docs); does not integrate with Vertex AI billing or Google Cloud Logging out of the box; does not understand ADK's multi-agent handoff graph; and does not perform cross-run drift or anomaly detection. Token usage is captured generically from response shapes, not Vertex-priced.

Mapping to AI Act articles

  • Article 12 (record-keeping): the JSONL hash-chained log is your tamper-evident substrate. Retention is your call.
  • Article 14 (human oversight): side_effect=True + compensate give you a defensible kill-and-rollback path. Plug your own approval UI before high-impact tools.
  • Article 15 (robustness): loop detection and per-run resource limits short-circuit runaway runs.
  • Article 50 (transparency): not covered; disclose AI-system presence to your users separately.

Common ADK use cases and likely risk tier

Use the risk classifier for a real determination — these are heuristics:

  • Internal knowledge assistants — usually minimal risk.
  • Customer-facing agents — limited risk (transparency obligations under Art. 50).
  • Document processing for legal / HR decisions — likely high-risk under Annex III if outputs influence employment, credit, or legal entitlement.
  • Healthcare triage agents — high-risk under Annex III.

When to consider Fuze Agent instead

If you're starting fresh and want compliance baked into the runtime — typed tools with explicit data classification, Ed25519-signed run-roots, replay-protected HITL approvals, EU-residency as a type — consider Fuze Agent instead of bolting @guard onto ADK. Fuze Agent is a TypeScript runtime; if your team needs Python, stay on ADK + @guard.