How to Build a claims processing Agent Using LangChain in Python for banking
A claims processing agent for banking takes a customer claim, classifies it, extracts the required details, checks policy and account context, and routes it to the right next step. That matters because claims workflows are high-volume, regulated, and expensive when handled manually; a good agent reduces turnaround time without weakening auditability or compliance.
Architecture
- •
Input normalization layer
- •Cleans email text, PDF/OCR output, or form submissions into structured text.
- •Keeps raw input for audit trails.
- •
LLM orchestration layer
- •Uses
ChatOpenAIthrough LangChain to classify claim type and extract fields. - •Keeps prompts narrow and deterministic.
- •Uses
- •
Policy retrieval layer
- •Uses
RetrievalQAor a retrieval chain over internal claims policies, product terms, and escalation rules. - •Grounds answers in approved bank documentation.
- •Uses
- •
Decision engine
- •Applies hard rules for eligibility, missing data, fraud flags, and escalation thresholds.
- •Never lets the model make final regulatory decisions on its own.
- •
Audit and trace layer
- •Logs inputs, outputs, retrieved documents, and decision reasons.
- •Supports compliance review and post-incident analysis.
- •
Human handoff layer
- •Routes uncertain or high-risk cases to a claims analyst.
- •Preserves context so the analyst does not restart from scratch.
Implementation
1) Set up a structured extraction chain
For banking workflows, you want structured output early. Use LangChain’s PydanticOutputParser so the model returns fields you can validate before any downstream action.
from typing import Literal, Optional
from pydantic import BaseModel, Field
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
class ClaimCase(BaseModel):
claim_type: Literal["card_dispute", "wire_error", "unauthorized_transfer", "fee_refund"]
customer_id: str = Field(..., description="Bank customer identifier")
account_last4: str = Field(..., min_length=4, max_length=4)
amount: float
currency: str = "USD"
incident_date: str
summary: str
needs_human_review: bool
parser = PydanticOutputParser(pydantic_object=ClaimCase)
prompt = ChatPromptTemplate.from_messages([
("system", "You extract banking claim details. Only use the provided text."),
("user", "{text}\n\n{format_instructions}")
]).partial(format_instructions=parser.get_format_instructions())
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
extract_chain = prompt | llm | parser
result = extract_chain.invoke({
"text": (
"Customer ID C12345 reports an unauthorized card transaction of USD 245.90 "
"on 2026-04-18. Account ending 8821. Wants reversal."
)
})
print(result.model_dump())
This pattern is useful because the bank can validate ClaimCase before anything touches core systems. If parsing fails, you fail closed and route to human review.
2) Ground decisions in policy documents with retrieval
Claims agents should not invent policy. Load approved bank policy docs into a vector store and retrieve them at runtime with RetrievalQA.
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain.chains import RetrievalQA
loader = TextLoader("bank_claims_policy.txt", encoding="utf-8")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = splitter.split_documents(docs)
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(chunks, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
qa_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
llm=qa_llm,
retriever=retriever,
return_source_documents=True,
)
response = qa_chain.invoke({
"query": "What is the escalation rule for unauthorized transfer claims above USD 500?"
})
print(response["result"])
for doc in response["source_documents"]:
print(doc.metadata)
In production, those source documents should be versioned policy artifacts with retention controls. That gives compliance teams a clear answer to “what policy was in force when this decision was made?”
3) Add deterministic routing after LLM extraction
Once you have structured data plus grounded policy context, keep the final routing logic in Python. This is where banking controls belong.
def route_claim(claim: ClaimCase) -> str:
if claim.needs_human_review:
return "human_review"
if claim.claim_type == "unauthorized_transfer" and claim.amount >= 500:
return "fraud_ops_escalation"
if claim.claim_type == "wire_error":
return "payments_operations"
if claim.claim_type == "card_dispute":
return "card_services"
return "general_claims_queue"
queue_name = route_claim(result)
print(queue_name)
Do not ask the model to decide queue assignment directly if that decision has operational or regulatory impact. Use the model for extraction and summarization; use code for policy enforcement.
4) Wrap it into a single callable workflow
A practical agent combines extraction, retrieval-backed reasoning, then routing. Keep each step observable.
def process_claim(raw_text: str):
claim = extract_chain.invoke({"text": raw_text})
policy_answer = qa_chain.invoke({
"query": f"Given this claim type {claim.claim_type}, what is the required next step?"
})
queue = route_claim(claim)
return {
"claim": claim.model_dump(),
"policy_guidance": policy_answer["result"],
"queue": queue,
"sources": [
doc.page_content[:300] for doc in policy_answer["source_documents"]
]
}
output = process_claim(
"Customer ID C12345 reports an unauthorized card transaction of USD 245.90 "
"on 2026-04-18. Account ending 8821."
)
print(output)
Production Considerations
- •
Compliance logging
- •Store raw input, extracted fields, retrieved sources, prompt version, model version, and final route.
- •This is your audit trail for internal risk teams and regulators.
- •
Data residency
- •Keep customer data in approved regions only.
- •If your bank has residency constraints, use region-bound model endpoints and isolate vector stores by jurisdiction.
- •
Guardrails
- •Validate structured output with Pydantic before execution.
- •Block unsupported actions like fund movement or account changes from model output alone.
- •
Monitoring
- •Track extraction failure rate, human escalation rate, retrieval hit quality, and latency.
- •Alert on spikes in “needs_human_review” because they often indicate prompt drift or upstream OCR issues.
Common Pitfalls
- •
Letting the LLM make final business decisions
- •Mistake: asking the model to approve or deny claims outright.
- •Fix: use the model for classification and summarization; keep approval logic in deterministic code with bank-approved rules.
- •
Skipping source grounding
- •Mistake: generating policy answers from memory.
- •Fix: always retrieve from versioned internal documents using a retriever-backed chain like
RetrievalQA.
- •
Ignoring schema validation
- •Mistake: passing free-form text directly into downstream systems.
- •Fix: enforce typed outputs with
PydanticOutputParser, reject malformed results, and escalate incomplete claims to humans.
- •
Treating logs as optional
- •Mistake: only storing final outcomes.
- •Fix: log prompts, retrieved docs, timestamps, confidence signals, and user-facing responses so audit can reconstruct every decision path.
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