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

By Cyprian AaronsUpdated 2026-04-21
loan-approvalllamaindexpythonwealth-management

A loan approval agent for wealth management evaluates client applications against policy, portfolio context, and compliance rules, then produces a recommendation with a traceable rationale. It matters because private banking and advisory teams need faster decisions without losing control over suitability checks, auditability, and jurisdiction-specific constraints.

Architecture

  • Client data ingestion

    • Pull structured data from CRM, core banking, and portfolio systems.
    • Normalize income, liabilities, AUM, collateral, and KYC fields.
  • Policy and compliance knowledge base

    • Index internal lending policy, suitability rules, AML/KYC procedures, and product constraints.
    • Keep this separate from client data so updates do not require code changes.
  • Decision engine

    • Use LlamaIndex to retrieve relevant policy snippets and client facts.
    • Generate a recommendation: approve, reject, or escalate for manual review.
  • Audit trail

    • Store retrieved sources, model output, timestamps, and decision inputs.
    • This is mandatory for regulated workflows in wealth management.
  • Guardrail layer

    • Block unsupported actions like changing credit policy or fabricating missing documents.
    • Force escalation when confidence is low or required fields are missing.

Implementation

1) Install dependencies and prepare your documents

Use LlamaIndex for retrieval plus a local embedding model if you want to keep data residency under control.

pip install llama-index llama-index-embeddings-huggingface llama-index-llms-openai pydantic

Create separate document sets for policy and client files. In production, client records should come from your internal systems; below is a minimal example using Document objects.

from llama_index.core import Document

policy_docs = [
    Document(text="""
    Wealth management lending policy:
    - Minimum net worth: $500k
    - Debt service ratio must be below 40%
    - Loans above $250k require manual review
    - Any PEP or sanctions match requires escalation
    """, metadata={"source": "lending_policy_v1"})
]

client_docs = [
    Document(text="""
    Client profile:
    - Net worth: $1.2M
    - Annual income: $180k
    - Existing debt service ratio: 28%
    - Jurisdiction: Singapore
    - PEP status: false
    """, metadata={"source": "crm_export_2026_04"})
]

2) Build indexed knowledge bases

Use VectorStoreIndex.from_documents() to create retrieval indexes. For wealth management, I recommend keeping policy and client data in separate indexes so you can control access boundaries more cleanly.

from llama_index.core import VectorStoreIndex

policy_index = VectorStoreIndex.from_documents(policy_docs)
client_index = VectorStoreIndex.from_documents(client_docs)

policy_retriever = policy_index.as_retriever(similarity_top_k=3)
client_retriever = client_index.as_retriever(similarity_top_k=3)

3) Define a structured decision output

Do not let the agent free-form its response. Use a schema so downstream systems can consume the result safely.

from pydantic import BaseModel, Field
from typing import Literal

class LoanDecision(BaseModel):
    decision: Literal["approve", "reject", "manual_review"]
    reason: str = Field(..., description="Short explanation grounded in retrieved evidence")
    risk_flags: list[str] = Field(default_factory=list)

Now wire the retrievers into an LLM-backed query engine. The key pattern is to retrieve evidence first, then ask the model to make a constrained decision using that evidence only.

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

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

def build_context(query: str) -> str:
    policy_nodes = policy_retriever.retrieve(query)
    client_nodes = client_retriever.retrieve(query)

    sections = ["POLICY EVIDENCE:"]
    for node in policy_nodes:
        sections.append(f"- {node.node.text}")

    sections.append("\nCLIENT EVIDENCE:")
    for node in client_nodes:
        sections.append(f"- {node.node.text}")

    return "\n".join(sections)

def decide_loan(query: str) -> LoanDecision:
    context = build_context(query)
    prompt = f"""
You are a loan approval assistant for wealth management.
Use only the evidence below. If required information is missing, choose manual_review.
Return JSON matching this schema:
decision: approve|reject|manual_review
reason: string
risk_flags: list of strings

Evidence:
{context}

Application:
{query}
"""
    response = Settings.llm.complete(prompt)
    
    # Minimal parsing pattern; in production use robust JSON validation.
    import json
    data = json.loads(response.text)
    return LoanDecision(**data)

result = decide_loan("Evaluate this client for a $200k secured loan.")
print(result.model_dump())

4) Add an audit log before returning the decision

Wealth management workflows need traceability. Capture the retrieved evidence and final answer so compliance teams can reconstruct why the agent made a recommendation.

import json
from datetime import datetime

def audit_record(application_id: str, query: str, decision: LoanDecision):
    record = {
        "application_id": application_id,
        "timestamp_utc": datetime.utcnow().isoformat(),
        "query": query,
        "decision": decision.model_dump(),
        "model": "gpt-4o-mini",
        "policy_version": "lending_policy_v1",
        "data_residency": "sg-region",
    }
    
    with open("loan_audit_log.jsonl", "a") as f:
        f.write(json.dumps(record) + "\n")

audit_record("LN-10492", "Evaluate this client for a $200k secured loan.", result)

Production Considerations

  • Deployment

    • Keep policy indexes in-region if your firm has data residency requirements.
    • Separate PII-bearing client retrieval from public or shared knowledge stores.
    • Use environment-based secrets management for model keys and database credentials.
  • Monitoring

    • Track approval rates, manual review rates, retrieval hit quality, and JSON parse failures.
    • Log every source document ID used in each decision.
    • Alert on sudden shifts in reject/approve ratios by jurisdiction or advisor desk.
  • Guardrails

    • Force manual_review when KYC status is stale or sanctions screening is incomplete.
    • Reject any request that asks the model to override policy thresholds.
    • Add deterministic checks outside the LLM for hard rules like minimum net worth or maximum debt ratio.
  • Compliance

    • Version every policy document and preserve historical versions used at decision time.
    • Make sure explanations are short but specific enough for audit and customer communication review.
    • Treat model output as recommendation only unless your legal/compliance team explicitly approves automation level.

Common Pitfalls

  • Mixing policy text with client records in one index

    • This creates noisy retrieval and weak access control.
    • Keep them separate and merge only at decision time.
  • Letting the model decide without hard validation

    • If net worth or debt ratio is missing, the agent may guess.
    • Run explicit rule checks before calling the LLM and force escalation on missing fields.
  • Skipping audit metadata

    • A plain “approved” response is useless during an internal review.
    • Store source IDs, timestamps, model version, and policy version for every decision.

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