How to Build a compliance checking Agent Using AutoGen in TypeScript for wealth management
A compliance checking agent for wealth management reviews client-facing content, recommendations, and advisor actions against policy before they go out the door. It matters because a single unsuitable recommendation, missing disclosure, or data handling mistake can create regulatory exposure, audit failures, and client harm.
Architecture
Build this agent as a small workflow, not a single prompt.
- •
Input normalizer
- •Takes emails, chat drafts, notes, trade rationale, or suitability summaries.
- •Converts them into a structured payload with client profile, product details, jurisdiction, and channel.
- •
Policy engine
- •Encodes firm rules like suitability thresholds, restricted products, disclosure requirements, and communication approvals.
- •Returns machine-readable findings such as
pass,review, orblock.
- •
AutoGen assistant agent
- •Uses an LLM to inspect the payload and explain policy violations in plain English.
- •Produces structured output for downstream systems.
- •
Audit logger
- •Stores input hashes, model output, policy version, timestamps, and reviewer decisions.
- •This is non-negotiable in wealth management.
- •
Human escalation path
- •Routes borderline cases to compliance officers.
- •Keeps the agent advisory unless policy confidence is high enough to auto-block.
- •
Data boundary layer
- •Redacts PII where possible and enforces residency rules.
- •Prevents client data from leaving approved regions or vendors.
Implementation
1) Install AutoGen for TypeScript and define your compliance schema
Use the TypeScript AutoGen package and keep the agent output strict. Wealth management workflows fail when the model returns vague prose instead of structured findings.
npm install @autogenai/autogen zod
import { AssistantAgent } from "@autogenai/autogen";
import { z } from "zod";
const ComplianceFindingSchema = z.object({
verdict: z.enum(["pass", "review", "block"]),
reasons: z.array(z.string()),
missingDisclosures: z.array(z.string()),
riskFlags: z.array(z.string()),
});
type ComplianceFinding = z.infer<typeof ComplianceFindingSchema>;
type ComplianceInput = {
advisorId: string;
clientId: string;
jurisdiction: "US" | "UK" | "EU" | "CA";
channel: "email" | "sms" | "crm_note" | "proposal";
content: string;
clientRiskProfile: "conservative" | "balanced" | "growth" | "aggressive";
productType: string;
};
2) Create the compliance agent with a strict system message
The assistant should act like a policy reviewer, not a financial advisor. Make it produce concise findings tied to suitability, disclosures, restricted language, and recordkeeping.
const complianceAgent = new AssistantAgent({
name: "wealth_compliance_agent",
systemMessage: `
You are a compliance checking agent for wealth management.
Check content for suitability issues, missing disclosures, misleading performance claims,
restricted product language, unsuitable recommendations, and recordkeeping concerns.
Return only valid JSON matching:
{
"verdict": "pass" | "review" | "block",
"reasons": string[],
"missingDisclosures": string[],
"riskFlags": string[]
}
Rules:
- Flag any performance guarantees.
- Flag advice that conflicts with stated risk profile.
- Flag missing jurisdiction-specific disclosures.
- Flag potentially non-compliant marketing language.
- Do not provide investment advice.
`,
});
3) Wrap the model call with validation and an audit trail
This pattern is what you want in production: call the agent, validate output with Zod, then persist both input metadata and result. If validation fails, route to human review instead of guessing.
async function checkCompliance(input: ComplianceInput): Promise<ComplianceFinding> {
const prompt = `
Review this wealth management communication for compliance:
Advisor ID: ${input.advisorId}
Client ID: ${input.clientId}
Jurisdiction: ${input.jurisdiction}
Channel: ${input.channel}
Client Risk Profile: ${input.clientRiskProfile}
Product Type: ${input.productType}
Content:
${input.content}
`;
const result = await complianceAgent.run(prompt);
const text =
typeof result === "string"
? result
: JSON.stringify(result);
const parsed = ComplianceFindingSchema.safeParse(JSON.parse(text));
if (!parsed.success) {
return {
verdict: "review",
reasons: ["Model output failed schema validation"],
missingDisclosures: [],
riskFlags: ["invalid_model_output"],
};
}
return parsed.data;
}
async function auditDecision(input: ComplianceInput, finding: ComplianceFinding) {
// Store hashes + metadata in your audit store here.
// Keep raw content in-region if required by policy.
console.log({
advisorIdHash: input.advisorId,
clientIdHash: input.clientId,
jurisdiction: input.jurisdiction,
verdict: finding.verdict,
riskFlags: finding.riskFlags,
timestamp: new Date().toISOString(),
policyVersion: "2026.04",
});
}
4) Add deterministic policy gates before the LLM
Do not rely on the model for everything. In wealth management you need hard stops for obvious violations like guaranteed returns or restricted products.
function hardPolicyGate(input: ComplianceInput): ComplianceFinding | null {
if (/guaranteed return|risk[- ]free|no loss/i.test(input.content)) {
return {
verdict: "block",
reasons: ["Prohibited performance guarantee language detected"],
missingDisclosures: [],
riskFlags: ["marketing_misrepresentation"],
};
}
if (input.jurisdiction === "US" && /crypto\s+derivatives/i.test(input.productType)) {
return {
verdict: "review",
reasons: ["Restricted product requires manual review"],
missingDisclosures: ["Product-specific regulatory disclosure"],
riskFlags: ["restricted_product"],
};
}
return null;
}
Then orchestrate both layers:
export async function runComplianceCheck(input: ComplianceInput) {
const gateResult = hardPolicyGate(input);
if (gateResult) {
await auditDecision(input, gateResult);
return gateResult;
}
const llmResult = await checkCompliance(input);
await auditDecision(input, llmResult);
return llmResult;
}
Production Considerations
- •
Deploy inside your approved data boundary
- •For wealth management clients in regulated regions, keep prompts and logs in-region.
- •If your firm has residency constraints for EU or UK data, do not ship raw client text to an unapproved endpoint.
- •
Log every decision with policy versioning
- •Store the exact rule set used for each check.
- •When compliance asks why something passed last month but fails today, you need reproducibility.
- •
Use confidence-based routing
- •Auto-block clear violations like guaranteed returns or missing mandatory disclaimers.
- •Send ambiguous cases to human review instead of letting the model decide alone.
- •
Monitor drift on real advisor language
- •Advisors change templates over time.
- •Track false positives on common phrases like “target allocation,” “illustrative,” or “not guaranteed” so you do not create alert fatigue.
Common Pitfalls
- •
Treating the LLM as the policy engine
- •Bad move. The model should interpret and explain policy; deterministic checks should enforce hard rules first.
- •Fix it by putting regex/rule-based gates ahead of AutoGen.
- •
Skipping structured output validation
- •If you parse free-form text directly into downstream systems, you will break incidentally on malformed responses.
- •Fix it with Zod validation and a fallback verdict of
review.
- •
Ignoring auditability and residency
- •In wealth management, “the model said so” is not an audit trail.
- •Fix it by storing input hashes, policy versions, timestamps, regional storage location, and reviewer overrides.
- •
Letting the agent see more data than it needs
- •Excess PII increases risk without improving compliance accuracy much.
- •Fix it by redacting account numbers, tax IDs, and unnecessary free-text before sending anything to the agent.
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