A multi-agent Trust & Safety incident-triage pipeline for a cryptocurrency exchange built using Google ADK (
google-adk), Gemini, and the Model Context Protocol (MCP).
In a cryptocurrency exchange, Trust & Safety (T&S) compliance requires rapid, high-confidence triage and investigation of suspicious account activities. Manual analysis of every automated flag is slow and prone to human error, yet fully automated workflows are highly risky: false-positive freezes damage customer relationships, and unauthorized state changes may violate regulatory rules.
Sentinel solves this challenge by implementing a secure, multi-agent automated triage pipeline. Sentinel orchestrates specialized AI agents to classify risk events under strict policy constraints, neutrally compile investigation dossiers via a local MCP server database, enforce Human-in-the-Loop safeguards for high-risk actions (e.g. account freezing), and draft incident Root Cause Analysis (RCA) reports—all while redacting sensitive PII from logs.
Instead of a monolithic script, Sentinel divides the investigation process into single-responsibility, bounded agents. This design pattern offers substantial security, performance, and maintainability advantages:
- Policy Confinement (TriageAgent):
The
TriageAgentclassifies risk flags against a predefined rules policy. To prevent unauthorized capabilities or code injection, it has no access to external tools or databases. It functions strictly as a pure decision/classification function. - Isolated Tool Access (InvestigationAgent):
The
InvestigationAgentis the only agent with access to the local database tools. Restricting tool capabilities to a single agent minimizes token usage, avoids prompt pollution, and isolates database read permissions from the decision-making nodes. - Decoupled Orchestration (OrchestratorAgent):
The
OrchestratorAgentmanages the pipeline sequence, handles control flow logic, performs input validation, and enforces PII redaction. Keeping flow orchestration decoupled from agent prompts ensures the execution pipeline remains predictable and reliable. - Human-in-the-Loop (HITL) Gatekeeping (ApprovalGate):
Any action deemed high-risk (such as freezing a customer's account) automatically triggers the
ApprovalGateto pause execution, requiring explicit approval from a human operator before any state change is committed. - Presentation Decoupling (ReportAgent & RCA Skill):
The
ReportAgentuses a reusable python-based skill to draft structured markdown reports. This separates the presentation layer from the core triage logic; compliance reporting templates can be modified without altering decision policies or database schemas.
The following sequence diagram outlines the Sentinel pipeline workflow, demonstrating the progression from triage through investigation and operator approval to logging and reporting:
graph TD
%% Define styles
classDef orchestrator fill:#f9f,stroke:#333,stroke-width:2px;
classDef agent fill:#bbf,stroke:#333,stroke-width:1px;
classDef mcp fill:#dfd,stroke:#333,stroke-width:1px;
classDef human fill:#ffd,stroke:#333,stroke-width:2px;
%% Nodes
A[Synthetic Flag Event] --> B(OrchestratorAgent)
B --> C[TriageAgent]
C -->|1. Classify & Recommend Action| B
B --> D[InvestigationAgent]
D -->|2. Request Context| E[Sentinel MCP Server]
E -->|Read CSV Database| E
E -->|3. Return Dossier| D
D -->|4. Return dossier| B
B --> F{Is Recommended Action High-Risk?}
F -->|Yes: freeze_account| G[Approval Gate]
G -->|5. Pause & Request Input| H[Human Operator]
H -->|6. Approve / Deny| G
G -->|7. Return decision| B
F -->|No: monitor/request_kyc/escalate| I[Skip Approval]
I --> B
B --> J{Is Approved or Low-Risk?}
J -->|Yes| K[ReportAgent]
K -->|8. Generate RCA| L[RCA Report Skill]
L -->|9. Structured Markdown| K
K -->|10. Return Report| B
J -->|No| M[Abort State Change]
M --> B
B --> N[State Change: Update accounts.csv]
B --> O[Redact PII & Log to decision_trail.log]
%% Apply Classes
class B orchestrator;
class C,D,K agent;
class E mcp;
class H human;
While the diagram above shows the runtime flow, the following diagram shows the static components and the trust boundary — who owns what, and which component is allowed to touch state, tools, or the human operator:
graph TB
IN[Flag event<br/>flag_id · run.py CLI]:::io
subgraph APP["Sentinel · ADK app (google-adk + Gemini)"]
ORCH[Orchestrator Agent<br/>control · routing · logging]:::orch
TRIAGE[Triage Agent<br/>constrained policy · no tools]:::agent
INV[Investigation Agent<br/>builds dossier]:::agent
GATE[Approval Gate<br/>HITL pause on high-risk]:::human
REP[Report Agent<br/>drafts RCA]:::agent
end
MCP[MCP Server<br/>4 read-only tools]:::tool
DATA[(Synthetic Data<br/>5 CSV tables)]:::io
SKILL[RCA Skill<br/>reusable function]:::tool
HUMAN([Human Operator<br/>approve / deny]):::human
LOG[/Decision Trail Log<br/>PII-redacted audit/]:::io
IN --> ORCH
ORCH --> TRIAGE
ORCH --> INV
ORCH --> GATE
ORCH --> REP
ORCH --> LOG
INV --> MCP
MCP --> DATA
GATE <--> HUMAN
REP --> SKILL
classDef orch fill:#ede9fe,stroke:#7c3aed,color:#3b0764;
classDef agent fill:#dbeafe,stroke:#2563eb,color:#1e3a8a;
classDef human fill:#fef3c7,stroke:#d97706,color:#78350f;
classDef tool fill:#dcfce7,stroke:#16a34a,color:#14532d;
classDef io fill:#f3f4f6,stroke:#6b7280,color:#1f2937;
Reading the trust boundary: the LLM agents (blue) never write state. Only the
Orchestratorperforms state changes (e.g. freezing an account), and only after theApproval Gatereturns a humanapprove. TheTriage Agenthas no tools by design; only theInvestigation Agentreaches data, and only through the read-only MCP server.
Sentinel is built on the Google Agent Development Kit (google-adk). It uses the Workflow API to construct the pipeline, the Agent API to define specialized personas with custom prompts, and the Context API to run sub-agents as node computations.
To lookup customer contexts securely, Sentinel runs a local FastMCP server (mcp_server/server.py) over a standard input/output (stdio) transport sub-process. The server exposes four read-only tools to fetch database records:
get_account: Details account risk score, registration country, and KYC level.get_recent_transactions: Lists transaction histories sorted descending.get_flags: Retrieves triggered flags.get_prior_cases: Looks up prior investigation cases.
- Constrained Policy: The
TriageAgentis restricted by its Pydantic output schema (TriageResult) to a set of allowed actions:monitor,request_kyc,escalate, andfreeze_account. - Approval Gate: Using
google-adk's long-running tool interrupts (RequestInput), the pipeline pauses when a high-risk action (freeze_account) is recommended. The pipeline yields execution and waits for human input (approve/deny). - PII Sanitization: Before writing to disk, the orchestrator invokes a regex-based sanitization parser (
redact_pii) to scrub IP addresses, device IDs, account IDs, and counterparty wallet addresses (e.g. Ethereum hexes and external wallets) to prevent PII leakage in compliance archives.
The markdown incident report generator (skills/rca_report.py) is designed as a standalone Python function. Decoupled from the ADK framework classes, it accepts standard Python primitives (strings and dicts), allowing it to be reused in external microservices, dashboards, or unit tests without framework dependencies.
Ensure you have Python 3.10+ installed on your system.
Clone the repository, navigate into the directory, create and activate a clean Python virtual environment:
git clone <repository-url>
cd sentinel
python3 -m venv .venv
source .venv/bin/activateInstall the pinned dependencies:
pip install -r requirements.txtCreate a .env file in the root directory by copying the example:
cp .env.example .envOpen .env and configure your API key:
GOOGLE_API_KEY="YOUR_GEMINI_API_KEY"
GEMINI_MODEL="gemini-2.5-flash"Verify that all components are functioning correctly from your clean environment:
# 1. Verify read-only MCP database tool retrieval
python3 verify_mcp.py
# 2. Verify all low-risk, high-risk approved, and high-risk denied scenarios
python3 verify_approval_gate.py
# 3. Verify the end-to-end multi-agent pipeline with bypassed approval gate
python3 verify_pipeline.pySentinel provides an interactive CLI (run.py) to process flags:
-
Low-Risk Action (monitor):
python3 run.py FLG-001
(Runs automatically end-to-end and drafts the RCA report without pausing)
-
High-Risk Action (freeze_account):
python3 run.py FLG-019
(Pauses at the approval gate, displays the dossier, and prompts the operator. Enter
approveto freeze the account and complete the report, ordenyto abort).
Important
This project operates entirely on synthetic data. All database records (data/*.csv), including account IDs, transactions, IP addresses, device names, and case logs are generated programmatically (generate_data.py). No real-world customer identifiers or active transaction records are processed, loaded, or exposed.
- Zero-Knowledge Decisions: State-changing operations (such as updating account statuses in
data/accounts.csv) are strictly decoupled from the LLMs. The agents cannot execute database writes; theOrchestratorAgentexecutes them programmatically only after verification of human operator approval. - PII Auditing: Decision trail logs stored in
data/decision_trail.logare filtered through a multi-pass regex filter to ensure that transaction IP countries, wallet IDs, and devices are redacted from the text, satisfying governance audits. - Safe Credentials: No credentials, API keys, or tokens are checked into the codebase. All runtime configuration is driven strictly through local environment variables.
Sentinel's oversight design maps directly onto the four functions of the NIST AI RMF. The human-in-the-loop gate, the constrained policy, and the decision trail together operationalize human oversight as a concrete, auditable control rather than a slogan.
| NIST Function | How Sentinel implements it |
|---|---|
| Govern — policies, roles, accountability | Constrained action policy: the TriageAgent's Pydantic schema permits only monitor, request_kyc, escalate, freeze_account. Single-responsibility agents enforce separation of duties — only the Orchestrator mutates state, and only after human approval. |
| Map — establish risk context | The TriageAgent classifies each flag's severity + category against rules.csv, which encodes the exchange's risk appetite (e.g. sanctioned address → freeze) before any action is considered. |
| Measure — analyze, assess, trace | The InvestigationAgent assembles an evidence dossier (account, transactions, flags, prior cases) via the read-only MCP server; every run emits a PII-redacted decision_trail.log (action, decision, rationale, timestamp) and a structured RCA report for traceability. |
| Manage — prioritize and mitigate | Risk-proportionate response: high-risk actions pause for human approve/deny, low-risk proceed automatically. LLMs cannot write state (mitigating irreversible automated error); read-only tools and PII redaction enforce least privilege and limit data exposure. |
A fuller, paste-ready version of this section for the project writeup lives in
docs/governance-nist-rmf.md.
Beyond the NIST framing, the project ships a full STRIDE security analysis in threat_model.md — evaluating Sentinel against Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, and Elevation of Privilege, with a per-threat status, the existing mitigation, and the remaining gap. Highlights:
- Strengths: read-only MCP tools, agent privilege separation (Triage has no tools), the human-in-the-loop gate, and active
redact_piisanitization of logs and reports (Information Disclosure rated low-risk). - Honest gaps for production: plaintext CSV storage lacks integrity controls, decision-trail logs are not tamper-proof (no WORM/signing), the approval gate has no operator authentication, and the
bypass_gatetest hook should be removed from the orchestrator's main control flow.
The document doubles as the roadmap from a portfolio demo to a production-hardened deployment.
- Workflow Resume Replay: Because ADK commits node execution states upon completion, using
rerun_on_resume=Trueon theorchestrator_workflownode causes steps 1 and 2 (Triage and Investigation) to run again when the operator resumes from a paused approval gate. - Mitigation: In a production setting, this is resolved by breaking down the orchestrator workflow into distinct, sequential nodes (
triage_node➔investigation_node➔approval_gate_node➔reporting_node) rather than a single multi-step node, enabling step results to be stored and persisted iteratively.