How to Build a customer support Agent Using LangChain in TypeScript for investment banking
A customer support agent for investment banking answers client and internal queries about products, trades, statements, onboarding, fees, and operational status without exposing sensitive data or giving non-compliant advice. It matters because support in this domain is not just about speed; it is about accuracy, auditability, data residency, and making sure every response stays inside regulatory and policy boundaries.
Architecture
- •
Channel adapter
- •Receives requests from web chat, email triage, or an internal CRM.
- •Normalizes user identity, account context, and message metadata.
- •
Policy and compliance layer
- •Blocks restricted topics like investment advice, MNPI leakage, and unauthorized account actions.
- •Applies jurisdiction rules, retention rules, and escalation triggers.
- •
Retriever
- •Pulls from approved sources only: product docs, SOPs, fee schedules, onboarding guides, incident runbooks.
- •Uses
VectorStoreRetrieveror a retriever built fromMemoryVectorStore.
- •
LLM orchestration
- •Uses LangChain
ChatOpenAIwith a controlled prompt. - •Keeps the model constrained to answer only from retrieved context.
- •Uses LangChain
- •
Audit logger
- •Stores request metadata, retrieved document IDs, final answer, and escalation decisions.
- •Needed for compliance review and dispute handling.
- •
Human handoff path
- •Routes complex cases to a licensed banker or operations team.
- •Required for complaints, suitability questions, trade disputes, and account-specific exceptions.
Implementation
- •Install LangChain packages and define the model
Use the current LangChain TypeScript packages. Keep the model temperature low so support responses stay deterministic.
npm install langchain @langchain/openai @langchain/core zod
import { ChatOpenAI } from "@langchain/openai";
const llm = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
});
- •Load approved support content into a vector store
For production you would back this with a managed vector DB in-region. The example below uses MemoryVectorStore to show the actual LangChain pattern.
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { Document } from "@langchain/core/documents";
const docs = [
new Document({
pageContent: "Wire transfer cutoff time is 3:00 PM ET on business days.",
metadata: { source: "ops-handbook", docId: "OPS-001" },
}),
new Document({
pageContent: "Investment banking clients must verify changes to settlement instructions via callback.",
metadata: { source: "security-policy", docId: "SEC-014" },
}),
];
const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
const retriever = vectorStore.asRetriever(4);
- •Build a retrieval chain with guardrailed prompting
Use ChatPromptTemplate, createStuffDocumentsChain, and createRetrievalChain. The prompt should explicitly forbid unsupported advice and force escalation when context is missing.
import {
ChatPromptTemplate,
} from "@langchain/core/prompts";
import {
createStuffDocumentsChain,
} from "langchain/chains/combine_documents";
import {
createRetrievalChain,
} from "langchain/chains/retrieval";
const prompt = ChatPromptTemplate.fromMessages([
[
"system",
[
"You are a customer support agent for an investment bank.",
"Answer only using the provided context.",
"Do not provide investment advice, legal advice, or instructions that bypass controls.",
"If the answer is not in context or requires account-specific action, say you need to escalate.",
"Always mention when a policy requires human review.",
].join(" "),
],
["human", "{input}"],
]);
const combineDocsChain = await createStuffDocumentsChain({
llm,
prompt,
});
const retrievalChain = await createRetrievalChain({
retriever,
combineDocsChain,
});
- •Add a request-level safety check before calling the chain
This is where you catch obvious policy violations before retrieval. In banking support flows I prefer explicit classification over hoping the prompt handles everything.
type SupportRequest = {
userId: string;
region: string;
message: string;
};
function needsEscalation(message: string): boolean {
const blocked = [
/what stock should/i,
/recommend/i,
/buy or sell/i,
/change my settlement instructions/i,
/ignore policy/i,
];
return blocked.some((pattern) => pattern.test(message));
}
export async function handleSupportRequest(req: SupportRequest) {
if (needsEscalation(req.message)) {
return {
answer:
"This request requires human review because it may involve restricted advice or a controlled account change.",
escalated: true,
reason: "policy_trigger",
};
}
const result = await retrievalChain.invoke({
input: req.message,
});
return {
answer: result.answer,
escalated: false,
sources: result.context?.map((d: any) => d.metadata?.docId ?? d.metadata?.source),
};
}
Production Considerations
- •
Data residency
- •Keep embeddings, logs, and retrieved documents in the same jurisdiction as the client data.
- •If your bank has EU clients, do not route sensitive content through non-compliant regions.
- •
Auditability
- •Log
userId, timestamp, prompt version, retrieveddocIds, final answer, and escalation outcome. - •Store enough to reconstruct why the agent answered what it did.
- •Log
- •
Guardrails
- •Add classifiers for complaints, fraud hints, trade errors, sanctions concerns, and suitability questions.
- •Force human handoff on anything that touches account changes or regulated advice.
- •
Monitoring
- •Track hallucination rate by sampling answers against source docs.
- •Monitor retrieval misses separately from LLM failures; they are different problems.
Common Pitfalls
- •
Using general web knowledge instead of approved bank content
- •This creates compliance risk fast.
- •Fix it by constraining the chain to internal documents only and rejecting unsupported answers.
- •
Letting the model handle policy decisions implicitly
- •Prompts are not controls.
- •Fix it with explicit pre-checks like classification rules or a separate moderation step before generation.
- •
Ignoring audit trails
- •If a client disputes an answer later, you need to show what was asked and what sources were used.
- •Fix it by persisting request metadata plus retrieved document references for every interaction.
- •
Deploying one global endpoint for all regions
- •That breaks data residency requirements in many banking setups.
- •Fix it by region-scoping storage, embeddings, logs, and inference endpoints per legal entity or market.
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