How to Build a underwriting Agent Using AutoGen in Python for retail banking

By Cyprian AaronsUpdated 2026-04-21
underwritingautogenpythonretail-banking

A retail banking underwriting agent automates the first pass of credit decisions: it pulls applicant data, checks policy rules, scores risk, and produces a decision package for a human underwriter or downstream workflow. It matters because retail banks need faster approvals without losing control over compliance, auditability, and consistent application of lending policy.

Architecture

  • Applicant intake layer

    • Receives structured application data from LOS, CRM, or API gateway.
    • Normalizes fields like income, employment status, debt obligations, and consent flags.
  • Policy and compliance agent

    • Encodes lending policy constraints.
    • Checks hard rules such as minimum age, residency, KYC status, and prohibited attributes.
  • Risk analysis agent

    • Evaluates affordability, debt-to-income ratio, and basic credit risk signals.
    • Produces a reasoned recommendation with evidence from the input payload.
  • Audit logger

    • Stores every prompt, tool call, model output, and final decision.
    • Required for model governance, dispute handling, and internal audit review.
  • Human review handoff

    • Escalates borderline or policy-sensitive cases to an underwriter.
    • Prevents the agent from making final decisions on unsupported cases.
  • AutoGen orchestration layer

    • Coordinates the agents using AssistantAgent, UserProxyAgent, and GroupChat.
    • Handles turn-taking and termination logic.

Implementation

  1. Install AutoGen and define your data contract

For retail banking, keep the input schema strict. Don’t let free-form text drive underwriting logic unless you’ve already validated it against your policy engine.

from dataclasses import dataclass
from typing import Optional

@dataclass
class Applicant:
    applicant_id: str
    country: str
    age: int
    monthly_income: float
    monthly_debt_payments: float
    employment_status: str
    has_kyc_passed: bool
    consent_to_process: bool
    requested_amount: float
    purpose: str
  1. Create AutoGen agents with explicit roles

Use separate agents for policy review and risk analysis. That keeps responsibilities clear and makes audit trails easier to interpret later.

import autogen

llm_config = {
    "model": "gpt-4o-mini",
    "api_key": "YOUR_OPENAI_API_KEY",
}

policy_agent = autogen.AssistantAgent(
    name="policy_agent",
    llm_config=llm_config,
    system_message=(
        "You are a retail banking underwriting policy checker. "
        "Only approve applications that satisfy policy constraints. "
        "If required data is missing or consent is false, reject escalation."
    ),
)

risk_agent = autogen.AssistantAgent(
    name="risk_agent",
    llm_config=llm_config,
    system_message=(
        "You are a retail banking risk analyst. "
        "Assess affordability using DTI and income stability. "
        "Return a concise recommendation with reasons."
    ),
)

user_proxy = autogen.UserProxyAgent(
    name="underwriting_orchestrator",
    human_input_mode="NEVER",
)
  1. Wire the conversation with a group chat

This is the core pattern. The orchestrator sends the applicant payload to both agents, collects their outputs, and terminates when a final recommendation is produced.

from autogen import GroupChat, GroupChatManager

applicant = Applicant(
    applicant_id="A12345",
    country="KE",
    age=34,
    monthly_income=180000.0,
    monthly_debt_payments=42000.0,
    employment_status="permanent",
    has_kyc_passed=True,
    consent_to_process=True,
    requested_amount=250000.0,
    purpose="personal loan",
)

message = f"""
Evaluate this retail banking loan application.

Applicant:
{applicant}

Return:
1) Policy eligibility verdict
2) Risk assessment verdict
3) Final recommendation: APPROVE / REFER / DECLINE
4) Reasons suitable for audit logging
"""

groupchat = GroupChat(
    agents=[user_proxy, policy_agent, risk_agent],
    messages=[],
    max_round=4,
)

manager = GroupChatManager(groupchat=groupchat, llm_config=llm_config)

result = user_proxy.initiate_chat(
    manager,
    message=message,
)

print(result.summary if hasattr(result, "summary") else "No summary available")
  1. Add deterministic pre-checks before the LLM call

This is where most banking teams get better outcomes than by asking the model to do everything. Hard rules should be enforced in Python before any agent reasoning starts.

def precheck(applicant: Applicant) -> list[str]:
    violations = []

    if not applicant.consent_to_process:
        violations.append("Missing consent to process personal data")
    if not applicant.has_kyc_passed:
        violations.append("KYC not completed")
    if applicant.age < 18:
        violations.append("Applicant below minimum age")
    
dti = applicant.monthly_debt_payments / max(applicant.monthly_income, 1)
if dti > 0.5:
        violations.append(f"DTI too high: {dti:.2f}")

return violations

violations = precheck(applicant)
if violations:
    print({"decision": "DECLINE", "reasons": violations})
else:
    print("Proceed to AutoGen workflow")

Production Considerations

  • Keep model decisions non-final for regulated credit

    • Use the agent for recommendation only.
    • Final approval should remain with a rules engine or human underwriter when required by policy.
  • Log everything for auditability

    • Persist input payloads, prompts, outputs, timestamps, model version, and rule outcomes.
    • Store logs in an immutable audit store tied to applicant_id.
  • Enforce data residency

    • Route customer data to region-approved infrastructure only.
    • Redact PII before sending anything unnecessary to the LLM.
  • Add guardrails around adverse action reasons

    • Ensure reasons are based on approved factors like income stability or DTI.
    • Never let the model generate prohibited explanations tied to protected attributes.

Common Pitfalls

  • Letting the LLM replace policy logic

    • Mistake: asking the agent to decide eligibility from scratch.
    • Fix: run deterministic eligibility checks first; use AutoGen for reasoning and summarization only.
  • Sending raw sensitive data into prompts

    • Mistake: passing full statements, IDs, or unredacted KYC documents.
    • Fix: minimize prompt payloads and redact anything not needed for underwriting analysis.
  • Skipping traceability across agent turns

    • Mistake: storing only the final answer.
    • Fix: capture each message in the GroupChat flow so compliance teams can reconstruct how the recommendation was produced.

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