How to Build a fraud detection Agent Using LangChain in Python for lending

By Cyprian AaronsUpdated 2026-04-21
fraud-detectionlangchainpythonlending

A fraud detection agent for lending screens applications, supporting documents, and applicant behavior to flag suspicious patterns before credit is extended. It matters because lending fraud directly hits loss rates, operational cost, and compliance exposure, and the review process needs to be fast enough to keep approvals moving.

Architecture

  • Document ingestion layer

    • Pulls application fields, bank statements, IDs, payslips, device signals, and bureau data.
    • Normalizes them into a single structured payload.
  • Rule-based risk prefilter

    • Catches obvious issues before the LLM runs.
    • Examples: mismatched identity fields, duplicate phone numbers, impossible income-to-expense ratios.
  • LangChain agent

    • Uses a chat model plus tools to inspect the case.
    • Produces a fraud risk assessment with reasons and next actions.
  • Evidence retrieval layer

    • Retrieves policy snippets, prior fraud patterns, and internal controls.
    • Keeps the agent grounded in lending policy instead of free-form guesses.
  • Decision output layer

    • Returns a structured result: risk score band, reasons, recommended action, and audit trail.
    • Feeds downstream underwriting or manual review queues.

Implementation

1) Define the case schema and rule prechecks

Start with structured input. For lending workflows, you want explicit fields for identity, income, device metadata, and document consistency.

from pydantic import BaseModel, Field
from typing import Optional

class LoanApplication(BaseModel):
    applicant_name: str
    national_id: str
    phone_number: str
    declared_income: float
    monthly_expenses: float
    employer_name: str
    bank_account_name: str
    device_fingerprint: Optional[str] = None
    ip_country: Optional[str] = None
    document_mismatch_flag: bool = False

def rule_prefilter(app: LoanApplication) -> list[str]:
    issues = []
    if app.declared_income < app.monthly_expenses * 1.5:
        issues.append("Low income-to-expense ratio")
    if app.applicant_name.lower() != app.bank_account_name.lower():
        issues.append("Applicant name does not match bank account name")
    if app.document_mismatch_flag:
        issues.append("Document mismatch flagged by OCR/manual validation")
    return issues

This gives you deterministic checks before the model sees anything. In lending, that matters because you need explainable controls for auditors and credit policy teams.

2) Build a LangChain tool for policy lookup

Use retrieval for internal fraud policy. A RetrievalQA chain is older style; in current LangChain builds, use create_retrieval_chain with a retriever.

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

policy_docs = [
    Document(page_content="Flag applications where identity and bank account names differ."),
    Document(page_content="Escalate cases with repeated device fingerprint across multiple applicants."),
    Document(page_content="Manual review required when declared income is inconsistent with bank inflows."),
]

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

The retriever gives the agent concrete policy context. That reduces hallucinated reasoning and makes decisions easier to defend in audit reviews.

3) Create a structured fraud analysis chain

For lending operations you want machine-readable output. PydanticOutputParser keeps the response format stable.

from pydantic import BaseModel
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from langchain_openai import ChatOpenAI

class FraudAssessment(BaseModel):
    risk_level: str
    reasons: list[str]
    recommended_action: str

parser = PydanticOutputParser(pydantic_object=FraudAssessment)

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a lending fraud analyst. Use only provided facts and policy context."),
    ("user", """
Application:
{application}

Rule issues:
{rule_issues}

Policy context:
{policy_context}

Return a structured assessment.
{format_instructions}
""")
])

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

chain = prompt | llm | parser

The key pattern here is prompt | llm | parser. That is the simplest production-friendly LangChain pipeline when you need controlled outputs instead of chatty prose.

4) Run the full assessment end to end

Combine deterministic checks with retrieved policy context. Then map the result into your underwriting queue or manual review system.

def assess_application(app: LoanApplication):
    rule_issues = rule_prefilter(app)
    docs = retriever.invoke(app.model_dump_json())
    policy_context = "\n".join(d.page_content for d in docs)

    result = chain.invoke({
        "application": app.model_dump_json(indent=2),
        "rule_issues": "\n".join(rule_issues) if rule_issues else "None",
        "policy_context": policy_context,
        "format_instructions": parser.get_format_instructions(),
    })
    return result

app = LoanApplication(
    applicant_name="Jane Doe",
    national_id="ID12345",
    phone_number="+15551234567",
    declared_income=1200,
    monthly_expenses=900,
    employer_name="Acme Ltd",
    bank_account_name="Jane Doe",
    device_fingerprint="fp_abc123",
)

assessment = assess_application(app)
print(assessment.model_dump())

This pattern is practical because it separates control logic from model reasoning. You can log rule_issues, retrieved documents, prompt inputs, and final output for every decision.

Production Considerations

  • Auditability

    • Store every input field used by the agent, retrieved policy chunks, model version, prompt version, and final output.
    • For lending disputes and regulator reviews, you need traceability from decision back to evidence.
  • Data residency and privacy

    • Keep borrower data in-region if your jurisdiction requires it.
    • Redact PII before sending text to external model providers where possible; use tokenized identifiers in logs.
  • Guardrails

    • Hard-block unsupported decisions like “approve” or “deny” unless your workflow explicitly allows them.
    • Limit the agent to “risk assessment” plus “manual review recommendation” so it does not override underwriting policy.
  • Monitoring

    • Track false positives by segment: channel, geography, product type, income band.
    • Monitor drift in document mismatch rates and device reuse patterns because fraud patterns change quickly in lending funnels.

Common Pitfalls

  • Using the LLM as the first line of defense

    • Don’t start with free-form analysis on raw application data.
    • Run deterministic checks first so obvious fraud signals are caught cheaply and consistently.
  • Returning unstructured text

    • A paragraph like “this looks suspicious” is useless in production.
    • Use PydanticOutputParser or another structured parser so downstream systems can route cases reliably.
  • Ignoring compliance constraints

    • Lending agents often touch regulated personal data.
    • Build for audit logs, retention policies, access control, and regional processing from day one; retrofitting those later is painful.

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