基于自然语言查询 MySQL 数据库的 ChatBI 工具(MVP):用户输入一句自然语言问题,系统自动生成 SQL、执行查询,并把结果以可视化表格(列名 + 结果行)的形式返回。
- 自然语言转 SQL:调用 LLM 根据用户问题生成 MySQL SQL 语句
- Schema 上下文:自动读取数据库表结构并注入 Prompt,提升 SQL 生成准确率
- 可视化输出:查询结果以带边框的对齐表格展示,支持中文对齐、
NULL、日期时间等类型 - 安全护栏:执行前校验 SQL 只读性,拒绝
INSERT/UPDATE/DELETE/DROP等破坏性语句 - 多模式入口:支持交互式对话、一次性查询和 FastAPI 服务
chatbi/
├── main.py # 主流程编排(校验 → 生成 SQL → 执行 → 格式化)
├── api_service.py # FastAPI 服务入口
├── prompt_builder.py # Prompt 构造
├── llm_client.py # LLM 调用(OpenAI 兼容接口)
├── query_parser.py # 用户查询解析与校验
├── schema_generator.py # 从数据库生成表结构描述
├── database.py # MySQL 查询执行,返回列名 + 结果行
├── result_formatter.py # 查询结果格式化为可视化表格
├── config.py # 数据库 / LLM 配置读取
├── .env.example # 环境变量示例(复制为 .env 使用)
├── pyproject.toml # 项目与依赖配置
└── src/chatbi/ # 包入口(chatbi 命令)
- Python >= 3.12
- MySQL(或其他兼容 MySQL 协议的数据库)
- 可访问的 OpenAI 兼容 API(可通过
OPENAI_BASE_URL切换服务商)
项目使用 uv 管理依赖:
uv sync也可以使用 pip:
pip install -e .将根目录的 .env.example 复制为 .env,并填写真实配置:
cp .env.example .env各配置项说明:
| 变量 | 说明 | 默认值 |
|---|---|---|
OPENAI_API_KEY |
LLM API Key(必填) | 无 |
OPENAI_BASE_URL |
LLM 接口地址 | https://api.openai.com/v1 |
LLM_MODEL |
使用的模型名称 | gpt-4o |
DB_HOST |
数据库地址 | localhost |
DB_PORT |
数据库端口 | 3306 |
DB_USER |
数据库用户名 | root |
DB_PASSWORD |
数据库密码 | 空 |
DB_NAME |
数据库名称 | chatbi_mvp |
python main.py输入 exit、quit 或 退出 结束程序。
python main.py "查询用户数量"
python main.py "列出销量前 10 的商品名称和销售额"uv run chatbipython api_service.py也可以通过 Uvicorn 启动:
uvicorn api_service:app --host 0.0.0.0 --port 8000服务启动后可访问:
- 查询接口:
POST http://127.0.0.1:8000/chatbi - 健康检查:
GET http://127.0.0.1:8000/health - Swagger 文档:
http://127.0.0.1:8000/docs
查询示例:
curl.exe -X POST "http://127.0.0.1:8000/chatbi" `
-H "Content-Type: application/json" `
-d '{"user_query":"统计每个城市有多少用户"}'成功响应:
{
"question": "统计每个城市有多少用户",
"sql": "SELECT city, COUNT(*) AS user_count FROM t_user GROUP BY city",
"columns": ["city", "user_count"],
"rows": [["上海", 128], ["北京", 96]],
"formatted_result": "+--------+------------+\n..."
}空问题或非只读 SQL 返回 HTTP 400;LLM、数据库或其他服务异常返回 HTTP 500。 API 当前允许任意来源跨域访问,适合开发环境;生产环境建议改为明确的域名白名单。
生成数据库 Schema 描述:
python schema_generator.py预览表格格式化效果(使用内置示例数据):
python result_formatter.py请输入您的查询: 统计每个城市有多少用户
用户问题: 统计每个城市有多少用户
生成的 SQL: SELECT city, COUNT(*) AS user_count FROM t_user GROUP BY city
+--------+------------+
| city | user_count |
+--------+------------+
| 上海 | 128 |
| 北京 | 96 |
+--------+------------+
用户问题
↓ QueryParser 校验(非空)
↓ schema_generator 生成表结构(会话内缓存,失败则降级)
↓ prompt_builder 构造 Prompt(系统指令 + Schema + 问题)
↓ llm_client 调用 LLM 生成 SQL
↓ is_read_only_sql 只读安全检查
↓ database.execute 执行查询,返回 {columns, rows}
↓ result_formatter.format_result 输出可视化表格
| 模块 | 职责 | 关键接口 |
|---|---|---|
main.py |
流程编排、只读校验、交互入口 | execute_chatbi()、run_chatbi()、main() |
api_service.py |
FastAPI 查询服务与健康检查 | app、query_chatbi() |
prompt_builder.py |
构造发送给 LLM 的消息 | build_messages(query, schema) |
llm_client.py |
LLM 调用与结果清理 | generate_sql(query, schema) |
query_parser.py |
用户输入解析与校验 | QueryParser.parse() / validate() |
schema_generator.py |
从数据库生成表结构描述 | generate_schema() |
database.py |
SQL 执行,返回列名与结果行 | Database.execute(sql) |
result_formatter.py |
结果格式化为表格 | format_result(result)、format_table(...) |
config.py |
数据库 / LLM 配置读取 | database_config、llm_config |
main.py在 SQL 执行前会做只读检查:仅允许SELECT/SHOW/EXPLAIN/DESCRIBE/WITH开头的语句;多条语句中只要混入写操作也会被拒绝。- 该检查属于粗略防护,不能替代完整的权限控制。生产环境建议使用只读数据库账号,遵循最小权限原则,并引入更严格的 SQL 校验(如 SQL 解析器、表/列白名单)。
- 将
QueryParser升级为意图识别与实体抽取(时间范围、表名、筛选条件等) - 为查询结果增加图表渲染(如 ECharts)
- 将命令行入口包装为 Web / API 服务,支持多轮对话