How to Build a compliance checking Agent Using CrewAI in Python for lending

By Cyprian AaronsUpdated 2026-04-21
compliance-checkingcrewaipythonlending

A compliance checking agent for lending reviews loan applications, supporting documents, and policy rules before a human underwriter approves the file. It matters because lending decisions are regulated, auditable, and expensive to reverse when you miss something like income verification gaps, adverse action requirements, or jurisdiction-specific disclosure rules.

Architecture

  • Input normalizer
    • Converts raw application data, PDFs, OCR text, and bureau summaries into a consistent payload.
  • Policy retrieval layer
    • Loads lending policy snippets, product rules, and jurisdiction-specific compliance requirements.
  • Compliance analyst agent
    • Checks the application against policy and flags violations, missing evidence, and ambiguous cases.
  • Audit trail store
    • Persists every decision, rule reference, model output, and human override for exam readiness.
  • Escalation router
    • Sends high-risk or low-confidence cases to a human compliance officer or underwriter.
  • Response formatter
    • Produces a structured result that downstream loan origination systems can consume.

Implementation

1. Define the compliance task clearly

For lending, the agent should not “approve” loans. It should classify issues against explicit policy and return a structured findings report.

from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool
from pydantic import BaseModel
from typing import List

class ComplianceFinding(BaseModel):
    rule_id: str
    severity: str
    message: str
    evidence: str

class ComplianceReport(BaseModel):
    status: str
    findings: List[ComplianceFinding]
    needs_human_review: bool

compliance_agent = Agent(
    role="Lending Compliance Analyst",
    goal="Review loan applications against lending policy and flag compliance issues",
    backstory=(
        "You are a senior lending compliance analyst. "
        "You identify missing disclosures, unsupported income claims, "
        "and policy exceptions that require escalation."
    ),
    tools=[SerperDevTool()],
    verbose=True,
)

2. Build a task that forces structured output

Use Task with explicit expected output so the model returns something your system can validate. In production, pair this with JSON schema validation before persisting results.

compliance_task = Task(
    description=(
        "Review the following loan application for compliance issues:\n"
        "{application_json}\n\n"
        "Check for:\n"
        "- Missing identity verification\n"
        "- Income documentation gaps\n"
        "- Debt-to-income policy breaches\n"
        "- Required disclosures by jurisdiction\n"
        "- Any exception requiring human review\n\n"
        "Return only a structured compliance report."
    ),
    expected_output=(
        "A compliance report with status, findings list, and needs_human_review flag."
    ),
    agent=compliance_agent,
)

3. Run the crew and validate the result

This is the actual execution pattern. Keep the application payload small enough to fit context windows; store large documents elsewhere and pass extracted text plus references.

import json

loan_application = {
    "applicant_id": "A-10291",
    "jurisdiction": "US-NY",
    "product": "30Y_FIXED",
    "income_documents": ["paystub_1.pdf", "paystub_2.pdf"],
    "declared_monthly_income": 8500,
    "monthly_debt": 4200,
    "id_verified": False,
    "disclosures_sent": ["privacy_notice"],
}

crew = Crew(
    agents=[compliance_agent],
    tasks=[compliance_task],
    process=Process.sequential,
    verbose=True,
)

result = crew.kickoff(inputs={"application_json": json.dumps(loan_application)})

print(result)

If you want stricter control in a real system, wrap the result in a Pydantic parser after kickoff() and reject anything that does not match your schema.

4. Add lending-specific rule checks outside the LLM

Do not rely on the model alone for hard constraints like DTI thresholds or residency restrictions. Put deterministic checks in code and let CrewAI handle interpretation and exception analysis.

def hard_rule_checks(app: dict) -> list[str]:
    issues = []

    dti = app["monthly_debt"] / app["declared_monthly_income"]
    if dti > 0.45:
        issues.append("DTI exceeds policy limit of 45%")

    if not app.get("id_verified"):
        issues.append("Identity verification missing")

    required_disclosures = {"privacy_notice", "adverse_action_notice"}
    missing = required_disclosures - set(app.get("disclosures_sent", []))
    if missing:
        issues.append(f"Missing disclosures: {', '.join(sorted(missing))}")

    return issues

Use these checks before or after the agent run. If any hard rule fails, mark the case for human review regardless of what the model says.

Production Considerations

  • Keep audit logs immutable
    • Store input payloads, retrieved policy versions, model outputs, timestamps, and reviewer overrides in append-only storage.
  • Separate deterministic rules from LLM reasoning
    • Regulatory thresholds like DTI caps or mandatory disclosures should live in code or rules engines, not prompt text.
  • Respect data residency
    • Loan files often contain PII and financial data. Keep processing in-region and avoid sending raw documents to external tools unless your legal team has approved it.
  • Add confidence-based escalation
    • Route cases with incomplete docs, conflicting evidence, or uncertain classifications to a human compliance reviewer.

Common Pitfalls

  • Using the agent as an approval engine
    • Don’t let it decide “approve” or “deny.” Its job is to detect compliance issues and produce findings for humans or downstream rules.
  • Stuffing full loan files into prompts
    • Large PDFs and scanned documents blow up context windows and leak sensitive data. Extract only relevant fields and store document references separately.
  • Skipping versioned policy inputs
    • If you don’t pin policy versions by date and product line, you can’t explain why one application was flagged differently from another.
  • Ignoring jurisdiction differences
    • Lending rules change by state or country. A New York mortgage file should not be evaluated with generic U.S. consumer credit logic alone.

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