How to Integrate LangGraph for banking with LangSmith for production AI

By Cyprian AaronsUpdated 2026-04-22
langgraph-for-bankinglangsmithproduction-ai

Combining LangGraph for banking with LangSmith gives you a production path for regulated AI agents: deterministic workflow control from LangGraph, plus traceability, debugging, and evaluation from LangSmith. That matters when your agent is handling things like KYC checks, loan pre-screening, payment disputes, or policy lookup where every step needs to be observable and auditable.

The practical win is simple: build the business logic as a graph, then instrument every node so you can see exactly what the agent did, what it saw, and where it failed.

Prerequisites

  • Python 3.10+
  • langgraph
  • langchain-core
  • langsmith
  • An LLM provider key set in your environment
  • A LangSmith account and API key
  • A LangSmith project created for this integration
  • Basic familiarity with Python async/sync functions

Install the packages:

pip install langgraph langchain-core langsmith

Set environment variables:

export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY="ls__your_key"
export LANGCHAIN_PROJECT="banking-agent-prod"
export OPENAI_API_KEY="your_openai_key"

Integration Steps

  1. Create a banking workflow with LangGraph

Use LangGraph to model the agent as a state machine. For banking, keep the graph explicit: intake, policy check, decision, and response.

from typing import TypedDict
from langgraph.graph import StateGraph, END

class BankingState(TypedDict):
    customer_id: str
    request_type: str
    risk_flag: bool
    decision: str

def intake_node(state: BankingState) -> BankingState:
    return state

def policy_check_node(state: BankingState) -> BankingState:
    # Replace with real KYC/AML/risk rules
    state["risk_flag"] = state["request_type"] in ["wire_transfer", "new_payee"]
    return state

def decision_node(state: BankingState) -> BankingState:
    state["decision"] = "manual_review" if state["risk_flag"] else "approved"
    return state

graph = StateGraph(BankingState)
graph.add_node("intake", intake_node)
graph.add_node("policy_check", policy_check_node)
graph.add_node("decision", decision_node)

graph.set_entry_point("intake")
graph.add_edge("intake", "policy_check")
graph.add_edge("policy_check", "decision")
graph.add_edge("decision", END)

app = graph.compile()
  1. Add LangSmith tracing through environment configuration

LangGraph runs on top of LangChain primitives, so once tracing is enabled with LANGCHAIN_TRACING_V2=true, your graph executions show up in LangSmith automatically. If you want explicit control over metadata, pass tags and run names when invoking.

result = app.invoke(
    {
        "customer_id": "cust_123",
        "request_type": "wire_transfer",
        "risk_flag": False,
        "decision": ""
    },
    config={
        "run_name": "banking-risk-workflow",
        "tags": ["banking", "kyc", "prod"],
        "metadata": {
            "tenant": "retail-banking",
            "environment": "production"
        }
    }
)

print(result)

This gives you trace-level visibility into each node execution in LangSmith.

  1. Instrument individual nodes with LangSmith run context

For production systems, don’t rely only on graph-level traces. Add node-level metadata so you can debug specific policy decisions and branch behavior.

from langsmith import Client

client = Client()

def policy_check_node(state: BankingState) -> BankingState:
    risk_reason = None

    if state["request_type"] == "wire_transfer":
        risk_reason = "wire transfer requires enhanced review"
        state["risk_flag"] = True
    elif state["request_type"] == "new_payee":
        risk_reason = "new payee triggers fraud controls"
        state["risk_flag"] = True
    else:
        state["risk_flag"] = False

    client.create_run(
        name="policy_check_audit",
        run_type="tool",
        inputs={"customer_id": state["customer_id"], "request_type": state["request_type"]},
        outputs={"risk_flag": state["risk_flag"], "reason": risk_reason},
        project_name="banking-agent-prod",
    )

    return state

Use this pattern for compliance-sensitive branches where auditors need a durable record beyond the default trace.

  1. Wrap an LLM call inside a graph node and trace it

Most banking agents need an LLM for summarization or customer-facing responses. Keep the orchestration in LangGraph and the observability in LangSmith.

from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

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

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a banking assistant. Keep responses concise and compliant."),
    ("human", "{message}")
])

def response_node(state: dict) -> dict:
    message = (
        f"Customer {state['customer_id']} request {state['request_type']} "
        f"was {state['decision']}."
    )
    chain = prompt | llm
    response = chain.invoke({"message": message})
    state["final_response"] = response.content
    return state

If tracing is enabled, this LLM call appears inside the same end-to-end run in LangSmith.

  1. Run the full graph and verify traces in LangSmith

Now connect the response node into your graph and execute it with production-like inputs.

from typing import TypedDict
from langgraph.graph import StateGraph, END

class AppState(TypedDict):
    customer_id: str
    request_type: str
    risk_flag: bool
    decision: str
    final_response: str

def intake_node(state: AppState) -> AppState:
    return state

def policy_check_node(state: AppState) -> AppState:
    state["risk_flag"] = state["request_type"] in ["wire_transfer", "new_payee"]
    return state

def decision_node(state: AppState) -> AppState:
    state["decision"] = "manual_review" if state["risk_flag"] else "approved"
    return response_node(state)

graph = StateGraph(AppState)
graph.add_node("intake", intake_node)
graph.add_node("policy_check", policy_check_node)
graph.add_node("decision", decision_node)

graph.set_entry_point("intake")
graph.add_edge("intake", "policy_check")
graph.add_edge("policy_check", "decision")
graph.add_edge("decision", END)

app = graph.compile()

output = app.invoke(
    {
        "customer_id": "cust_123",
        "request_type": "wire_transfer",
        "risk_flag": False,
        "decision": "",
        "final_response": ""
    },
    config={
        "run_name": "banking-agent-prod",
        "tags": ["langgraph", "langsmith", "banking"]
    }
)

print(output["decision"])
print(output["final_response"])

Testing the Integration

Run a simple smoke test that checks both graph execution and trace emission.

result = app.invoke(
    {
        "customer_id": "cust_999",
        "request_type": "balance_inquiry",
        "risk_flag": False,
        "decision": "",
        "final_response": ""
     },
     config={
         "run_name": "__integration_test__",
         "tags": ["smoke-test"]
     }
)

assert result["decision"] == "approved"
assert isinstance(result["final_response"], str)
print("Integration test passed")

Expected output:

Integration test passed

In LangSmith, you should see a run named __integration_test__ with child spans for each node and any LLM calls made inside them.

Real-World Use Cases

  • KYC triage agent
    Use LangGraph to route customers through identity verification steps, then use LangSmith to inspect failures by branch, model prompt, or data source.

  • Fraud review assistant
    Build a graph that scores transactions, escalates suspicious cases, and generates analyst summaries. Trace every decision path for auditability.

  • Policy-driven customer support
    Route requests like chargebacks, card replacement, or wire limits through deterministic steps while using LangSmith to evaluate response quality across versions.


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