Introduction: The LLM Selection Challenge in Regulated Industries
When building enterprise AI for banking, the question is never just "Which model is smartest?" It is "Which model is most reliable, compliant, and cost-effective for this specific workflow?" Linguistic fluency is table stakes; domain correctness is the differentiator.
Comparing LLMs for a regulated use case requires a structured evaluation process that goes beyond generic benchmarks. You must test models against your actual data, your specific compliance rules, and your latency budgets. A model that scores higher on MMLU might still fail at interpreting your bank’s internal credit policy correctly.
This article details a rigorous LLM comparison framework and demonstrates it through a complete implementation of "ReguCheck," an enterprise-grade multi-agent system built with LangGraph, RAG, memory, and state management. This system doesn’t just answer questions—it validates them, remembers context, and provides auditable reasoning traces.
The Evaluation Framework: Comparing Models for Domain Correctness
Before writing code, establish a comparison methodology. Here is the process used to evaluate LLMs for bank policy tasks:
1. Define Task-Specific Metrics
Generic benchmarks are irrelevant. Define metrics tied to business outcomes:
Factual Groundedness: % of claims directly supported by retrieved policy chunks.
Compliance Adherence: % of responses passing automated regulatory checks.
Reasoning Completeness: Does the CoT trace cover all required policy clauses?
Latency (TTFT + TPS): Time-to-first-token and tokens-per-second under load.
Cost per Verified Answer: Total API cost divided by compliant responses (not total responses).
2. Build a Golden Test Set
Create 100–200 annotated query-answer pairs from real bank operations:
Simple factual lookups ("What is the max tenure for home loans?")
Multi-hop reasoning ("Can a self-employed applicant with a 720 CIBIL score get a gold loan?")
Edge cases with conflicting policies
Adversarial queries designed to trigger hallucinations
Each entry includes: expected answer, required source documents, and compliance flags.
3. Run Controlled Experiments
Use the same RAG pipeline, same prompts, same retrieval top-k across all candidate models. Only swap the LLM. Measure:
Retrieval recall/precision (constant across models)
Answer correctness (variable)
Compliance pass rate (variable)
Token usage and latency (variable)
4. Evaluate Beyond the Final Answer
Inspect intermediate outputs:
Are reasoning steps logically sound even if the final answer is wrong?
Does the model correctly identify when information is missing?
How well does it follow structured output formats?
5. Make a Weighted Decision
Create a scoring matrix. In banking, compliance adherence and groundedness typically carry 3x weight over fluency or speed. The "best" model is the one that maximizes verified correct answers within budget—not the one with the highest benchmark score.
Real-Time Use Case: "ReguCheck" – Multi-Agent Bank Policy Compliance System
Scenario: GlobalTrust Bank needs an internal assistant for branch managers handling loan eligibility queries. Policies span hundreds of PDFs updated monthly. Answers must be RBI-compliant, cite sources, and maintain conversation context.
Business Problem: Standard chatbots hallucinate interest rates, miss conditional clauses, and provide no audit trail. Managers cannot trust AI outputs for customer-facing decisions.
AI Solution: A LangGraph-based multi-agent system where:
A Router Agent classifies query intent
A Retrieval Agent fetches relevant policy chunks
An Analyst Agent reasons step-by-step using CoT
A Compliance Agent validates the draft against source text
State carries all intermediate artifacts for auditing
Memory preserves session context for follow-ups
This architecture makes LLM comparison meaningful because you can measure each model's performance within the validation loop, not in isolation.
Architecture Overview: Enterprise LangGraph + RAG with Memory & State

Key Architectural Decisions:
LangGraph over Chains: Enables conditional routing (compliance rejection loops), persistent state, and explicit node boundaries for evaluation.
Separate Compliance Agent: Decouples generation from validation, allowing independent model selection (e.g., cheaper model for retrieval, stronger model for compliance).
Structured State: Pydantic-enforced state schema ensures type safety and makes every intermediate artifact inspectable for evaluation.
Session Memory: Conversation history stored separately from working state enables multi-turn reasoning without polluting the current task context.
Agent Design: Specialized Roles for Accuracy and Safety
| Agent | Responsibility | Why Separate? |
|---|---|---|
| Router | Classify query domain (Loan/KYC/Fraud/General) | Prevents irrelevant retrieval; enables domain-specific prompting |
| Retriever | Vector search + metadata filtering | Isolation allows swapping embedding models independently |
| Analyst | CoT reasoning over retrieved chunks | Focused prompt improves reasoning quality; easier to evaluate |
| Compliance | Validate draft against source text | Acts as adversarial critic; can use different model than analyst |
| Responder | Format output, cite sources, update memory | Ensures consistent UX regardless of upstream model changes |
Why not one agent? Monolithic prompts conflate retrieval, reasoning, and validation. When evaluating models, you cannot tell if failures stem from poor retrieval, weak reasoning, or inadequate validation. Separation enables granular model comparison per task.
RAG Pipeline: Grounding Responses in Bank Policy Documents
Document Ingestion
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = PyPDFLoader("bank_policies/home_loan_v3.pdf")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=600,
chunk_overlap=80,
separators=["\n\n", "\n", ". ", " "]
)
chunks = splitter.split_documents(docs)
Embeddings & Vector Store
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = Chroma.from_documents(
chunks, embeddings,
persist_directory="./bank_policy_db",
collection_metadata={"hnsw:space": "cosine"}
)
Retrieval with Metadata Filtering
def retrieve(query: str, domain: str, k: int = 4):
return vector_store.similarity_search_with_score(
query, k=k,
filter={"domain": domain} # Pre-filter by routed domain
)
Evaluation Note: Keep retrieval constant during LLM comparison. If retrieval quality varies, you’re testing the retriever, not the LLM.
LangGraph Workflow: Orchestrating Stateful Multi-Agent Reasoning
State Definition
from typing import Annotated, List, Optional, Dictfrom pydantic import BaseModel, Field
import operator
class ReguCheckState(BaseModel):
messages: Annotated[List[dict], operator.add] = Field(default_factory=list)
user_query: str = ""
domain: Optional[str] = None
retrieved_chunks: List[Dict[str, str]] = Field(default_factory=list)
reasoning_trace: List[str] = Field(default_factory=list)
draft_answer: Optional[str] = None
compliance_status: Optional[str] = None # APPROVED / REJECTED
compliance_feedback: Optional[str] = None
final_answer: Optional[str] = None
sources: List[str] = Field(default_factory=list)
Conditional Routing
def route_after_compliance(state: ReguCheckState) -> str:
if state.compliance_status == "REJECTED":
return "analyst" # Loop back for revision
return "responder"This conditional edge is critical for evaluation: it measures how often each model produces compliant-first drafts vs. requiring correction loops.

Join the conversation! Your thoughts help the community grow.