168 lines
8.9 KiB
Markdown
168 lines
8.9 KiB
Markdown
---
|
||
title: MySQL 数据源与问数系统
|
||
created: 2026-05-15
|
||
updated: 2026-05-15
|
||
type: entity
|
||
tags: [database, tool, agent, backend, frontend]
|
||
sources: [packages/backend/src/modules/netaclaw/entity/data_source.ts, packages/backend/src/modules/netaclaw/entity/data_source_query_audit.ts, packages/backend/src/modules/netaclaw/service/data_source.ts, packages/backend/src/modules/netaclaw/service/mysql_pool.ts, packages/backend/src/modules/netaclaw/service/mysql_schema.ts, packages/backend/src/modules/netaclaw/service/mysql_query.ts, packages/backend/src/modules/netaclaw/service/secret_crypto.ts, packages/backend/src/modules/netaclaw/tools/builtin/mysql.ts, packages/backend/src/modules/netaclaw/controller/admin/data_source.ts, packages/frontend/src/modules/base/views/data-source.vue, packages/backend/skills/data-analyst-mysql/SKILL.md]
|
||
---
|
||
|
||
# MySQL 数据源与问数系统
|
||
|
||
MySQL 数据源与问数系统为 [[netaclaw-module]] 增加授权范围内的只读数据库分析能力。它由后台数据源配置页、连接密钥加密、连接池、schema/sample/query 服务、`mysql_*` 工具、SQL guard 和查询审计组成,并通过 [[tool-system]] 暴露给 Agent。
|
||
|
||
## 管理入口
|
||
|
||
前端入口在“系统管理 -> 数据源管理”,页面文件为 `packages/frontend/src/modules/base/views/data-source.vue`。
|
||
|
||
该页面支持:
|
||
|
||
- 新增、编辑、删除、启停 MySQL 数据源。
|
||
- 配置 Host/IP、端口、数据库、用户名、密码和 SSL。
|
||
- 配置授权 Agent、表白名单、表黑名单、脱敏列、schema 可见性、最大返回行数、最大 JOIN 表数、查询超时和连接超时。
|
||
- 点击“测试连接”调用 `/admin/netaclaw/data-source/test`,不会直接把连接信息暴露给 Agent。
|
||
|
||
菜单来自 `base_sys_menu` 动态路由,不是前端静态路由。`packages/backend/src/modules/base/event/menu.ts` 在后端启动时会确保 `/sys/data-source` 被同步到“系统管理”下,并给 admin 角色补齐菜单权限;`packages/backend/src/modules/base/menu.json` 保留种子菜单配置。详见 [[base-module]] 和 [[frontend-architecture]]。
|
||
|
||
## 后端 API
|
||
|
||
`packages/backend/src/modules/netaclaw/controller/admin/data_source.ts` 暴露管理端接口:
|
||
|
||
| API | 方法 | 说明 |
|
||
| --- | --- | --- |
|
||
| `/admin/netaclaw/data-source/list` | GET | 管理员查看全部数据源;传 `agentId` 时返回该 Agent 授权摘要 |
|
||
| `/admin/netaclaw/data-source/save` | POST | 新增或部分更新数据源配置 |
|
||
| `/admin/netaclaw/data-source/delete` | POST | 删除数据源并关闭连接池 |
|
||
| `/admin/netaclaw/data-source/test` | POST | 使用现有或临时配置测试连接 |
|
||
|
||
`NetaClawDataSourceService` 对外返回管理安全投影:保留 host、database、username 等管理字段,但只返回 `hasPassword`,不返回明文密码或密文。给 Agent 的 `listForAgent()` 只返回 `name`、`label`、`database`、`status`,不返回连接地址、账号或密码。
|
||
|
||
## 数据模型
|
||
|
||
### netaclaw_data_source
|
||
|
||
`NetaClawDataSourceEntity` 存储连接配置与授权策略:
|
||
|
||
- `name` 唯一标识,供工具调用时作为 `source`。
|
||
- `type` 当前固定为 `mysql`。
|
||
- `host`、`port`、`database`、`username` 描述连接目标。
|
||
- `passwordEncrypted` 保存 AES-256-GCM 加密后的密码。
|
||
- `readonly` 固定为只读语义。
|
||
- `status` 控制是否可用。
|
||
- `allowedAgentIds` 限定哪些 Agent 可使用该数据源。
|
||
- `extra` 保存安全策略:`allowedTables`、`blockedTables`、`maskedColumns`、`schemaVisibility`、`maxRows`、`maxJoinTables`、`queryTimeoutMs`、`connectTimeout`、`poolConnectionLimit`、`ssl`。
|
||
|
||
### netaclaw_data_source_query_audit
|
||
|
||
`NetaClawDataSourceQueryAuditEntity` 记录 `mysql_query` 的审计结果:
|
||
|
||
- `dataSourceId`、`agentId`、`userId`、`toolCallId` 绑定调用来源。
|
||
- `sqlHash` 保存 SQL 哈希,`sqlPreview` 保存前 1000 字符预览。
|
||
- `status` 为 `success`、`rejected` 或 `failed`。
|
||
- `rejectReason` 记录 SQL guard 拒绝原因。
|
||
- `elapsedMs`、`rowCount`、`errorCode` 用于诊断查询表现和失败原因。
|
||
|
||
## 密钥加密
|
||
|
||
`SecretCryptoService` 使用 AES-256-GCM 加密连接密码。密钥来源优先级:
|
||
|
||
```text
|
||
NETA_SECRET_KEY -> APP_SECRET -> module.user.jwt.secret
|
||
```
|
||
|
||
本地开发环境没有配置 `NETA_SECRET_KEY` / `APP_SECRET` 时,会回退到用户模块已有的 JWT secret,避免“测试连接”因为缺失环境变量失败。生产环境仍建议显式配置专用 `NETA_SECRET_KEY`,避免与登录 token 密钥耦合。
|
||
|
||
## 连接池
|
||
|
||
`MysqlPoolManager` 基于 `mysql2/promise` 创建连接池:
|
||
|
||
- `getPool(source)` 按数据源 ID 缓存连接池。
|
||
- `createTransientPool(source)` 用于测试连接,不进入长期缓存。
|
||
- `closePool(id)` 在保存或删除数据源后关闭旧池,避免连接配置变更后继续复用旧连接。
|
||
- pool 配置使用 `extra.poolConnectionLimit`、`extra.connectTimeout`、`extra.ssl`,并通过 `SecretCryptoService` 解密密码。
|
||
|
||
## 工具集
|
||
|
||
`packages/backend/src/modules/netaclaw/tools/builtin/mysql.ts` 注册四个工具,toolset 为 `mysql`:
|
||
|
||
| 工具 | 说明 |
|
||
| --- | --- |
|
||
| `mysql_list_sources` | 列出当前 Agent 授权的数据源摘要 |
|
||
| `mysql_schema` | 查询授权表的字段、索引、主键、外键和脱敏标记 |
|
||
| `mysql_table_sample` | 查询授权表少量未脱敏字段样本 |
|
||
| `mysql_query` | 执行经过 guard 校验的只读 SELECT,并写审计 |
|
||
|
||
这些工具在 manifest 中统一走 `main-process-proxy`,即使被子 Agent 调用,也必须回到主进程执行。这样连接池、密钥、授权、SQL guard 和审计不会落到 worker 进程。
|
||
|
||
## Schema 与 Sample
|
||
|
||
`MysqlIntrospectionService` 负责 schema 和样本读取。
|
||
|
||
关键规则:
|
||
|
||
- `schemaVisibility=allowed-only` 时,只返回白名单表详情。
|
||
- `schemaVisibility=all-names-only` 时,白名单外表只可见表名/表注释,不返回字段详情。
|
||
- `mysql_schema` 从 `information_schema.TABLES` 获取 `TABLE_COMMENT`,从 `information_schema.COLUMNS` 获取字段,避免误把 `TABLE_COMMENT` 当成 COLUMNS 字段。
|
||
- `mysql_table_sample` 默认只选择未脱敏字段。
|
||
- 采样校验使用小写映射,但实际 SQL 使用 schema 中的原始列名,兼容 `tenantId`、`isCrewMaster` 这类驼峰字段。
|
||
- 显式请求脱敏列会抛 `masked_column_denied`。
|
||
|
||
典型成功链路:
|
||
|
||
```text
|
||
mysql_list_sources
|
||
-> mysql_schema
|
||
-> mysql_table_sample
|
||
-> 模型基于样本总结分析
|
||
```
|
||
|
||
复杂分析或聚合问题可进一步调用 `mysql_query`。
|
||
|
||
## SQL Guard
|
||
|
||
`MysqlQueryService` 的 `validateMysqlReadOnlySql()` 在执行前做只读防护:
|
||
|
||
- 只允许 `SELECT`。
|
||
- 拒绝 `SHOW`、`DESCRIBE`、`EXPLAIN`、DML、DDL、DCL、事务、`CALL`、CTE、UNION、用户变量、临时表、危险函数、文件输出、锁定读、隐式 JOIN、派生表和 offset limit。
|
||
- 拒绝 schema-qualified 表名,禁止跨库访问。
|
||
- 检查所有表都在 `allowedTables` 中,且不在 `blockedTables` 中。
|
||
- 限制 JOIN 表数和最大行数。
|
||
- 无显式 `LIMIT` 时包一层外部 LIMIT,最多取 `maxRows + 1` 判断截断。
|
||
- 允许单个末尾分号,并在执行前去掉;真正多语句仍被拒绝。
|
||
- 对含脱敏字段的表,拒绝 `SELECT *`、`table.*`、别名通配以及脱敏列引用。
|
||
|
||
执行时使用 MySQL `execute()`,查询失败会写 `failed` 审计;guard 拒绝会写 `rejected` 审计。
|
||
|
||
## Prompt Skill
|
||
|
||
`packages/backend/skills/data-analyst-mysql/SKILL.md` 是 prompt 类型 skill,用于约束 Agent 的 MySQL 问数流程:
|
||
|
||
- 必须先 `mysql_list_sources`,不能猜数据源。
|
||
- 先 `mysql_schema` 再设计 SQL。
|
||
- 只能使用 `mysql_*` 工具处理数据库问题。
|
||
- 输出需要包含结论、SQL、假设和限制。
|
||
- 明确禁止修改数据、绕过授权、暴露连接信息和输出未授权内容。
|
||
|
||
该 skill 需要与 [[skill-system]]、[[skill-runtime]] 和 [[tool-governance]] 共同理解:skill 只是行为约束,真正的安全边界由服务端授权、SQL guard、连接池和审计共同实现。
|
||
|
||
## 常见故障
|
||
|
||
| 症状 | 原因 | 修复点 |
|
||
| --- | --- | --- |
|
||
| “系统管理”里看不到“数据源管理” | 现有库菜单来自 `base_sys_menu`,不会自动读取 `menu.json` | `base/event/menu.ts` 启动同步 `/sys/data-source` |
|
||
| 测试连接报缺少 `NETA_SECRET_KEY` / `APP_SECRET` | 加密服务只读环境变量 | `SecretCryptoService` 回退到 `module.user.jwt.secret` |
|
||
| `mysql_schema` 报 `TABLE_COMMENT` 不存在 | 表注释来自 `information_schema.TABLES`,不是 COLUMNS | schema 查询拆分 TABLES 和 COLUMNS |
|
||
| `SELECT ... LIMIT 20;` 被拒绝 | SQL guard 把任意分号都当多语句 | 允许单个末尾分号并规范化 |
|
||
| `mysql_table_sample` 对驼峰字段报 `column_not_allowed` | 校验与执行列名大小写映射不一致 | 用小写校验、原始列名执行 |
|
||
|
||
## 相关页面
|
||
|
||
- [[netaclaw-module]]
|
||
- [[tool-system]]
|
||
- [[tool-catalog]]
|
||
- [[tool-governance]]
|
||
- [[skill-system]]
|
||
- [[frontend-architecture]]
|
||
- [[base-module]]
|
||
- [[database-entity-overview]]
|