引言
数据已成为任何成功企业不可或缺的资源,它能为明智的决策制定提供有价值的见解。鉴于数据的重要性,许多公司都在构建用于存储和分析数据的系统。然而,很多时候获取和分析所需数据并非易事,尤其是随着数据系统的日益复杂。
随着生成式人工智能的出现,数据处理工作变得轻松了许多 —— 我们现在可以使用简单的自然语言,就能获得基本准确且与输入高度契合的输出结果。这一点也适用于使用 SQL 进行的数据处理和分析,我们可以通过自然语言来请求生成查询语句。
在本文中,我们将开发一个简易的 API 应用程序,它能将自然语言转换为数据库可理解的 SQL 查询语句。我们将使用三个主要工具:OpenAI、FastAPI 和 SQLite。
以下是具体规划。
文本转 SQL 应用程序开发
首先,我们需要准备项目所需的所有内容。你只需提供 OpenAI API 密钥,我们将用它来访问生成式模型。为了实现应用程序的容器化,我们将使用 Docker,你可以通过 Docker Desktop 在本地环境中获取它。
其他组件(如 SQLite)在安装 Python 时会自动可用,而 FastAPI 将在后续步骤中安装。
项目的整体结构如下:
plaintext
text_to_sql_app/
├── app/
│ ├── __init__.py
│ ├── database.py
│ ├── openai_utils.py
│ └── main.py
├── demo.db
├── init_db.sql
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── .env
你可以按照上述结构创建项目,也可以使用以下代码仓库简化操作。我们仍会逐一讲解每个文件,帮助你理解应用程序的开发过程。
首先,在.env文件中填入之前获取的 OpenAI API 密钥,代码如下:
plaintext
OPENAI_API_KEY=你的API密钥
接着,在requirements.txt中填写项目所需的库:
plaintext
fastapi
uvicorn
sqlalchemy
openai
pydantic
python-dotenv
然后,编写__init__.py文件的代码:
plaintext
from pathlib import Path
from dotenv import load_dotenv
load_dotenv(dotenv_path=Path(__file__).resolve().parent.parent / ".env", override=False)
上述代码确保环境中包含所有必需的密钥。
之后,在database.py文件中编写 Python 代码,用于连接后续创建的 SQLite 数据库(名为demo.db)并提供执行 SQL 查询的方法:
plaintext
from sqlalchemy import create_engine, text
from sqlalchemy.orm import Session
ENGINE = create_engine("sqlite:///demo.db", future=True, echo=False)
def run_query(sql: str) -> list[dict]:
with Session(ENGINE) as session:
rows = session.execute(text(sql)).mappings().all()
return [dict(r) for r in rows]
接下来,准备openai_utils.py文件,该文件将接收数据库模式和输入的问题,输出包含 SQL 查询的 JSON(并添加防护机制以防止任何写入操作):
plaintext
import os
import json
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
_SYSTEM_PROMPT = """
你需要将自然语言问题转换为只读的SQLite SQL语句。
禁止输出INSERT / UPDATE / DELETE语句。
返回格式为JSON:{ "sql": "..." }。
"""
def text_to_sql(question: str, schema: str) -> str:
response = client.chat.completions.create(
model="gpt-4o-mini",
temperature=0.1,
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": _SYSTEM_PROMPT},
{"role": "user",
"content": f"数据库模式:\n{schema}\n\n问题: {question}"}
]
)
payload = json.loads(response.choices[0].message.content)
return payload["sql"]
完成代码编写和连接配置后,我们将使用 FastAPI 构建应用程序。该应用程序将接收自然语言问题和数据库模式,将其转换为 SQL 的SELECT查询语句,在 SQLite 数据库中执行该查询,并以 JSON 格式返回结果。这是一个可通过命令行界面(CLI)访问的 API。
plaintext
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from sqlalchemy import inspect
from .database import ENGINE, run_query
from .openai_utils import text_to_sql
app = FastAPI(title="文本转SQL演示")
class NLRequest(BaseModel):
question: str
@app.on_event("startup")
def capture_schema() -> None:
insp = inspect(ENGINE)
global SCHEMA_STR
SCHEMA_STR = "\n".join(
f"CREATE TABLE {t} ({', '.join(c['name'] for c in insp.get_columns(t))});"
for t in insp.get_table_names()
)
@app.post("/query")
def query(req: NLRequest):
try:
sql = text_to_sql(req.question, SCHEMA_STR)
if not sql.lstrip().lower().startswith("select"):
raise ValueError("仅允许SELECT语句")
return {"sql": sql, "result": run_query(sql)}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
以上就是主应用程序的全部内容。接下来需要准备数据库。你可以使用下面的代码作为init_db.sql文件的示例(也可根据需求自行修改):
plaintext
DROP TABLE IF EXISTS order_items;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS payments;
DROP TABLE IF EXISTS products;
DROP TABLE IF EXISTS customers;
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
country TEXT,
signup_date DATE
);
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
category TEXT,
price REAL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER,
order_date DATE,
total REAL,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
CREATE TABLE order_items (
order_id INTEGER,
product_id INTEGER,
quantity INTEGER,
unit_price REAL,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);
CREATE TABLE payments (
id INTEGER PRIMARY KEY,
order_id INTEGER,
payment_date DATE,
amount REAL,
method TEXT,
FOREIGN KEY (order_id) REFERENCES orders(id)
);
INSERT INTO customers (id, name, country, signup_date) VALUES
(1,'爱丽丝','美国','2024-01-05'),
(2,'鲍勃','英国','2024-03-10'),
(3,'崔','韩国','2024-06-22'),
(4,'达拉','印度尼西亚','2025-01-15');
INSERT INTO products (id, name, category, price) VALUES
(1,'专业笔记本电脑','电子产品',1500.00),
(2,'降噪耳机','电子产品',300.00),
(3,'站立式办公桌','家具',450.00),
(4,'人体工学椅','家具',250.00),
(5,'27英寸显示器','电子产品',350.00);
INSERT INTO orders (id, customer_id, order_date, total) VALUES
(1,1,'2025-02-01',1850.00),
(2,2,'2025-02-03',600.00),
(3,3,'2025-02-05',350.00),
(4,1,'2025-02-07',450.00);
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
(1,1,1,1500.00),
(1,2,1,300.00),
(1,5,1,350.00),
(2,3,1,450.00),
(2,4,1,250.00),
(3,5,1,350.00),
(4,3,1,450.00);
INSERT INTO payments (id, order_id, payment_date, amount, method) VALUES
(1,1,'2025-02-01',1850.00,'信用卡'),
(2,2,'2025-02-03',600.00,'贝宝'),
(3,3,'2025-02-05',350.00,'信用卡'),
(4,4,'2025-02-07',450.00,'银行转账');
然后,在命令行中执行以下代码,为项目创建 SQLite 数据库:
plaintext
sqlite3 demo.db < init_db.sql
数据库准备就绪后,创建Dockerfile以实现应用程序的容器化:
plaintext
FROM python:3.12-slim
WORKDIR /code
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
同时,创建docker-compose.yml文件,以便更顺畅地运行应用程序:
plaintext
services:
text2sql:
build: .
env_file: .env
ports:
- "8000:8000"
restart: unless-stopped
volumes:
- ./demo.db:/code/demo.db
一切准备就绪后,启动 Docker Desktop 并执行以下代码构建应用程序:
plaintext
docker compose build --no-cache
docker compose up -d
如果所有步骤都顺利完成,你可以使用以下代码测试应用程序。例如,查询数据中有多少位客户:
plaintext
curl -X POST "http://localhost:8000/query" -H "Content-Type: application/json" -d "{\"question\":\"有多少位客户?\"}"
输出结果如下:
plaintext
{"sql":"SELECT COUNT(*) AS customer_count FROM customers;","result":[{"customer_count":4}]}
我们也可以尝试更复杂的查询,比如每位客户的订单数量:
plaintext
curl -X POST "http://localhost:8000/query" -H "Content-Type: application/json" -d "{\"question\":\"每位客户下了多少订单?\"}"
输出结果如下:
plaintext
{"sql":"SELECT customer_id, COUNT(*) AS number_of_orders FROM orders GROUP BY customer_id;","result":[{"customer_id":1,"number_of_orders":2},{"customer_id":2,"number_of_orders":1},{"customer_id":3,"number_of_orders":1}]}
以上就是构建基础文本转 SQL 应用程序的全部步骤。你可以进一步为其添加前端界面,或根据自身需求构建更复杂的系统。
总结
数据是所有数据工作的核心,企业依靠数据制定决策。很多时候,我们所使用的系统过于复杂,需要借助生成式人工智能来辅助操作。
在本文中,我们学习了如何使用 OpenAI 模型、FastAPI 和 SQLite 开发简易的文本转 SQL 应用程序。
原文链接:https://www.kdnuggets.com/creating-a-text-to-sql-app-with-openai-fastapi-sqlite
