How to Build a fraud detection Agent Using AutoGen in Python for payments
A fraud detection agent for payments watches transaction events, reasons over risk signals, and decides whether to approve, step-up, hold, or escalate a payment for review. For card-not-present flows, instant transfers, and wallet payouts, that matters because the wrong decision costs real money: chargebacks, false declines, manual review load, and compliance exposure.
Architecture
- •
Transaction intake service
- •Receives payment events from your gateway, PSP, or internal ledger.
- •Normalizes fields like amount, currency, merchant_id, customer_id, device_id, IP, and velocity counters.
- •
Risk feature builder
- •Computes signals the agent can reason over:
- •amount anomalies
- •geo mismatch
- •device fingerprint changes
- •velocity spikes
- •beneficiary novelty
- •Keep this deterministic and outside the LLM.
- •Computes signals the agent can reason over:
- •
AutoGen risk analyst agent
- •Uses
AssistantAgentto interpret the structured transaction context. - •Produces a decision plus rationale in a strict schema.
- •Uses
- •
Policy / approval agent
- •Enforces payment rules such as:
- •block high-risk transfers above threshold
- •require step-up auth for medium risk
- •route edge cases to human review
- •This is where compliance rules live.
- •Enforces payment rules such as:
- •
Audit logger
- •Stores input features, model output, final action, timestamps, and reviewer overrides.
- •Required for disputes, internal controls, and regulatory traceability.
- •
Human review queue
- •Handles uncertain cases.
- •Prevents the agent from being the final authority on high-value or regulated flows.
Implementation
1) Install AutoGen and define a strict transaction schema
Use a structured payload so the agent sees consistent inputs. In payments systems, free-form text is how you get bad decisions and bad audits.
from pydantic import BaseModel, Field
from typing import Literal
class TransactionRiskInput(BaseModel):
transaction_id: str
amount: float = Field(gt=0)
currency: str
merchant_id: str
customer_id: str
country: str
ip_country: str
device_trust_score: float = Field(ge=0.0, le=1.0)
velocity_5m: int = Field(ge=0)
account_age_days: int = Field(ge=0)
payment_method: Literal["card", "bank_transfer", "wallet", "crypto"]
2) Create an AutoGen assistant that returns a decision in JSON
This uses autogen.AssistantAgent and a plain UserProxyAgent. The assistant should only analyze the provided data and return a compact decision object.
import autogen
llm_config = {
"config_list": [
{
"model": "gpt-4o-mini",
"api_key": os.environ["OPENAI_API_KEY"],
}
],
"temperature": 0,
}
fraud_analyst = autogen.AssistantAgent(
name="fraud_analyst",
llm_config=llm_config,
system_message=(
"You are a payments fraud analyst. "
"Given structured transaction data, return JSON with keys: "
"decision (approve|step_up|hold|block), risk_score (0-100), "
"reason_codes (array of short strings), and summary. "
"Do not invent missing fields."
),
)
user_proxy = autogen.UserProxyAgent(
name="risk_system",
human_input_mode="NEVER",
)
3) Run the analysis and parse the result into an enforcement layer
The key pattern is: LLM analyzes, deterministic code enforces. Do not let the model directly move money.
import json
import os
def assess_transaction(txn: TransactionRiskInput) -> dict:
prompt = f"""
Analyze this payment transaction and return JSON only:
{txn.model_dump_json(indent=2)}
"""
user_proxy.initiate_chat(
fraud_analyst,
message=prompt,
clear_history=True,
max_turns=1,
)
def enforce_policy(risk_result: dict) -> str:
score = risk_result["risk_score"]
decision = risk_result["decision"]
if score >= 85:
return "block"
if score >= 60:
return "hold"
if decision == "step_up":
return "step_up"
return "approve"
txn = TransactionRiskInput(
transaction_id="tx_10001",
amount=1250.00,
currency="USD",
merchant_id="m_7788",
customer_id="c_4421",
country="US",
ip_country="NG",
device_trust_score=0.18,
velocity_5m=7,
)
4) Add audit logging before any downstream action
For payments, every decision needs an evidence trail. Store both the model output and the policy override so you can explain why a transfer was blocked or approved.
from datetime import datetime
def audit_event(txn: TransactionRiskInput, analysis_text: str, final_action: str) -> dict:
record = {
"timestamp": datetime.utcnow().isoformat(),
"transaction_id": txn.transaction_id,
"input": txn.model_dump(),
"analysis_text": analysis_text,
"final_action": final_action,
"model_name": llm_config["config_list"][0]["model"],
"policy_version": "payments-fraud-v1",
}
# Example usage would persist `record` to your audit store.
Production Considerations
- •
Keep sensitive payment data out of prompts
- •Mask PANs, tokenize account numbers, and avoid raw PII where possible.
- •Use feature IDs or derived signals instead of full customer profiles.
- •
Enforce data residency
- •If transactions must stay in-region for regulatory reasons, route model calls through region-specific infrastructure.
- •Do not send EU payment data to an unauthorized region just because the API is available there.
- •
Build hard guardrails around actions
- •The agent should recommend; your policy engine should decide.
- •High-value transfers should require deterministic thresholds plus human approval for edge cases.
- •
Monitor fraud ops metrics
- •Track false positives, false negatives, chargeback rate, manual review rate, latency per decision.
- •Split metrics by corridor, payment method, issuer country, merchant category code, and customer segment.
Common Pitfalls
- •
Letting the LLM make the final payment decision
- •Mistake: using model output directly as
approveorblock. - •Avoid it by placing a deterministic policy layer between analysis and execution.
- •Mistake: using model output directly as
- •
Sending raw cardholder or bank data into prompts
- •Mistake: passing PANs, CVVs, IBANs, or full addresses to the model.
- •Avoid it by tokenizing sensitive fields and only sending derived features needed for reasoning.
- •
Ignoring auditability and replay
- •Mistake: storing only the final label with no evidence trail.
- •Avoid it by logging input features, model output text or structured JSON, policy version, and reviewer overrides.
- •
Treating all corridors the same
- •Mistake: one global threshold for every market.
- •Avoid it by tuning policies per region because fraud patterns differ across card-present vs card-not-present flows, domestic vs cross-border payments, and regulated vs unregulated rails.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
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