LLM应用 中级 12分钟

自然语言转SQL

业务人员通过自然语言查询数据库,系统自动生成并执行SQL,返回可视化结果。

Vanna.aiStreamlitPostgreSQLOpenAIPlotly

项目概述

数据分析是企业决策的核心环节,但大多数业务人员缺乏 SQL 技能,每次查询都需要依赖数据工程师,排期长、沟通成本高。

本案例基于 Vanna.ai 框架搭建了一套 NL2SQL 系统,业务人员用自然语言提问,系统自动生成 SQL 并执行查询,结果以可视化图表呈现。系统还具备"从错误中学习"的能力——当生成的 SQL 出错时,人工修正后系统会自动学习,下次类似查询准确率提升。

关键指标

85%
首次查询准确率
< 3s
平均响应时间
92%+
学习后准确率
降低 70%
数据工程师负担

系统架构

系统采用 Vanna.ai 的核心思路:通过 RAG 架构将数据库元信息(表结构、字段含义、示例查询)作为上下文注入 LLM,实现"上下文增强的 NL2SQL"。

┌──────────────────────────────────────────────────────┐
│                  用户界面 (Streamlit)                   │
│  ┌──────────────────────────────────────────────┐    │
│  │  输入: "上个月哪个产品线销售额最高?"            │    │
│  └───────────────────┬──────────────────────────┘    │
│                      ▼                                │
│  ┌──────────────────────────────────────────────┐    │
│  │  Vanna.ai NL2SQL 引擎                         │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐    │    │
│  │  │ 语义理解  │→│ 元数据检索 │→│ SQL生成   │    │    │
│  │  └──────────┘  └──────────┘  └──────────┘    │    │
│  │         │             │             │         │    │
│  │         ▼             ▼             ▼         │    │
│  │  ┌────────────────────────────────────────┐   │    │
│  │  │      向量库:表结构 + 示例查询 + DDL     │   │    │
│  │  └────────────────────────────────────────┘   │    │
│  └───────────────────┬──────────────────────────┘    │
│                      ▼                                │
│  ┌──────────────────────────────────────────────┐    │
│  │  SQL执行 + 结果可视化 (Plotly)                 │    │
│  └──────────────────────────────────────────────┘    │
│                                                       │
│  ┌──────────────────────────────────────────────┐    │
│  │  训练闭环:人工修正 → 向量库更新 → 持续学习    │    │
│  └──────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────┘

实现细节

1

训练数据准备

DDL 导入

自动扫描数据库获取所有表的 DDL(CREATE TABLE 语句),提取表名、字段名、数据类型、主外键关系、索引等元信息。

文档注入

为每个字段添加业务含义描述,如 `order_amount` → "订单金额(单位:元,含税)"。这些描述向量化后作为检索上下文。

示例查询

整理 30-50 个典型的 Q&A 对("问题-SQL"),覆盖常见的查询模式:聚合、分组、时间过滤、多表JOIN。以 few-shot 形式存入向量库。

2

查询生成流程

问题理解

LLM 对用户问题做初步解析,提取关键信息:目标实体(销售额、订单量等)、约束条件(时间范围、类别过滤)、聚合方式(总计、平均值、趋势)。

上下文检索

从向量库中检索与问题语义最相似的 DDL 片段、字段描述和示例查询。Top-5 结果作为上下文注入到 LLM 的 prompt 中。

SQL 生成与执行

LLM 根据上下文 + 问题生成 SQL。系统自动做安全校验(是否只读、是否有危险操作),通过后执行并返回结果。结果自动用 Plotly 生成图表。

3

错误学习与持续优化

错误检测

自动捕获 SQL 执行错误(语法错误、字段不存在、类型不匹配),并分析错误原因。如果是 LLM 的问题,进入修正流程。

人工修正闭环

当生成的 SQL 不正确时,业务人员可在界面上直接修改 SQL。修正后的正确 SQL 会自动加入训练示例库,下次遇到类似查询准确率提升。

查询日志分析

定期分析查询日志,发现高频问题模式(如"用户总是问本月数据但忘记指定时间范围"),更新 prompt 模板和训练数据。

Vanna.ai 核心实现

import vanna
import streamlit as st
from vanna.openai import OpenAI_Chat
from vanna.chromadb import ChromaDB_VectorStore

class MyVanna(ChromaDB_VectorStore, OpenAI_Chat):
    def __init__(self, config):
        ChromaDB_VectorStore.__init__(self, config=config)
        OpenAI_Chat.__init__(self, config=config)

# 初始化
vn = MyVanna(config={
    "api_key": os.getenv("OPENAI_API_KEY"),
    "model": "gpt-4o"
})

# 1. 训练:导入数据库 schema
vn.train(ddl="""
    CREATE TABLE orders (
        id BIGINT PRIMARY KEY,
        customer_id BIGINT REFERENCES customers(id),
        product_id BIGINT REFERENCES products(id),
        order_amount DECIMAL(10,2) COMMENT '订单金额,单位元',
        order_date DATE COMMENT '订单日期',
        status VARCHAR(20) COMMENT '订单状态'
    );
""")

# 2. 训练:注入业务文档
vn.train(documentation="order_amount字段单位为人民币元,含税")
vn.train(documentation="status字段取值:pending/paid/shipped/delivered/cancelled")

# 3. 训练:添加示例查询
vn.train(question="上个月的总销售额是多少?",
         sql="SELECT SUM(order_amount) FROM orders "
             "WHERE order_date >= date_trunc('month', CURRENT_DATE - INTERVAL '1 month') "
             "AND order_date < date_trunc('month', CURRENT_DATE)")

# 4. Streamlit 交互界面
st.title("📊 自然语言查数据")
question = st.text_input("用自然语言描述你要查什么:")

if question:
    sql = vn.generate_sql(question)
    st.code(sql, language="sql")

    # 显示查询结果
    df = vn.run_sql(sql)
    st.dataframe(df)

    # 自动可视化(如果有聚合数据)
    fig = vn.get_plotly_figure(question, sql, df)
    if fig:
        st.plotly_chart(fig)

    # 人工修正回路
    fix = st.text_area("如果结果不对,可以修改 SQL:")
    if fix:
        vn.train(question=question, sql=fix)
        st.success("已学习!下次类似查询会使用这个修正。")
        

经验教训

  • 字段的业务描述是最重要的投资 — 只导 DDL 准确率只有 60%,加上字段业务描述后提升到 80%+。投入时间写好注释事半功倍
  • 复杂 JOIN 是主要失败点 — 3 表以上的 JOIN 查询更容易出错。建议先让用户明确说明涉及哪些表,或者提供可视化的 ER 图辅助
  • 安全校验不能依赖 LLM — 即使 GPT-4 也可能生成 DELETE/UPDATE,必须加 SQL 白名单或只读用户
  • 学习效应有"数据漂移"问题 — 随着时间推移,旧示例可能不再适用。建议每季度清理低质量训练样本,保持向量库质量

更多案例

查看其他 AI 工程化落地案例

返回案例库