How to Integrate LangChain for retail banking with PostgreSQL for startups
Retail banking agents need two things to be useful: access to structured customer data and a reliable way to reason over it. LangChain gives you the orchestration layer for prompts, tools, and retrieval, while PostgreSQL gives you the durable system of record for balances, transactions, KYC flags, and audit trails.
For startups, this combo is the practical path to shipping an AI agent that can answer account questions, summarize transaction history, and route cases without building a separate data platform first.
Prerequisites
- •Python 3.10+
- •A PostgreSQL instance running locally or in the cloud
- •A database user with read/write access
- •
langchain,langchain-openai,langchain-community, andpsycopg2-binary - •An OpenAI API key or another chat model provider supported by LangChain
- •A retail-banking schema in PostgreSQL with tables like:
- •
customers - •
accounts - •
transactions - •
support_cases
- •
Install the dependencies:
pip install langchain langchain-openai langchain-community psycopg2-binary sqlalchemy
Set your environment variables:
export OPENAI_API_KEY="your-key"
export POSTGRES_URL="postgresql+psycopg2://bank_user:bank_pass@localhost:5432/banking_db"
Integration Steps
1) Connect LangChain to your PostgreSQL database
Use SQLAlchemy under the hood so LangChain can inspect and query your database safely. The SQLDatabase class from langchain_community.utilities is the standard entry point.
from langchain_community.utilities import SQLDatabase
db_uri = "postgresql+psycopg2://bank_user:bank_pass@localhost:5432/banking_db"
db = SQLDatabase.from_uri(
db_uri,
include_tables=["customers", "accounts", "transactions", "support_cases"]
)
print(db.get_usable_table_names())
This keeps the agent scoped to the tables it actually needs. For banking workloads, table scoping matters because you do not want the model exploring unrelated operational data.
2) Create a SQL-aware LangChain tool for retail banking queries
If your agent needs to answer questions like “What was the last debit on account X?” you want a text-to-SQL path. create_sql_query_chain generates SQL from natural language using your database schema.
from langchain_openai import ChatOpenAI
from langchain.chains import create_sql_query_chain
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
sql_chain = create_sql_query_chain(llm, db)
question = "Show the five most recent transactions for customer_id 1024."
sql_query = sql_chain.invoke({"question": question})
print(sql_query)
In production, do not execute raw generated SQL blindly. Put a validation layer in front of execution so only read-only queries against approved tables can run.
A simple pattern:
from sqlalchemy import text
def run_safe_query(query: str):
blocked = ["INSERT", "UPDATE", "DELETE", "DROP", "ALTER", "TRUNCATE"]
if any(word in query.upper() for word in blocked):
raise ValueError("Write operations are not allowed")
return db.run(text(query))
result = run_safe_query(sql_query)
print(result)
3) Build a banking agent that can reason over database results
Now wrap the SQL tool inside a LangChain agent. This is where startup teams usually get value fast: one chat interface that can retrieve customer context and summarize it.
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
@tool
def fetch_customer_summary(customer_id: int) -> str:
query = f"""
SELECT c.customer_id, c.full_name, a.account_number, a.balance
FROM customers c
JOIN accounts a ON c.customer_id = a.customer_id
WHERE c.customer_id = {customer_id}
"""
return db.run(query)
prompt = ChatPromptTemplate.from_messages([
("system", "You are a retail banking assistant. Use tools for database lookups."),
("human", "{input}"),
])
tools = [fetch_customer_summary]
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
response = executor.invoke({
"input": "Give me a summary for customer 1024."
})
print(response["output"])
This pattern works well when you need controlled access to customer records. You can add more tools later for case lookup, fraud flags, or product eligibility checks.
4) Add retrieval for policy documents and combine it with PostgreSQL data
Retail banking agents often need both structured data and policy context. Store policies in documents and use LangChain retrieval alongside PostgreSQL queries.
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
docs_texts = [
"Overdraft fee is charged after 5 business days of negative balance.",
"KYC review is required every 12 months for active retail customers.",
]
vectorstore = FAISS.from_texts(docs_texts, embedding=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 1})
policy_docs = retriever.invoke("When is overdraft fee applied?")
print(policy_docs[0].page_content)
The usual production flow is:
- •retrieve policy guidance from vector search,
- •fetch live account data from PostgreSQL,
- •generate a grounded response with both sources.
That keeps answers consistent with bank policy instead of relying on model memory.
5) Persist conversation state in PostgreSQL for auditability
Startups building financial agents should store conversation history. PostgreSQL is enough for this if you keep it simple and append-only.
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, Text
engine = create_engine(db_uri)
metadata = MetaData()
chat_history = Table(
"chat_history",
metadata,
Column("id", Integer, primary_key=True),
Column("session_id", Text),
Column("user_message", Text),
Column("assistant_message", Text),
)
metadata.create_all(engine)
with engine.begin() as conn:
conn.execute(chat_history.insert().values(
session_id="sess_001",
user_message="What is my current balance?",
assistant_message="Your current balance is $4,820.15."
))
This gives you traceability for support review and compliance workflows. In banking systems, that matters as much as response quality.
Testing the Integration
Run one query end-to-end: generate SQL from LangChain, execute it against PostgreSQL, then print the result.
test_question = "List all active accounts with balance above 5000."
generated_sql = sql_chain.invoke({"question": test_question})
print("Generated SQL:")
print(generated_sql)
rows = db.run(generated_sql)
print("\nQuery Result:")
print(rows)
Expected output will look like this:
Generated SQL:
SELECT account_number, balance FROM accounts WHERE status = 'active' AND balance > 5000;
Query Result:
[(“001245889”, Decimal('8200.50')), (“001245901”, Decimal('13450.00'))]
If you get an empty result set:
- •verify table names match your schema
- •confirm the DB user has read permissions
- •check that your sample data includes matching rows
Real-World Use Cases
- •
Customer service assistant
- •Answer balance questions
- •Summarize recent transactions
- •Pull case history before escalation
- •
Loan pre-screening agent
- •Query income-related fields from PostgreSQL
- •Check internal eligibility rules from policy docs
- •Generate a pre-qualification summary for ops teams
- •
Fraud triage workflow
- •Retrieve suspicious transaction patterns from PostgreSQL
- •Combine them with rule-based policy context in LangChain
- •Draft an analyst note for human review
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