How to Build a compliance checking Agent Using CrewAI in TypeScript for banking
A compliance checking agent reviews banking content, customer actions, or internal workflows against policy rules before anything goes live. In practice, that means catching KYC gaps, prohibited language, sanctions-risk phrases, missing disclosures, and policy violations early enough to reduce regulatory exposure and audit pain.
Architecture
- •
Input adapter
- •Pulls the artifact to review: a draft email, chatbot response, loan note, or transaction summary.
- •Normalizes it into a structured payload with metadata like jurisdiction, product type, and customer segment.
- •
Policy retrieval layer
- •Loads the relevant controls from your compliance library.
- •Keeps rules versioned by region: FCA, OCC, FINRA, GDPR, local banking secrecy laws.
- •
Compliance agent
- •Uses CrewAI
Agentto reason over the input against policy. - •Produces a structured verdict: pass, fail, needs human review.
- •Uses CrewAI
- •
Task orchestration
- •Uses CrewAI
TaskandCrewto define the workflow. - •Separates extraction, rule checking, and final decisioning so you can audit each step.
- •Uses CrewAI
- •
Audit logger
- •Persists prompts, outputs, rule versions, timestamps, and reviewer decisions.
- •This is non-negotiable in banking. If you cannot explain the decision later, it is not production-ready.
- •
Human escalation path
- •Routes uncertain cases to a compliance officer.
- •Prevents false confidence on edge cases like cross-border marketing or sanctions-adjacent wording.
Implementation
1) Install dependencies and define the compliance schema
Use the TypeScript package for CrewAI plus a schema validator so outputs are machine-checkable. Banking workflows should never depend on free-form text alone.
npm install @crewai/crewai zod dotenv
import { z } from "zod";
export const ComplianceVerdictSchema = z.object({
status: z.enum(["pass", "fail", "review"]),
riskLevel: z.enum(["low", "medium", "high"]),
findings: z.array(
z.object({
ruleId: z.string(),
issue: z.string(),
severity: z.enum(["minor", "major", "critical"]),
})
),
rationale: z.string(),
});
export type ComplianceVerdict = z.infer<typeof ComplianceVerdictSchema>;
2) Create an agent with banking-specific instructions
The agent should be narrow. Do not ask it to “be helpful” in general terms; ask it to check policy against specific controls and return structured output only.
import "dotenv/config";
import { Agent } from "@crewai/crewai";
export const complianceAgent = new Agent({
role: "Banking Compliance Checker",
goal:
"Review banking content against internal policy and applicable regulatory rules.",
backstory:
"You are a strict compliance analyst for retail and commercial banking. You flag missing disclosures, prohibited claims, sanctions risk indicators, privacy issues, and jurisdictional conflicts.",
verbose: true,
});
3) Define tasks for extraction and compliance review
A two-step workflow works better than one giant prompt. First extract the facts. Then check them against policy.
import { Task } from "@crewai/crewai";
import { complianceAgent } from "./agent.js";
const extractTask = new Task({
description: `
Extract the relevant compliance facts from the input.
Return only JSON with fields:
- productType
- jurisdiction
- customerType
- containsPromotions
- containsPII
- mentionsSanctionsSensitiveTerms
`,
expectedOutput: "Structured JSON facts for downstream compliance review.",
agent: complianceAgent,
});
const reviewTask = new Task({
description: `
Given the extracted facts and the original content, determine whether this passes banking compliance.
Rules:
- Reject if required disclosures are missing.
- Flag if PII appears without a clear business need.
- Escalate if sanctions-sensitive terms appear.
- Respect jurisdiction-specific restrictions.
Return verdict in JSON matching the required schema.
`,
expectedOutput: "A JSON compliance verdict with status, riskLevel, findings, rationale.",
agent: complianceAgent,
});
4) Run the crew and validate output before using it
This is where most teams get sloppy. Always validate the model output before persisting or acting on it.
import { Crew } from "@crewai/crewai";
import { ComplianceVerdictSchema } from "./schema.js";
async function main() {
const crew = new Crew({
agents: [complianceAgent],
tasks: [extractTask as any, reviewTask as any],
verbose: true,
process: "sequential",
});
const result = await crew.kickoff({
inputs: {
content:
"This loan offer guarantees approval within minutes. Please send your SSN and date of birth to proceed.",
jurisdiction: "US",
productType: "consumer-lending",
policyVersion: "2026.01",
},
});
const parsed = ComplianceVerdictSchema.parse(JSON.parse(String(result)));
console.log(parsed);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});
If you want a cleaner production pattern, wrap the result in an application service that stores:
- •raw input
- •prompt template version
- •policy version
- •model output
- •parsed verdict
- •human override decision
That gives you an audit trail regulators can actually follow.
Production Considerations
- •
Deploy inside your controlled environment
- •Keep execution in-region if your bank has data residency constraints.
- •Do not ship customer data to unmanaged endpoints or default SaaS regions without legal approval.
- •
Log everything needed for audit
- •Store prompt version, task version, policy version, model name, timestamps, and final verdict.
- •Make logs immutable or append-only where possible.
- •
Add hard guardrails before LLM execution
- •Redact account numbers, SSNs, passport IDs, card PANs before sending text to the agent.
- •Use deterministic rules first for obvious violations like banned phrases or missing disclaimers.
- •
Route uncertain cases to humans
- •Anything
revieworhighrisk should go to a compliance queue. - •Never auto-release customer-facing content when sanctions screening or cross-border issues are involved.
- •Anything
Common Pitfalls
- •
Treating the agent as a source of truth
- •The model is a reviewer, not a regulator.
- •Avoid this by pairing it with deterministic policy checks and mandatory human escalation on high-risk cases.
- •
Sending raw sensitive data into prompts
- •Banking data often includes PII, account details, and confidential transaction context.
- •Avoid this by redacting sensitive fields upstream and using minimal context necessary for review.
- •
Skipping schema validation
- •Free-form text responses break downstream automation fast.
- •Avoid this by enforcing Zod validation on every output before storage or actioning.
- •
Ignoring jurisdictional policy drift
- •A rule valid in one market can be illegal in another.
- •Avoid this by versioning policies per region and passing jurisdiction explicitly into every task.
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