How to Build a fraud detection Agent Using AutoGen in Python for banking

By Cyprian AaronsUpdated 2026-04-21
fraud-detectionautogenpythonbanking

A fraud detection agent in banking does one job well: it reviews transaction activity, asks targeted questions when something looks off, and escalates only the cases that need human review. That matters because banks need fast triage without turning every alert into an analyst ticket, while still keeping a clean audit trail for compliance and model governance.

Architecture

  • Transaction intake layer

    • Pulls events from core banking, card processing, or streaming queues.
    • Normalizes fields like amount, merchant, country, device fingerprint, and customer history.
  • Risk scoring tool

    • A deterministic Python function or ML model wrapper that assigns a fraud score.
    • Keeps the agent grounded in bank-approved rules and features.
  • AutoGen assistant agent

    • Uses autogen.AssistantAgent to reason over the score, transaction context, and policy rules.
    • Produces a decision: approve, hold, or escalate.
  • Human reviewer agent

    • Uses autogen.UserProxyAgent to represent the analyst or operations queue.
    • Handles manual approval for ambiguous or high-risk cases.
  • Audit logger

    • Persists prompts, tool outputs, decisions, timestamps, and reviewer actions.
    • Required for investigations, model validation, and regulatory traceability.
  • Policy guardrails

    • Enforces data minimization, residency constraints, and prohibited actions.
    • Prevents the agent from exposing PII or making unsupported decisions.

Implementation

1) Install AutoGen and define a bank-safe transaction schema

Use a strict schema before the LLM sees anything. In banking systems, the agent should never receive raw dumps of customer records when a compact feature set is enough.

pip install pyautogen pydantic
from pydantic import BaseModel, Field
from typing import Literal

class Transaction(BaseModel):
    transaction_id: str
    customer_id: str
    amount: float = Field(gt=0)
    country: str
    merchant_category: str
    device_trust_score: float = Field(ge=0.0, le=1.0)
    velocity_1h: int = Field(ge=0)
    chargeback_history_90d: int = Field(ge=0)
    risk_band: Literal["low", "medium", "high"]

2) Build a deterministic scoring tool

Keep the first pass explainable. The agent should consume a score produced by bank-approved logic or an internal model endpoint.

def fraud_risk_score(txn: Transaction) -> dict:
    score = 0

    if txn.amount > 5000:
        score += 25
    if txn.country not in {"US", "GB", "DE", "FR"}:
        score += 20
    if txn.device_trust_score < 0.4:
        score += 20
    if txn.velocity_1h > 5:
        score += 15
    if txn.chargeback_history_90d > 2:
        score += 25
    if txn.risk_band == "high":
        score += 15

    decision = "approve"
    if score >= 60:
        decision = "escalate"
    elif score >= 35:
        decision = "hold"

    return {
        "score": min(score, 100),
        "decision": decision,
        "reasons": [
            k for k, v in {
                "high_amount": txn.amount > 5000,
                "foreign_country": txn.country not in {"US", "GB", "DE", "FR"},
                "low_device_trust": txn.device_trust_score < 0.4,
                "high_velocity": txn.velocity_1h > 5,
                "recent_chargebacks": txn.chargeback_history_90d > 2,
                "high_risk_band": txn.risk_band == "high",
            }.items() if v
        ]
    }

3) Wire AutoGen agents with a human-in-the-loop review path

This is the core pattern: one assistant proposes action based on structured evidence; one user proxy represents the analyst who can approve escalation decisions. Use llm_config with your approved model endpoint and keep prompts narrow.

import autogen

llm_config = {
    "model": "gpt-4o-mini",
    "api_key": os.environ["OPENAI_API_KEY"],
}

fraud_agent = autogen.AssistantAgent(
    name="fraud_agent",
    llm_config=llm_config,
    system_message=(
        "You are a banking fraud triage agent. "
        "Only use provided transaction features and risk output. "
        "Return concise decisions: approve, hold, or escalate. "
        "Never request PII or suggest bypassing controls."
    ),
)

reviewer = autogen.UserProxyAgent(
    name="fraud_reviewer",
    human_input_mode="ALWAYS",
)

txn = Transaction(
    transaction_id="tx_10042",
    customer_id="cust_7781",
    amount=8420.50,
    country="NG",
    merchant_category="electronics",
    device_trust_score=0.31,
    velocity_1h=8,
    chargeback_history_90d=3,
    risk_band="high",
)

risk = fraud_risk_score(txn)

message = f"""
Transaction:
{txn.model_dump()}

Risk output:
{risk}

Task:
Decide whether to approve, hold for review, or escalate to a human investigator.
Include short reasons tied to the provided features only.
"""

reviewer.initiate_chat(fraud_agent, message=message)

4) Add audit logging before any downstream action

In banking you need to reconstruct why a transaction was blocked or released. Log inputs, outputs, model version, reviewer identity, and timestamps to an immutable store.

import json
from datetime import datetime

def audit_event(txn: Transaction, risk: dict, agent_reply: str):
    record = {
        "timestamp_utc": datetime.utcnow().isoformat(),
        "transaction_id": txn.transaction_id,
        "customer_id": txn.customer_id,
        "risk_score": risk["score"],
        "risk_decision": risk["decision"],
        "risk_reasons": risk["reasons"],
        "agent_reply": agent_reply,
        "model_name": llm_config["model"],
    }
    print(json.dumps(record))

# Example placeholder after chat response collection in your app layer.

Production Considerations

  • Deploy inside your bank’s approved environment

    • Keep inference in-region for data residency requirements.
    • If you use hosted models, verify contractual controls for retention and training exclusion.
  • Treat every decision as auditable

    • Store prompt text sparingly but store structured inputs, tool outputs, final action, and reviewer override.
    • Tie each event to a case ID for SAR/AML investigation workflows where applicable.
  • Use guardrails around PII

    • Redact account numbers, card PANs, addresses, and free-text notes before sending anything to the LLM.
    • Prefer feature vectors over raw customer records.
  • Monitor false positives and reviewer drift

    • Track hold/escalate rates by segment: geography, channel, merchant category.
    • If analysts override too often on specific patterns، update rules or retrain the scoring layer.

Common Pitfalls

  • Letting the LLM make unsupported decisions

    • Don’t ask it to invent fraud labels from raw text alone.
    • Feed it deterministic scores plus bounded context so it explains rather than guesses.
  • Passing too much customer data into prompts

    • This creates privacy risk and bloats token usage.
    • Send only features needed for triage; mask identifiers unless they are required for case routing.
  • Skipping auditability

    • If you cannot explain why a payment was blocked six months later، you do not have a production banking workflow.
    • Log input features، tool outputs، model version، prompt template version، and human override outcome.
  • Ignoring regional compliance constraints

    • Some jurisdictions restrict cross-border processing of financial data.
    • Pin workloads to approved regions and validate vendor terms against your bank’s data residency policy.

Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides