Skip to content

kallekaa/SQL-Query-Agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SQL Query Agent

A minimal CLI LangGraph ReAct agent that answers natural-language questions about a SQLite database.

The agent can inspect database schema, generate read-only SQL, execute queries, and summarize results in plain English from the terminal.

Setup

python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

Create a .env file before using ask or chat:

Copy-Item .env.example .env

Then edit .env. OpenRouter is the default provider, so set OPENROUTER_API_KEY and OPENROUTER_MODEL. For OpenAI, set SQL_AGENT_MODEL_PROVIDER=openai and OPENAI_API_KEY. For a local OpenAI-compatible model server, set SQL_AGENT_MODEL_PROVIDER=local.

Example:

OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-5.4-mini
SQL_AGENT_MODEL_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=openai/gpt-5.4-mini
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_SITE_URL=
OPENROUTER_APP_NAME=SQL Query Agent
DATABASE_FILE=./data/sample.db
SQL_AGENT_MAX_ROWS=100
SQL_AGENT_MEMORY_ENABLED=true
SQL_AGENT_MEMORY_FILE=
SQL_AGENT_AUDIT_LOG_FILE=
SQL_PLANNER_MODEL_PROVIDER=
SQL_PLANNER_MODEL=
SQL_PLANNER_BASE_URL=
SQL_PLANNER_MAX_STEPS=25
LOCAL_MODEL_BASE_URL=http://localhost:1234/v1
LOCAL_MODEL_NAME=
LOCAL_MODEL_API_KEY=local
LANGSMITH_TRACING=false
LANGSMITH_API_KEY=
LANGSMITH_PROJECT=sql-query-agent

Quick Start

Create a sample database:

sql-agent init-sample --db ./data/sample.db

If your .env contains DATABASE_FILE=./data/sample.db, you can also run:

sql-agent init-sample --db $env:DATABASE_FILE

Ask one question:

sql-agent ask "How many customers do we have?" --show-sql

Start an interactive session:

sql-agent chat

Start the browser UI:

sql-agent ui

Start a goal-oriented planner/orchestrator session:

sql-agent plan

Use a local OpenAI-compatible model server:

sql-agent ask "How many customers do we have?" --provider local --show-sql --show-table

OpenRouter is the default provider, so this is equivalent to passing --provider openrouter:

sql-agent ask "How many customers do we have?" --model openai/gpt-5.4-mini --show-sql

Use --db on ask, chat, ui, or plan to override the database path for a single run.

CLI Commands

Command Description
sql-agent init-sample --db ./data/sample.db Creates the sample SQLite database with customers, products, orders, and order items.
sql-agent init-sample --db ./data/sample.db --overwrite Recreates the sample database if it already exists.
sql-agent ask "How many customers do we have?" Runs one question through the agent and prints one answer. Uses the database path from .env unless --db is passed.
sql-agent ask "How many customers do we have?" --show-sql Runs one question and also prints any SQL query the agent used.
sql-agent ask "How many customers do we have?" --show-table Runs one question and also prints SQL query results as formatted CLI tables.
sql-agent ask "How many customers do we have?" --show-sql --show-table Prints both the generated SQL and the returned SQL result table.
sql-agent ask "..." --db ./data/sample.db Runs one question against a specific SQLite database file.
sql-agent ask "..." --model openai/gpt-5.4-mini Runs one question with a specific OpenRouter model instead of OPENROUTER_MODEL from .env.
sql-agent ask "..." --provider openai --model gpt-5.4-mini Runs one question using OpenAI with a specific OpenAI model.
sql-agent ask "..." --provider openrouter --model openai/gpt-5.4-mini Runs one question using OpenRouter with a specific OpenRouter model slug.
sql-agent ask "..." --provider local Runs one question using a local OpenAI-compatible model server. If no model is configured, the app uses the first model from /models.
sql-agent ask "..." --provider local --base-url http://localhost:11434/v1 Runs one question against a specific local OpenAI-compatible base URL, such as an Ollama-compatible endpoint.
sql-agent ask "..." --max-rows 50 Limits the number of rows returned by the SQL query tool for that run.
sql-agent ask "..." --memory-file ./data/custom-memory.md Uses a specific markdown file for persistent database notes.
sql-agent ask "..." --no-memory Disables persistent markdown memory for that run.
sql-agent ask "..." --audit-log ./logs/query-audit.jsonl Appends query audit events as JSON Lines for generated SQL, timing, row count, truncation, provider/model, and errors.
sql-agent chat Starts an interactive single-user terminal session. Uses .env for model and database settings.
sql-agent chat --show-sql Starts interactive mode and prints SQL queries used for each answer.
sql-agent chat --show-table Starts interactive mode and prints SQL query result tables after each answer.
sql-agent chat --provider openrouter --show-sql --show-table Starts interactive mode using OpenRouter and prints SQL plus result tables.
sql-agent chat --provider local --show-sql --show-table Starts interactive mode with a local OpenAI-compatible model server and prints SQL plus result tables.
sql-agent chat --db ./data/sample.db Starts interactive mode against a specific SQLite database file.
sql-agent chat --no-memory Starts interactive mode without loading or writing persistent memory.
sql-agent ui Starts a lightweight local browser UI at http://127.0.0.1:8000/ and opens it automatically.
sql-agent ui --no-open Starts the browser UI without opening a browser tab.
sql-agent ui --host 127.0.0.1 --port 8765 Starts the browser UI on a specific host and port.
sql-agent ui --db ./data/sample.db --provider local Starts the browser UI with the same model and database overrides used by ask and chat.
sql-agent plan Starts an interactive planner/orchestrator session. Each user turn is treated as a goal.
sql-agent plan --show-steps Prints planner-to-SQL-agent call summaries while the planner works.
sql-agent plan --show-sql --show-table Shows SQL and result tables from the SQL-agent calls made by the planner.
sql-agent plan --planner-provider openai --planner-model gpt-5.4-mini Uses a planner-specific model while the SQL agent keeps its own configured model.
sql-agent plan --max-steps 40 Allows up to 40 SQL-agent calls for each planner turn.
sql-agent --help Shows top-level CLI help.
sql-agent <command> --help Shows help for a specific command, such as ask, chat, or init-sample.

Agentic Workflow

The app uses a ReAct-style LangGraph agent: the model receives the user question, decides whether it needs database context, calls tools when needed, then produces a final natural-language answer from the tool results.

The plan command adds a second ReAct-style planner/orchestrator. The planner receives the user's broader goal, breaks it into focused data questions, and can call the original SQL agent repeatedly through one tool named ask_sql_agent. The planner never receives direct SQL execution access; all schema lookup, SQL generation, read-only validation, query execution, result display, and database memory behavior still flow through the original SQL agent.

Key Code Path

These are the main steps the code follows when you run sql-agent ask, sql-agent chat, sql-agent ui, or sql-agent plan:

  1. sql_query_agent/cli.py parses init-sample, ask, chat, ui, and plan commands.
  2. config_from_env in sql_query_agent/agent.py loads .env, applies CLI overrides, validates the provider, resolves the model settings, and chooses the database path.
  3. build_agent creates a LangGraph ReAct agent with a ChatOpenAI client and two tools: get_schema and query_database.
  4. _system_prompt builds the instruction prompt. It includes today's date, the available table names, optional persistent memory notes, and the rules for read-only SQL.
  5. _table_context loads table names only. Detailed columns, keys, and relationships are left out until the model explicitly calls get_schema.
  6. When schema detail is needed, get_schema calls sql_query_agent/db.py to return columns, primary keys, and foreign keys for selected tables.
  7. When data is needed, query_database sends generated SQL to sql_query_agent/db.py, where the SQL is validated as read-only before SQLite executes it.
  8. _print_intermediate_query_output prints generated SQL or result tables during the run when --show-sql or --show-table is enabled.
  9. _answer_from_result reads the LangGraph message trace and returns a clean AgentAnswer containing the final text, generated SQL, and query result payloads.
  10. If persistent memory is enabled and the answer used successful query results, _remember_after_answer asks the model whether one short reusable database note should be appended to markdown.

How One Question Is Answered

  1. You run a command such as sql-agent ask "How many customers do we have?" --show-sql.
  2. The CLI turns that command into an AgentConfig, including the database path, model provider, row limit, display flags, and memory settings.
  3. The agent starts with lightweight database context: table names and any saved memory notes. This keeps the prompt small and lets the model request more detail only when needed.
  4. The model chooses the next action. It can answer directly, call get_schema for database structure, or call query_database for actual rows.
  5. If SQL is generated, db.py rejects write/admin statements and only allows one read-only SQLite SELECT or WITH query.
  6. The query result goes back to the model, and the model writes the final answer in natural language.
  7. The CLI prints the final answer. If requested, it also prints the generated SQL and result table.
  8. After a successful database-backed answer, memory may append a reusable note such as a table relationship or business term mapping for future questions.

In short:

User question
  -> CLI command
  -> LangGraph ReAct agent
  -> startup table-name context
  -> optional detailed schema lookup
  -> read-only SQL validation
  -> SQLite query execution
  -> natural-language answer

Configuration

Values are loaded from .env in the current working directory. CLI flags take precedence over environment variables.

Variable Purpose
SQL_AGENT_MODEL_PROVIDER Model provider: openrouter, openai, or local. Defaults to openrouter.
OPENAI_API_KEY OpenAI API key used when SQL_AGENT_MODEL_PROVIDER=openai.
OPENAI_MODEL Tool-capable model used by the agent. Defaults to gpt-5.4-mini in .env.example.
OPENAI_BASE_URL Optional OpenAI-compatible base URL for OpenAI/proxy usage.
OPENROUTER_API_KEY OpenRouter API key used when SQL_AGENT_MODEL_PROVIDER=openrouter.
OPENROUTER_MODEL OpenRouter model slug used by the agent, such as openai/gpt-5.4-mini.
OPENROUTER_BASE_URL OpenRouter-compatible base URL. Defaults to https://openrouter.ai/api/v1.
OPENROUTER_SITE_URL Optional OpenRouter attribution site URL sent as HTTP-Referer.
OPENROUTER_APP_NAME Optional OpenRouter attribution app name sent as X-Title.
LOCAL_MODEL_BASE_URL OpenAI-compatible local model server base URL. Defaults to http://localhost:1234/v1.
LOCAL_MODEL_NAME Optional local model name. If empty, the app uses the first model returned by LOCAL_MODEL_BASE_URL/models.
LOCAL_MODEL_API_KEY API key sent to the local model server. Defaults to local.
DATABASE_FILE Default SQLite database path.
SQL_AGENT_DB_PATH Legacy SQLite database path alias. Used only when DATABASE_FILE is not set.
SQL_AGENT_MAX_ROWS Maximum rows returned by the query tool. Defaults to 100.
SQL_AGENT_MEMORY_ENABLED Enables persistent markdown memory when true. Defaults to true.
SQL_AGENT_MEMORY_FILE Optional markdown memory file path. Defaults to a per-database sidecar such as ./data/sample.memory.md.
SQL_AGENT_AUDIT_LOG_FILE Optional JSON Lines file for query audit events. Disabled when empty.
SQL_PLANNER_MODEL_PROVIDER Optional planner provider: openrouter, openai, or local. Defaults to the SQL agent provider.
SQL_PLANNER_MODEL Optional planner model name. Defaults to the SQL agent model when the planner provider matches.
SQL_PLANNER_BASE_URL Optional planner OpenAI-compatible base URL. Defaults to the SQL agent base URL when the planner provider matches.
SQL_PLANNER_MAX_STEPS Maximum SQL-agent calls per planner turn. Defaults to 25.
LANGSMITH_TRACING Set to true to send LangGraph/LangChain traces to LangSmith.
LANGSMITH_API_KEY LangSmith API key used when tracing is enabled.
LANGSMITH_PROJECT LangSmith project name for traces. Defaults to sql-query-agent in .env.example.
LANGSMITH_ENDPOINT Optional LangSmith endpoint for non-default regions.

Database path precedence:

  1. --db
  2. DATABASE_FILE
  3. SQL_AGENT_DB_PATH
  4. ./data/sample.db

Persistent Memory

The agent can keep short reusable notes about the database in a markdown file. Memory is enabled by default and is loaded into the system prompt before each ask run and each chat turn.

The browser UI uses the same memory behavior as ask: each submitted question can load existing notes and append one new reusable note after a successful database-backed answer.

By default, the memory file is a sidecar next to the configured database. For DATABASE_FILE=./data/sample.db, the memory file is ./data/sample.memory.md.

After a database-backed answer, the model gets a small follow-up prompt asking whether one short note should be appended. Notes should help future SQL generation, such as remembering table relationships, business term mappings, useful joins, status meanings, date semantics, or reusable metric definitions. One-off answers and raw counts should not be written.

Use SQL_AGENT_MEMORY_FILE or --memory-file to choose a different file. Use SQL_AGENT_MEMORY_ENABLED=false or --no-memory to disable loading and writing memory.

Query Audit Logging

Query audit logging is disabled by default. To enable it, set SQL_AGENT_AUDIT_LOG_FILE or pass --audit-log to ask, chat, or ui:

sql-agent ask "How many paid orders do we have?" --audit-log ./logs/query-audit.jsonl

Each executed query tool call appends one JSON object per line with the generated SQL, duration, row count, truncation flag, provider, model, database path, status, and error type/message if validation or SQLite execution failed. Audit events do not include API keys, prompts, final answers, or result rows.

Local Models

The local provider works with local servers that expose OpenAI-compatible /v1/chat/completions and /v1/models endpoints. The selected local model must support tool/function calling because the SQL agent uses tools for schema lookup and query execution.

Common local base URLs:

  • LM Studio: http://localhost:1234/v1
  • Ollama: http://localhost:11434/v1

If LOCAL_MODEL_NAME and --model are omitted, the app calls {LOCAL_MODEL_BASE_URL}/models and uses the first returned model id.

OpenRouter

The openrouter provider is the default. It uses OpenRouter's OpenAI-compatible chat API at https://openrouter.ai/api/v1. Set an OpenRouter API key and model slug in .env:

SQL_AGENT_MODEL_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-...
OPENROUTER_MODEL=openai/gpt-5.4-mini

OpenRouter model names include the provider prefix, such as openai/..., anthropic/..., or another slug from the OpenRouter model list. You can also pass the model per run:

sql-agent ask "How many customers do we have?" --provider openrouter --model openai/gpt-5.4-mini

OPENROUTER_SITE_URL and OPENROUTER_APP_NAME are optional attribution headers.

LangSmith Monitoring

LangSmith tracing is opt-in. To monitor agent actions, update .env:

LANGSMITH_TRACING=true
LANGSMITH_API_KEY=ls_...
LANGSMITH_PROJECT=sql-query-agent

When tracing is enabled, LangChain/LangGraph automatically sends model calls, tool calls, and graph steps to LangSmith. The app also attaches metadata to each run:

  • database_file
  • provider
  • model
  • base_url
  • max_rows
  • chat session_id and turn for interactive runs

If your LangSmith account is outside the default US region, also set LANGSMITH_ENDPOINT.

Safety

The agent runtime only executes read-only SQL. Queries are parsed with sqlglot, only one SELECT or WITH statement is accepted, and SQLite is opened with a read-only connection URI.

The sample database initializer writes a demo database, but that command is separate from agent query execution.

Rejected SQL returns a tool error instead of running against the database.

Tests

pytest

Project Layout

sql_query_agent/
  agent.py        LangGraph agent setup and .env-backed config.
  cli.py          argparse command-line interface.
  db.py           SQLite schema/query tools and read-only SQL validation.
  planner.py      Planner/orchestrator agent that calls the SQL agent as a tool.
  sample_data.py  Sample database initializer.
  web_ui.py       Lightweight stdlib browser UI server and JSON API.
  static/         Browser UI HTML, CSS, JavaScript, and vendored markdown scripts.
tests/            Minimal pytest coverage for config, SQL safety, schema, and CLI setup.

About

SQL-Query-Agent

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors