Vanna:让数据库查询变得简单的开源RAG框架

在数据驱动的时代,数据库查询是每个业务人员和技术人员都需要掌握的技能。但传统的SQL查询对于非技术人员来说,往往是一道难以逾越的门槛。今天我要给大家介绍一个开源神器——Vanna,它能让你用自然语言就能轻松查询数据库,真正实现"一句话查数据"!

一、Vanna核心定位——专业的Text-to-SQL解决方案

Vanna是一款开源的检索增强生成(RAG)框架,专注于解决"自然语言转SQL(Text-to-SQL)"的需求。它支持与各类数据库交互,让非技术人员通过自然语言即可完成数据库查询,同时满足企业级场景下的定制化与本地化部署需求。

Vanna的核心价值在于:

  • 降低技术门槛:让不懂SQL的业务人员也能轻松查询数据库

  • 提升查询准确性:基于RAG技术,有效解决大语言模型的"幻觉"问题

  • 灵活适配:支持多种大语言模型、向量数据库和关系型数据库

  • 企业级安全:支持本地化部署,保障企业数据安全

二、Vanna核心特点

特点 详细说明
开源与可定制化 提供完整 Python 库,支持本地化部署;允许用户自主选择大语言模型(LLM)、向量数据库(VectorDB)和关系型数据库(如 MySQL、PostgreSQL),适配不同技术栈需求。
RAG 增强准确性 基于检索增强生成技术,通过学习数据库元数据(DDL 语句、表注释)、示例 SQL、业务文档等信息,提升复杂 SQL 查询(如多表关联、聚合函数)的生成准确率,减少 LLM "幻觉"。
多场景适配 覆盖企业数据分析(如销售报表生成)、智能客服(如用户订单查询)、电商搜索(如商品库存统计)、金融报告(如交易流水核对)等场景,降低数据库使用门槛。
灵活的基础设施 支持主流 LLM(如 OpenAI、本地部署的 Ollama)、向量数据库(如 ChromaDB);可通过扩展接口适配非默认支持的数据库类型,兼容性强。

三、Vanna工作原理——RAG技术加持的智能查询

Vanna的核心在于其独特的RAG(检索增强生成)技术实现。传统的Text-to-SQL方案往往存在"幻觉"问题,即大语言模型生成的SQL语句可能不符合实际的数据库结构。Vanna通过以下方式解决了这个问题:

1. 智能训练机制

Vanna会学习数据库的元数据信息,包括:

  • 数据库表结构(DDL语句)

  • 字段含义说明

  • 历史查询示例

  • 业务规则文档

# 训练表结构
vn.train(ddl="""
CREATE TABLE heros (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL COMMENT '英雄名称',
    attack_max FLOAT COMMENT '最大攻击力',
    hp_max FLOAT COMMENT '最大生命值',
    role_main VARCHAR(255) COMMENT '主要定位'
);
""")
​
# 训练业务规则
vn.train(documentation="""
英雄表查询规则:
1. attack_max字段表示英雄的最大攻击力
2. role_main字段表示英雄的主要定位,包括坦克、战士、法师、刺客、射手、辅助
3. 查询"前N名"时需要使用ORDER BY和LIMIT
""")
​
# 训练示例SQL
vn.train(sql="""
-- 示例:查询role为"战士"的英雄中,生命值前3名的记录
SELECT name, hp_max 
FROM heros 
WHERE role_main = '战士' 
ORDER BY hp_max DESC 
LIMIT 3;
""")

2. 两阶段查询流程

Vanna的查询过程分为两个阶段:

第一阶段:知识检索

  • 将用户问题转换为向量

  • 在向量数据库中检索相关知识(表结构、业务规则等)

第二阶段:智能生成

  • 将检索到的知识与用户问题组合成Prompt

  • 交给大语言模型生成准确的SQL语句

3. 核心函数解析

Vanna的查询流程依赖多个核心函数协同工作,其中ask是入口函数,会自动调用其他函数完成全流程:

  1. ask函数(核心入口)

    • 接收用户自然语言问题

    • 串联"检索→SQL生成→执行→可视化"全流程

    • 返回查询结果与图表

  2. generate_sql函数(SQL生成)

  3. run_sql函数(SQL执行)

    • 执行生成的SQL语句

    • 与数据库交互并返回结果(Pandas DataFrame格式)

  4. 可视化函数

四、Vanna使用步骤(完整流程)

1. 安装Vanna

# 基础安装
pip install vanna
​
# 完整安装(支持ChromaDB向量库、Ollama本地LLM、MySQL数据库)
pip install vanna[chromadb,ollama,mysql]
​
# 完整安装(支持ChromaDB、OpenAI、MySQL)
pip install vanna[chromadb,openai,mysql]

2. 环境准备与初始化

from vanna.openai import OpenAI_Chat
from vanna.chromadb.chromadb_vector import ChromaDB_VectorStore
import mysql.connector
from openai import OpenAI
​
class MyVanna(ChromaDB_VectorStore, OpenAI_Chat):
    def __init__(self, config=None):
        chroma_config = config.copy() if config else {}
        openai_config = config.copy() if config else {}
        
        if config and 'client' in config:
            self.client = config['client']
        else:
            self.client = None
            
        if 'client' in chroma_config:
            del chroma_config['client']
            
        ChromaDB_VectorStore.__init__(self, config=chroma_config)
        OpenAI_Chat.__init__(self, config=openai_config)
​
# 初始化Vanna
client = OpenAI(
    api_key='your-api-key',
    base_url='https://api.openai.com/v1'
)
​
vn = MyVanna(config={
    'model': 'gpt-4', 
    'client': client
})

3. 数据库连接与训练

# 连接数据库
vn.connect_to_mysql(
    host='your-host',
    dbname='action', 
    user='your-username', 
    password='your-password', 
    port=3306
)
​
# 自动训练所有表的schema
connection = mysql.connector.connect(
    host='your-host',
    database='action',
    user='your-username',
    password='your-password',
    port=3306
)
​
cursor = connection.cursor()
cursor.execute("""
    SELECT TABLE_NAME 
    FROM information_schema.TABLES 
    WHERE TABLE_SCHEMA = 'action'
""")
tables = cursor.fetchall()
​
# 训练每个表的结构
for (table_name,) in tables:
    try:
        cursor.execute(f"SHOW CREATE TABLE {table_name}")
        _, create_table = cursor.fetchone()
        vn.train(ddl=create_table)
        print(f"已训练表 {table_name}")
    except Exception as e:
        print(f"训练表 {table_name} 失败: {str(e)}")

4. 自然语言查询

现在我们可以用自然语言进行查询了:

# 查询攻击力最高的5个英雄
result = vn.ask("找出英雄攻击力最高的前5个英雄")
print(result["df"])  # 查看查询结果
​
# 带条件的查询
vn.ask("查询定位为法师的英雄,按最大生命值降序排列,显示前10条")
​
# 复杂查询
vn.ask("统计不同英雄定位的数量,显示定位名称和对应英雄数")
​
# 单独生成SQL(用于调试)
sql = vn.generate_sql("查询heros表中定位为刺客的英雄,攻击力前3名")
print("生成的SQL:", sql)

五、Vanna提升复杂SQL查询准确率的机制

Vanna通过以下机制显著提升复杂SQL查询的生成准确率:

1. 多维度知识学习

Vanna不仅仅依赖大语言模型的通用知识,而是通过训练让模型学习特定数据库的详细信息:

  • DDL语句训练:明确表结构、字段类型和约束关系

  • 业务文档训练:理解字段含义和业务规则

  • 示例SQL训练:学习常见的查询模式和逻辑

2. 检索增强生成(RAG)技术

在查询时,Vanna会:

  1. 将用户问题转换为向量表示

  2. 在向量数据库中检索最相关的知识(DDL、文档、示例SQL)

  3. 将检索到的知识与问题组合成结构化Prompt

  4. 交给大语言模型生成准确的SQL语句

这种方式有效避免了大语言模型因不了解具体数据库结构而产生的"幻觉"问题。

3. 多层次验证机制

Vanna提供了多种验证和调试手段:

六、Vanna在不同场景下的应用示例

1. 企业数据分析

业务人员可以直接问:"上个月销售额最高的产品是哪些?",Vanna会自动生成SQL并返回结果。

# 销售数据分析示例
vn.ask("查询上个月销售额最高的前10个产品,显示产品名称和销售额")
vn.ask("统计各部门的销售业绩,按销售额降序排列")

2. 智能客服系统

客户询问:"我的订单状态是什么?",系统自动查询数据库并返回结果。

# 订单查询示例
vn.ask("查询客户ID为12345的最新订单状态和预计送达时间")

3. 电商库存管理

运营人员问:"库存不足100的商品有哪些?",系统立即返回相关商品列表。

# 库存管理示例
vn.ask("查询库存数量少于100的商品,按库存数量升序排列")
vn.ask("统计各类别商品的库存总价值")

4. 金融报表生成

财务人员问:"本月交易金额超过10000的客户有哪些?",系统自动生成报表。

# 金融数据分析示例
vn.ask("查询本月交易金额超过10000的客户,显示客户姓名和交易总额")
vn.ask("统计不同风险等级的投资产品数量和平均收益率")

5. 游戏数据分析(基于heros表)

针对我们示例中的英雄数据表,可以进行以下查询:

# 英雄数据分析示例
vn.ask("查询所有定位为'坦克'的英雄,按最大生命值降序排列")
vn.ask("统计不同英雄定位的数量,显示定位名称和对应英雄数")
vn.ask("查询攻击力大于800的英雄,显示名称、攻击力和定位,按攻击力升序排列")
vn.ask("找出战士职业中生命值前5名的英雄")
vn.ask("统计各攻击范围类型的英雄数量")

七、实践打卡任务

结合前文内容,完成以下Text-to-SQL实践,熟练掌握Vanna的使用流程:

1. 任务目标

基于本地MySQL数据库中的heros表,使用Vanna实现自然语言查询,验证"从训练到查询"的全流程。

2. 任务步骤

  1. 环境准备:安装Vanna及扩展组件(vanna[chromadb,openai,mysql]),确保MySQL服务正常运行。

  2. 数据准备:在MySQL中创建heros表,并插入测试数据。

  3. Vanna训练:通过DDL语句、业务文档训练模型,明确heros表结构与查询规则。

  4. 自然语言查询:使用vn.ask完成至少3个查询需求。

  5. 结果验证:检查生成的SQL语法正确性,对比执行结果与预期是否一致。

八、总结

Vanna作为一款开源的Text-to-SQL框架,真正实现了让非技术人员也能轻松查询数据库的目标。其基于RAG技术的实现方式,有效解决了大语言模型在数据库查询中的准确性问题。

通过简单的训练和配置,Vanna就能帮助企业和个人实现数据查询的智能化,大大降低了数据分析的门槛。无论是业务人员、客服还是运营,都能通过自然语言快速获取所需数据,真正实现了"数据民主化"。

如果你也在为复杂的SQL查询而头疼,不妨试试Vanna这个开源神器,相信它会给你带来全新的数据查询体验!

完整代码

from vanna.openai import OpenAI_Chat
from vanna.chromadb.chromadb_vector import ChromaDB_VectorStore
import mysql.connector
import time
from openai import OpenAI

class MyVanna(ChromaDB_VectorStore, OpenAI_Chat):
    def __init__(self, config=None):
        # 分离配置,只将OpenAI客户端传递给OpenAI_Chat
        chroma_config = config.copy() if config else {}
        openai_config = config.copy() if config else {}
        
        # 保存client到实例属性
        if config and 'client' in config:
            self.client = config['client']
        else:
            self.client = None
        
        # 从chroma_config中移除client
        if 'client' in chroma_config:
            del chroma_config['client']
            
        # 初始化两个基类
        ChromaDB_VectorStore.__init__(self, config=chroma_config)
        OpenAI_Chat.__init__(self, config=openai_config)

# 创建OpenAI客户端
client = OpenAI(
    api_key='sk-mi9MCut4OAAfY4SUE8a4fCQBLfMZPZpV5an6Ove0PPUSVVBq',
    base_url='https://chatapi.littlewheat.com/v1'
)

# 初始化Vanna实例
vn = MyVanna(config={
    'model': 'gpt-4o-mini', 
    'client': client
})

vn.connect_to_mysql(host='rm-uf6z891lon6dxuqblqo.mysql.rds.aliyuncs.com', 
                    dbname='action', user='student123', password='student321', port=3306)

# 连接到MySQL数据库
try:
    connection = mysql.connector.connect(
        host='rm-uf6z891lon6dxuqblqo.mysql.rds.aliyuncs.com',
        database='action',
        user='student123',
        password='student321',
        port=3306
    )
    print("成功连接到MySQL数据库")
    
    # 获取所有表名
    cursor = connection.cursor()
    cursor.execute("""
        SELECT TABLE_NAME 
        FROM information_schema.TABLES 
        WHERE TABLE_SCHEMA = 'action'
    """)
    tables = cursor.fetchall()
    
    # 训练每个表的schema
    for (table_name,) in tables:
        try:
            # 获取表的创建语句
            cursor.execute(f"SHOW CREATE TABLE {table_name}")
            _, create_table = cursor.fetchone()
            
            print(f"正在训练表 {table_name} 的schema...")
            vn.train(ddl=create_table)
            
        except Exception as e:
            print(f"训练表 {table_name} 失败: {str(e)}")
            continue
    
    print("Schema训练完成")
    
    # 示例:使用Vanna进行自然语言查询
    question = "找出英雄攻击力最高的前5个英雄"
    #print(f"\n问题: {question}")
    vn.ask(question)
    #vn.ask("查询heros表中 英雄攻击力前5名的英雄")
    # sql=vn.generate_sql("查询heros表中 英雄攻击力前5名的英雄")
    # print('sql=', sql)
    # df = vn.run_sql(sql)
    # print('df=', df)
    
except mysql.connector.Error as err:
    print(f"数据库连接错误: {err}")
finally:
    if 'connection' in locals() and connection.is_connected():
        cursor.close()
        connection.close()
        print("MySQL连接已关闭")


# In[ ]:


# #help(vn.ask)
# #vn.ask("查询heros表中 英雄攻击力前5名的英雄")
# sql=vn.generate_sql("查询heros表中 英雄攻击力前5名的英雄")
# print('sql=', sql)
# vn.run_sql(sql)

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐