How to Build a fraud detection Agent Using LangGraph in Python for banking
A fraud detection agent in banking watches transaction streams, scores risk, decides when to escalate, and produces an audit trail for every decision. It matters because false negatives cost money and trust, while false positives create customer friction, ops load, and regulatory noise.
Architecture
- •
Transaction intake node
Receives a payment event, card authorization, ACH transfer, or account action with normalized fields like amount, merchant, device fingerprint, geolocation, and customer history. - •
Risk enrichment node
Pulls internal signals such as velocity checks, account age, prior chargebacks, KYC tier, sanctions flags, and recent login anomalies. - •
Decision node
Applies deterministic policy thresholds first, then optionally uses an LLM only for explanation or case routing. For banking, the actual block/allow/escalate decision should be explicit and auditable. - •
Case creation node
Opens a fraud case when the score crosses a threshold. This is where you attach evidence for analysts and compliance review. - •
Audit and logging node
Writes the full state transition: input features, score breakdown, policy version, model version, and final action. This is non-negotiable in regulated environments. - •
Human-in-the-loop checkpoint
Routes ambiguous cases to an analyst queue instead of auto-declining high-value customers or suspicious-but-unconfirmed events.
Implementation
1) Define the graph state
Use a typed state object so each step has a clear contract. Keep the state small and deterministic; don’t pass raw payloads around if you can normalize early.
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
class FraudState(TypedDict):
transaction_id: str
amount: float
merchant: str
country: str
velocity_1h: int
kyc_risk: int
chargeback_rate: float
risk_score: float
decision: Literal["allow", "review", "block"]
audit_log: list[str]
2) Build deterministic fraud nodes
This pattern works well in banking because it is explainable. The score is a weighted function of business signals; the decision is based on thresholds you can version-control and audit.
def enrich_risk(state: FraudState) -> FraudState:
score = 0.0
if state["amount"] > 5000:
score += 25
if state["velocity_1h"] > 5:
score += 20
if state["country"] not in {"US", "GB", "DE", "CA"}:
score += 15
if state["kyc_risk"] >= 7:
score += 20
if state["chargeback_rate"] > 0.03:
score += 20
return {
**state,
"risk_score": min(score, 100.0),
"audit_log": state.get("audit_log", []) + [f"enriched risk_score={min(score, 100.0)}"]
}
def decide_action(state: FraudState) -> FraudState:
score = state["risk_score"]
if score >= 70:
decision = "block"
elif score >= 40:
decision = "review"
else:
decision = "allow"
return {
**state,
"decision": decision,
"audit_log": state["audit_log"] + [f"decision={decision}"]
}
3) Add conditional routing with LangGraph
LangGraph’s StateGraph lets you branch based on computed risk. Use add_conditional_edges() so the flow stays readable and easy to test.
def route_by_decision(state: FraudState):
return state["decision"]
def create_case(state: FraudState) -> FraudState:
return {
**state,
"audit_log": state["audit_log"] + [f"case_created for {state['transaction_id']}"]
}
def finalize(state: FraudState) -> FraudState:
return {
**state,
"audit_log": state["audit_log"] + ["finalized"]
}
graph = StateGraph(FraudState)
graph.add_node("enrich_risk", enrich_risk)
graph.add_node("decide_action", decide_action)
graph.add_node("create_case", create_case)
graph.add_node("finalize", finalize)
graph.add_edge(START, "enrich_risk")
graph.add_edge("enrich_risk", "decide_action")
graph.add_conditional_edges(
"decide_action",
route_by_decision,
{
"allow": "finalize",
"review": "create_case",
"block": "create_case",
},
)
graph.add_edge("create_case", END)
graph.add_edge("finalize", END)
app = graph.compile()
4) Run the agent on a transaction event
This is the part you wire into your payment service or stream processor. The output should be persisted with the same trace ID used by your transaction ledger.
initial_state: FraudState = {
"transaction_id": "txn_12345",
"amount": 8200.0,
"merchant": "electronics_store_91",
"country": "NG",
"velocity_1h": 8,
"kyc_risk": 6,
"chargeback_rate": 0.05,
"risk_score": 0.0,
"decision": "allow",
"audit_log": [],
}
result = app.invoke(initial_state)
print(result["decision"])
print(result["risk_score"])
print(result["audit_log"])
Production Considerations
- •
Keep PII inside your boundary
Don’t send raw customer data to external LLM APIs unless your legal team has approved it and residency requirements are satisfied. In many banks, the agent should run inside VPC/private cloud with encrypted storage and strict egress controls. - •
Version every policy and threshold
Store model version, rules version, and graph version with each decision. When compliance asks why a transfer was blocked, you need to reproduce the exact path that produced the result. - •
Add human review for borderline scores
Auto-blocking everything above a threshold will burn good customers. Route medium-risk cases to analysts and keep analyst decisions as training data for future tuning. - •
Instrument for drift and false positives
Track approval rate by segment, fraud capture rate, analyst overturn rate, latency per node, and queue depth. A fraud agent that is accurate but slow will still hurt authorization rates.
Common Pitfalls
- •
Using an LLM as the primary decision engine
Don’t let a prompt decide whether to block a card transaction. Use deterministic rules or trained classifiers for the actual action; reserve LLMs for summarization or analyst notes. - •
Skipping auditability
If your graph doesn’t record inputs, thresholds applied, branch taken, and final output, it won’t survive bank review. Always persist structured audit logs alongside the event ID. - •
Ignoring residency and access control
A fraud workflow often touches highly sensitive customer data. Keep execution local to approved regions, encrypt at rest/in transit, and restrict who can inspect traces or replay states.
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