How to Build a underwriting Agent Using LlamaIndex in TypeScript for investment banking
An underwriting agent for investment banking takes in deal materials, financial statements, market data, and internal policy docs, then turns them into a structured risk and suitability assessment. It matters because underwriting teams need faster first-pass analysis without losing control over compliance, auditability, and decision quality.
Architecture
- •
Document ingestion layer
- •Pulls PDFs, Word docs, pitch books, issuer financials, credit memos, and policy documents.
- •Normalizes them into text chunks with source metadata preserved.
- •
Indexing layer
- •Uses
VectorStoreIndexfor semantic retrieval over deal documents. - •Keeps separate indexes for internal policy, historical deals, and live transaction materials.
- •Uses
- •
Retrieval + reasoning layer
- •Uses
QueryEngineorChatEngineto answer underwriting questions like:- •Does this issuer meet our sector exposure policy?
- •What covenants are missing?
- •What are the key risks in the offering?
- •Uses
- •
Tooling layer
- •Adds deterministic tools for calculations like leverage ratios, interest coverage, or debt sizing.
- •Keeps numeric work out of the LLM where possible.
- •
Control plane
- •Enforces prompt constraints, citation requirements, escalation rules, and human review thresholds.
- •Logs every response with document references for audit.
- •
Deployment boundary
- •Runs inside a controlled environment with approved model endpoints.
- •Supports data residency requirements and retention policies.
Implementation
1) Install the core packages
For a TypeScript underwriting agent, keep the stack small: LlamaIndex for retrieval orchestration and a model provider you can approve through your bank’s vendor process.
npm install llamaindex zod dotenv
If you’re using OpenAI-compatible models through LlamaIndex TS, set your environment variables:
export OPENAI_API_KEY="your-key"
2) Load underwriting documents into an index
This example loads local files from a deal folder and builds a vector index. The key pattern is preserving metadata so every answer can be traced back to the source document.
import "dotenv/config";
import { Document, VectorStoreIndex } from "llamaindex";
import fs from "node:fs";
import path from "node:path";
async function loadDocumentsFromFolder(folderPath: string) {
const files = fs.readdirSync(folderPath);
const docs: Document[] = [];
for (const file of files) {
const fullPath = path.join(folderPath, file);
const text = fs.readFileSync(fullPath, "utf-8");
docs.push(
new Document({
text,
metadata: {
fileName: file,
source: "deal-room",
dealId: "DEAL-2026-014",
},
})
);
}
return docs;
}
async function buildIndex() {
const documents = await loadDocumentsFromFolder("./deal-materials");
return await VectorStoreIndex.fromDocuments(documents);
}
That gives you a searchable index over the deal room. In production, replace raw file reads with your approved ingestion pipeline from SharePoint, S3 with residency controls, or an internal DMS.
3) Add an underwriting query engine with citations
The underwriting agent should not just summarize. It needs to answer with evidence so analysts can review the source material quickly.
import { QueryEngineTool } from "llamaindex";
async function main() {
const index = await buildIndex();
const queryEngine = index.asQueryEngine({
similarityTopK: 5,
});
const tool = new QueryEngineTool({
queryEngine,
metadata: {
name: "underwriting_docs",
description:
"Searches deal materials for risks, covenants, leverage metrics, and issuer disclosures.",
},
});
const response = await tool.call({
input:
"Summarize the main underwriting risks in this deal and cite supporting sections.",
});
console.log(response.toString());
}
main().catch(console.error);
This is the right pattern when your users need traceability. You want answers grounded in retrieved chunks, not free-form model memory.
4) Wrap it with deterministic checks
Underwriting workflows need hard stops. If leverage exceeds policy or if required disclosures are missing, route to human review instead of returning a polished but unsafe answer.
type UnderwritingResult = {
recommendation: "approve" | "review" | "reject";
reasons: string[];
};
function applyPolicyChecks(metrics: {
netLeverage: number;
interestCoverage: number;
}): UnderwritingResult {
if (metrics.netLeverage > 5.0) {
return {
recommendation: "review",
reasons: ["Net leverage exceeds policy threshold of 5.0x"],
};
}
if (metrics.interestCoverage < 2.0) {
return {
recommendation: "reject",
reasons: ["Interest coverage below minimum threshold of 2.0x"],
};
}
return {
recommendation: "approve",
reasons: ["Meets current policy thresholds"],
};
}
Use LlamaIndex for retrieval and synthesis. Use normal TypeScript code for policy logic. That split keeps your control surface clean.
Production Considerations
- •
Compliance and audit
- •Store every prompt, retrieved chunk ID, model output, and final decision.
- •Make responses cite document names and page/section references where possible.
- •Keep an immutable audit trail for model-assisted recommendations.
- •
Data residency
- •Keep issuer data inside approved regions.
- •Avoid sending confidential offering materials to unmanaged endpoints.
- •If your bank requires it, use self-hosted inference or region-locked providers only.
- •
Human-in-the-loop controls
- •Force manual approval on high-risk deals, missing disclosures, or low-confidence retrieval.
- •Never auto-generate final underwriting decisions without analyst sign-off.
- •Route exceptions to compliance or credit committees based on predefined thresholds.
- •
Monitoring
- •Track retrieval quality: top-k hit rate, citation coverage, hallucination reports.
- •Monitor latency separately for ingestion, retrieval, and generation.
- •Alert on prompt injection attempts inside uploaded documents.
Common Pitfalls
- •
Treating the agent like a decision maker
- •Don’t let the model issue final approvals.
- •Use it to prepare analysis; keep approval logic in deterministic code and human review workflows.
- •
Skipping metadata
- •If you don’t attach file name, deal ID, section headers, and timestamps at ingestion time, your audit trail collapses later.
- •Preserve provenance from day one.
- •
Mixing public web data with confidential deal data without boundaries
- •That creates compliance risk fast.
- •Separate indexes by sensitivity class and restrict which tools can access which corpus.
- •
Letting numeric analysis stay in prompts
- •Ask the model to interpret numbers; don’t ask it to calculate leverage ratios from scratch unless you verify them elsewhere.
- •Compute financial metrics in TypeScript and pass the results into the agent as structured inputs.
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