How to Build a compliance checking Agent Using LlamaIndex in TypeScript for investment banking

By Cyprian AaronsUpdated 2026-04-21
compliance-checkingllamaindextypescriptinvestment-banking

A compliance checking agent for investment banking reviews client communications, trade instructions, pitch materials, and internal documents against policy and regulatory rules before they go out the door. It matters because the failure mode is expensive: unsuitable recommendations, restricted language, missing disclosures, data residency violations, and weak audit trails can turn a routine workflow into a regulatory event.

Architecture

  • Document ingestion layer

    • Pulls policies, approved templates, product sheets, and regulatory guidance from controlled sources.
    • In practice this is usually a mix of PDFs, DOCX files, SharePoint exports, and internal policy repositories.
  • Embedding + vector index

    • Stores compliance knowledge in a VectorStoreIndex for semantic retrieval.
    • Lets the agent pull the most relevant policy passages for a given email, memo, or slide deck.
  • Retriever + response synthesizer

    • Uses a retriever to fetch evidence and a synthesizer to produce a structured compliance verdict.
    • The output should be deterministic enough for audit review: pass/fail, cited rules, and remediation notes.
  • Rule layer

    • Handles hard checks that should not depend on model judgment.
    • Examples: banned phrases, missing disclaimers, restricted jurisdiction references, PII leakage, or use of non-approved product names.
  • Audit logging layer

    • Persists inputs, retrieved evidence chunks, model output, timestamps, user identity, and final decision.
    • This is mandatory if you need to explain why a document was approved or rejected later.
  • Deployment guardrails

    • Enforces data residency, access control, model selection rules, and redaction before any text leaves your environment.
    • For investment banking this is not optional; it is part of the control framework.

Implementation

  1. Install LlamaIndex packages and load compliance documents

    Use LlamaIndex’s TypeScript packages to ingest policy documents into memory first. For production you would swap SimpleDirectoryReader for a controlled connector with access checks and residency controls.

    npm install llamaindex
    
    import {
      SimpleDirectoryReader,
      VectorStoreIndex,
      Settings,
      OpenAI,
    } from "llamaindex";
    
    async function buildIndex() {
      Settings.llm = new OpenAI({
        model: "gpt-4o-mini",
        temperature: 0,
      });
    
      const docs = await new SimpleDirectoryReader().loadData({
        directoryPath: "./compliance-docs",
      });
    
      const index = await VectorStoreIndex.fromDocuments(docs);
      return index;
    }
    
  2. Create a retriever-backed compliance checker

    The pattern here is simple: retrieve policy evidence first, then ask the LLM to classify the text against those rules. Keep temperature at zero and force structured output so reviewers can trace the decision.

    import {
      VectorStoreIndex,
      QueryEngineTool,
      ToolMetadata,
      OpenAI,
      Settings,
    } from "llamaindex";
    
    type ComplianceResult = {
      status: "PASS" | "FAIL";
      reasons: string[];
      citedPolicySnippets: string[];
    };
    
    async function checkText(index: VectorStoreIndex, inputText: string): Promise<ComplianceResult> {
      Settings.llm = new OpenAI({ model: "gpt-4o-mini", temperature: 0 });
    
      const queryEngine = index.asQueryEngine({
        similarityTopK: 4,
      });
    
      const prompt = `
    

You are a compliance reviewer for investment banking. Assess the text against retrieved policy context only. Return JSON with keys: status, reasons, citedPolicySnippets. Status must be PASS or FAIL.

Text: ${inputText} `;

 const response = await queryEngine.query({ query: prompt });
 const raw = response.response ?? "";

 return JSON.parse(raw) as ComplianceResult;

}


3. **Add hard-rule checks before the model runs**

Some checks should never depend on retrieval. If an analyst pastes client PII into an external-facing draft or uses banned marketing language for restricted products, block it immediately.

```ts
const bannedPatterns = [
  /\bguaranteed returns?\b/i,
  /\bno risk\b/i,
  /\bconfidential client\b/i,
  /\bSSN\b/i,
];

function hardRuleCheck(text: string) {
  const violations = bannedPatterns
    .filter((pattern) => pattern.test(text))
    .map((pattern) => pattern.source);

  return {
    allowed: violations.length === 0,
    violations,
  };
}

async function reviewDocument(index: VectorStoreIndex, text: string) {
  const hardCheck = hardRuleCheck(text);
  if (!hardCheck.allowed) {
    return {
      status: "FAIL",
      reasons: ["Hard rule violation detected"],
      citedPolicySnippets: hardCheck.violations,
    };
  }

  return checkText(index, text);
}
  1. Wrap it in an auditable workflow

    For banking workflows you need an immutable trail. Log the input hash, policy version used for retrieval, top-k snippets returned by the retriever, final verdict, and reviewer identity.

    import crypto from "crypto";
    
    function sha256(value: string) {
      return crypto.createHash("sha256").update(value).digest("hex");
    }
    
    async function auditedReview(index: VectorStoreIndex, userId: string, text: string) {
      const inputHash = sha256(text);
      const result = await reviewDocument(index, text);
    
      console.log(JSON.stringify({
        eventType: "compliance_review",
        userId,
        inputHash,
        result,
        reviewedAt: new Date().toISOString(),
        system: "investment-banking-compliance-agent",
      }));
    
      return result;
    }
    

Production Considerations

  • Keep sensitive data in-region

    • If your bank requires UK/EU/US residency boundaries, do not send raw content to a hosted model outside that boundary.
    • Use regional deployments and control where embeddings are generated and stored.
  • Separate advisory from blocking decisions

    • Let the agent recommend remediation for low-risk issues.
    • Hard failures like PII leakage or prohibited claims should block release automatically.
  • Track every version

    • Store policy version IDs alongside each decision.
    • When legal updates suitability language or disclosure requirements change, old decisions must remain explainable under the rules active at that time.
  • Monitor false negatives aggressively

    • In investment banking the dangerous error is approving something that should have been blocked.
    • Build sampling queues for human QA on pass decisions and measure precision/recall by document type.

Common Pitfalls

  1. Using retrieval alone for all checks

    Semantic retrieval is good for policy interpretation but weak for exact violations. Fix this by combining VectorStoreIndex with explicit regex or deterministic rule checks before LLM evaluation.

  2. Letting the model free-form its answer

    A narrative response is hard to audit and impossible to integrate into downstream systems reliably. Force structured JSON output with fixed fields like status, reasons, and citedPolicySnippets.

  3. Ignoring document provenance

    If you cannot prove which policy version or source file influenced the verdict, compliance teams will reject the system. Persist source metadata from ingestion through retrieval and keep hashes of both inputs and outputs.


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