How to Build a compliance checking Agent Using CrewAI in TypeScript for pension funds

By Cyprian AaronsUpdated 2026-04-21
compliance-checkingcrewaitypescriptpension-funds

A compliance checking agent for pension funds reviews member communications, investment actions, contribution flows, and policy documents against regulatory rules before anything reaches a human approver. It matters because pension operations are high-trust, heavily regulated, and expensive to fix after the fact; a missed disclosure, unsuitable investment instruction, or residency violation can turn into an audit finding fast.

Architecture

Build this agent as a narrow workflow, not a general-purpose assistant.

  • Input adapter

    • Pulls pension fund artifacts from your system of record: member letters, trustee reports, contribution files, investment change requests, and policy PDFs.
    • Normalizes them into a single task payload with metadata like jurisdiction, scheme type, and effective date.
  • Compliance rule context

    • Loads pension-specific policies: disclosure rules, contribution limits, transfer rules, KYC/AML checks, retention requirements, and data residency constraints.
    • Keeps rules versioned so every decision is traceable to the exact policy set used.
  • Review agent

    • Uses an LLM to classify risk, extract obligations, and flag violations.
    • Produces structured output: pass/fail, severity, violated rule IDs, and recommended next action.
  • Audit logger

    • Stores every input, output, prompt version, model version, and rule version.
    • This is non-negotiable for pension funds where you need defensible evidence during audits.
  • Human escalation queue

    • Routes high-risk or ambiguous cases to compliance officers.
    • Prevents the agent from making final decisions on regulated edge cases.

Implementation

1) Install CrewAI and define the compliance task shape

Use TypeScript with a thin wrapper around CrewAI. Keep the task payload structured so the model is not guessing at free-form text.

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

export const ComplianceCaseSchema = z.object({
  caseId: z.string(),
  jurisdiction: z.string(),
  schemeType: z.enum(["DB", "DC", "Hybrid"]),
  documentType: z.enum(["member_letter", "trustee_report", "transfer_request", "investment_change"]),
  content: z.string(),
  policyVersion: z.string(),
  dataResidencyRegion: z.string()
});

export type ComplianceCase = z.infer<typeof ComplianceCaseSchema>;

2) Create the compliance agent and task

The actual CrewAI pattern is new Agent(...), new Task(...), and new Crew(...). For pension funds, make the role explicit and constrain the output format.

// src/complianceCrew.ts
import { Agent, Task, Crew } from "@crewai/crewai";
import { ComplianceCase } from "./types";

export function buildComplianceCrew(input: ComplianceCase) {
  const complianceAgent = new Agent({
    role: "Pension Fund Compliance Reviewer",
    goal:
      "Review pension fund documents for regulatory breaches, policy violations, and data residency issues.",
    backstory:
      "You are a senior compliance analyst specializing in pension fund operations, trustee governance, transfer rules, disclosures, and record retention.",
    verbose: true,
    allowDelegation: false
  });

  const reviewTask = new Task({
    description: `
Review this pension fund case for compliance issues.

Jurisdiction: ${input.jurisdiction}
Scheme type: ${input.schemeType}
Document type: ${input.documentType}
Policy version: ${input.policyVersion}
Data residency region: ${input.dataResidencyRegion}

Content:
${input.content}

Return strict JSON with:
- decision: PASS | REVIEW | FAIL
- severity: LOW | MEDIUM | HIGH
- violatedRules: array of strings
- rationale: short explanation
- escalationRequired: boolean
- auditNotes: string
`,
    expectedOutput: "Strict JSON only",
    agent: complianceAgent
  });

  return new Crew({
    agents: [complianceAgent],
    tasks: [reviewTask],
    verbose: true
  });
}

3) Run the crew and validate output before storing it

Do not trust raw model output. Parse it with Zod and reject anything that does not match your schema. That gives you a predictable contract for downstream workflow engines.

// src/index.ts
import { ComplianceCaseSchema } from "./types";
import { buildComplianceCrew } from "./complianceCrew";
import { z } from "zod";

const ReviewResultSchema = z.object({
  decision: z.enum(["PASS", "REVIEW", "FAIL"]),
  severity: z.enum(["LOW", "MEDIUM", "HIGH"]),
  violatedRules: z.array(z.string()),
  rationale: z.string(),
  escalationRequired: z.boolean(),
  auditNotes: z.string()
});

async function main() {
  const input = ComplianceCaseSchema.parse({
    caseId: "CASE-10291",
    jurisdiction: "UK",
    schemeType: "DB",
    documentType: "transfer_request",
    content:
      "Member requests transfer to overseas arrangement. No evidence of scam warning acknowledgement included.",
    policyVersion: "2026.01",
    dataResidencyRegion: "eu-west-1"
  });

  const crew = buildComplianceCrew(input);
  const result = await crew.kickoff();

  const parsed = ReviewResultSchema.parse(JSON.parse(String(result)));
  console.log(parsed);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

4) Add a human-in-the-loop gate for high-risk cases

For pension funds, anything involving transfers out of scheme assets, cross-border processing, or missing disclosures should be escalated. The agent should recommend action; it should not approve edge cases autonomously.

type Decision = {
  decision: "PASS" | "REVIEW" | "FAIL";
};

function requiresHumanReview(result: Decision): boolean {
  return result.decision !== "PASS";
}

Production Considerations

  • Keep data residency explicit

    • If your fund operates in the UK or EU, ensure prompts and outputs stay in approved regions.
    • Do not send member PII to a model endpoint outside your legal boundary without a documented transfer mechanism.
  • Version everything

    • Store policyVersion, prompt template hash, model name, and CrewAI package version with each decision.
    • Auditors will ask why one case passed last month and failed this month.
  • Add deterministic guardrails

    • Use schema validation on every response.
    • Block any output that lacks rule IDs or tries to make unsupported legal claims like “this is fully compliant.”
  • Monitor by exception rate

    • Track pass/review/fail rates by document type and jurisdiction. A spike in REVIEW on transfer requests usually means either policy drift or bad upstream extraction.

Common Pitfalls

  1. Using the agent as the final authority

    • Bad pattern for pensions.
    • Fix it by routing all high-risk outcomes to a human compliance officer with an immutable audit trail.
  2. Letting free-text prompts define policy

    • If the policy lives only in prose inside the prompt, you cannot prove what rule was applied.
    • Fix it by keeping rules in versioned source files or a rules service and injecting only the relevant subset per case.
  3. Ignoring jurisdictional differences

    • UK DB transfers are not processed like EU DC contribution checks.
    • Fix it by passing jurisdiction and schemeType into every task so the agent evaluates against the right control set.
  4. Skipping response validation

    • Model output will drift under load or after prompt changes.
    • Fix it by parsing every response with Zod before persisting or acting on it.

A pension compliance agent is useful only if it is boringly reliable. Keep the scope narrow, force structured outputs, log everything that matters for auditability, and make human review part of the design rather than an exception you bolt on later.


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