How to Build a loan approval Agent Using LlamaIndex in TypeScript for wealth management
A loan approval agent for wealth management takes a client’s financial profile, policy rules, and supporting documents, then produces a recommendation with traceable reasoning. It matters because wealth clients expect fast decisions, but the bank still needs compliance controls, auditability, and consistent treatment across high-net-worth cases.
Architecture
- •
Document ingestion layer
- •Pulls PDFs, statements, KYC files, and internal policy docs into a searchable index.
- •In wealth management, this usually includes portfolio reports, tax returns, trust documents, and collateral schedules.
- •
Retrieval layer
- •Uses
VectorStoreIndexto find the right policy clauses and client evidence. - •This keeps the agent grounded in approved underwriting rules instead of free-form model guesses.
- •Uses
- •
Decision engine
- •Combines retrieved policy context with structured client data.
- •Produces a recommendation such as approve, refer to manual review, or reject.
- •
Guardrail layer
- •Blocks unsupported decisions and forces escalation when data is incomplete.
- •Important for compliance checks like affordability, concentration risk, AML flags, and jurisdiction-specific constraints.
- •
Audit and traceability layer
- •Stores prompts, retrieved chunks, model outputs, and final decision rationale.
- •Wealth management teams need this for model governance and regulator review.
Implementation
- •
Load policy documents into a vector index
Use
SimpleDirectoryReaderto ingest your underwriting policies andVectorStoreIndexto make them retrievable. In production, swap the storage backend for one that satisfies your data residency requirements.import { SimpleDirectoryReader } from "llamaindex"; import { VectorStoreIndex } from "llamaindex"; async function buildPolicyIndex() { const documents = await new SimpleDirectoryReader({ directoryPath: "./data/policies", }).loadData(); const index = await VectorStoreIndex.fromDocuments(documents); return index; } - •
Create a retriever-driven query engine
The agent should not “decide” from memory. It should retrieve policy text first using
asQueryEngine()so the output is grounded in the bank’s actual lending rules.import { QueryEngineTool } from "llamaindex"; async function createPolicyTool() { const index = await buildPolicyIndex(); const queryEngine = index.asQueryEngine({ similarityTopK: 3, }); return new QueryEngineTool({ queryEngine, metadata: { name: "loan_policy_search", description: "Searches approved lending policies for underwriting guidance.", }, }); } - •
Build an agent that can answer with evidence
For TypeScript projects using LlamaIndex,
OpenAIAgentis the practical pattern when you want tool use plus controlled reasoning. Feed it a single retrieval tool first; add more tools later for KYC lookup or portfolio exposure checks.import { OpenAIAgent } from "llamaindex"; import { OpenAI } from "llamaindex"; type LoanCase = { applicantName: string; requestedAmount: number; annualIncome: number; liquidAssets: number; jurisdiction: string; notes: string; }; async function buildAgent() { const policyTool = await createPolicyTool(); const llm = new OpenAI({ model: "gpt-4o-mini", temperature: 0, }); return OpenAIAgent.fromTools({ tools: [policyTool], llm, systemPrompt: "You are a loan approval assistant for wealth management. " + "Use only retrieved policy content and provided case data. " + "If evidence is missing or conflicting, return 'manual_review'. " + "Always cite the policy basis in your answer.", }); } async function evaluateLoan(caseData: LoanCase) { const agent = await buildAgent(); const prompt = `
Review this loan case:
Applicant: ${caseData.applicantName} Requested amount: ${caseData.requestedAmount} Annual income: ${caseData.annualIncome} Liquid assets: ${caseData.liquidAssets} Jurisdiction: ${caseData.jurisdiction} Notes: ${caseData.notes}
Return:
- •
decision: approve | reject | manual_review
- •
rationale
- •
policy_citations `;
const response = await agent.chat({ message: prompt }); console.log(response.message.content); }
- •
Add a deterministic pre-check before the LLM
Do not ask the model to calculate basic thresholds if you can do it in code. Wealth management lending often has hard rules around debt service ratios, asset coverage, or jurisdictional eligibility. Use code for those checks and let the agent explain them.
function precheck(caseData: LoanCase) { const assetCoverageRatio = caseData.liquidAssets / caseData.requestedAmount; if (caseData.jurisdiction !== "US" && caseData.jurisdiction !== "UK") { return { decision: "manual_review", reason: "Unsupported jurisdiction" }; } if (assetCoverageRatio < 0.25) { return { decision: "reject", reason: "Insufficient liquid asset coverage" }; } return { decision: "proceed", reason: "Passed deterministic checks" }; }
Production Considerations
- •
Keep client data in-region
- •Wealth clients often have residency constraints on PII and financial records.
- •Store embeddings and source docs in approved regions only; verify your vector store deployment location.
- •
Log every retrieval path
- •Persist the question, retrieved chunks, model output, and final decision.
- •Auditors will ask why a specific client was approved or escalated.
- •
Force escalation on missing evidence
- •If KYC status is stale, statements are incomplete, or policy retrieval returns low confidence, return
manual_review. - •Do not let the model infer missing facts from pattern matching.
- •If KYC status is stale, statements are incomplete, or policy retrieval returns low confidence, return
- •
Separate explanation from decisioning
- •The final decision should come from explicit business logic where possible.
- •Let LlamaIndex generate explanations tied to retrieved policy text; do not let it invent underwriting authority.
Common Pitfalls
- •
Using the LLM as the source of truth
- •Mistake: asking the model to “decide based on experience.”
- •Fix: retrieve policy text with
VectorStoreIndexfirst and constrain outputs to approved categories.
- •
Skipping deterministic risk checks
- •Mistake: letting the agent compute ratios or eligibility entirely in natural language.
- •Fix: calculate hard thresholds in TypeScript before calling
OpenAIAgent.
- •
Ignoring audit requirements
- •Mistake: storing only the final answer.
- •Fix: persist prompts, retrieved sources, timestamps, model version, and decision path so compliance can reconstruct every outcome.
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