How to Build a underwriting Agent Using LangChain in Python for pension funds
A underwriting agent for pension funds screens member applications, contribution changes, transfer requests, and benefit-related documents against policy rules, compliance requirements, and risk thresholds. It matters because pension operations are high-volume, regulated, and audit-heavy; a bad decision can create downstream liabilities, regulatory exposure, or member harm.
Architecture
- •
Document ingestion layer
- •Pulls PDFs, forms, emails, and structured records from the pension administration system.
- •Normalizes text with metadata like member ID, document type, jurisdiction, and effective date.
- •
Policy retrieval layer
- •Uses a vector store or keyword index to fetch the right pension policy, scheme rules, and regulatory guidance.
- •Keeps the agent grounded in current rules instead of model memory.
- •
Underwriting reasoning chain
- •Takes the extracted facts and policy context.
- •Produces a decision: approve, refer to human review, or reject with reasons.
- •
Audit logging layer
- •Stores every input, retrieved policy chunk, intermediate step, and final output.
- •Required for internal audit and regulator review.
- •
Guardrails and validation layer
- •Enforces schema validation on outputs.
- •Blocks unsupported claims, missing evidence, or decisions outside policy thresholds.
- •
Human-in-the-loop escalation
- •Routes edge cases to an underwriter when confidence is low or a rule conflict appears.
- •Essential for exceptions like early retirement transfers, ill-health claims, or cross-border residency issues.
Implementation
1) Load policy documents into a retriever
For pension funds, your agent should answer from scheme rules and compliance docs first. A FAISS vector store plus RetrievalQA is enough to start if your corpus is small and controlled.
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
loader = PyPDFLoader("pension_scheme_rules.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = splitter.split_documents(docs)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = FAISS.from_documents(chunks, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
2) Define a strict underwriting output schema
Do not let the model free-write decisions. Use PydanticOutputParser so every response has a predictable shape for downstream systems and audit logs.
from pydantic import BaseModel, Field
from typing import Literal
class UnderwritingDecision(BaseModel):
decision: Literal["approve", "refer", "reject"] = Field(...)
risk_level: Literal["low", "medium", "high"] = Field(...)
rationale: str = Field(...)
policy_refs: list[str] = Field(default_factory=list)
missing_info: list[str] = Field(default_factory=list)
3) Build the LangChain pipeline
This pattern uses ChatPromptTemplate, create_stuff_documents_chain, and create_retrieval_chain. The model gets the member case plus retrieved policy text and returns structured output through the parser.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains.retrieval import create_retrieval_chain
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
parser = PydanticOutputParser(pydantic_object=UnderwritingDecision)
prompt = ChatPromptTemplate.from_messages([
("system",
"You are an underwriting assistant for a pension fund. "
"Use only the provided policy context. "
"If information is missing or ambiguous, choose 'refer'. "
"Never invent policy rules."),
("human",
"Case details:\n{input}\n\n"
"Policy context:\n{context}\n\n"
"{format_instructions}")
]).partial(format_instructions=parser.get_format_instructions())
document_chain = create_stuff_documents_chain(llm=llm, prompt=prompt)
qa_chain = create_retrieval_chain(retriever=retriever,
combine_docs_chain=document_chain)
case_input = """
Member age: 58
Request: transfer out of scheme to another provider
Country of residence: UK
Requested amount: £240000
Notes: no employer consent attached
"""
result = qa_chain.invoke({"input": case_input})
decision = parser.parse(result["answer"])
print(decision.model_dump())
4) Add an explicit human review gate
For pension funds, refer cases that touch protected benefits, tax implications, residency constraints, or incomplete evidence. A simple threshold-based gate keeps risky decisions out of straight-through processing.
def needs_human_review(decision: UnderwritingDecision) -> bool:
if decision.decision == "refer":
return True
if decision.risk_level == "high":
return True
if len(decision.missing_info) > 0:
return True
return False
if needs_human_review(decision):
print("Route to underwriter queue")
else:
print("Proceed with automated processing")
Production Considerations
- •
Data residency
- •Keep member data in-region if your pension fund operates under local residency rules.
- •If using hosted LLMs or vector databases, verify where prompts, embeddings, and logs are stored.
- •
Auditability
- •Log the exact prompt inputs, retrieved chunks IDs, model version, temperature setting, and final structured output.
- •Regulators will care more about traceability than clever prompts.
- •
Guardrails
- •Validate every response against a schema before it reaches core systems.
- •Reject any output that references policies not present in retrieval results.
- •
Monitoring
- •Track referral rate, rejection rate, hallucination incidents, retrieval hit quality, and latency.
- •Watch for drift when scheme rules change after trustee updates or regulatory changes.
Common Pitfalls
- •
Using the model as if it knows pension rules
- •Don’t rely on parametric memory for scheme-specific logic.
- •Fix this by grounding every decision in retrieved policy documents and versioned rule sets.
- •
Skipping exception handling
- •Pension underwriting has edge cases: ill-health retirement, AVC transfers, overseas members.
- •Fix this by routing ambiguous cases to
referinstead of forcing an automated decision.
- •
Ignoring compliance metadata
- •If you don’t store document source, effective date, jurisdiction, and reviewer trail, audit becomes painful.
- •Fix this by attaching metadata at ingestion time and persisting it through the chain outputs.
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