Stage 3: LangChain SQL Agent
Goal: Rebuild the SQL agent using LangChain to see what frameworks provide.
Time: 30-45 min | Level: Intermediate
What We're Building
Same SQL agent as Stage 2, but:
- Less code (~80 lines vs ~110 lines)
- More features (schema validation, uery, schema, list_tables vs just execute_sql)
- Battle-tested reliability
- Built-in security & validation
Quick Start
pip install langchain langchain-openai langchain-community langchain-core
python -m sql_agent.agent_langchain
Code Comparison
Stage 2: Manual ReAct loop (~110 lines)
for iteration in range(max_iterations):
response = client.chat.completions.create(...)
if not response.tool_calls:
return response.content
for tool_call in response.tool_calls:
# Manual tool execution, message management, error handling
Stage 3: One function call (~80 lines)
llm = ChatOpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
db = SQLDatabase.from_uri(f"sqlite:///{db_path}")
agent = create_sql_agent(llm, db, agent_type="tool-calling", verbose=True)
result = agent.invoke({"input": question}) # Everything automatic!
What We Learned Building Stage 3
1. Schema Validation Catches Bugs
The Bug:
-- database.py (typo in FK reference)
FOREIGN KEY (customer_id) REFERENCES customer(id) -- ❌ Should be "customers"
Stage 2: ✗ Ran fine, bug went unnoticed
Stage 3: ✓ Immediate error: NoSuchTableError: customer
LangChain uses SQLAlchemy to validate foreign keys, data types, and relationships on startup.
2. Security by Design (Not Validation)
Stage 2 Approach (Blacklist):
# ~20 lines of validation code
dangerous = ["DROP", "DELETE", "INSERT", "UPDATE"]
if keyword in query: raise Exception("Blocked!")
Stage 3 Approach (Whitelist):
# 0 lines - tools define what's possible
agent = create_sql_agent(...) # Only SELECT queries allowed
Test it yourself:
run_agent("Delete all customers from the database")
# Agent responds: "I am only allowed to select data without making
# changes, I can't execute this."
Tools provided: sql_db_query (SELECT only), sql_db_schema, sql_db_list_tables
Tools NOT provided: DELETE, INSERT, UPDATE, DROP
Result: Dangerous operations are impossible, not just blocked.
| Security | Stage 2 | Stage 3 |
|---|---|---|
| Method | Blacklist (block bad things) | Whitelist (allow only good things) |
| Code | ~20 lines | 0 lines |
| Safe by default? | No | Yes |
3. Import Structure Matters
When using LangChain as a module, we had to fix imports throughout:
# Before (broke with `python -m sql_agent.database`)
from data_prep import generate_sample_data
# After (works both ways)
try:
from sql_agent.data_prep import generate_sample_data
except ImportError:
from data_prep import generate_sample_data
Frameworks push you toward better Python packaging practices.
Key Features
| Feature | Stage 2 | Stage 3 |
|---|---|---|
| Code size | 110 lines | 80 lines |
| Tools | 1 (execute_sql) | 3 (query, schema, list) |
| Security | Manual validation | Impossible by design |
| Schema validation | None | FK/type checking |
| Error handling | Basic try/catch | Built-in retry |
| Schema discovery | Static | Dynamic at runtime |
Common Issues We Hit
NoSuchTableError: customer
Cause: Foreign key typo: REFERENCES customer(id) instead of customers(id)
Fix: LangChain validates schema - fix your FK references!
api_keys is not default parameter
Cause: Typo api_keys="ollama" (plural)
Fix: Use api_key="ollama" (singular)
ModuleNotFoundError: No module named 'models'
Cause: Imports broke when running as module Fix: Use try/except pattern (see Import Structure above)
When to Use Each
Stage 2 (Basic): Learning, full control, minimal dependencies Stage 3 (LangChain): Production, speed, battle-tested features
Try These
# Works - better error handling than Stage 2
"Find the top 3 best selling books"
# Fails gracefully - explains why
"Delete all customers" # Agent: "I can only read data"
# Better errors
"Show me the users table" # Stage 3: "Table 'users' doesn't exist"
Key Takeaways
- Frameworks catch silent bugs - FK validation found our typo
- Security by design > validation - Whitelist beats blacklist
- Less code, more features - 30% smaller, 200% more capable
- Build Stage 2 first - Understand internals, then use frameworks
Full code: sql_agent/agent_langchain.py
Next Step
Working basic agent! Next: Stage 4: Custom tools to see how to blend your custom tools into LangChain.