""" SQL Query Agent using LangChain. Connects to a SQLite database and answers natural language questions by generating and executing SQL queries. Usage: python agent.py # uses demo database python agent.py --db path/to/db.sqlite # your database python agent.py --db mydb.sqlite --question "How many users signed up last month?" """ import argparse import os import sqlite3 from urllib.parse import quote from dotenv import load_dotenv from langchain_community.utilities import SQLDatabase from langchain_openai import ChatOpenAI from langchain_community.agent_toolkits.sql.base import create_sql_agent from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit load_dotenv() def create_demo_database(db_path: str): """Creates a demo e-commerce SQLite database for testing.""" conn = sqlite3.connect(db_path) cur = conn.cursor() cur.executescript(""" CREATE TABLE IF NOT EXISTS customers ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE, country TEXT, created_at DATE DEFAULT CURRENT_DATE ); CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, category TEXT, price REAL NOT NULL, stock INTEGER DEFAULT 0 ); CREATE TABLE IF NOT EXISTS orders ( id INTEGER PRIMARY KEY, customer_id INTEGER REFERENCES customers(id), product_id INTEGER REFERENCES products(id), quantity INTEGER NOT NULL, total REAL NOT NULL, order_date DATE DEFAULT CURRENT_DATE ); INSERT OR IGNORE INTO customers VALUES (1,'Alice Johnson','alice@example.com','USA','2024-01-15'), (2,'Bob Smith','bob@example.com','UK','2024-02-20'), (3,'Carlos Lima','carlos@example.com','Brazil','2024-03-10'), (4,'Diana Prince','diana@example.com','USA','2024-01-05'); INSERT OR IGNORE INTO products VALUES (1,'Laptop Pro','Electronics',1299.99,45), (2,'Wireless Mouse','Electronics',29.99,200), (3,'Python Book','Books',49.99,120), (4,'Standing Desk','Furniture',599.99,15); INSERT OR IGNORE INTO orders VALUES (1,1,1,1,1299.99,'2024-04-01'), (2,1,2,2,59.98,'2024-04-01'), (3,2,3,1,49.99,'2024-04-05'), (4,3,4,1,599.99,'2024-04-10'), (5,4,1,1,1299.99,'2024-04-12'), (6,2,2,3,89.97,'2024-04-15'); """) conn.commit() conn.close() def sqlite_uri(db_path: str, read_only: bool = True) -> str: abs_path = os.path.abspath(db_path) if read_only: return f"sqlite:///file:{quote(abs_path)}?mode=ro&uri=true" return f"sqlite:///{abs_path}" def build_agent(db_path: str, read_only: bool = True): db = SQLDatabase.from_uri(sqlite_uri(db_path, read_only=read_only)) llm = ChatOpenAI(model="openai/gpt-4o-mini", temperature=0) toolkit = SQLDatabaseToolkit(db=db, llm=llm) agent = create_sql_agent( llm=llm, toolkit=toolkit, agent_type="openai-tools", verbose=False, ) return agent, db def main(): parser = argparse.ArgumentParser(description="SQL Query Agent") parser.add_argument("--db", default="data/demo.sqlite", help="SQLite database path") parser.add_argument("--question", help="Natural language question (omit for interactive)") parser.add_argument("--allow-write", action="store_true", help="Open the SQLite database read-write instead of read-only") args = parser.parse_args() if args.db == "data/demo.sqlite" and not os.path.exists("data/demo.sqlite"): print("Creating demo e-commerce database...") create_demo_database("data/demo.sqlite") agent, db = build_agent(args.db, read_only=not args.allow_write) print(f"\nConnected to: {args.db}") print(f"Mode: {'read-write' if args.allow_write else 'read-only'}") print(f"Tables: {', '.join(db.get_table_names())}\n") if args.question: print(f"Question: {args.question}") result = agent.invoke({"input": args.question}) print(f"\nAnswer: {result['output']}") else: print("SQL Agent ready. Ask questions in natural language. Type 'quit' to exit.\n") while True: question = input("You: ").strip() if question.lower() in ("quit", "exit", "q"): break if not question: continue result = agent.invoke({"input": question}) print(f"\nAgent: {result['output']}\n") if __name__ == "__main__": main()