How to Build a loan approval Agent Using LlamaIndex in Python for insurance

By Cyprian AaronsUpdated 2026-04-21
loan-approvalllamaindexpythoninsurance

A loan approval agent for insurance reviews applicant data, policy context, and underwriting rules, then produces a recommendation with evidence. It matters because insurance decisions need consistency, auditability, and compliance; a good agent reduces manual review time without turning the process into an untraceable black box.

Architecture

  • Document ingestion layer
    • Pulls policy PDFs, underwriting guides, KYC notes, claim history, and product eligibility rules into a retrievable index.
  • Retrieval layer
    • Uses VectorStoreIndex plus metadata filters to fetch only the relevant insurance documents for a given applicant.
  • Decision engine
    • Applies structured rules for hard stops like sanctions hits, missing KYC, or policy exclusions before any LLM reasoning.
  • LLM reasoning layer
    • Uses an LLM through LlamaIndex to summarize evidence and draft an approve/reject/manual-review recommendation.
  • Audit layer
    • Persists inputs, retrieved chunks, model output, and final decision for compliance review and model governance.
  • Guardrail layer
    • Prevents unsupported decisions by forcing citations and rejecting outputs that lack policy evidence.

Implementation

  1. Load insurance documents into a retrievable index

Start with the underwriting material you actually use in production: product terms, eligibility rules, fraud indicators, and internal SOPs. In LlamaIndex, the common pattern is to build a VectorStoreIndex from Document objects.

from llama_index.core import Document, VectorStoreIndex

docs = [
    Document(
        text="""
        Insurance loan approval policy:
        - Reject if KYC is incomplete.
        - Reject if applicant appears on sanctions list.
        - Manual review if debt-to-income ratio > 0.45.
        - Approve only if all mandatory disclosures are signed.
        """,
        metadata={"source": "underwriting_policy_v1", "type": "policy"}
    ),
    Document(
        text="""
        Product eligibility:
        - Minimum age 21
        - Employment history >= 12 months
        - No active fraud investigation
        """,
        metadata={"source": "product_rules_v3", "type": "product"}
    ),
]

index = VectorStoreIndex.from_documents(docs)
query_engine = index.as_query_engine(similarity_top_k=3)
  1. Add a deterministic pre-check before the LLM

Do not ask the model to infer obvious compliance failures. Hard rules should short-circuit the workflow so your agent stays explainable and predictable.

def hard_rule_check(applicant: dict) -> tuple[bool, str]:
    if not applicant.get("kyc_complete", False):
        return False, "Reject: KYC incomplete"
    if applicant.get("sanctions_hit", False):
        return False, "Reject: sanctions match"
    if applicant.get("mandatory_disclosures_signed", False) is False:
        return False, "Reject: missing mandatory disclosures"
    return True, "Passed hard checks"

applicant = {
    "name": "Jane Doe",
    "kyc_complete": True,
    "sanctions_hit": False,
    "mandatory_disclosures_signed": True,
    "debt_to_income": 0.52,
}

passed, reason = hard_rule_check(applicant)
print(passed, reason)
  1. Use LlamaIndex to retrieve evidence and generate a decision

This is where the agent becomes useful. Retrieve the relevant policy text, pass it alongside structured applicant data, and force the model to return a concise decision with rationale.

from llama_index.core import Settings
from llama_index.llms.openai import OpenAI

Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0)

def build_decision_prompt(applicant: dict) -> str:
    return f"""
You are assisting an insurance underwriting team.
Use only the provided policy evidence to make a recommendation.

Applicant data:
- name: {applicant["name"]}
- debt_to_income: {applicant["debt_to_income"]}
- kyc_complete: {applicant["kyc_complete"]}
- sanctions_hit: {applicant["sanctions_hit"]}
- mandatory_disclosures_signed: {applicant["mandatory_disclosures_signed"]}

Return one of:
- APPROVE
- REJECT
- MANUAL_REVIEW

Include:
1) decision
2) short rationale
3) cited policy evidence from retrieval
"""

if passed:
    response = query_engine.query(build_decision_prompt(applicant))
    print(str(response))
  1. Wrap it in an auditable service boundary

For production insurance workflows, store every step: raw applicant payload, rule-check result, retrieved nodes, final answer, timestamp, and model version. That gives you traceability during audits and disputes.

import json
from datetime import datetime

def approve_loan(applicant: dict):
    passed, reason = hard_rule_check(applicant)
    audit_record = {
        "timestamp": datetime.utcnow().isoformat(),
        "applicant": applicant,
        "hard_check_result": {"passed": passed, "reason": reason},
    }

    if not passed:
        audit_record["decision"] = reason.split(": ")[0]
        return audit_record

    response = query_engine.query(build_decision_prompt(applicant))
    audit_record["decision"] = str(response)
    return audit_record

result = approve_loan(applicant)
print(json.dumps(result, indent=2))

Production Considerations

  • Keep compliance rules outside the LLM
    • Sanctions screening, KYC completeness, consent checks, and mandatory disclosure validation should be deterministic code or upstream services.
  • Log retrieval context for audits
    • Store the exact chunks returned by VectorStoreIndex queries so reviewers can reconstruct why a decision was made.
  • Control data residency
    • If you handle regulated customer data in specific jurisdictions, keep embeddings and vector stores in-region and avoid sending raw PII to external systems unless your legal team has approved it.
  • Add guardrails around output format
    • Force structured outputs like APPROVE, REJECT, or MANUAL_REVIEW, then validate them before downstream execution.

Common Pitfalls

  • Letting the model decide on hard compliance failures
    • Fix this by checking KYC/sanctions/disclosure status before any retrieval or generation step.
  • Retrieving too much irrelevant policy text
    • Fix this by splitting documents cleanly and using metadata like source, type, jurisdiction, and product_line.
  • Skipping audit logging
    • Fix this by persisting applicant input, retrieved nodes, prompt text, model output, and final action in an immutable store.

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