453 lines
20 KiB
Markdown
453 lines
20 KiB
Markdown
# MySQL 智能问数设计
|
||
|
||
> 日期:2026-05-15
|
||
> 状态:Draft v2
|
||
> Owner:与 lixin 共识
|
||
> 范围:MySQL 只读数据源配置、Agent Tool、跨表 JOIN、问数 Prompt Skill
|
||
|
||
## 0. v2 架构修订
|
||
|
||
v2 根据系统架构 review 收紧了安全边界:
|
||
|
||
- `mysql_query` MVP 只允许 `SELECT`,不再允许自由 `SHOW`、`DESCRIBE`、`EXPLAIN`。
|
||
- schema、索引、外键和少量样例通过受控工具读取,不通过自由 SQL 读取。
|
||
- 增加表级白名单、表级黑名单和列级脱敏配置。
|
||
- 增加 JOIN 成本保护:查询超时、最大 JOIN 表数、禁止无条件 JOIN、结果行数硬上限。
|
||
- 增加连接池生命周期设计。
|
||
- 增加查询审计表。
|
||
- 明确新增公共 `SecretCryptoService`,不复用当前 SkillSecretService 的明文写入行为。
|
||
- 区分后台安全投影和 Agent 工具投影,`mysql_list_sources` 不向 Agent 暴露 host、username、passwordEncrypted。
|
||
- 对显式 `LIMIT` 做硬上限校验,禁止用超大 LIMIT 绕过结果限制。
|
||
- MVP 对 `maskedColumns` 采取保守策略:自由 SQL 直接引用脱敏列时拒绝执行,避免 alias 绕过。
|
||
|
||
## 1. 背景与目标
|
||
|
||
Neta 当前已经有完整的 Agent Tool 治理、Skill Runtime 和 Agent 配置体系。智能问数能力需要同时满足两类需求:
|
||
|
||
- 平台能力:配置 MySQL 连接、加密保存密码、控制哪些 Agent 可用、执行只读 SQL、限制超时和返回行数。
|
||
- 问数流程:让 Agent 先理解 schema,必要时澄清业务口径,再生成 SQL,最后解释结果、口径和限制。
|
||
|
||
因此本功能采用“Tool 做执行能力,Skill 做问数方法论”的组合方案。MySQL 连接和 SQL 执行进入 NetaClaw Tool 系统;问数策略进入一个 prompt skill,供需要问数的 Agent 绑定。
|
||
|
||
## 2. 非目标
|
||
|
||
第一版不实现以下能力:
|
||
|
||
- 不支持 PostgreSQL、SQLite、SQL Server、Oracle 等其他数据库。
|
||
- 不执行 INSERT、UPDATE、DELETE、CREATE、ALTER、DROP、TRUNCATE、CALL、事务语句或存储过程。
|
||
- 不做可视化 BI 图表、不新增复杂 dashboard。
|
||
- 不做自然语言到 SQL 专用模型微调。
|
||
- 不自动优化复杂 SQL,只提供 schema、样例和执行反馈,让 Agent 在只读约束内生成 SQL。
|
||
- 不跨多个数据源做 JOIN;跨表 JOIN 仅限同一个 MySQL 数据源和当前连接可访问的库。
|
||
- 第一版不允许用户通过 `mysql_query` 自由执行 `SHOW`、`DESCRIBE`、`EXPLAIN`;这些元信息由 `mysql_schema` 等受控工具提供。
|
||
|
||
## 3. 用户体验
|
||
|
||
管理员在后台新增 MySQL 数据源,填写名称、连接地址、端口、数据库名、账号和密码。密码只保存加密值,不在接口回显明文。
|
||
|
||
管理员在 Agent 配置中启用 `mysql` toolset,并按需绑定 `data-analyst-mysql` skill。用户在对话中问“上个月各门店销售额是多少”“按客户和订单表统计复购率”这类问题时,Agent 会先读取 schema,必要时读取表样例,再执行只读 SQL。回答中需要包含:
|
||
|
||
- 结论。
|
||
- 使用的 SQL。
|
||
- 关联口径,尤其是跨表 JOIN 的关联字段。
|
||
- 结果限制,例如采样、LIMIT、字段缺失或关联字段为推断关系。
|
||
|
||
## 4. 架构
|
||
|
||
整体架构沿用 NetaClaw 当前工具链路:
|
||
|
||
```text
|
||
netaclaw_data_source + netaclaw_data_source_query_audit
|
||
-> SecretCryptoService + DataSourceService 授权过滤
|
||
-> MysqlPoolManager + MysqlQueryService / MysqlIntrospectionService
|
||
-> mysql_list_sources / mysql_schema / mysql_table_sample / mysql_query
|
||
-> Tool Catalog + Tool Governance + Tool Resolver
|
||
-> Agent Runtime
|
||
-> data-analyst-mysql Skill 约束问数流程
|
||
```
|
||
|
||
新增能力分为四层:
|
||
|
||
1. 数据源配置层:保存 MySQL 连接和 Agent 授权。
|
||
2. MySQL 执行层:封装连接池、schema 查询、样例读取和只读 SQL 执行。
|
||
3. Tool 层:向 Agent 暴露稳定工具接口,并接入 catalog、resolver、runtime policy。
|
||
4. Skill 层:定义问数流程、JOIN 推理规则和回答格式。
|
||
|
||
## 5. 数据模型
|
||
|
||
新增实体 `NetaClawDataSourceEntity`,表名 `netaclaw_data_source`。
|
||
|
||
字段:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `name` | varchar(100), unique | 数据源唯一名,工具调用使用 |
|
||
| `label` | varchar(200) | 展示名 |
|
||
| `type` | varchar(20) | 固定为 `mysql` |
|
||
| `host` | varchar(255) | MySQL host |
|
||
| `port` | int | 默认 3306 |
|
||
| `database` | varchar(128) | 默认库名 |
|
||
| `username` | varchar(255) | 连接用户名 |
|
||
| `passwordEncrypted` | text | AES-256-GCM 加密密码 |
|
||
| `readonly` | int | 1 表示只读,MVP 固定为 1 |
|
||
| `status` | int | 0 禁用,1 启用 |
|
||
| `allowedAgentIds` | json | 为空表示不授权任何 Agent;数组内 Agent 可用 |
|
||
| `extra` | json | SSL、connectTimeout、queryTimeout、maxRows、allowedTables、blockedTables、maskedColumns、maxJoinTables 等扩展 |
|
||
|
||
`extra` 权限字段:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `allowedTables` | 可访问表白名单,空数组表示默认不开放任何业务表 |
|
||
| `blockedTables` | 表黑名单,优先级高于白名单 |
|
||
| `maskedColumns` | 需要脱敏的列,格式为 `{ "table.column": "hash|partial|redact" }` |
|
||
| `schemaVisibility` | `allowed-only` 或 `all-names-only`,MVP 默认 `allowed-only` |
|
||
| `queryTimeoutMs` | 单次 SQL 超时,默认 10000,硬上限 30000 |
|
||
| `maxRows` | 默认 200,硬上限 1000 |
|
||
| `maxJoinTables` | 默认 4,硬上限 6 |
|
||
| `poolConnectionLimit` | 默认 3,硬上限 10 |
|
||
|
||
密码必须用新增公共 `SecretCryptoService` 加密保存。当前 `SkillSecretService.encrypt()` 新写入路径是 JSON 明文,不能作为数据源密码的加密实现直接复用。
|
||
|
||
服务端必须区分两种投影:
|
||
|
||
- 管理端安全投影:可返回 `host`、`port`、`database`、`username`、`hasPassword`、授权与权限配置,但不返回 `passwordEncrypted` 或明文密码。
|
||
- Agent 工具投影:`mysql_list_sources` 只返回 `name`、`label`、`database`、`status`,不返回 `host`、`username`、`passwordEncrypted`、权限细节或连接串。
|
||
|
||
新增实体 `NetaClawDataSourceQueryAuditEntity`,表名 `netaclaw_data_source_query_audit`。
|
||
|
||
字段:
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
| --- | --- | --- |
|
||
| `dataSourceId` | int | 数据源 ID |
|
||
| `agentId` | int | 调用 Agent |
|
||
| `userId` | int | 当前用户,可为空 |
|
||
| `toolCallId` | varchar(100) | 工具调用 ID |
|
||
| `sqlHash` | varchar(64) | SQL SHA-256 |
|
||
| `sqlPreview` | text | 截断后的 SQL 摘要,不含结果集 |
|
||
| `status` | varchar(20) | success / rejected / failed |
|
||
| `rejectReason` | varchar(100) | 被 guard 拒绝原因 |
|
||
| `elapsedMs` | int | 耗时 |
|
||
| `rowCount` | int | 返回行数 |
|
||
| `errorCode` | varchar(50) | MySQL errno / SQLSTATE,可为空 |
|
||
|
||
## 6. 后端服务
|
||
|
||
新增 `SecretCryptoService`:
|
||
|
||
- `encryptJson(value: Record<string, string>): string`
|
||
- `decryptJson(ciphertext: string): Record<string, string>`
|
||
- `encryptText(value: string): string`
|
||
- `decryptText(ciphertext: string): string`
|
||
- 使用 AES-256-GCM,密钥来自 `NETA_SECRET_KEY`,开发环境可回退到 `APP_SECRET`。
|
||
- 密文必须带版本化 envelope,例如 base64(JSON.stringify({ v: 1, alg: 'aes-256-gcm', iv, tag, ct })),便于后续密钥轮换、格式识别和 Skill secret 迁移。
|
||
- 新数据源密码只允许写入加密格式;Skill secret 的现有兼容逻辑可后续迁移到该公共服务。
|
||
|
||
新增 `DataSourceService`:
|
||
|
||
- `listForAgent(agentId: number): Promise<AgentDataSourceSummary[]>`
|
||
- `listAdminSafe(): Promise<AdminSafeDataSource[]>`
|
||
- `getAuthorizedSource(name: string, agentId: number): Promise<NetaClawDataSourceEntity>`
|
||
- `saveConfig(input): Promise<AdminSafeDataSource>`
|
||
- `testConnection(input): Promise<{ ok: boolean; error?: string }>`
|
||
- 应用 `allowedAgentIds`、`allowedTables`、`blockedTables`、`maskedColumns` 和 `schemaVisibility`。
|
||
- 编辑已有数据源并测试连接时,如果没有传新密码,必须从数据库读取旧 `passwordEncrypted`,不能依赖接口回显密文。
|
||
|
||
新增 `MysqlPoolManager`:
|
||
|
||
- `getPool(source): Promise<Pool>`
|
||
- 按 `dataSource.id` 缓存连接池。
|
||
- 默认 `connectionLimit=3`,由 `extra.poolConnectionLimit` 覆盖但不得超过 10。
|
||
- 保存、禁用或删除数据源时关闭对应 pool。
|
||
- 连接失败时不泄漏 host、username、password,只返回脱敏错误。
|
||
|
||
新增 `MysqlIntrospectionService`:
|
||
|
||
- `listSchema(source, options): Promise<MysqlSchemaResult>`
|
||
- 读取 `information_schema.columns`、`statistics`、`key_column_usage`。
|
||
- 只返回授权表。字段输出要应用 `maskedColumns` 标记,让 Agent 知道该列敏感;MVP 中查询或样例请求直接引用该列时由后端拒绝,后续具备可靠列血缘解析后再支持脱敏展示。
|
||
|
||
新增 `MysqlQueryService`:
|
||
|
||
- `executeReadOnly(source, sql, options): Promise<MysqlQueryResult>`
|
||
- 使用 `mysql2/promise`。
|
||
- 每次查询设置超时、最大行数和结果截断。
|
||
- `SELECT` 查询若没有 `LIMIT`,外层包裹为 `SELECT * FROM (<sql>) AS neta_limited_query LIMIT ?`。
|
||
- 显式 `LIMIT` 必须小于等于 `maxRows`;超过上限直接拒绝或重写为安全上限,不能先拉取大结果再在 Node 中 `slice`。
|
||
- 执行前调用 SQL guard、授权表检查和 JOIN 成本检查。
|
||
- 自由 SQL 引用 `maskedColumns` 时,MVP 直接拒绝执行,避免 `SELECT email AS e` 绕过脱敏;后续只有在引入 SQL parser 并能追踪 source column 到 output alias 后,才允许查询脱敏列并在返回前脱敏。
|
||
- 执行错误必须通过 `sanitizeMysqlError(err)` 脱敏,只返回 `code`、`sqlState`、`errno` 和固定短文案,不返回原始 `err.message`。
|
||
- 执行后写 query audit,不记录结果集。
|
||
|
||
## 7. Tool 设计
|
||
|
||
新增 toolset:`mysql`。
|
||
|
||
### mysql_list_sources
|
||
|
||
输入:
|
||
|
||
```json
|
||
{}
|
||
```
|
||
|
||
输出:
|
||
|
||
```json
|
||
{
|
||
"sources": [
|
||
{
|
||
"name": "sales_prod",
|
||
"label": "销售生产库",
|
||
"database": "sales",
|
||
"status": 1
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
只返回当前 Agent 授权的数据源摘要,不返回 host、username、passwordEncrypted、明文密码、连接串或表/列权限细节。
|
||
|
||
### mysql_schema
|
||
|
||
输入:
|
||
|
||
```json
|
||
{
|
||
"source": "sales_prod",
|
||
"tables": ["orders", "customers"]
|
||
}
|
||
```
|
||
|
||
`tables` 可选。为空时返回表摘要和字段摘要;指定时返回详细字段、索引和外键。
|
||
|
||
输出包含:
|
||
|
||
- `tables[].name`
|
||
- `tables[].comment`
|
||
- `tables[].columns[]`
|
||
- `tables[].primaryKey[]`
|
||
- `tables[].indexes[]`
|
||
- `tables[].foreignKeys[]`
|
||
|
||
### mysql_table_sample
|
||
|
||
输入:
|
||
|
||
```json
|
||
{
|
||
"source": "sales_prod",
|
||
"table": "orders",
|
||
"columns": ["id", "customer_id", "created_at"],
|
||
"limit": 5
|
||
}
|
||
```
|
||
|
||
用于辅助 Agent 判断字段含义和 JOIN key。`limit` 最大 20。表名和列名只能来自 introspection 结果,后端需要做 identifier 校验和反引号转义。
|
||
|
||
`mysql_table_sample` 不能通过拼接自由 SQL 绕过权限。后端必须先校验:
|
||
|
||
- `table` 在 `allowedTables` 内且不在 `blockedTables` 内。
|
||
- `columns` 均来自 `mysql_schema` 可见字段。
|
||
- 如果请求列命中 `maskedColumns`,MVP 返回拒绝错误;后续可加入专门脱敏样例能力。
|
||
|
||
### mysql_query
|
||
|
||
输入:
|
||
|
||
```json
|
||
{
|
||
"source": "sales_prod",
|
||
"sql": "SELECT c.name, COUNT(*) AS order_count FROM customers c JOIN orders o ON o.customer_id = c.id GROUP BY c.name ORDER BY order_count DESC LIMIT 20"
|
||
}
|
||
```
|
||
|
||
允许:
|
||
|
||
- `SELECT`
|
||
- 单数据源内的跨表 JOIN
|
||
|
||
禁止:
|
||
|
||
- 多语句。
|
||
- SQL 注释,包括 `--`、`#`、`/* ... */` 和 MySQL 版本注释。
|
||
- DML、DDL、DCL、事务和存储过程。
|
||
- `SELECT ... INTO OUTFILE`、`LOAD_FILE`、`SLEEP`、`BENCHMARK`。
|
||
- `SHOW`、`DESCRIBE`、`DESC`、`EXPLAIN` 自由 SQL。
|
||
- `UNION`、CTE、用户变量和临时表。
|
||
- 无限制返回大结果集。
|
||
|
||
输出:
|
||
|
||
```json
|
||
{
|
||
"columns": ["name", "order_count"],
|
||
"rows": [
|
||
{ "name": "A 客户", "order_count": 12 }
|
||
],
|
||
"rowCount": 1,
|
||
"truncated": false,
|
||
"elapsedMs": 38,
|
||
"sql": "SELECT ..."
|
||
}
|
||
```
|
||
|
||
## 8. SQL 安全策略
|
||
|
||
安全校验采用保守白名单规则。不要把 Skill prompt 当作安全边界,所有强制规则必须在 Tool/Service 层实现。
|
||
|
||
- 第一版优先采用 SQL parser 或词法 token guard;如果没有稳定 parser,就采用更严格的拒绝策略。
|
||
- 只允许单条 `SELECT`。
|
||
- 拒绝 SQL 注释,避免版本注释和注释夹带绕过。
|
||
- 拒绝包含分号的多语句。
|
||
- 拒绝非白名单开头语句。
|
||
- 拒绝 `UNION`、CTE、`INTO`、用户变量、临时表、危险函数和文件访问能力。
|
||
- 查询涉及的表必须全部落在 `allowedTables` 内,并且不在 `blockedTables` 内。
|
||
- SQL guard 必须逐个检查 `JOIN` 片段:每个显式 `JOIN` 在下一个 `JOIN`、`WHERE`、`GROUP BY`、`ORDER BY`、`LIMIT` 或语句结束前都必须出现 `ON` 或 `USING`。
|
||
- 自由 SQL 引用 `maskedColumns` 时直接拒绝,避免 alias 绕过。
|
||
- `SELECT` 必须限制行数;无 LIMIT 时后端包裹追加限制。
|
||
- 显式 LIMIT 必须不超过 `maxRows`。
|
||
- 查询超时默认 10 秒。
|
||
- 最大返回行数默认 200,硬上限 1000。
|
||
- 默认最大 JOIN 表数 4,硬上限 6。
|
||
- 拒绝无 `ON` / `USING` 的显式 JOIN,避免笛卡尔积。
|
||
- 错误信息只返回 SQLSTATE、errno/code 和固定短文案,不返回原始连接错误、连接密码、host、username 或完整连接串。
|
||
- 每次查询无论成功、失败或被拒绝,都写入审计记录。
|
||
|
||
这层只保证工具层安全,不负责判断业务 SQL 是否“正确”。业务口径由 Skill 和 Agent 交互处理。
|
||
|
||
## 9. 跨表 JOIN 策略
|
||
|
||
跨表 JOIN 是 MVP 能力。Agent 可生成多表 `JOIN`、`LEFT JOIN` 和聚合查询,但必须遵循 Skill 约束:
|
||
|
||
- 先调用 `mysql_schema` 获取相关表字段、索引和外键。
|
||
- 有外键时优先使用外键关系。
|
||
- 无外键时,可根据字段名、字段类型、索引和样例值推断关联字段。
|
||
- 推断关联必须在回答中标明,例如“orders.customer_id 与 customers.id 为字段名和样例推断关联”。
|
||
- 对不确定的关联关系或业务口径,必须先向用户澄清。
|
||
- JOIN 查询仍受 `mysql_query` 的只读、超时、LIMIT 和最大行数限制。
|
||
- JOIN 查询不得超过 `maxJoinTables`。
|
||
- `JOIN` 必须带 `ON` 或 `USING`,不允许隐式逗号连接。
|
||
- 每个 `JOIN` 都必须有自己的 `ON` 或 `USING` 条件,不能只检查整条 SQL 是否出现过一次 `ON`。
|
||
- 如果查询因超时或成本限制失败,Agent 应缩小时间范围、减少表数量、添加过滤条件或向用户澄清。
|
||
|
||
## 10. Skill 设计
|
||
|
||
新增 prompt skill:`packages/backend/skills/data-analyst-mysql/SKILL.md`。
|
||
|
||
Frontmatter:
|
||
|
||
```yaml
|
||
---
|
||
name: data-analyst-mysql
|
||
description: 使用 MySQL 工具进行只读智能问数,支持 schema 分析、跨表 JOIN、口径澄清和 SQL 结果解释。
|
||
version: 1.0.0
|
||
metadata:
|
||
skillType: llm
|
||
tags: [mysql, data-analysis, question-answering]
|
||
conditions:
|
||
requires_tools: ["mysql_list_sources", "mysql_schema", "mysql_query"]
|
||
---
|
||
```
|
||
|
||
Skill 指令要求:
|
||
|
||
- 先确认可用数据源。
|
||
- 先读取相关 schema,再写 SQL。
|
||
- 需要 JOIN 时先找外键、索引和样例值。
|
||
- 跨表 JOIN 不确定时先解释拟采用的关联字段,必要时澄清。
|
||
- 不确定业务口径时使用 `clarify`。
|
||
- 只执行只读 SQL。
|
||
- 回答必须包含结论、SQL、口径说明和限制。
|
||
- 不暴露连接地址、账号、密码。
|
||
- 不承诺“全量统计”除非 SQL 口径和过滤范围明确。
|
||
|
||
## 11. Tool Resolver 和 Runtime Policy
|
||
|
||
`tools/catalog.ts` 新增 `TOOLSET_MYSQL = 'mysql'`,并集中 import MySQL tool 文件。
|
||
|
||
MySQL 工具注册:
|
||
|
||
- `visibility: 'tool'`
|
||
- `capability: 'text'`
|
||
- `isCore: false`
|
||
- `canDisable: true`
|
||
|
||
主 Agent 可直接执行 MySQL 工具。子 Agent 默认不在 worker 本地执行 MySQL 工具,runtime policy 应将这类工具路由为 `main-process-proxy` 或 `disabled`。推荐第一版走 `main-process-proxy`,避免子进程持有数据库密钥。
|
||
|
||
## 12. 管理接口
|
||
|
||
新增 admin controller:`controller/admin/data_source.ts`。
|
||
|
||
接口:
|
||
|
||
- `GET /admin/netaclaw/data-source/list`
|
||
- `POST /admin/netaclaw/data-source/save`
|
||
- `POST /admin/netaclaw/data-source/test`
|
||
- `POST /admin/netaclaw/data-source/delete`
|
||
|
||
保存接口接受明文密码,但只用于加密保存。若编辑时密码为空,则保留原密码。
|
||
|
||
Agent 编辑页后续可接入授权配置,但 MVP 后端先支持 `allowedAgentIds` 字段和接口。
|
||
|
||
## 13. 测试策略
|
||
|
||
测试采用 TDD。
|
||
|
||
优先新增单元测试:
|
||
|
||
- `mysql_sql_guard.test.ts`:验证只读 SQL 白名单、危险关键字、多语句、`SELECT INTO OUTFILE`、`SLEEP` 拒绝。
|
||
- `mysql_sql_guard.test.ts` 还要验证 `SHOW`、`DESCRIBE`、`EXPLAIN`、注释、`UNION`、CTE、用户变量、无条件 JOIN、部分 JOIN 缺少 ON/USING、显式 LIMIT 超过 maxRows、查询脱敏列、超过最大 JOIN 表数被拒绝。
|
||
- `mysql_schema_mapper.test.ts`:验证 information_schema 行映射为表、字段、索引和外键结构。
|
||
- `mysql_query_service.test.ts`:用 mock mysql pool 验证 LIMIT 包裹、超时参数、结果截断和错误脱敏。
|
||
- `mysql_query_service.test.ts` 还要验证表级授权、脱敏列拒绝、审计写入、pool 复用和 `mysql_table_sample` identifier 白名单。
|
||
- `secret_crypto.test.ts`:验证数据源密码使用 AES-GCM 加密,密文不包含明文密码。
|
||
- `data_source_projection.test.ts`:验证管理端投影不返回密码,Agent 投影不返回 host/username/passwordEncrypted。
|
||
- `tool_resolver.test.ts`:验证 `mysql` toolset 可以被 Agent 启用,并在子 Agent runtime profile 中不走 worker-local。
|
||
- `entity_exports.test.ts`:验证新增 entity 导出。
|
||
|
||
不在单元测试里连接真实 MySQL。真实连接测试通过 admin `test` 接口和后续手工环境验证完成。
|
||
|
||
## 14. 分阶段交付
|
||
|
||
### Phase 1:后端最小闭环
|
||
|
||
- 新增数据源 entity、公共 secret crypto service、data source service。
|
||
- 新增 query audit entity。
|
||
- 新增公共 `SecretCryptoService`。
|
||
- 新增 `MysqlPoolManager`。
|
||
- 新增 MySQL SQL guard、schema mapper、query service。
|
||
- 新增四个工具:`mysql_list_sources`、`mysql_schema`、`mysql_table_sample`、`mysql_query`。
|
||
- 接入 catalog、resolver、runtime policy。
|
||
- 新增 `data-analyst-mysql` prompt skill。
|
||
- 补充单元测试。
|
||
|
||
### Phase 2:管理端配置
|
||
|
||
- 新增数据源管理接口。
|
||
- 前端工具管理或 Agent 编辑页接入数据源授权。
|
||
- 支持测试连接和编辑密码。
|
||
|
||
### Phase 3:问数体验增强
|
||
|
||
- 根据真实问数记录优化 Skill。
|
||
- 增加 query result 前端结构化渲染。
|
||
- 增加审计日志查询页和慢查询提示。
|
||
|
||
## 15. 验收标准
|
||
|
||
- 管理员可以配置一个 MySQL 数据源并授权给指定 Agent。
|
||
- 数据源密码以 AES-GCM 密文保存,接口不回显密文或明文。
|
||
- 启用 `mysql` toolset 的 Agent 能列出授权数据源。
|
||
- Agent 只能读取授权表的 schema、字段、索引和外键。
|
||
- Agent 能读取授权表的少量样例。
|
||
- Agent 能执行只读 SELECT 查询。
|
||
- Agent 能执行同一数据源、授权表范围内的跨表 JOIN 查询。
|
||
- `SHOW`、`DESCRIBE`、`EXPLAIN`、多语句、注释、写入语句、无条件 JOIN 和超出 JOIN 表数限制的 SQL 被拒绝。
|
||
- 查询或样例请求命中 `maskedColumns` 时,MVP 后端拒绝执行,避免 alias 绕过;后续如引入 SQL parser 再实现安全脱敏返回。
|
||
- 每次查询写入审计记录。
|
||
- 危险 SQL 被拒绝,错误原因可读且不泄漏连接秘密。
|
||
- Agent 绑定 `data-analyst-mysql` skill 后,回答包含结论、SQL、口径和限制。
|
||
- 单元测试覆盖 SQL guard、schema mapper、query service、secret crypto、pool manager、tool resolver 和 entity 导出。
|