How to Build a transaction monitoring Agent Using LangChain in Python for wealth management
A transaction monitoring agent in wealth management watches client activity, flags suspicious patterns, and routes cases for review before they become compliance incidents. It matters because the business is not just detecting fraud or AML risk; it also has to preserve client trust, support auditability, and respect jurisdiction-specific rules around data handling and escalation.
Architecture
- •
Transaction ingestion layer
- •Pulls trades, transfers, deposits, withdrawals, and account events from internal systems.
- •Normalizes records into a consistent schema for downstream analysis.
- •
Policy and risk rules engine
- •Encodes wealth-management-specific thresholds like unusual transfer size, velocity spikes, beneficiary changes, or cross-border activity.
- •Combines deterministic rules with LLM-based interpretation.
- •
LangChain agent orchestration
- •Uses
ChatOpenAI, tools, and a structured output parser to decide whether a case needs escalation. - •Keeps the decision path explicit for audit review.
- •Uses
- •
Case enrichment tools
- •Fetches KYC profile data, account history, relationship manager notes, and prior alerts.
- •Gives the model context before it classifies risk.
- •
Audit logging layer
- •Stores prompts, tool calls, outputs, timestamps, and rule triggers.
- •Supports compliance review and model governance.
- •
Human review queue
- •Sends high-risk or ambiguous cases to operations/compliance analysts.
- •Prevents the agent from making final decisions on regulated outcomes.
Implementation
1. Define the transaction schema and enrichment tools
Start by modeling the transaction data and exposing only the minimum tools the agent needs. In wealth management, that usually means profile lookup, historical activity lookup, and alert creation.
from typing import Literal
from pydantic import BaseModel, Field
from langchain_core.tools import tool
class Transaction(BaseModel):
account_id: str
client_id: str
amount: float
currency: str
direction: Literal["inbound", "outbound"]
country: str
channel: Literal["wire", "ach", "internal_transfer", "check"]
timestamp: str
@tool
def get_client_profile(client_id: str) -> dict:
"""Fetch KYC/AML profile for a client."""
return {
"client_id": client_id,
"risk_rating": "medium",
"pep_flag": False,
"country_of_residence": "US",
"source_of_wealth_verified": True,
}
@tool
def get_recent_activity(account_id: str) -> dict:
"""Fetch recent transaction summary."""
return {
"account_id": account_id,
"avg_outbound_30d": 12000,
"max_outbound_30d": 50000,
"num_outbound_7d": 4,
}
@tool
def create_case(payload: dict) -> str:
"""Create an alert case in the case management system."""
return f"case_created:{payload['account_id']}:{payload['risk_level']}"
2. Build a structured decision model
Use ChatOpenAI with structured output so the agent returns predictable fields. For monitoring systems, free-form text is a liability; you want machine-readable decisions every time.
from pydantic import BaseModel
from langchain_openai import ChatOpenAI
class MonitoringDecision(BaseModel):
risk_level: Literal["low", "medium", "high"]
escalate: bool
reason: str
rule_hits: list[str]
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0).with_structured_output(MonitoringDecision)
3. Create the LangChain agent with tools
Use create_tool_calling_agent and AgentExecutor so the model can call enrichment tools before producing a final decision. This pattern works well when you need traceable tool usage for audits.
from langchain import hub
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.messages import HumanMessage
prompt = hub.pull("hwchase17/openai-tools-agent")
tools = [get_client_profile, get_recent_activity, create_case]
agent = create_tool_calling_agent(llm=llm.bind_tools(tools), tools=tools, prompt=prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=False)
transaction_text = """
Account A123 sent outbound wire transfer of USD 250000 to Germany.
Client has medium risk rating. Transfer is outside normal pattern.
"""
result = executor.invoke({
"input": transaction_text,
})
print(result)
4. Add post-processing for escalation and audit logging
The agent should not directly decide compliance outcomes without guardrails. Wrap its output with deterministic checks and log everything needed for review.
import json
from datetime import datetime
def monitor_transaction(txn: Transaction) -> dict:
profile = get_client_profile.invoke({"client_id": txn.client_id})
activity = get_recent_activity.invoke({"account_id": txn.account_id})
prompt = f"""
Transaction:
{txn.model_dump_json()}
Profile:
{json.dumps(profile)}
Activity:
{json.dumps(activity)}
Decide whether this needs escalation for compliance review.
"""
decision = llm.invoke(prompt)
audit_record = {
"timestamp": datetime.utcnow().isoformat(),
"transaction": txn.model_dump(),
"profile": profile,
"activity": activity,
"decision": decision.model_dump(),
}
if decision.escalate:
case_id = create_case.invoke({
"account_id": txn.account_id,
"risk_level": decision.risk_level,
"reason": decision.reason,
"rule_hits": decision.rule_hits,
})
audit_record["case_id"] = case_id
return audit_record
Production Considerations
- •
Data residency
- •Keep client data in-region if your wealth platform operates across jurisdictions.
- •Don’t send raw PII to external model endpoints unless legal review has approved that flow.
- •
Auditability
- •Persist prompts, tool invocations, outputs, model version, and timestamps.
- •Regulators will care about why an alert was generated more than whether the model was “accurate.”
- •
Guardrails
- •Hard-code deterministic thresholds for obvious violations like sanctions hits or prohibited jurisdictions.
- •Use the LLM only for contextual classification and narrative summarization.
- •
Monitoring
- •Track false positives by client segment, region, advisor team, and transaction type.
- •Wealth management clients often have legitimate large transfers; poor tuning will drown analysts in noise.
Common Pitfalls
- •
Letting the LLM make final compliance decisions
- •Avoid this by making the model produce a recommendation only.
- •Final escalation should be based on policy rules plus human review where required.
- •
Sending too much client data into prompts
- •Avoid dumping full account histories or unredacted notes into context.
- •Pass only fields needed for the specific alert decision.
- •
Skipping structured outputs
- •Free-form text breaks downstream automation fast.
- •Use
with_structured_output()or Pydantic-based parsing so every response has predictable fields.
- •
Ignoring regional compliance differences
- •A threshold that works in one jurisdiction may be wrong in another.
- •Encode country-specific policies separately and keep them versioned alongside your prompts.
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