How to Build a transaction monitoring Agent Using LlamaIndex in TypeScript for retail banking

By Cyprian AaronsUpdated 2026-04-21
transaction-monitoringllamaindextypescriptretail-banking

A transaction monitoring agent in retail banking watches customer activity, scores transactions for suspicious patterns, and prepares analyst-ready summaries for review. It matters because you need to catch fraud, mule activity, structuring, and unusual behavior early without flooding compliance teams with false positives.

Architecture

  • Transaction ingestion layer

    • Pulls card, ACH, wire, and internal transfer events from your core banking or streaming system.
    • Normalizes fields like amount, merchant, channel, counterparty, timestamp, and customer profile.
  • Risk feature builder

    • Converts raw transactions into features the agent can reason over:
      • velocity counts
      • amount spikes
      • geolocation mismatch
      • new beneficiary usage
      • cash-like merchant categories
  • LlamaIndex retrieval layer

    • Stores policy docs, AML typologies, SAR guidance, and internal playbooks.
    • Uses VectorStoreIndex and asRetriever() to fetch relevant policy context for each alert.
  • Agent reasoning layer

    • Uses a ReActAgent or tool-driven workflow to combine transaction data with retrieved policy context.
    • Produces a structured assessment: risk level, rationale, and next action.
  • Case management output

    • Writes the result into your alert queue or case system.
    • Keeps an audit trail with input features, retrieved documents, model output, and final disposition.
  • Controls and governance

    • Enforces PII redaction, data residency boundaries, approval gates, and human review for adverse decisions.

Implementation

1. Install dependencies and define your document corpus

For a banking use case, keep policy content separate from customer data. The agent should retrieve AML rules, fraud typologies, and internal procedures from approved documents only.

npm install llamaindex zod
import {
  Document,
  VectorStoreIndex,
  Settings,
  OpenAIEmbedding,
} from "llamaindex";

Settings.embedModel = new OpenAIEmbedding({
  model: "text-embedding-3-small",
});

const policyDocs = [
  new Document({
    text: `
      Retail banking AML policy:
      - Escalate if there are rapid transfers across multiple beneficiaries.
      - Escalate if cash-like transactions exceed normal customer behavior.
      - Review transactions that show structuring below reporting thresholds.
      - Require analyst review before any SAR recommendation.
    `,
    metadata: { source: "aml_policy_v3", jurisdiction: "US" },
  }),
  new Document({
    text: `
      Fraud typologies:
      - Account takeover often shows new device + unusual payee + high-value transfer.
      - Mule activity often shows inbound funds followed by quick outbound movement.
      - Card testing often shows many small authorizations in a short time window.
    `,
    metadata: { source: "fraud_typologies_2025", jurisdiction: "US" },
  }),
];

const index = await VectorStoreIndex.fromDocuments(policyDocs);
const retriever = index.asRetriever({ similarityTopK: 3 });

2. Build a transaction risk summarizer

Do not send raw full account histories into the model. Precompute a compact event summary with only the fields needed for review.

type TransactionEvent = {
  transactionId: string;
  customerId: string;
  amount: number;
  currency: string;
  channel: "ACH" | "WIRE" | "CARD" | "INTERNAL";
  merchantCategory?: string;
  beneficiaryCount24h: number;
  newBeneficiaryUsed: boolean;
  crossBorder: boolean;
  velocityCount1h: number;
};

function buildRiskSummary(txn: TransactionEvent) {
  return [
    `transactionId=${txn.transactionId}`,
    `customerId=${txn.customerId}`,
    `amount=${txn.amount} ${txn.currency}`,
    `channel=${txn.channel}`,
    `merchantCategory=${txn.merchantCategory ?? "n/a"}`,
    `beneficiaryCount24h=${txn.beneficiaryCount24h}`,
    `newBeneficiaryUsed=${txn.newBeneficiaryUsed}`,
    `crossBorder=${txn.crossBorder}`,
    `velocityCount1h=${txn.velocityCount1h}`,
  ].join("\n");
}

3. Create an agent that retrieves policy context and writes an analyst note

This pattern uses LlamaIndex’s actual ReActAgent API with a retrieval tool. The agent reads the transaction summary plus relevant policy snippets and produces a concise alert note.

import {
  OpenAI,
  ReActAgent,
} from "llamaindex";

const llm = new OpenAI({
  model: "gpt-4o-mini",
});

const retrievePolicyTool = {
  type: "function" as const,
  function: {
    name: "retrieve_policy_context",
    description:
      "Retrieve AML/fraud policy context relevant to a retail banking transaction.",
    parameters: {
      type: "object",
      properties: {
        query: { type: "string" },
      },
      required: ["query"],
      additionalProperties: false,
    },
  },
};

const agent = new ReActAgent({
  llm,
  tools: [
    {
      ...retrievePolicyTool,
      call: async ({ query }: { query?: string }) => {
        const nodes = await retriever.retrieve(query ?? "");
        return nodes.map((n) => n.node.getContent()).join("\n---\n");
      },
    } as any,
  ],
});

export async function scoreTransaction(txn: TransactionEvent) {
  const summary = buildRiskSummary(txn);

  const prompt = `
You are assisting a retail banking financial crime analyst.

Task:
1. Assess whether this transaction is suspicious based on the provided summary.
2. Retrieve relevant AML/fraud policy context before answering.
3. Return a short analyst note with:
   - risk_level
   - key_factors
   - recommended_action

Transaction summary:
${summary}

Important constraints:
- Do not make final SAR decisions.
- Do not mention protected attributes.
- Keep output suitable for audit logging.
`;

  const response = await agent.chat(prompt);
  return response.message.content;
}

Step through the flow

  1. The transaction event is normalized into a compact summary.
  2. The agent retrieves relevant AML or fraud guidance using retrieve_policy_context.
  3. The model combines live event features with approved policy text.
  4. The output becomes an analyst note for case management or queue triage.

Production Considerations

  • Data residency

    • Keep embeddings, vector stores, and LLM endpoints in-region when regulations require it.
    • For EU or country-specific deployments, do not route customer data to cross-border hosted services unless legal approval exists.
  • Auditability

    • Log the exact transaction summary, retrieved document IDs, prompt version, model version, and final output.
    • Store immutable traces so compliance can reconstruct why an alert was generated.
  • Guardrails

    • Redact PII before prompting where possible.
    • Block unsupported outputs like “close case automatically” or “file SAR now” unless a human approves it.
  • Monitoring

    • Track precision/recall on confirmed cases, analyst override rate, retrieval quality, and latency per alert.
    • Watch for drift in typologies; fraud patterns change faster than most models do.

Common Pitfalls

  • Sending raw customer histories into the prompt

At retail-banking scale this creates privacy risk and noisy prompts. Summarize only what the model needs for triage and keep sensitive identifiers out of the prompt body.

  • Using retrieval without document governance

If your vector store contains stale policies or draft procedures, the agent will cite bad guidance confidently. Version documents tightly and index only approved content.

  • Treating the agent as the decision-maker

The agent should assist analysts, not replace them. Final disposition on suspicious activity must stay under human control with clear escalation rules.


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