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

By Cyprian AaronsUpdated 2026-04-21
customer-supportlangchaintypescriptinsurance

A customer support agent for insurance answers policy questions, helps with claims status, explains coverage, and routes sensitive cases to a human when needed. It matters because insurance support sits on top of regulated data, strict audit requirements, and high customer frustration; a bad answer is not just a UX problem, it can become a compliance issue.

Architecture

  • Chat model layer

    • Use a hosted LLM through LangChain’s ChatOpenAI for natural language responses.
    • Keep temperature low so policy answers stay stable.
  • Policy knowledge retrieval

    • Index policy docs, claims FAQs, and product guides in a vector store.
    • Retrieve only the minimum relevant context for each user question.
  • Conversation state

    • Store session history so the agent can handle follow-up questions like “what about my deductible?”
    • Keep memory scoped per customer and per claim case.
  • Tooling layer

    • Add tools for claims lookup, policy lookup, and escalation.
    • The model should not invent account or claim data; it should call tools.
  • Guardrails

    • Block PII leakage, unsupported advice, and out-of-policy commitments.
    • Force human handoff for disputes, denials, cancellations, and legal complaints.
  • Audit logging

    • Persist prompts, retrieved documents, tool calls, and final outputs.
    • Insurance teams need traceability for every customer-facing answer.

Implementation

1. Install the LangChain packages

Use the current TypeScript packages from the LangChain ecosystem.

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

Set your environment variables before running anything:

export OPENAI_API_KEY="your-key"

2. Build the retrieval chain over insurance documents

This example uses MemoryVectorStore for simplicity. In production, swap it for pgvector, Pinecone, or another durable store with residency controls.

import { ChatOpenAI } from "@langchain/openai";
import { MemoryVectorStore } from "@langchain/community/vectorstores/memory";
import { OpenAIEmbeddings } from "@langchain/openai";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { Document } from "@langchain/core/documents";
import { createRetrievalChain } from "langchain/chains/retrieval";
import { createStuffDocumentsChain } from "langchain/chains/combine_documents";
import { ChatPromptTemplate } from "@langchain/core/prompts";

const docs = [
  new Document({
    pageContent:
      "Home insurance covers fire damage, burst pipes, and theft subject to policy limits and deductibles.",
    metadata: { source: "policy-handbook", version: "2025-01" },
  }),
  new Document({
    pageContent:
      "Claims are typically reviewed within 3 business days after all required documents are received.",
    metadata: { source: "claims-faq", version: "2025-01" },
  }),
];

const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 50 });
const splitDocs = await splitter.splitDocuments(docs);

const embeddings = new OpenAIEmbeddings();
const vectorStore = await MemoryVectorStore.fromDocuments(splitDocs, embeddings);
const retriever = vectorStore.asRetriever(4);

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

const prompt = ChatPromptTemplate.fromMessages([
  [
    "system",
    `You are an insurance customer support agent.
Answer only using the provided context.
If the answer is not in context, say you need to escalate to a human agent.
Do not provide legal advice or guess about coverage.`,
  ],
  ["human", "{input}"],
]);

const combineDocsChain = await createStuffDocumentsChain({
  llm,
  prompt,
});

const ragChain = await createRetrievalChain({
  retriever,
  combineDocsChain,
});

const result = await ragChain.invoke({
  input: "Does home insurance cover burst pipes?",
});

console.log(result.answer);

This is the core pattern for insurance support: retrieve approved content first, then generate a constrained response. It prevents the model from freelancing on coverage details.

3. Add a claims lookup tool and force tool use where needed

For account-specific questions like claim status or policy number lookup, use tools instead of asking the model to infer anything.

import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { AgentExecutor } from "langchain/agents";
import { createOpenAIToolsAgent } from "langchain/agents";

const claimsLookupTool = tool(
  async ({ claimId }: { claimId: string }) => {
    // Replace with your internal claims service call
    if (claimId === "CLM-1001") {
      return JSON.stringify({
        claimId,
        status: "In review",
        updatedAt: "2026-04-20T10:30:00Z",
      });
    }
    return JSON.stringify({ error: "Claim not found" });
  },
  {
    name: "claims_lookup",
    description: "Fetch claim status by claim ID.",
    schema: z.object({
      claimId: z.string().min(1),
    }),
  }
);

const agentPrompt = ChatPromptTemplate.fromMessages([
  ["system", `You are an insurance support agent. Use tools for any account-specific data.`],
]);

const agent = await createOpenAIToolsAgent({
  llm,
  tools: [claimsLookupTool],
  prompt: agentPrompt,
});

const executor = new AgentExecutor({
  agent,
  tools: [claimsLookupTool],
});

const response = await executor.invoke({
  input: "Check claim CLM-1001",
});

console.log(response.output);

The important part here is separation of concerns:

  • retrieval handles general policy knowledge
  • tools handle live customer data
  • the model only orchestrates

4. Add a hard escalation rule for regulated cases

Insurance support must route certain topics to humans immediately. Don’t let the model improvise on denials, complaints, or legal threats.

function requiresEscalation(text: string): boolean {
  const normalized = text.toLowerCase();
  return [
    "appeal denial",
    "lawsuit",
    "legal",
    "complaint",
    "cancel my policy",
    "bad faith",
    "regulator",
    "ombudsman",
    "fraud accusation",
  ].some((phrase) => normalized.includes(phrase));
}

export async function handleSupportRequest(input: string) {
  if (requiresEscalation(input)) {
    return {
      route: "human_handoff",
      message:
        "I’m routing this to a licensed support specialist who can review your case.",
    };
    
}
  
if (input.includes("claim")) {
    const result = await executor.invoke({ input });
    return { route: "tool_agent", message: result.output };
}
  
const result = await ragChain.invoke({ input });
return { route: "rag_agent", message: result.answer };
}

This kind of routing belongs outside the LLM. If you wait for the model to decide whether something is sensitive enough to escalate, you’ve already lost control of the workflow.

Production Considerations

  • Data residency

    • Keep embeddings and conversation logs in-region if your insurer operates under local residency rules.
    • Don’t send raw PII into third-party systems unless your legal team has approved it.
  • Auditability

    • Log user input, retrieved document IDs, tool calls, model output, and final route decision.
    • For claims or coverage answers, you need to reconstruct exactly why the agent said what it said.
  • Guardrails

    • Redact policy numbers, phone numbers, addresses, and health-related details before logging.
    • Enforce “no guarantees” language around coverage until a licensed adjuster confirms eligibility.
  • Monitoring

    • Track escalation rate, hallucination rate on known test sets, average retrieval precision, and tool failure rate.
    • Review failed conversations weekly with compliance and operations teams.

Common Pitfalls

  1. Letting the model answer coverage questions without retrieval

    • This produces confident but wrong answers.
    • Fix it by grounding every policy explanation in retrieved approved documents.
  2. Using memory as a database

    • Conversation history is not source-of-truth for claims status or policy terms.
    • Fix it by reading live data through tools and treating memory only as conversational context.
  3. Skipping escalation logic for sensitive cases

    • Denials, cancellations, complaints, fraud allegations, and legal threats need human review.
    • Fix it with deterministic pre-checks before any LLM call.
  4. Ignoring audit logs

    • If compliance asks why an answer was given, “the model said so” is useless.
    • Fix it by persisting prompts, retrieved chunks metadata, tool outputs, and final responses with timestamps.

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