How to Build a compliance checking Agent Using LangChain in Python for payments

By Cyprian AaronsUpdated 2026-04-21
compliance-checkinglangchainpythonpayments

A compliance checking agent for payments reviews transaction data, merchant details, and policy rules before money moves. Its job is to flag risky or non-compliant activity early, produce an auditable decision trail, and reduce the chance of violating AML, sanctions, PCI-DSS, or internal policy.

Architecture

  • Transaction intake
    • Accepts payment payloads like amount, currency, country, merchant category, customer identifiers, and timestamps.
  • Policy/rules layer
    • Encodes hard controls such as sanctions screening thresholds, country restrictions, velocity limits, and KYC requirements.
  • Retriever for compliance context
    • Pulls relevant policy snippets, SOPs, and regulatory guidance from a vector store using VectorStoreRetriever.
  • LLM reasoning layer
    • Uses a LangChain ChatOpenAI model to classify the transaction against policy context and explain the decision.
  • Decision formatter
    • Forces structured output with PydanticOutputParser so downstream systems can consume approve, review, or block.
  • Audit logging
    • Persists the input payload, retrieved evidence, model output, and final decision for regulators and internal audit.

Implementation

1) Define the compliance schema

Use a strict output schema. Payments systems need deterministic outputs because free-form text is useless in a rules engine.

from typing import Literal
from pydantic import BaseModel, Field

class ComplianceDecision(BaseModel):
    decision: Literal["approve", "review", "block"] = Field(
        description="Final compliance action for the payment"
    )
    reason: str = Field(description="Short explanation tied to policy")
    policy_refs: list[str] = Field(description="Policy IDs or section references")

2) Load policy context into a retriever

For production you would load your policies from approved internal documents. The key point is that the agent should cite retrieved policy text instead of inventing answers.

from langchain_core.documents import Document
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

docs = [
    Document(
        page_content="POL-AML-001: Transactions over $10,000 require enhanced review.",
        metadata={"policy_id": "POL-AML-001"}
    ),
    Document(
        page_content="POL-SAN-004: Payments involving sanctioned countries must be blocked.",
        metadata={"policy_id": "POL-SAN-004"}
    ),
]

embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 2})

3) Build the LangChain prompt and chain

This pattern uses ChatPromptTemplate, PydanticOutputParser, and RunnablePassthrough. The model gets the transaction plus retrieved policy context and returns structured JSON-like output.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.runnables import RunnablePassthrough

parser = PydanticOutputParser(pydantic_object=ComplianceDecision)

prompt = ChatPromptTemplate.from_messages([
    ("system",
     "You are a payments compliance checker. "
     "Use only the provided policy context. "
     "If sanctions or prohibited geography appear, block. "
     "If risk is unclear or thresholds are near limits, review."),
    ("human",
     "Transaction:\n{transaction}\n\n"
     "Policy context:\n{context}\n\n"
     "{format_instructions}")
])

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

def format_docs(docs):
    return "\n\n".join(
        f"[{d.metadata.get('policy_id', 'UNKNOWN')}] {d.page_content}"
        for d in docs
    )

chain = (
    {
        "transaction": RunnablePassthrough(),
        "context": retriever | format_docs,
        "format_instructions": lambda _: parser.get_format_instructions(),
    }
    | prompt
    | llm
    | parser
)

4) Run a payment through the agent

Keep the transaction object small and normalized. Never send raw card data to an LLM unless you have a very specific approved design; tokenize first.

transaction = {
    "transaction_id": "txn_10021",
    "amount_usd": 12500,
    "currency": "USD",
    "merchant_country": "US",
    "customer_country": "US",
    "merchant_category": "crypto_exchange",
}

result = chain.invoke(transaction)
print(result.model_dump())

A typical result might look like:

{
  "decision": "review",
  "reason": "Amount exceeds enhanced review threshold under POL-AML-001.",
  "policy_refs": ["POL-AML-001"]
}

Production Considerations

  • Keep hard rules outside the model
    • Sanctions hits, blocked geographies, and PCI-sensitive checks should be enforced in deterministic code before or after the LLM call.
  • Log every decision path
    • Store input payload hashes, retrieved policy IDs, model version, prompt version, and final output in an immutable audit store.
  • Control data residency
    • If payment data must stay in-region, deploy embeddings/vector stores and model endpoints inside the required jurisdiction.
  • Add human review queues
    • Anything ambiguous should route to an analyst with full evidence: transaction data, matched policies, and model rationale.

Common Pitfalls

  • Using the LLM as the source of truth
    • Don’t ask it to “decide compliance” without retrieval and hard rules. Use it for classification and explanation over approved policy content.
  • Sending raw PANs or sensitive identifiers
    • Tokenize card numbers, mask account data, and strip unnecessary PII before calling LangChain components.
  • Ignoring auditability
    • If you cannot reproduce why a transaction was blocked or reviewed six months later, your design is not production-ready.

Keep learning

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

Related Guides