基于大模型与FastAPI构建文本转SQL应用
引言
数据已成为任何成功业务不可或缺的资源,为明智决策提供宝贵洞察。鉴于数据的重要性,许多机构正在构建存储和分析数据的系统。然而,随着数据系统日益复杂,获取和分析必要数据常常变得困难[citation:1]。
随着生成式人工智能的出现,数据工作变得容易得多。我们现在可以使用简单的自然语言来接收基本准确的输出,这些输出能紧密跟随我们提供的输入。这同样适用于使用SQL进行数据处理和分析,我们可以请求生成查询[citation:1]。
在本文中,我们将开发一个简单的API应用程序,将自然语言翻译成数据库能够理解的SQL查询。我们将使用三个主要工具:某机构的大语言模型、FastAPI和SQLite[citation:1]。
文本转SQL应用开发
首先,我们将准备项目所需的一切。您只需要提供某机构的大语言模型API密钥,我们将用它来访问生成模型。为了将应用程序容器化,我们将使用Docker,您可以通过Docker Desktop在本地实现获取[citation:1]。
其他组件,如SQLite,在安装Python时通常已经可用,而FastAPI将在后续安装[citation:1]。
以下是整体的项目结构:
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
创建上述结构,您也可以使用现成的代码仓库以简化操作。我们仍将浏览每个文件,以了解如何开发此应用程序[citation:1]。
首先,在 .env 文件中填入之前获取的某机构大语言模型API密钥:
OPENAI_API_KEY=YOUR-API-KEY
接着,填写 requirements.txt 文件,包含我们将要使用的必要库:
fastapi
uvicorn
sqlalchemy
openai
pydantic
python-dotenv
接下来,我们转到 __init__.py 文件,并在其中放入以下代码,以确保环境包含所有必需的密钥:
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查询的方法:
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 文件,该文件将接收数据库模式(schema)和输入问题。输出将是包含SQL查询的JSON(并设有防护措施以防止任何写入操作)[citation:1]。
import os
import json
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
_SYSTEM_PROMPT = """
You convert natural-language questions into read-only SQLite SQL.
Never output INSERT / UPDATE / DELETE.
Return 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"schema:\n{schema}\n\nquestion: {question}"}
]
)
payload = json.loads(response.choices[0].message.content)
return payload["sql"]
在代码和连接都准备好之后,我们将使用FastAPI来准备应用程序。该应用程序将接受自然语言问题和数据库模式,将它们转换为SQL SELECT查询,通过SQLite数据库运行这些查询,并将结果以JSON形式返回。该应用程序将成为一个可以通过命令行接口访问的API[citation:1]。
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="Text-to-SQL Demo")
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("Only SELECT statements are allowed")
return {"sql": sql, "result": run_query(sql)}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
以上就是主应用程序所需的全部内容。接下来,我们将准备数据库。在 init_db.sql 中使用下面的数据库作为示例,但您可以根据需要随时更改[citation:1]。
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,'Alice','USA','2024-01-05'),
(2,'Bob','UK','2024-03-10'),
(3,'Choi','KR','2024-06-22'),
(4,'Dara','ID','2025-01-15');
INSERT INTO products (id, name, category, price) VALUES
(1,'Laptop Pro','Electronics',1500.00),
(2,'Noise-Canceling Headphones','Electronics',300.00),
(3,'Standing Desk','Furniture',450.00),
(4,'Ergonomic Chair','Furniture',250.00),
(5,'Monitor 27"','Electronics',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,'Credit Card'),
(2,2,'2025-02-03',600.00,'PayPal'),
(3,3,'2025-02-05',350.00,'Credit Card'),
(4,4,'2025-02-07',450.00,'Bank Transfer');
然后,在命令行中运行以下代码,为我们的项目创建SQLite数据库:
sqlite3 demo.db < init_db.sql
数据库准备就绪后,我们将创建一个Dockerfile来容器化我们的应用程序[citation:1]。
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 文件,以便更流畅地运行应用程序[citation:1]。
services:
text2sql:
build: .
env_file: .env
ports:
- "8000:8000"
restart: unless-stopped
volumes:
- ./demo.db:/code/demo.db
一切准备就绪后,启动您的Docker Desktop并运行以下代码来构建应用程序[citation:1]。
docker compose build --no-cache
docker compose up -d
如果一切顺利,您可以使用以下代码测试应用程序。我们将询问数据中有多少客户[citation:1]。
curl -X POST "http://localhost:8000/query" -H "Content-Type: application/json" -d "{\"question\":\"How many customers?\"}"
输出将如下所示:
{"sql":"SELECT COUNT(*) AS customer_count FROM customers;","result":[{"customer_count":4}]}
我们可以尝试更复杂的查询,例如每个客户的订单数量:
curl -X POST "http://localhost:8000/query" -H "Content-Type: application/json" -d "{\"question\":\"What is the number of orders placed by each customer\"}"
输出类似如下:
{"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应用程序所需的全部内容。您可以根据需要,通过前端界面和更复杂的系统来进一步增强它[citation:1]。
总结
数据是所有数据工作的核心,机构利用它来做决策。很多时候,我们拥有的系统过于复杂,需要依靠生成式人工智能来帮助我们驾驭它[citation:1]。
在本文中,我们学习了如何使用某机构的大语言模型、FastAPI和SQLite来开发一个简单的文本转SQL应用程序[citation:1]。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)或者 我的个人博客 https://blog.qife122.com/
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)
公众号二维码

公众号二维码


浙公网安备 33010602011771号