How to Build a loan approval Agent Using LlamaIndex in Python for lending
A loan approval agent helps a lending team turn borrower data, policy rules, and internal knowledge into a consistent decision workflow. In practice, it can pre-screen applications, retrieve the right policy clauses, explain why a file is approvable or not, and produce an audit trail that compliance teams can review.
Architecture
- •
Application intake layer
- •Normalizes borrower inputs: income, debt, employment, requested amount, jurisdiction, and product type.
- •Pulls structured fields from your LOS or CRM before the agent runs.
- •
Policy retrieval layer
- •Indexes underwriting manuals, credit policy docs, regulatory guidance, and product eligibility rules.
- •Uses LlamaIndex retrieval to fetch only the relevant passages for each application.
- •
Decision engine
- •Combines application facts with retrieved policy context.
- •Produces a recommendation such as
approve,refer, ordeclinewith explicit reasons.
- •
Guardrail and validation layer
- •Enforces hard rules like minimum credit score thresholds, debt-to-income caps, and prohibited attributes.
- •Blocks unsupported decisions and forces human review when confidence is low.
- •
Audit logging layer
- •Stores inputs, retrieved evidence, model output, and final decision.
- •Gives compliance teams traceability for fair lending reviews and adverse action workflows.
- •
Human review workflow
- •Routes edge cases to an underwriter.
- •Captures override reasons so the system learns from exceptions without silently drifting.
Implementation
1. Load lending policy documents into a LlamaIndex index
Use SimpleDirectoryReader to load policy files and VectorStoreIndex to make them retrievable. For lending systems, keep these docs versioned by product and jurisdiction.
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.settings import Settings
from llama_index.llms.openai import OpenAI
# Configure your LLM
Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0)
# Load underwriting policies
documents = SimpleDirectoryReader(
input_dir="./lending_policies"
).load_data()
# Build searchable index
policy_index = VectorStoreIndex.from_documents(documents)
policy_retriever = policy_index.as_retriever(similarity_top_k=3)
2. Define the application payload and retrieve relevant policy context
Keep borrower data structured. The agent should not guess on core financial fields; those should come from validated upstream systems.
from dataclasses import dataclass
@dataclass
class LoanApplication:
applicant_id: str
income_monthly: float
existing_debt_monthly: float
requested_amount: float
credit_score: int
employment_months: int
state: str
product_type: str
def build_query(app: LoanApplication) -> str:
return (
f"Loan approval review for {app.product_type} in {app.state}. "
f"Credit score={app.credit_score}, monthly income={app.income_monthly}, "
f"monthly debt={app.existing_debt_monthly}, requested amount={app.requested_amount}, "
f"employment months={app.employment_months}. "
"Return applicable underwriting rules and any exceptions."
)
application = LoanApplication(
applicant_id="A12345",
income_monthly=8500,
existing_debt_monthly=2100,
requested_amount=25000,
credit_score=712,
employment_months=26,
state="TX",
product_type="personal_loan",
)
query = build_query(application)
nodes = policy_retriever.retrieve(query)
policy_context = "\n\n".join([node.node.get_content() for node in nodes])
3. Run a structured decision prompt through LlamaIndex’s query engine pattern
For production lending flows, use a fixed prompt that forces the model to return a constrained output. Here we use PromptTemplate with a SummaryIndex-style query flow over the retrieved context.
from llama_index.core import Document
from llama_index.core.indices.list import SummaryIndex
from llama_index.core.prompts import PromptTemplate
decision_prompt = PromptTemplate(
"""
You are a lending decision assistant.
Use only the provided policy context and application data.
Do not use protected characteristics or infer anything about them.
Return:
1) decision: approve | refer | decline
2) reasons: bullet list
3) required_actions: bullet list if refer or decline
Application:
{application_data}
Policy Context:
{policy_context}
"""
)
doc = Document(text=policy_context)
summary_index = SummaryIndex.from_documents([doc])
query_engine = summary_index.as_query_engine(response_mode="compact")
response = query_engine.query(
decision_prompt.format(
application_data=str(application),
policy_context=policy_context,
)
)
print(response)
4. Add hard guardrails before finalizing the outcome
The model should recommend; your code should decide whether that recommendation is allowed. This is where you enforce lending rules like DTI ceilings or minimum score thresholds.
def hard_rule_check(app: LoanApplication) -> list[str]:
violations = []
dti = app.existing_debt_monthly / app.income_monthly
if dti > 0.45:
violations.append(f"DTI too high: {dti:.2f}")
if app.credit_score < 660:
violations.append(f"Credit score below minimum: {app.credit_score}")
if app.employment_months < 12:
violations.append(f"Insufficient employment history: {app.employment_months} months")
return violations
violations = hard_rule_check(application)
if violations:
final_decision = "refer"
else:
final_decision = "approve"
audit_record = {
"applicant_id": application.applicant_id,
"decision": final_decision,
"model_response": str(response),
"rule_violations": violations,
}
print(audit_record)
Production Considerations
- •
Deploy regionally for data residency
- •Keep borrower PII in-region if your jurisdiction requires it.
- •Separate the vector store by country or business unit when policies differ across regions.
- •
Log every retrieval and decision path
- •Store which policy chunks were used for each outcome.
- •This matters for adverse action notices, internal audits, and dispute handling.
- •
Add explicit compliance guardrails
- •Block protected attributes from prompts and retrieval filters.
- •Validate that explanations reference permissible factors only: income stability, DTI, payment history, collateral quality.
- •
Monitor drift in both model behavior and policy content
- •Track approval rates by product, branch, state, and risk band.
- •Re-index when underwriting manuals change so stale rules do not keep influencing decisions.
Common Pitfalls
- •
Using the LLM as the source of truth for eligibility
- •Don’t let the model invent thresholds or exceptions.
- •Keep hard underwriting rules in code and use LLM output only for interpretation and explanation.
- •
Mixing protected attributes into prompts
- •Never pass race, religion, marital status, age-inference signals, or similar fields into the reasoning prompt.
- •If you need fair lending analysis, handle it in separate offline compliance workflows with strict access control.
- •
Skipping versioning on policies and prompts
- •A loan decision without document versioning is hard to defend later.
- •Version underwriting docs, prompt templates, retrievers settings, and model names together so every decision is reproducible.
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