How to Build a underwriting Agent Using LlamaIndex in Python for pension funds
A underwriting agent for pension funds takes a member or employer request, pulls the relevant policy, contribution, eligibility, and risk documents, and produces a structured recommendation with citations. It matters because pension underwriting is not just document retrieval; it needs traceable decisions, compliance checks, and consistent handling of regulated data.
Architecture
- •
Document ingestion layer
- •Loads fund rules, scheme documents, actuarial notes, member communications, and historical underwriting decisions.
- •Use
SimpleDirectoryReaderfor local files or custom loaders for controlled sources.
- •
Indexing layer
- •Builds a searchable knowledge base over policy PDFs, contracts, and internal guidance.
- •Use
VectorStoreIndexwith chunking tuned for long legal and actuarial text.
- •
Query orchestration layer
- •Routes the user request to retrieval plus synthesis.
- •Use
QueryEnginefrom LlamaIndex with a response synthesizer that returns grounded answers.
- •
Decision support layer
- •Converts retrieved evidence into an underwriting recommendation: approve, reject, refer to human review.
- •Use a structured output schema so downstream systems can validate the result.
- •
Audit and traceability layer
- •Captures source nodes, timestamps, model version, and decision rationale.
- •Required for pension funds where compliance teams will ask “why did the agent say this?”
- •
Guardrail layer
- •Blocks unsupported advice, missing evidence, or out-of-policy recommendations.
- •Important for residency controls and regulated decision-making.
Implementation
- •
Install dependencies and load controlled documents
Keep the first version simple: local files only, no external connectors until your governance is clear. For pension funds, this is usually the right starting point because it keeps data residency inside your environment.
pip install llama-index llama-index-llms-openai llama-index-embeddings-openaifrom llama_index.core import SimpleDirectoryReader docs = SimpleDirectoryReader( input_dir="./pension_docs", required_exts=[".pdf", ".txt", ".md"] ).load_data() - •
Build the index with metadata that supports audit
Add metadata before indexing so every chunk carries source context. For underwriting workflows, you want document type, scheme name, jurisdiction, and effective date attached to each node.
from llama_index.core import VectorStoreIndex for doc in docs: doc.metadata = { "source_system": "pension_policy_repo", "jurisdiction": "UK", "doc_type": doc.metadata.get("file_type", "unknown"), "scheme_name": "Default Pension Scheme" } index = VectorStoreIndex.from_documents(docs) - •
Create a query engine that returns grounded recommendations
Use a retriever-backed query engine so every answer comes from retrieved policy text. In production underwriting, I prefer forcing the model to answer in a structured format that can be validated by code after generation.
from pydantic import BaseModel, Field from typing import Literal from llama_index.core import Settings from llama_index.llms.openai import OpenAI Settings.llm = OpenAI(model="gpt-4o-mini", temperature=0) class UnderwritingDecision(BaseModel): decision: Literal["approve", "reject", "refer"] rationale: str = Field(description="Short explanation grounded in retrieved policy text") cited_documents: list[str] = Field(default_factory=list) query_engine = index.as_query_engine(similarity_top_k=5) prompt = """ You are an underwriting assistant for a pension fund. Decide whether this request should be approved, rejected, or referred. Use only the provided policy evidence. Return a concise decision with citations. Request: Member requests early access due to disability claim and employer contribution history mismatch. """ response = query_engine.query(prompt) print(response.response) print([n.node.metadata for n in response.source_nodes]) - •
Wrap the agent behind a service boundary
Don’t expose raw LlamaIndex responses directly to business users. Put a thin service layer in front of it that validates output shape, logs source nodes, and enforces “refer to human” when confidence or evidence is weak.
def underwrite_request(query: str): result = query_engine.query(query) sources = [] for sn in result.source_nodes: md = sn.node.metadata or {} sources.append({ "file": md.get("file_name"), "doc_type": md.get("doc_type"), "jurisdiction": md.get("jurisdiction") }) return { "answer": result.response, "sources": sources } payload = underwrite_request( "Assess whether this pension transfer request meets policy requirements." ) print(payload)
Production Considerations
- •
Deploy inside your controlled network
- •Pension data often includes personal and financial information.
- •Keep embeddings storage, vector DBs, and model endpoints in-region if your policy requires data residency.
- •
Log every decision with evidence
- •Store user prompt, retrieved node IDs, model version, timestamp, and final recommendation.
- •This gives compliance teams an audit trail when they challenge an approval or rejection.
- •
Add deterministic guardrails
- •Reject outputs without enough supporting citations.
- •If retrieval returns weak matches or conflicting policies, force
referinstead of guessing.
- •
Monitor drift in source documents
- •Pension schemes change rules often.
- •Rebuild indexes when policy docs change and track which version of the scheme was used for each decision.
Common Pitfalls
- •
Using generic retrieval settings on legal/policy text
Default chunking often breaks long pension clauses across unrelated sections. Tune chunk size and overlap so exclusions, exceptions, and effective dates stay together.
- •
Letting the model make unsupported recommendations
If your prompt does not force citation-based reasoning, you’ll get confident nonsense. Require source nodes in every response and reject answers without them.
- •
Ignoring jurisdiction and scheme versioning
A rule that applies to one fund may not apply to another scheme or country. Always attach jurisdiction and effective date metadata before indexing.
- •
Treating the agent like a chatbot instead of a decision system
Underwriting needs repeatability. Put validation code around the model output so “approve/reject/refer” is enforced by your application logic, not just by prompt wording.
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