---
title: "talents - Text2SqlTalent 数据库自然语言查询"
---


`Text2SqlTalent` 是一个面向 Agent 的结构化数据库对话组件，实现了从自然语言意图到安全 SQL 执行的完整闭环。它内置自适应方言引擎、分级元数据策略、语义关联引导和多层执行护栏，让 Agent 能够像数据分析师一样与数据库交互。

相关依赖包：[solon-ai-skill-text2sql](/article/1367)


### 1、核心特性

**自适应方言引擎**：

* 内置 6 种 `SqlDialect` 实现，覆盖主流数据库产品：
  * **MySQL** — 同时兼容 MariaDB、TiDB（通过 `matched()` 关键字匹配）
  * **PostgreSQL** — 兼容人大金仓（Kingbase）、瀚高（Highgo）
  * **Oracle** — 兼容达梦（Dameng）
  * **SQLite** — 针对缺失标准日期函数及弱类型特性进行优化
  * **H2** — 支持兼容模式自动检测（如检测到 MySQL 模式会自动切换为 MySqlDialect）
  * **Generic SQL** — ANSI SQL 标准兜底方言
* 通过 `SqlDialectManager` 统一注册与检索，支持用户自定义方言扩展（`SqlDialectManager.register()`）
* 自动处理标识符转义、分页语法差异（LIMIT / ROWNUM / FETCH FIRST）、系统函数差异

**分级元数据策略**（`SchemaMode`）：

* **FULL 模式**（表 `<= 20`）：在 `onAttach` 阶段自动注入全量表结构信息（字段名、类型、长度、是否可空、主键标记、备注）到 System Prompt，AI 拥有"全局视角"，无需额外工具调用
* **DYNAMIC 模式**（表 `> 20` 或手动指定）：仅注入表清单与外键关系地图到 Instruction，引导 AI 在需要时通过 `get_table_schema` 工具按需探测具体字段，有效节省 Token 消耗

**语义关联引导**：

* 通过 JDBC `DatabaseMetaData.getImportedKeys()` 自动提取外键关系，格式化为 `table.fk_col -> pk_table.pk_col` 的关联描述
* 同时提取表级别和列级别的 COMMENT 备注，为 AI 提供多表 Join 的"语义线索"

**执行安全护栏**（四层防护）：

* **只读校验**：`readOnly(true)` 时强制拦截非 SELECT 语句
* **别名修复**：通过正则 `AS_PATTERN` 自动为 AI 遗漏引号的别名添加方言转义（如 MySQL 加反引号、Oracle/PG 加双引号）
* **分页注入**：当 SQL 未包含 LIMIT/ROWNUM/FETCH FIRST/TOP 时，自动追加方言对应的分页语法（默认 maxRows=50）
* **结果截断**：查询结果超出 `maxContextLength`（默认 8000 字符）时自动截断，防止上下文溢出


### 2、配置参数

| 参数 | 方法 | 默认值 | 说明 |
| --- | --- | --- | --- |
| maxRows | `.maxRows(int)` | 50 | 单次查询返回最大行数，控制分页注入的 LIMIT 值 |
| maxContextLength | `.maxContextLength(int)` | 8000 | 查询结果 JSON 的最大字符长度，超出时截断并追加 `[Truncated]` 标记 |
| schemaMode | `.schemaMode(SchemaMode)` | 自动判断（表<=20 为 FULL，>20 为 DYNAMIC） | 元数据注入策略 |
| readOnly | `.readOnly(boolean)` | true | 是否强制只读模式（仅允许 SELECT） |
| dialect | `.dialect(SqlDialect)` | 自动识别 | 手动指定方言适配器，覆盖自动检测 |

构造函数支持两种数据源接入方式：

```java
// 方式一：直接传入 DataSource
new Text2SqlTalent(dataSource, "users", "orders")

// 方式二：传入 SqlUtils 实例（适用于已有 SqlUtils 的场景）
new Text2SqlTalent(sqlUtils, "users", "orders")
```


### 3、应用示例

将数据库变成智能体的知识库，只需几行代码：

```java
// 1. 初始化 Talent，指定数据源及允许 AI 访问的表范围
Text2SqlTalent sqlTalent = new Text2SqlTalent(dataSource, "users", "orders", "order_refunds")
        .maxRows(50)            // 限制单次返回最大行数，防止大表扫描
        .readOnly(true)         // 强制只读模式，拦截任何写操作
        .maxContextLength(8000) // 结果截断长度，保护 AI 上下文
        .schemaMode(SchemaMode.DYNAMIC); // 如果表非常多，建议手动开启动态模式

// 2. 构建 ReAct 智能体
ReActAgent agent = ReActAgent.of(chatModel)
        .role("财务分析专家")
        .instruction("你负责分析订单与退款数据。请结合表备注理解字段含义，金额单位均为元。")
        .defaultTalentAdd(sqlTalent)
        .build();

// 3. 执行任务
// Agent 会经历：Reasoning (分析表名) -> Action (生成/执行 SQL) -> Observation (结果分析)
String response = agent.prompt("张三在 2024 年一共有多少笔退款订单？总金额是多少？")
       .call()
       .getContent();
```

手动指定方言的示例（适用于自动识别不准确的场景）：

```java
Text2SqlTalent sqlTalent = new Text2SqlTalent(dataSource, "users", "orders")
        .dialect(new PostgreDialect())  // 强制使用 PostgreSQL 方言
        .readOnly(true);
```


### 4、工作流程：从意图到结果

**阶段一：初始化与方言探测**（`onAttach` → `schemaInit`）

* 采用双重检查锁（Double-Check Locking）保证线程安全的懒加载
* 通过 `Connection.getMetaData().getDatabaseProductName()` 获取数据库产品名
* `SqlDialectManager.select()` 遍历已注册方言，调用每个方言的 `matched()` 进行关键字匹配
* 匹配成功后，调用 `dialect.adaptDialect(conn)` 进行动态适配（如 H2 检测兼容模式）
* 加载所有指定表的备注（REMARKS）和外键关联关系（ImportedKeys）

**阶段二：Schema 注入**（`getInstruction`）

* AI 在 System Prompt 中会看到当前数据库的方言类型（如 MySQL）和系统时间
* **FULL 模式**：AI 直接获得所有表的完整字段结构（字段名、类型、长度、是否可空、主键标记、备注）
* **DYNAMIC 模式**：AI 仅获得表名清单与外键关系线索，需先调用 `get_table_schema` 探测后再编写 SQL
* 注入方言特定的执行指引（`dialect.getCustomInstruction()`），如 SQLite 的日期函数替代方案

**阶段三：SQL 修复与执行**（`execute_sql`）

* 自动去除末尾分号
* 正则修复别名（`AS alias` → `AS "alias"` / `AS \`alias\``），处理 AI 容易遗漏的引号问题
* 只读模式校验（仅允许 SELECT 开头的语句）
* 分页补全：检测 SQL 是否已包含 LIMIT/ROWNUM/FETCH FIRST/TOP，未包含则自动注入
* 结果转 JSON 返回，超出 maxContextLength 时截断

**阶段四：纠错反馈**

* 如果执行报错，Talent 利用方言引擎的 `getErrorHint()` 将复杂的 SQL 错误转化为对 AI 友好的纠错建议
* AI 可根据 Hint 调整 SQL 后重试


### 5、内置工具（Tool）

| 工具名称 | 触发条件 | 功能说明 |
| --- | --- | --- |
| `execute_sql` | 始终可用 | 执行 SELECT 查询。自动进行别名修复、只读检查、分页补全和结果截断 |
| `get_table_schema` | 仅 DYNAMIC 模式 | 按需获取指定表的详细字段定义（字段名、类型、长度、是否可空、主键标记、备注），不在 FULL 模式下暴露以避免冗余 |


### 6、内置方言能力矩阵

| 方言 | 匹配的数据库产品 | 标识符转义 | 分页语法 | 特殊适配 |
| --- | --- | --- | --- | --- |
| MySqlDialect | MySQL、MariaDB、TiDB | 反引号 `` ` `` | `LIMIT n` | `findSchema` 返回 null（MySQL 无 schema 层） |
| PostgreDialect | PostgreSQL、Kingbase、Highgo | 双引号 `""` | `LIMIT n` | 区分大小写，别名必须加双引号 |
| OracleDialect | Oracle、Dameng（达梦） | 双引号 `""`（强制大写） | `ROWNUM <= n`（子查询包装） | GROUP BY 严格要求 |
| SqliteDialect | SQLite | 双引号 `""` | `LIMIT n` | 日期函数替代方案（strftime）、布尔值 0/1、`findSchema` 返回 null |
| H2Dialect | H2 Database | 双引号 `""`（强制大写） | `LIMIT n` | 兼容模式检测：若 MODE=MySQL 则自动切换为 MySqlDialect |
| GenericDialect | 其他（兜底） | 双引号 `""` | `FETCH FIRST n ROWS ONLY` | 遵循 ANSI SQL 标准 |

自定义方言扩展：

```java
// 实现 SqlDialect 接口
public class MyDialect implements SqlDialect {
    // ... 实现各方法
}

// 注册到方言管理器（优先于默认方言匹配）
SqlDialectManager.register(new MyDialect());
```


### 7、Instruction 结构详解

当 Talent 被 Attach 到 Agent 时，`getInstruction()` 会生成结构化的提示词，包含三个章节：

```
## 1. 环境上下文
- 数据库方言: MySQL
- 系统时间: 2024-01-15 10:30:00

## 2. 数据库结构 (Schema)     ← FULL 模式：完整字段列表
   或 数据库目录与关系地图     ← DYNAMIC 模式：仅表名 + 外键线索

## 3. 执行准则
1. 权限: 只读模式，严禁写操作。
2. 方言特供指引: [dialect.getCustomInstruction()]
3. 结果截断: 默认必须分页，仅展示前 50 条。
4. 纠错逻辑: 若执行报错，请对比方言要求检查引号、函数名或保留字。
```


### 8、最佳实践建议

* **完善注释**：在数据库中为表和关键字段添加 COMMENT。Text2SqlTalent 会通过 `DatabaseMetaData` 自动提取这些备注，这是提升 AI 字段识别准确率的最有效手段
* **权限隔离**：虽然 Talent 有 `readOnly(true)` 护栏，但仍强烈建议在数据库层面为 AI 连接配置只读账号
* **表范围控制**：在构造函数中只传入当前任务相关的表名，减少干扰并显著降低 Token 消耗
* **手动指定方言**：对于使用兼容模式的数据库（如 H2 的 MySQL 模式），建议通过 `.dialect()` 手动指定，或依赖 `adaptDialect()` 的自动检测
* **DYNAMIC 模式适用场景**：当表字段总量超过 200 列或表数量超过 20 张时，建议使用 `SchemaMode.DYNAMIC`，可显著减少首次 Instruction 的 Token 消耗
