How to Build a compliance checking Agent Using LangChain in Python for pension funds
A compliance checking agent for pension funds reviews requests, documents, and transactions against internal policy and regulatory rules before a human approves them. It matters because pension operations are high-trust, heavily audited, and full of edge cases where a small mistake can trigger regulatory exposure, member harm, or costly remediation.
Architecture
- •
Document ingestion layer
- •Pulls policy PDFs, fund rules, investment mandates, and member communication standards.
- •Normalizes them into text chunks for retrieval.
- •
Policy retrieval layer
- •Uses embeddings and a vector store to find the exact clauses relevant to a request.
- •Keeps the agent grounded in source documents instead of model memory.
- •
Compliance reasoning layer
- •Runs the LLM over the request plus retrieved policy context.
- •Produces a structured decision:
approve,reject, orneeds_review.
- •
Audit logging layer
- •Stores the input, retrieved clauses, model output, timestamps, and decision rationale.
- •Gives compliance teams a defensible trail for every recommendation.
- •
Guardrails layer
- •Blocks unsupported actions like final approval without human sign-off.
- •Enforces data minimization and redaction for member PII.
- •
Human review integration
- •Escalates ambiguous or high-risk cases to a compliance officer.
- •Prevents automation from crossing control boundaries.
Implementation
1) Load policy documents and build a retriever
For pension funds, your source of truth is usually policy PDFs, scheme rules, investment guidelines, and communication templates. Start by chunking those documents and indexing them with a vector store so the agent can cite the right clause.
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
# Load pension fund policy docs
loaders = [
PyPDFLoader("policies/investment_mandate.pdf"),
PyPDFLoader("policies/member_comms_policy.pdf"),
PyPDFLoader("policies/escalation_rules.pdf"),
]
docs = []
for loader in loaders:
docs.extend(loader.load())
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
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 structured compliance output
Do not let the model free-text its way through a regulated workflow. Use PydanticOutputParser so your downstream system gets predictable fields for decisioning and audit.
from pydantic import BaseModel, Field
from typing import Literal, List
class ComplianceResult(BaseModel):
decision: Literal["approve", "reject", "needs_review"] = Field(...)
risk_level: Literal["low", "medium", "high"] = Field(...)
reasons: List[str] = Field(...)
cited_clauses: List[str] = Field(...)
3) Build the LangChain prompt + chain
This pattern uses ChatPromptTemplate, create_retrieval_chain, and StrOutputParser style composition. The key is to pass retrieved policy context into the prompt and force the model to justify every outcome with citations.
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=ComplianceResult)
prompt = ChatPromptTemplate.from_messages([
("system",
"You are a compliance checking assistant for a pension fund. "
"Use only the provided policy context. "
"If policy evidence is insufficient, return needs_review."),
("human",
"Request:\n{request}\n\n"
"Policy context:\n{context}\n\n"
"{format_instructions}")
])
document_chain = create_stuff_documents_chain(
llm=llm,
prompt=prompt.partial(format_instructions=parser.get_format_instructions())
)
compliance_chain = create_retrieval_chain(retriever, document_chain)
result = compliance_chain.invoke({
"request": (
"A trustee wants to approve an exception to the standard asset allocation "
"for a new alternative investment product."
)
})
parsed = parser.parse(result["answer"])
print(parsed.model_dump())
4) Add an approval gate before any downstream action
A compliance agent should recommend; it should not execute. Put a hard gate between model output and business action so only low-risk approvals with strong citations move forward automatically.
def route_decision(compliance_result: ComplianceResult) -> str:
if compliance_result.decision == "approve" and compliance_result.risk_level == "low":
return "auto_approved_for_human_confirmation"
if compliance_result.decision == "needs_review":
return "send_to_compliance_officer"
return "blocked"
route = route_decision(parsed)
print(route)
Production Considerations
- •
Keep data residency explicit
- •Pension fund records often have jurisdictional constraints.
- •Pin your model endpoint and vector store region to approved geographies and avoid sending member PII outside that boundary.
- •
Log every retrieval and decision
- •Store document IDs, clause text, prompt version, model version, output JSON, and final human disposition.
- •Auditors will ask why the agent made a recommendation; “the model said so” is not acceptable.
- •
Add deterministic guardrails
- •Reject outputs that do not parse into
ComplianceResult. - •Block any recommendation that lacks cited clauses or returns empty evidence for high-risk requests.
- •Reject outputs that do not parse into
- •
Separate advisory from execution
- •The agent should never update records or approve exceptions directly.
- •Use human-in-the-loop review for trustee decisions, benefit exceptions, complaints handling, and any case involving regulated communications.
Common Pitfalls
- •
Using the LLM without retrieval
- •Problem: The agent hallucinates policy interpretations from general training data.
- •Fix: Always ground decisions in retrieved pension fund policies using
Retriever+create_retrieval_chain.
- •
Letting free-text answers drive automation
- •Problem: Unstructured responses are hard to validate and impossible to audit reliably.
- •Fix: Force structured outputs with
PydanticOutputParseror equivalent schema validation before any workflow step.
- •
Ignoring jurisdiction and residency constraints
- •Problem: Member data crosses borders or lands in non-approved systems.
- •Fix: Keep document stores regional, redact PII before prompting where possible, and restrict model providers to approved processing locations.
- •
Skipping human escalation paths
- •Problem: Edge cases get auto-classified when they should be reviewed by compliance staff.
- •Fix: Route ambiguous cases to
needs_reviewwhenever evidence is thin, conflicts exist between clauses, or the request touches trustee discretion.
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