AI Learning
advanced ⏱️ 17 min read · 🎬 ~27 min video

Fighting Financial Crime with AI Agents: AML, Alert Triage and Production Compliance Workflows

How to build AI agents that accelerate anti-money laundering investigation, reduce alert fatigue in compliance teams, and reach production safely using in-house MCPs, MCP gateways, and rigorous evals.

This lesson is original educational writing based on this video by Anthropic (published May 22, 2026). All credit for the original content goes to the creators.

#agents #enterprise
Video thumbnail: Fighting Financial Crime with AI Agents: AML, Alert Triage and Production Compliance Workflows
Original video — all credit to the creators. Watch the original on YouTube ↗

1. The alert-fatigue problem

Financial crime compliance teams live inside a brutal mismatch. Transaction monitoring systems flag every pattern that could indicate money laundering. Experienced analysts know that ninety percent of those alerts are false positives — structuring patterns that resolve cleanly once you look at two more data points, or transactions that match a rule only in the narrowest technical sense. But the rules can’t be relaxed; regulators require documentation that every alert was reviewed.

The result is alert fatigue: analysts spend most of their day triaging noise, which means less bandwidth for the genuinely suspicious cases that warrant a Suspicious Activity Report (SAR) or a referral for law-enforcement action.

This is an ideal problem for AI agents because the bottleneck is not judgment — it is information retrieval and synthesis. A skilled analyst knows exactly what to look up (counterparty profiles, prior transaction history, sanctions list membership, historical context on the customer) and can form a disposition in minutes. The agent can perform that same retrieval in seconds, every time, at scale, and surface a pre-populated investigation summary for the analyst to review.

2. The three-stage AML lifecycle and where agents fit

Every AML alert moves through three stages before it is closed. Agents can reduce human time at each one without replacing human judgment on the final call.

DetectionTransactionMonitoring SystemRule / ML enginegenerates~1000s alerts/day90 %+ false positivesAI TriageAgent (Claude Cowork)Fetch customer profileQuery txn history 90dCheck sanctions listsDraft disposition summaryAnalyst DecisionReads AI summary+ evidence linksClose (no action)File SARAgent handles the retrieval; human holds the final judgement
AML alert lifecycle: transaction monitoring generates alerts; agents triage and enrich them; analysts make final disposition and file SARs when warranted.

Stage 1 — Detection (automated, no AI change needed)

Transaction monitoring engines — whether rules-based (structuring, velocity, round-number thresholds) or ML-based — already run fully automatically. Don’t replace them; they encode regulatory and audit requirements. The output is a queue of alerts.

Stage 2 — Triage (where agents deliver the biggest gain)

This is where the agent earns its keep. For each alert, the agent:

  1. Pulls context from every relevant system — core banking, CRM, prior case notes, watch lists, negative news — through tools exposed via an in-house MCP server.
  2. Synthesises a plain-language summary: “This alert triggered the structuring rule because the customer made three cash deposits of $9,800 over 72 hours. Their account was opened 14 months ago, average monthly balance $22,000. No prior alerts. No sanctions matches. Counterparties are known household members.”
  3. Proposes a disposition — close or escalate — with a cited confidence level and a list of the evidence it used.

The analyst receives a pre-populated case, not a blank form. Decision time per alert drops from 20-40 minutes to under five.

Stage 3 — Disposition (human, supported by agent output)

The analyst reads, can interrogate the agent (“what was the cash activity in the prior 12 months?”), adds any context the agent missed, and makes the legally required human judgement. If they file a SAR, the agent’s summary can feed directly into the SAR narrative draft.

3. Building an in-house MCP server for compliance data

The agent needs structured access to internal systems without touching raw database credentials or PII endpoints directly. The right architecture is an in-house MCP server: a thin service you control that translates the agent’s tool calls into authenticated, audited, permission-checked queries against your internal APIs.

A compliance MCP server typically exposes four categories of tools:

Tool categoryExample toolsNotes
Customer dataget_customer_profile, get_account_historyReturns only fields needed for AML review; no credit card numbers
Transaction dataquery_transactions, get_counterparty_infoDate-bounded; expensive queries rate-limited
Watchlistscheck_sanctions, check_pep_list, check_adverse_newsCan call third-party data providers (Refinitiv, LexisNexis)
Case managementget_prior_cases, create_case_note, draft_sar_narrativeWrite operations gated on analyst confirmation

Keep the server implementation small and boring. Each tool is a function that:

  1. Validates the input (date ranges, account IDs)
  2. Calls the internal API with a service-account credential
  3. Strips any fields not needed for the agent’s task
  4. Returns clean JSON

The MCP gateway layer

For enterprise deployments, add an MCP gateway between Claude Cowork and your in-house MCP servers. The gateway is the single security boundary where you enforce:

  • Authentication — which agents are allowed to call which tools
  • Audit logging — every tool call, with the agent session ID, is written to a compliance log (this itself may be a regulatory requirement)
  • Rate limiting and cost controls — prevent runaway agent loops from hammering production systems
  • PII masking — strip or hash personal identifiers before they enter model context if your data governance policy requires it

The gateway pattern also lets you swap the underlying model or the underlying data source independently. Your compliance team’s workflows don’t need to change when a new Claude model ships.

ClaudeCowork AgentMCP Gatewayauth · audit lograte limit · PII maskroute to backendCustomer & Txn MCPWatchlist MCPCase Mgmt MCPCore BankingRefinitiv / OFACCase System
MCP gateway architecture: Claude Cowork calls one gateway endpoint; the gateway enforces auth, audit logging, and rate limiting before routing to in-house MCP servers backed by internal APIs.

4. Evals: the prerequisite for production

Shipping a compliance agent without evals is not an option in a regulated environment. Before you deploy, you need quantitative evidence that the agent makes better (or at least equivalent) decisions to an analyst working unaided — at a reproducible rate, across a representative set of cases.

Building the eval dataset

Start with historical, closed alerts — cases where an analyst already made a disposition that turned out to be correct. Work with your compliance SMEs to label a set of 100-500 alerts with:

  • The correct disposition (close / escalate)
  • The key evidence that drove the decision
  • A brief rationale

This becomes your golden set. Treat it like a test suite: never use it for prompt iteration, only for final evaluation.

Metrics that matter in compliance

Generic accuracy (“did the agent get the right answer?”) is not enough. For AML triage, track at minimum:

MetricTarget directionWhy it matters
False-negative rate (missed escalations)Minimize ruthlesslyMissing a true SAR is a regulatory failure
False-positive rate (unnecessary escalations)Reduce, but never at the cost of false negativesSaves analyst time
Evidence coverageMaximizeDid the agent retrieve all the data points a human would check?
Hallucination rateZero toleranceAny fabricated transaction detail makes the summary legally inadmissible

The last two require a different eval approach than a binary correct/incorrect label. Use LLM-as-judge with a carefully written rubric, validated against expert human ratings: have the judge model score the agent’s summary on evidence completeness and factual grounding against the source data it retrieved.

Human-expert baseline

Before shipping, compare the agent’s performance on the golden set to a sample of analyst decisions made without AI assistance. This baseline serves two purposes: it gives you a deployment go / no-go criterion, and it gives you the documented evidence regulators may ask for.

5. Transferable patterns: beyond financial crime

The architecture described above — alert queue → agent triage → human disposition — is not specific to AML. The same pattern applies anywhere a rules engine generates more signals than humans can review. Here are three direct analogues:

Fraud operations

Card fraud and account takeover teams face identical alert-fatigue dynamics. The agent tools change (device fingerprinting APIs, velocity checks on card-not-present transactions, merchant risk scores) but the workflow is the same. The key difference: fraud decisions are often time-critical (seconds, not minutes), so latency budgets for tool calls are tighter.

Insider threat and security operations

SIEM systems generate thousands of alerts per day about anomalous employee behaviour or network activity. Tier-1 analysts spend most of their time on alerts that resolve with three lookups (employee tenure, normal working hours, peer group baseline). An agent can handle that retrieval in parallel across hundreds of alerts simultaneously and route only the genuinely ambiguous cases to a human analyst.

Content policy enforcement

Trust and safety teams reviewing reported content follow the same pattern: automated classifier generates a queue, human makes the final call, but the human needs context (user history, prior violations, policy interpretation) to decide efficiently. An agent can assemble that context and pre-classify cases by severity.

The transferable design decisions

Whatever your domain, these decisions carry over:

  1. Read-only tools by default. Write operations (filing a case, sending a report, taking action against an account) require explicit analyst confirmation. An agent that can take irreversible actions autonomously is an unacceptable risk in any regulated context.
  2. Cite every claim. The agent’s summary must link every factual assertion to the source data that supports it. This makes verification fast and supports audit trails.
  3. Explicit uncertainty. When the agent cannot find sufficient evidence to propose a disposition, it should say so explicitly — not guess. “I was unable to retrieve transaction history for this customer from the past 90 days due to an API timeout” is far more useful than a hedged close recommendation.
  4. Treat the MCP server as the compliance boundary. Do not let the model call internal APIs directly. Every data access goes through the MCP server, which logs, validates, and enforces data governance.

Check your understanding

5 questions · your answers are saved in this browser only

  1. 1. What is the primary reason AI agents are well-suited to AML alert triage?

  2. 2. Why should write operations (e.g. filing a SAR, closing a case) require explicit analyst confirmation rather than being executed autonomously by the agent?

  3. 3. What is the purpose of an MCP gateway in a compliance agent architecture?

  4. 4. Which metric should be minimised most aggressively when evaluating an AML triage agent?

  5. 5. Which of the following best describes how to handle the case where an agent cannot retrieve sufficient evidence to propose a disposition?

Build it yourself

Follow these exact steps to reproduce it yourself · estimated time: ~20 min

Prerequisites

  • Anthropic API key
  • Python 3.10+
  • A JSON file of sample alerts (see Step 1)

Step 1 — Create a sample alert dataset

For this exercise, create a file alerts.json with a few mock AML alerts. Each alert should have an alertId, a customerId, a ruleTriggered, and a transactionAmount:

[
  {
    "alertId": "ALT-001",
    "customerId": "CUST-9921",
    "ruleTriggered": "STRUCTURING_CASH",
    "transactionAmount": 9800,
    "transactionDate": "2026-06-01",
    "notes": "Third deposit under $10k in 5 days"
  },
  {
    "alertId": "ALT-002",
    "customerId": "CUST-4477",
    "ruleTriggered": "HIGH_VELOCITY_WIRES",
    "transactionAmount": 250000,
    "transactionDate": "2026-06-03",
    "notes": "Four outbound wires to new beneficiaries in 48h"
  }
]

Step 2 — Build stub MCP tools

Create compliance_mcp.py. For now, return realistic-looking mock data so you can iterate on the agent without a real database:

def get_customer_profile(customer_id: str) -> dict:
    """Return customer KYC profile."""
    return {
        "customerId": customer_id,
        "accountAge": "14 months",
        "riskRating": "medium",
        "averageMonthlyBalance": 22000,
        "priorAlerts": 0,
        "sanctionsMatch": False,
    }

def query_transactions(customer_id: str, days: int = 90) -> list[dict]:
    """Return recent transactions for a customer."""
    # Stub: return two mock transactions
    return [
        {"date": "2026-05-28", "amount": 9800, "type": "CASH_DEPOSIT"},
        {"date": "2026-06-01", "amount": 9800, "type": "CASH_DEPOSIT"},
    ]

def check_sanctions(customer_id: str) -> dict:
    return {"match": False, "listsChecked": ["OFAC_SDN", "EU_CONSOLIDATED"]}

Step 3 — Write the triage agent

import anthropic, json

client = anthropic.Anthropic()

TOOLS = [
    {
        "name": "get_customer_profile",
        "description": "Retrieve KYC profile and risk rating for a customer.",
        "input_schema": {
            "type": "object",
            "properties": {"customer_id": {"type": "string"}},
            "required": ["customer_id"],
        },
    },
    {
        "name": "query_transactions",
        "description": "Fetch the customer's recent transactions.",
        "input_schema": {
            "type": "object",
            "properties": {
                "customer_id": {"type": "string"},
                "days": {"type": "integer", "default": 90},
            },
            "required": ["customer_id"],
        },
    },
    {
        "name": "check_sanctions",
        "description": "Check whether the customer appears on any sanctions list.",
        "input_schema": {
            "type": "object",
            "properties": {"customer_id": {"type": "string"}},
            "required": ["customer_id"],
        },
    },
]

SYSTEM = """You are an AML triage assistant. For each alert you receive:
1. Retrieve the customer profile, recent transactions, and sanctions status using the available tools.
2. Synthesise the evidence into a concise summary (3-5 sentences).
3. Propose a disposition: CLOSE or ESCALATE, with a confidence level (low/medium/high).
4. List every data point you used to reach that conclusion.
If any tool call fails, state that explicitly rather than guessing.
Do not take any write actions — your output is for analyst review only."""

def dispatch_tool(name, inputs):
    from compliance_mcp import get_customer_profile, query_transactions, check_sanctions
    if name == "get_customer_profile":
        return get_customer_profile(inputs["customer_id"])
    if name == "query_transactions":
        return query_transactions(inputs["customer_id"], inputs.get("days", 90))
    if name == "check_sanctions":
        return check_sanctions(inputs["customer_id"])

def triage_alert(alert: dict) -> str:
    messages = [{"role": "user", "content": f"Triage this alert: {json.dumps(alert)}"}]
    while True:
        response = client.messages.create(
            model="claude-opus-4-5",
            max_tokens=1024,
            system=SYSTEM,
            tools=TOOLS,
            messages=messages,
        )
        if response.stop_reason == "end_turn":
            return next(b.text for b in response.content if hasattr(b, "text"))
        # Handle tool calls
        tool_results = []
        for block in response.content:
            if block.type == "tool_use":
                result = dispatch_tool(block.name, block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": json.dumps(result),
                })
        messages.append({"role": "assistant", "content": response.content})
        messages.append({"role": "user", "content": tool_results})

if __name__ == "__main__":
    with open("alerts.json") as f:
        alerts = json.load(f)
    for alert in alerts:
        print(f"\n=== {alert['alertId']} ===")
        print(triage_alert(alert))

Step 4 — Run and review

python triage_agent.py

Read each summary critically: does it cite the data it used? Is the confidence level calibrated? Does it acknowledge when data is missing? Refine the system prompt until the summaries are ones you’d actually trust as a starting point for analyst review.

Step 5 — Add a simple eval harness

Create eval.json with the correct dispositions for your mock alerts, then run the agent against it and report accuracy:

with open("eval.json") as f:
    golden = json.load(f)  # [{"alertId": "ALT-001", "correctDisposition": "CLOSE"}, ...]

correct = 0
for item in golden:
    alert = next(a for a in alerts if a["alertId"] == item["alertId"])
    summary = triage_alert(alert)
    predicted = "ESCALATE" if "ESCALATE" in summary else "CLOSE"
    if predicted == item["correctDisposition"]:
        correct += 1
    else:
        print(f"MISMATCH {item['alertId']}: predicted {predicted}, expected {item['correctDisposition']}")

print(f"\nAccuracy: {correct}/{len(golden)} ({100*correct//len(golden)}%)")

Related lessons

intermediate 🎬 Anthropic · ~34 min

Build AI Agents with Claude in Microsoft Azure AI Foundry

A hands-on guide to provisioning Claude in Microsoft Azure AI Foundry, connecting it to MCP servers via Claude Code, and deploying enterprise-grade AI agents — from zero to working code.

#agents #enterprise #productivity