How to Build a transaction monitoring Agent Using LlamaIndex in Python for lending

By Cyprian AaronsUpdated 2026-04-21
transaction-monitoringllamaindexpythonlending

A transaction monitoring agent for lending watches borrower and account activity, scores suspicious patterns, and explains why a case needs review. In lending, that matters because you are not just catching fraud; you are protecting underwriting quality, meeting compliance obligations, and creating an audit trail that can stand up to internal review or regulator scrutiny.

Architecture

  • Transaction ingestion layer
    • Pulls payments, disbursements, chargebacks, ACH returns, and account events from your core banking or loan servicing system.
  • Feature normalization layer
    • Converts raw transactions into a consistent schema: amount, velocity, merchant category, repayment status, delinquency flags, geography, and device metadata.
  • LlamaIndex retrieval layer
    • Uses VectorStoreIndex plus QueryEngine to retrieve policy docs, lending rules, prior case notes, and historical alerts.
  • Decisioning agent
    • Uses an LLM-backed ReActAgent or query engine to classify risk, summarize evidence, and recommend escalation.
  • Case management output
    • Writes structured alerts into your workflow system with reason codes and supporting evidence.
  • Audit and controls
    • Stores prompts, retrieved context, model outputs, and final decisions for traceability.

Implementation

1) Define the transaction schema and load policy context

You want the agent to reason over both live transaction data and your lending policies. Keep the transaction payload structured and keep policy documents indexed separately.

from dataclasses import dataclass
from typing import List
from llama_index.core import VectorStoreIndex, Document
from llama_index.core.settings import Settings
from llama_index.llms.openai import OpenAI

@dataclass
class Transaction:
    txn_id: str
    borrower_id: str
    amount: float
    txn_type: str
    channel: str
    country: str
    days_past_due: int
    is_chargeback: bool = False

policy_docs = [
    Document(text="Flag borrowers with >3 failed repayment attempts in 30 days."),
    Document(text="Escalate if cash advance behavior appears after disbursement."),
    Document(text="Review cross-border repayments against residency restrictions."),
]

Settings.llm = OpenAI(model="gpt-4o-mini")
policy_index = VectorStoreIndex.from_documents(policy_docs)
policy_query_engine = policy_index.as_query_engine(similarity_top_k=2)

2) Build a risk assessment function with retrieval-backed reasoning

The pattern here is simple: retrieve the relevant lending rules first, then ask the model to assess a single transaction against those rules. That keeps outputs grounded in your policy corpus.

def assess_transaction(txn: Transaction) -> str:
    query = (
        f"Evaluate this lending transaction for monitoring risk:\n"
        f"txn_id={txn.txn_id}\n"
        f"borrower_id={txn.borrower_id}\n"
        f"amount={txn.amount}\n"
        f"txn_type={txn.txn_type}\n"
        f"channel={txn.channel}\n"
        f"country={txn.country}\n"
        f"days_past_due={txn.days_past_due}\n"
        f"is_chargeback={txn.is_chargeback}\n"
        "Return a concise risk assessment with reason codes."
    )

    policy_context = policy_query_engine.query(
        "What lending monitoring rules apply to repayment anomalies, chargebacks, "
        "cross-border activity, and post-disbursement cash movement?"
    )

    prompt = f"""
Policy context:
{policy_context}

Transaction:
{query}

Output format:
- risk_level: low|medium|high
- reason_codes: comma-separated list
- summary: one short paragraph
"""
    return Settings.llm.complete(prompt).text


sample_txn = Transaction(
    txn_id="TXN-10021",
    borrower_id="B-4421",
    amount=1250.0,
    txn_type="repayment",
    channel="ach",
    country="KE",
    days_past_due=18,
)

print(assess_transaction(sample_txn))

3) Turn it into an agent that can answer analyst questions

If your ops team wants interactive investigation, wrap the retrieval engine in a ReActAgent. This gives analysts a natural-language interface while still grounding answers in indexed policy material.

from llama_index.core.agent import ReActAgent

tools = [
    policy_query_engine.as_tool(
        name="lending_policy_lookup",
        description="Look up lending monitoring rules and escalation criteria."
    )
]

agent = ReActAgent.from_tools(
    tools=tools,
    llm=Settings.llm,
    verbose=True,
)

response = agent.chat(
    "For a borrower who made three small repayments followed by a large cash withdrawal "
    "two days after disbursement, what should the monitoring team do?"
)

print(response)

4) Emit structured alerts for downstream case management

Do not send free-form text into your case system. Convert the model output into a strict structure so you can route alerts consistently.

import json

def create_alert(txn: Transaction) -> dict:
    result_text = assess_transaction(txn)

    alert = {
        "transaction_id": txn.txn_id,
        "borrower_id": txn.borrower_id,
        "risk_assessment": result_text,
        "source": "llamaindex-monitoring-agent",
        "status": "open",
    }
    return alert


alert = create_alert(sample_txn)
print(json.dumps(alert, indent=2))

Production Considerations

  • Deploy in-region for data residency
    • Lending data often has residency constraints. Keep embeddings, vector stores, and LLM endpoints in approved regions.
  • Log every decision path
    • Store retrieved policy chunks from QueryEngine, prompt text, model output, timestamp, and analyst override. That is your audit trail.
  • Add guardrails around adverse actions
    • The agent should recommend review or escalation only. It should not make final credit decisions without human approval.
  • Monitor drift on transaction patterns
    • Repayment behavior changes by geography and product type. Track false positives by cohort so you do not bury analysts in noise.

Common Pitfalls

  1. Using raw LLM output as the source of truth

    • Avoid this by forcing structured fields like risk_level, reason_codes, and summary. Parse or validate before writing to your case system.
  2. Mixing policy docs with noisy transactional history in one index

    • Keep policies separate from event data. Policy retrieval should be stable; transaction facts should come from your operational database or feature store.
  3. Ignoring explainability requirements

    • Every alert needs a clear reason chain: which rule triggered, which facts were observed, and what evidence was retrieved. If you cannot explain it to compliance or audit, it is not production-ready.
  4. Letting the agent see unnecessary personal data

    • Minimize PII before indexing or prompting. Mask account numbers, addresses, and identifiers unless they are required for the decision path.

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