db-qa
by qq1014241423
README.md
# MCP 数据库问答系统
通过自然语言(中文)查询 **MySQL / 达梦 DM / 人大金仓 KingbaseES** 三种数据库的内容。
> 例如:用户问 _"2025 年固废重量排名前十的企业"_,系统自动定位 `t_solid_waste` 表,生成并执行 SQL,返回排行榜。
---
## 架构概览
```
┌───────────────┐ ┌─────────────────────┐
│ 浏览器 Web UI │ ──▶ │ FastAPI 后端 │
└───────────────┘ │ /api/ask │
│ ├─ DeepSeek (生成SQL)│
│ ├─ SQL 安全校验 │
│ └─ 数据库适配层 │
└──────┬───────────────┘
│
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
MySQL 达梦 DM 人大金仓
(dmPython) (psycopg2)
另外,项目也暴露一个标准的 MCP Server(stdio 协议),
可以直接挂到 Cursor / Claude Desktop 里使用。
```
---
## 目录结构
```
10.mcp/
├── requirements.txt
├── .env.example # 环境变量模板(API Key、端口等)
├── config/
│ ├── databases.yaml.example # 数据库连接配置模板
│ └── glossary.yaml # 业务术语表 + Few-shot 样例
├── src/
│ ├── core/
│ │ ├── config.py # 配置加载
│ │ ├── manager.py # 数据库连接管理
│ │ ├── adapters/ # 三数据库适配器(mysql/dameng/kingbase)
│ │ └── security/
│ │ └── sql_validator.py # SQL 安全校验(白名单 + 强制 LIMIT)
│ ├── mcp_server/
│ │ └── server.py # MCP Server 入口(stdio)
│ └── webapp/
│ ├── main.py # FastAPI 后端
│ ├── llm.py # DeepSeek 调用
│ ├── prompts.py # 系统提示词
│ └── static/index.html # 单页 Web UI
├── scripts/
│ ├── install_dameng_driver.md
│ ├── install_kingbase_driver.md
│ └── platform_db_init.py # 平台 PG 建表(Alembic upgrade head)
├── migrations/
│ └── platform/ # Alembic 迁移(多企业租户控制面)
└── tests/
└── test_sql_validator.py
```
---
## 安装与启动
### 1. 创建虚拟环境(首次)
```powershell
cd G:\phpstudy_pro\WWW\10.mcp
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
```
> 达梦驱动需要单独装:见 `scripts/install_dameng_driver.md`
### 2. 配置
```powershell
copy .env.example .env
copy config\databases.yaml.example config\databases.yaml
```
然后编辑:
- `.env` —— 填入 `DEEPSEEK_API_KEY`
- `config/databases.yaml` —— 填入三个数据库的连接信息(**强烈建议使用只读账号!**)
### 2b. 平台 PostgreSQL(多企业租户,Phase 0)
运维先建空库 `mcp_platform` 与应用账号 `mcp_app`(见 `docs/MCP多企业租户升级需求与Cursor提问手册.md` §4.8.3),再在 `.env` 配置:
```env
PLATFORM_DATABASE_URL=postgresql://mcp_app:密码@127.0.0.1:5432/mcp_platform
PLATFORM_SECRET_KEY=<openssl rand -hex 32>
```
**表结构由 Alembic 自动创建**(勿手工执行 §4.2 SQL):
```powershell
.\.venv\Scripts\pip.exe install -r requirements.txt
.\.venv\Scripts\python.exe scripts\platform_db_init.py
```
验收:`\dt` 应见 `enterprises`、`users`、`database_connections`、`skills`、`agents`、`mcp_instances`、`audit_logs` 及 `alembic_version`。
> Phase 0 阶段 yaml/json 配置仍并行可用;Web 启动时若已配置 `PLATFORM_DATABASE_URL` 会校验连通与 schema 版本。
### 2c. 登录与初始超管(Phase 1)
在 `.env` 增加(首次部署必填 `BOOTSTRAP_ADMIN_PASSWORD`):
```env
BOOTSTRAP_ADMIN_USER=admin
BOOTSTRAP_ADMIN_PASSWORD=你的强密码
```
启动 Web 后,若 `users` 表为空会自动创建 `platform_admin`。浏览器打开首页即显示登录页;侧栏底部可查看当前用户与登出。
用户/企业数据全部在 PostgreSQL;`/api/databases` 等配置接口在 Phase 2 前仍读 yaml,但已要求 JWT 登录。
### 2d. 配置迁入 PostgreSQL(Phase 2)
1. 升级 schema:`python scripts/platform_db_init.py`(含 migration 002)
2. 导入遗留配置:
```powershell
.\.venv\Scripts\python.exe scripts\migrate_files_to_platform_pg.py --dry-run
.\.venv\Scripts\python.exe scripts\migrate_files_to_platform_pg.py `
--default-enterprise-code default --default-enterprise-name 默认企业
```
3. 平台管理员在顶栏切换「当前管理企业」;API 通过 `X-Enterprise-Id` 隔离租户。
4. 回退只读文件模式:`.env` 设置 `LEGACY_FILE_CONFIG=1`(不写回 yaml/json)。
### 3. 跑测试(可选)
```powershell
.\.venv\Scripts\python.exe -m pytest tests/ -v
```
### 4. 启动 Web 服务
```powershell
.\.venv\Scripts\python.exe -m uvicorn src.webapp.main:app --host 127.0.0.1 --port 8765 --reload
```
打开浏览器访问:<http://127.0.0.1:8765>
### 5. (可选)MCP Server
#### 5a. Cursor / Claude Desktop(stdio,默认)
在 Cursor 的 MCP 设置里加:
```json
{
"mcpServers": {
"db-qa": {
"command": "G:/phpstudy_pro/WWW/10.mcp/.venv/Scripts/python.exe",
"args": ["-m", "src.mcp_server.server"],
"cwd": "G:/phpstudy_pro/WWW/10.mcp"
}
}
}
```
之后在 Cursor 里就能直接 `@db-qa` 调用以下工具:
- `list_databases` —— 列所有可用数据库
- `list_tables` —— 列某个库的所有表
- `describe_table` —— 查看表结构
- `search_schema` —— 按关键词搜索表/字段
- `execute_query` —— 执行 SELECT(带安全校验)
> 不设置 `MCP_TRANSPORT` 时默认为 **stdio**,与上述 Cursor 配置兼容。
#### 5b. Dify / HTTP 客户端(streamable-http)
Dify 等需要 **MCP HTTP** 传输时,启动现行 8001 或 8002 实例(与 Web 8765 互不冲突):
```powershell
cd G:\phpstudy_pro\WWW\10.mcp
.\scripts\start-mcp-8001-local-test.ps1
# 或:.\scripts\start-mcp-8002-zh-data-dm-only.ps1 -InstanceId <8002实例UUID>
```
端点:`http://127.0.0.1:8001/mcp`、`http://127.0.0.1:8002/mcp`
- Dify 在本机:URL 填 `http://127.0.0.1:8001/mcp` 或 `http://127.0.0.1:8002/mcp`
- Dify 在 Docker(Windows/Mac):将主机改为 `host.docker.internal`
详见 `docs/Dify+MCP问数方案分析.md`;安装前检查见 `docs/Dify本机安装前检查清单.md`。
#### 5c. 多 Dify 应用数据库隔离(多 MCP 实例)
不同 Dify 应用连接不同端口的 MCP 进程。生产实例由 Web 平台生成独立
`data/instances/<UUID>.env` 和 `databases.fragment.yaml`,工具与数据源由
`tool_profile` 控制:
```powershell
# 首次执行:创建隔离的本地测试企业并复制数据库连接(正式企业不修改)
.\.venv\Scripts\python.exe scripts\local\provision_local_test_enterprise.py --pg-host 192.168.1.185
# 8001:只从 zhenhai-local-test 企业渲染配置,含 26 个工具
.\scripts\start-mcp-8001-local-test.ps1 -EnterpriseCode zhenhai-local-test
# 8002:zh_data_dm_only,25 个工具,可见 alias 只能是 ZH_DATA + ZH_DWD
.\scripts\start-mcp-8002-zh-data-dm-only.ps1 -InstanceId <8002实例UUID>
```
- 当前只允许 `full`(8001)和 `zh_data_dm_only`(8002)两个 Profile。
- 本地数据库管理固定选择 `zhenhai-local-test`;修改同名 alias 不会改动正式 `zhenhai` 企业的连接。
- 8001 的许可、环评、验收、镇海空气等主题按固定路由读取达梦;其它现行 PG 主题使用本地测试企业配置的内网地址。
- `.env.local`、`scripts/local/`、`data/local-test/` 和本地 8001 渲染/启动脚本均被发布检查拒绝,不能进入正式部署包。
- 8002 禁止访问 PostgreSQL,不在空结果、SQL 错误或超时后自动切库。
- `list_databases` 只返回白名单内 alias;其它工具对越权 `db_alias` 会拒绝。
- 8001 和 8002 分别由独立进程监听。
**服务器重启后**:见 `docs/服务启动说明.md`。8002 的 Dify 提示词见
`docs/Dify-8002-纯达梦zh_data_dm_only-系统提示词.txt`。
---
## 安全策略
| 层级 | 防护措施 |
|---|---|
| 数据库账号 | 强烈建议**只读账号**(最强保险) |
| SQL 校验 | AST 解析 + 关键字粗筛 + 黑名单表 |
| 拦截类型 | INSERT / UPDATE / DELETE / DROP / ALTER / TRUNCATE / CREATE / GRANT / 多语句 / SLEEP / OUTFILE |
| 资源限制 | 强制注入 `LIMIT`、设置 `SQL_TIMEOUT`、`SQL_MAX_ROWS` |
| 审计 | 所有问答和 SQL 落到 `logs/audit-YYYY-MM-DD.log` |
测试:
```powershell
.\.venv\Scripts\python.exe -m pytest tests/ -v
# 已覆盖 20 个用例:合法 SELECT 通过 / 各种 DML/DDL/多语句/危险函数 全部拦截
```
---
## 提升 SQL 准确率的关键
LLM 能不能把 _"固废重量排名前十的企业"_ 翻译成正确的 SQL,**取决于它能不能从你的库里看出业务关系**。
有三个杠杆按效果从高到低:
1. **数据库表/字段必须有中文 `COMMENT`**(最重要!)
- MySQL:`ALTER TABLE t_solid_waste MODIFY weight DECIMAL(10,2) COMMENT '固废重量(吨)';`
- 达梦:`COMMENT ON COLUMN t_solid_waste.weight IS '固废重量(吨)';`
2. **维护 `config/glossary.yaml` 的业务术语表 + Few-shot 样例**
- 把行业黑话("固废 = t_solid_waste")告诉 LLM
- 把典型问答 SQL 当作示例写进去
3. **后续可加的优化**(待实现)
- 表结构向量化检索(RAG):表多了之后,把全部 schema 塞 prompt 会爆 token
- 错误反馈循环:LLM 生成的 SQL 报错时,把错误信息再喂回去让它重试
---
## 常见问题
**Q1:能不能加增、删、改?**
A:故意不加。LLM 偶尔会"幻觉",写错条件可能批量删数据。如果业务确实需要,建议走"生成 SQL → 人工确认 → 单独走业务后台执行"的流程。
**Q2:phpStudy 的 MySQL 默认账号是 root,怎么建只读账号?**
```sql
CREATE USER 'mcp_readonly'@'localhost' IDENTIFIED BY 'a-strong-password';
GRANT SELECT ON your_db.* TO 'mcp_readonly'@'localhost';
FLUSH PRIVILEGES;
```
**Q3:DeepSeek 怎么收费?**
A:deepseek-chat 模型按 token 计费,输入约 ¥0.001/千 token,输出 ¥0.002/千 token。
一次问答(schema + 问题 + 回答)通常 1~3 千 token,单次成本 < ¥0.005。
**Q4:可以换成本地 Ollama 模型吗?**
A:可以。改 `.env` 的 `DEEPSEEK_BASE_URL` 指向 Ollama 的 OpenAI 兼容端点(如 `http://localhost:11434/v1`),`DEEPSEEK_MODEL` 改成本地模型名(如 `qwen2.5:14b`)。
---
## 后续路线图
- [ ] RAG 向量检索:先用 bge-m3 嵌入表结构,提问时只取 Top-K 相关表
- [ ] SQL 报错自愈:执行失败后把 error 反馈给 LLM 重试一次
- [ ] 多轮对话上下文:把上一次的 SQL/结果带入下一轮
- [ ] 角色权限:不同账号能看到不同的库/表
- [ ] 图表可视化:检测到聚合查询时自动出柱状/折线图
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing