基于大模型与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/
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

公众号二维码

公众号二维码

posted @ 2025-12-23 22:17  CodeShare  阅读(0)  评论(0)    收藏  举报