How to Build a policy Q&A Agent Using CrewAI in TypeScript for insurance

By Cyprian AaronsUpdated 2026-04-21
policy-q-acrewaitypescriptinsurancepolicy-qanda

A policy Q&A agent answers customer and internal questions against insurance policy documents, endorsements, and product guides without forcing a human to grep PDFs. For insurance, this matters because the agent has to be accurate, auditable, and constrained by compliance rules: one wrong answer about coverage, exclusions, or claims handling can create regulatory and financial exposure.

Architecture

  • Policy ingestion layer

    • Pulls policy PDFs, word docs, product brochures, and endorsement schedules into a normalized text store.
    • Keeps document version metadata so the agent can answer against the correct policy effective date.
  • Retrieval layer

    • Uses embeddings or keyword search to fetch only the relevant clauses.
    • Returns source snippets with page references for auditability.
  • CrewAI agent

    • A single Agent configured as an insurance policy assistant.
    • Instructed to answer only from retrieved context and to say when the policy text is insufficient.
  • Tools

    • A retrieval tool for clause lookup.
    • A document metadata tool for policy version, jurisdiction, and effective date checks.
  • Task orchestration

    • A Task that defines the question-answering behavior and output format.
    • A Crew that executes the agent with strict guardrails.
  • Response validation layer

    • Enforces response structure: answer, citations, confidence, escalation flag.
    • Blocks unsupported claims before they reach customers or adjusters.

Implementation

1) Install CrewAI for TypeScript and define your data model

The TypeScript API follows the same pattern you want in production: define tools first, then bind them to an Agent, then execute a Task through a Crew.

npm install @crewai/core zod
// types.ts
import { z } from "zod";

export const PolicyAnswerSchema = z.object({
  answer: z.string(),
  citations: z.array(
    z.object({
      docId: z.string(),
      page: z.number(),
      quote: z.string()
    })
  ),
  needsHumanReview: z.boolean(),
});

export type PolicyAnswer = z.infer<typeof PolicyAnswerSchema>;

2) Build a retrieval tool for policy clauses

In insurance, retrieval is not optional. The agent must cite exact language from the right jurisdiction and policy version instead of generating a generic explanation.

// tools/policySearchTool.ts
import { Tool } from "@crewai/core";

type ClauseHit = {
  docId: string;
  page: number;
  quote: string;
};

const POLICY_INDEX = [
  {
    docId: "auto_policy_2024_ca",
    page: 12,
    text: "Collision coverage pays for direct and accidental loss to your covered auto caused by collision with another object."
  },
  {
    docId: "auto_policy_2024_ca",
    page: 18,
    text: "We do not provide coverage for intentional loss or damage caused by fraud."
  }
];

export const policySearchTool = new Tool({
  name: "policy_search",
  description:
    "Search insurance policy clauses by question. Returns matching excerpts with document IDs and page numbers.",
  func: async (query: string): Promise<string> => {
    const q = query.toLowerCase();
    const hits: ClauseHit[] = POLICY_INDEX.filter((item) =>
      item.text.toLowerCase().includes(q.split(" ")[0] ?? "")
    ).map((item) => ({
      docId: item.docId,
      page: item.page,
      quote: item.text,
    }));

    return JSON.stringify(hits);
  },
});

3) Create the agent with strict instructions

This is where most teams get sloppy. The agent should not infer coverage from memory; it should retrieve clauses, answer narrowly, and escalate when the source material is incomplete.

// agent.ts
import { Agent } from "@crewai/core";
import { policySearchTool } from "./tools/policySearchTool";

export const policyQnAAgent = new Agent({
  name: "Insurance Policy Q&A Agent",
  role: "Policy interpretation assistant for insurance operations",
  goal:
    "Answer customer and internal questions using only retrieved policy language and cite exact sources.",
  backstory:
    "You assist underwriting support, claims ops, and customer service. You never invent coverage terms.",
  tools: [policySearchTool],
});

4) Define the task and run the crew

Use a task that forces structured output. In production, this makes downstream validation and logging much easier.

// main.ts
import { Crew, Task } from "@crewai/core";
import { policyQnAAgent } from "./agent";
import { PolicyAnswerSchema } from "./types";

const task = new Task({
  description:
    "Answer this insurance policy question using only retrieved clauses. If coverage cannot be confirmed from the provided text, set needsHumanReview=true.\n\nQuestion: Does collision coverage apply if the insured intentionally damaged their own vehicle?",
  expectedOutput:
    'JSON with fields: answer (string), citations (array of {docId,page,quote}), needsHumanReview (boolean)',
});

async function run() {
  const crew = new Crew({
    agents: [policyQnAAgent],
    tasks: [task],
    verbose: true,
  });

  const result = await crew.kickoff();

  const parsed = PolicyAnswerSchema.parse(JSON.parse(result.output));
  console.log(parsed);
}

run().catch((err) => {
  console.error(err);
});

Production Considerations

  • Audit trails

    • Log every question, retrieved clause IDs, final answer, model version, and timestamp.
    • Keep immutable records for claims disputes and regulator reviews.
  • Data residency

    • Store policies and embeddings in-region if you operate under local residency requirements.
    • Do not send sensitive customer identifiers into prompts unless absolutely necessary.
  • Guardrails

    • Block answers when retrieval confidence is low or when no exact clause is found.
    • Add a mandatory escalation path for exclusions, claim denials, cancellation terms, and legal interpretation.
  • Monitoring

    • Track citation coverage rate, human escalation rate, hallucination reports, and latency per query.
    • Alert on spikes in “needsHumanReview” because they usually indicate broken ingestion or stale policy versions.

Common Pitfalls

  • Using stale policy documents

    If your index mixes expired and current policies, the agent will answer against old wording. Fix this by versioning every document with effective dates and filtering retrieval by jurisdiction plus issue date.

  • Letting the agent freeform answers

    Insurance Q&A must be grounded in source text. Force structured output and reject any response without citations or with unsupported claims about exclusions, limits, or eligibility.

  • Ignoring compliance review paths

    Some questions are not safe to automate end-to-end. Questions about cancellations, underwriting declines, fraud indicators, or claim denials should trigger human review even if the model sounds confident.


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