How to Build a underwriting Agent Using AutoGen in Python for insurance
An underwriting agent automates the first pass of risk evaluation for insurance submissions. It reads the application, pulls out key facts, checks them against underwriting rules, and produces a recommendation with reasons and evidence. That matters because underwriters spend a lot of time on repetitive triage, and insurers need faster decisions without losing auditability or control.
Architecture
- •
Submission intake service
- •Accepts structured application data, documents, and attachments.
- •Normalizes fields like applicant type, location, coverage requested, and loss history.
- •
Policy/rules retrieval layer
- •Pulls underwriting guidelines, appetite rules, exclusions, and referral thresholds.
- •Keeps product-specific logic outside the model prompt.
- •
AutoGen agent group
- •Uses a
UserProxyAgentto orchestrate execution. - •Uses one or more
AssistantAgentinstances for extraction, risk analysis, and recommendation.
- •Uses a
- •
Decision engine
- •Converts model output into a deterministic underwriting outcome:
- •approve
- •refer
- •decline
- •Applies hard stops for compliance and eligibility.
- •Converts model output into a deterministic underwriting outcome:
- •
Audit trail store
- •Persists prompts, tool calls, retrieved rules, model outputs, and final decision.
- •Required for dispute handling and regulatory review.
- •
Human review queue
- •Routes borderline cases to an underwriter when confidence is low or rules require manual approval.
Implementation
1) Install AutoGen and define your underwriting schema
Use a strict input/output contract. In insurance workflows, free-form JSON is not enough unless you validate it before any decision logic runs.
pip install pyautogen pydantic
from typing import Literal, Optional
from pydantic import BaseModel, Field
class UnderwritingSubmission(BaseModel):
applicant_name: str
line_of_business: Literal["commercial_auto", "property", "general_liability"]
state: str
annual_revenue: float = Field(gt=0)
years_in_business: int = Field(ge=0)
prior_losses_3y: int = Field(ge=0)
coverage_limit: float = Field(gt=0)
class UnderwritingDecision(BaseModel):
decision: Literal["approve", "refer", "decline"]
risk_score: int = Field(ge=0, le=100)
reasons: list[str]
referral_notes: Optional[str] = None
2) Create the AutoGen agents
The pattern here is simple: one assistant extracts/assesses risk; the user proxy executes the conversation and can call tools. For production underwriting, keep the assistant constrained to analysis and let deterministic code make the final call.
import os
from autogen import AssistantAgent, UserProxyAgent
llm_config = {
"model": "gpt-4o-mini",
"api_key": os.environ["OPENAI_API_KEY"],
}
underwriter_agent = AssistantAgent(
name="underwriter_agent",
llm_config=llm_config,
system_message=(
"You are an insurance underwriting assistant. "
"Analyze submissions against underwriting guidelines. "
"Return concise risk factors and a recommendation. "
"Never invent policy terms. If data is missing, say so."
),
)
user_proxy = UserProxyAgent(
name="user_proxy",
human_input_mode="NEVER",
code_execution_config=False,
)
3) Add deterministic rules and run the conversation
This is where you prevent the model from making final business decisions on its own. The agent proposes; your rule engine disposes.
def apply_underwriting_rules(submission: UnderwritingSubmission) -> UnderwritingDecision:
reasons = []
if submission.state in {"AK", "LA"} and submission.line_of_business == "property":
return UnderwritingDecision(
decision="decline",
risk_score=95,
reasons=["Property not eligible in this state per appetite rules."]
)
if submission.prior_losses_3y >= 3:
reasons.append("Three or more prior losses in the last 3 years.")
if submission.coverage_limit > 5000000:
reasons.append("Requested limit exceeds straight-through threshold.")
if submission.years_in_business < 2:
reasons.append("Insufficient operating history.")
if len(reasons) >= 2:
return UnderwritingDecision(decision="refer", risk_score=72, reasons=reasons)
return UnderwritingDecision(decision="approve", risk_score=28, reasons=reasons or ["Within appetite."])
submission = UnderwritingSubmission(
applicant_name="Acme Logistics LLC",
line_of_business="commercial_auto",
state="TX",
annual_revenue=4200000,
years_in_business=4,
prior_losses_3y=1,
coverage_limit=1000000,
)
prompt = f"""
Review this insurance submission:
{submission.model_dump_json(indent=2)}
Provide:
1. Key risk factors
2. Missing information
3. A recommendation of approve/refer/decline with brief rationale
"""
chat_result = user_proxy.initiate_chat(
underwriter_agent,
message=prompt,
)
print(chat_result.summary)
print(apply_underwriting_rules(submission).model_dump_json(indent=2))
4) Persist audit evidence
For insurance use cases, every recommendation needs traceability. Store the exact input payload, model output, rule version, timestamp, and reviewer identity if a human overrides it.
import json
from datetime import datetime
def write_audit_record(submission: UnderwritingSubmission, model_summary: str):
record = {
"timestamp_utc": datetime.utcnow().isoformat(),
"submission": submission.model_dump(),
"model_summary": model_summary,
"rule_version": "uw-rules-v1.3",
"decision_source": "autogen_plus_rules_engine",
}
with open("underwriting_audit.jsonl", "a", encoding="utf-8") as f:
f.write(json.dumps(record) + "\n")
write_audit_record(submission, chat_result.summary)
Production Considerations
- •
Keep PII inside your boundary
- •Redact SSNs, driver license numbers, bank data, and medical details before sending context to the model.
- •If you operate across regions, enforce data residency so submissions stay in approved jurisdictions.
- •
Separate recommendation from decision
- •The LLM should propose risk factors.
- •A deterministic policy engine should enforce eligibility thresholds, referral rules, sanctions checks, and product appetite.
- •
Log everything needed for audit
- •Persist prompts, retrieved guideline versions, outputs, overrides, timestamps, and user IDs.
- •Regulators will care about why a policy was referred or declined.
- •
Monitor drift by line of business
- •Track approval rates, referral rates, override rates by state/product/channel.
- •If one channel suddenly spikes referrals or declines after a prompt change, treat that as a regression.
Common Pitfalls
- •
Letting the model make binding underwriting decisions
- •Avoid this by using the LLM only for extraction and analysis.
- •Final disposition should come from explicit business rules plus human approval where required.
- •
Embedding underwriting rules directly in prompts
- •Don’t hardcode appetite logic in natural language alone.
- •Store rules in versioned code or a policy service so changes are testable and auditable.
- •
Skipping validation on incoming submissions
- •Validate every field with schemas like Pydantic before calling AutoGen.
- •Bad inputs create bad recommendations fast: wrong state codes, negative limits, missing loss history all poison downstream decisions.
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