How to Build a customer support Agent Using LangChain in TypeScript for lending

By Cyprian AaronsUpdated 2026-04-21
customer-supportlangchaintypescriptlending

A customer support agent for lending answers borrower questions, explains loan terms, checks application status, and routes sensitive cases to humans when needed. It matters because lending support sits at the intersection of customer experience, compliance, and risk: one bad answer can create regulatory exposure, confuse a borrower about repayment obligations, or leak data.

Architecture

Build this agent with a narrow, auditable surface area:

  • Chat model layer

    • Use ChatOpenAI from LangChain for response generation.
    • Keep temperature low so answers stay consistent and policy-aligned.
  • Loan knowledge retrieval

    • Index product docs, FAQ content, repayment policies, and disclosures in a vector store.
    • Use a retriever-backed chain so the agent grounds answers in approved content.
  • Customer context tool

    • Add a tool for reading application status, payment schedule, or case metadata from your internal API.
    • Keep this tool read-only unless you have explicit workflows for changes.
  • Policy and compliance guardrails

    • Detect regulated requests like credit decisions, adverse action explanations, hardship claims, or disputes.
    • Route these to safe templates or human review instead of free-form generation.
  • Conversation memory

    • Store only what you need for the session.
    • Avoid retaining PII longer than necessary; lending systems usually have stricter retention rules than generic support stacks.
  • Audit logging

    • Log prompts, retrieved documents, tool calls, and final outputs.
    • You need this for model debugging, complaint handling, and regulatory review.

Implementation

1) Install the LangChain packages

Use the current TypeScript packages and keep your dependencies explicit.

npm install langchain @langchain/openai @langchain/core zod

If you use a vector store like Pinecone or pgvector later, add that separately. Don’t bundle storage decisions into the first pass.

2) Create the support prompt and model

For lending support, the prompt should force grounded answers and escalation behavior. Keep it strict: if the agent doesn’t know, it should say so and route out.

import { ChatOpenAI } from "@langchain/openai";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { RunnableSequence } from "@langchain/core/runnables";

const model = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

const prompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    `You are a customer support agent for a lending company.
Rules:
- Answer only using provided context or tool results.
- Do not provide legal advice or credit decisions.
- If asked about disputes, adverse action reasons, debt collection escalation, or complaints, escalate to a human agent.
- Never reveal full SSNs, card numbers, bank account numbers, or internal risk scores.
- Be concise and professional.`,
  ],
  ["human", "{question}"],
]);

const supportChain = RunnableSequence.from([prompt, model]);

This is the baseline generation path. In production you will usually wrap it with retrieval and policy checks before calling the model.

3) Add retrieval over approved lending content

The common pattern is retrieval first, then answer generation. The retriever should only search approved docs: loan products, repayment rules, late fee policy, hardship program info, and contact details.

import { Document } from "@langchain/core/documents";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { StringOutputParser } from "@langchain/core/output_parsers";

const docs = [
  new Document({
    pageContent:
      "Personal loans can be repaid biweekly or monthly. Customers may request a due date change once every six months.",
    metadata: { source: "repayment-policy", version: "2025-01" },
  }),
  new Document({
    pageContent:
      "Late fees apply after a payment is more than 10 days overdue. Customers in hardship may request review through support.",
    metadata: { source: "collections-policy", version: "2025-01" },
  }),
];

const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectorStore = await MemoryVectorStore.fromDocuments(docs, embeddings);
const retriever = vectorStore.asRetriever(3);

const ragPrompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    `Answer using only the context below. If the answer is not in context, say you do not have enough information and offer escalation.

Context:
{context}`,
  ],
  ["human", "{question}"],
]);

const ragChain = RunnableSequence.from([
  async (input: { question: string }) => {
    const relevantDocs = await retriever.invoke(input.question);
    return {
      question: input.question,
      context: relevantDocs.map((d) => d.pageContent).join("\n\n"),
    };
  },
  ragPrompt,
  model,
  new StringOutputParser(),
]);

This keeps product policy answers anchored to approved text. For lending teams, that matters more than raw fluency.

4) Wire in an escalation rule for regulated requests

Do not let every question hit the model. A small policy router catches high-risk intents before generation.

type Route = "answer" | "escalate";

function classifyLendingRequest(question: string): Route {
  const q = question.toLowerCase();

  const riskySignals = [
    "credit decision",
    "why was i denied",
    "adverse action",
    "appeal my denial",
    "dispute",
    "complaint",
    "collections",
    "hardship letter",
    "modify my loan",
  ];

  return riskySignals.some((term) => q.includes(term)) ? "escalate" : "answer";
}

export async function handleSupportQuestion(question: string) {
  const route = classifyLendingRequest(question);

  if (route === "escalate") {
    return {
      response:
        "I can connect you with a specialist for that request. I’m not able to handle credit decision reviews or dispute-related issues here.",
      routedToHuman: true,
    };
  }

  const response = await ragChain.invoke({ question });
  return { response, routedToHuman: false };
}

This is simple on purpose. In real deployments you can replace keyword matching with an intent classifier built on top of RunnableLambda, but start with deterministic rules for regulated flows.

Production Considerations

  • Deployment

    • Keep the agent behind authenticated channels only.
    • For lending workloads tied to jurisdictional data rules, pin runtime and storage to the correct region to satisfy data residency requirements.
  • Monitoring

    • Log every prompt version, retrieved document IDs/versions, tool call input/output, and final response.
    • Track escalation rate by intent; spikes often mean your retrieval corpus is stale or your policy rules are too loose.
  • Guardrails

    • Redact PII before sending text to the model where possible.
    • Block outputs that include credit scores, underwriting rationale beyond approved templates, or unsupported promises about approval timelines.
  • Compliance

    • Version your policy content like code.
    • When disclosures change — late fees, APR ranges, hardship options — redeploy both docs and prompts together so support answers don’t drift out of sync with legal copy.

Common Pitfalls

  1. Letting the model answer from memory instead of grounded docs

    • This causes inconsistent responses around repayment terms and fees.
    • Fix it by forcing RAG-only answers and returning “I don’t have enough information” when retrieval fails.
  2. Using one generic prompt for all lending intents

    • Support questions are not all equal. Denial explanations and repayment FAQs have different compliance requirements.
    • Split flows by intent and escalate high-risk cases early.
  3. Logging raw customer data without controls

    • Support transcripts often contain SSNs, bank details, income data, and complaint narratives.
    • Redact sensitive fields before persistence and keep audit logs separate from conversational memory.

A lending support agent is useful only when it stays narrow: answer approved questions fast, refuse unsafe ones cleanly, and leave an audit trail that compliance can trust. If you get those three right with LangChain in TypeScript — retrieval grounding، deterministic routing، and strict escalation — you have something production teams can actually ship.


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