How to Build a compliance checking Agent Using LangChain in Python for retail banking
A compliance checking agent in retail banking reviews customer-facing text, internal workflows, and proposed actions against policy rules before they go live. It matters because a bad disclosure, an unsuitable product recommendation, or a missed KYC/AML flag can become a regulatory incident, not just a product bug.
Architecture
- •
Policy corpus
- •Source documents: product terms, fair lending rules, complaints handling policy, marketing approval rules, KYC/AML procedures.
- •Store them in a versioned document store so every decision can be traced to the exact policy revision.
- •
Retriever
- •A vector retriever over approved compliance content.
- •Use it to fetch the minimum relevant policy sections for each check, instead of dumping the whole handbook into the prompt.
- •
LLM reasoning layer
- •A LangChain
ChatOpenAImodel that classifies risk, extracts violations, and explains why something is non-compliant. - •Keep temperature at
0for deterministic review behavior.
- •A LangChain
- •
Rule engine / deterministic checks
- •Hard checks for things that should never be probabilistic: prohibited phrases, missing disclaimers, restricted jurisdictions, PII leakage.
- •Use Python logic alongside the LLM. Banking compliance should not depend on model creativity.
- •
Audit logger
- •Persist input text, retrieved policy snippets, model output, timestamps, user IDs, and decision status.
- •This is what your compliance team will ask for when they want evidence.
- •
Human escalation path
- •If confidence is low or the issue is high-risk, route to a compliance analyst.
- •The agent should recommend; it should not silently approve borderline cases.
Implementation
1) Load policies and build a retriever
Use approved policy documents only. In retail banking, this usually means internal compliance manuals plus jurisdiction-specific disclosures.
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import FAISS
loader = DirectoryLoader(
"policies/",
glob="**/*.txt",
loader_cls=TextLoader,
)
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) Build a compliance prompt and LangChain chain
The prompt should force structured output and require citations from retrieved policy text. That gives you something auditable instead of a free-form answer.
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system",
"You are a retail banking compliance reviewer. "
"Assess whether the input violates policy. "
"Only use the provided policy context. "
"Return: status (PASS/FAIL/REVIEW), reasons, and cited policy snippets."),
("human",
"Customer-facing text:\n{text}\n\nPolicy context:\n{context}")
])
def format_docs(docs):
return "\n\n".join(
f"[Source: {d.metadata.get('source', 'unknown')}]\n{d.page_content}"
for d in docs
)
chain = (
{
"context": retriever | format_docs,
"text": RunnablePassthrough(),
}
| prompt
| llm
| StrOutputParser()
)
3) Add deterministic guardrails before the LLM
Retail banking needs hard stops for obvious violations like card numbers or disallowed promises. Put these checks in front of the model so you fail fast on clear breaches.
import re
PROHIBITED_PATTERNS = [
r"\bguaranteed approval\b",
r"\bno credit check\b",
r"\binstant loan\b",
]
def deterministic_check(text: str):
findings = []
for pattern in PROHIBITED_PATTERNS:
if re.search(pattern, text.lower()):
findings.append(f"Matched prohibited phrase: {pattern}")
if re.search(r"\b\d{16}\b", text):
findings.append("Possible card number detected")
if re.search(r"\b\d{3}-\d{2}-\d{4}\b", text):
findings.append("Possible SSN detected")
return findings
def review_text(text: str):
hard_failures = deterministic_check(text)
if hard_failures:
return {
"status": "FAIL",
"reason": hard_failures,
"decision_source": "rules"
}
result = chain.invoke(text)
return {
"status": "LLM_REVIEW",
"result": result,
"decision_source": "langchain"
}
sample_text = (
"You are guaranteed approval for this personal loan with no credit check."
)
print(review_text(sample_text))
4) Make outputs auditable
For banking use cases, log every decision with enough detail to reconstruct it later. If you cannot explain why the agent passed or failed a case six months later, it is not production-ready.
import json
from datetime import datetime
def audit_log(record: dict, path: str = "audit_log.jsonl"):
record["timestamp"] = datetime.utcnow().isoformat()
with open(path, "a", encoding="utf-8") as f:
f.write(json.dumps(record) + "\n")
text = "Apply now. Guaranteed approval. No credit check required."
result = review_text(text)
audit_log({
"input_text": text,
"result": result,
})
Production Considerations
- •
Data residency
- •Keep policy indexes and audit logs in-region if your bank operates under local residency rules.
- •Do not send customer data or internal policy docs to unmanaged endpoints.
- •
Monitoring
- •Track false positives, false negatives, escalation rate, and time-to-review.
- •Slice metrics by product line: cards, loans, deposits, complaints handling.
- •
Guardrails
- •Enforce PII redaction before LLM calls where possible.
- •Block generation of final customer copy unless both deterministic checks and model review pass.
- •
Human override
- •Route
REVIEWcases to a compliance queue with full context. - •Never auto-close high-risk items like affordability language, fee disclosures, or sanctions-related matches.
- •Route
Common Pitfalls
- •
Using the LLM as the only control
- •This is the fastest way to create an unreliable system.
- •Fix it by combining regex/rules with retrieval-based reasoning and human escalation.
- •
Retrieving too much policy context
- •If you stuff entire manuals into prompts, the model will miss important clauses and you will burn tokens.
- •Fix it with tight chunking, top-k retrieval, and versioned source documents.
- •
No audit trail
- •Banking teams need evidence of what was checked and why it passed or failed.
- •Fix it by logging input text, retrieved sources, model output, rule hits, timestamp, and reviewer ID.
A good retail banking compliance agent is boring in the right way. It flags risky language consistently, cites the right policies, escalates when uncertain, and leaves behind an audit trail your risk team can trust.
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