详解Oracle 26ai Select AI原理:自然语言如何精准转换SQL?

# 详解Oracle 26ai Select AI原理:自然语言如何精准转换SQL?

写上一篇文章的时候,我在测试环境里搭了一套Select AI,让业务部的老王上去试用。效果出乎意料得好,他一个不会写SQL的人,十五分钟内查到了七八条以前得找我帮忙的数据。

文章发出来之后,好几个同行私信问我:这东西到底是怎么做到的?自然语言转SQL不是新鲜事,很多年前就有人在搞,OpenAI刚出GPT-3.5的时候也有人拿它写过SQL。但那些方案要么准确率不行,要么就是"小样本演示好看,上了生产就崩"。Oracle这个Select AI的准确率为什么能稳定在八九成?它和那些"把问题直接扔给LLM让它猜"的方案有什么本质区别?

说实话,最开始我也好奇这个问题。于是花了几天时间,把官方文档翻了个遍,在测试环境里反复看 `showprompt` 的输出,调了各种Profile参数对比差异。这篇文章就是把这段时间的探索结果整理出来,从原理层面说清楚Select AI的工作机制。

## 先看一张架构分工图

要理解Select AI的原理,首先得搞清楚一个问题: **SQL到底是谁生成的?**

直觉上可能会觉得:是LLM生成的。用户说人话,LLM把它转成SQL。听起来没错,但实际操作里,如果只靠LLM自己"猜"SQL,结果会非常不可控。LLM可能记得"customers"表有"customer_id"和"name"列,但它不知道你的表里"status"字段是 '0' 和 '1' 还是 'active' 和 'inactive'。更不用说那些复杂的业务含义了。

Oracle Select AI的做法不是"把问题扔给LLM完事",而是在用户问题和LLM之间加了一个关键的中间层—— **Prompt Augmentation(提示词扩充)**

整个流程大致是这样:

```
用户输入自然语言问题
    ↓
数据库解析问题、确定使用哪个AI Profile
    ↓
数据库从数据字典中读取 schema 元数据
(表名、列名、数据类型、注释、主外键约束等)
    ↓
数据库把用户问题和这些元数据拼成一个完整的提示词
    ↓
完整提示词发送给LLM
    ↓
LLM基于提示词中的schema信息生成SQL
    ↓
数据库执行SQL,返回结果
    ↓
(可选)结果再发给LLM做自然语言描述
```

关键点在于: **LLM不是空手写SQL的,它收到了一个"增强版"的提示词,里面包含了数据库的结构信息和业务描述。**

这样就解决了LLM对数据库一无所知的问题。LLM不需要"记住"你的表结构——每次生成SQL之前,数据库都会把最新的表结构信息告诉它。

## 提示词里到底有什么

为了看清楚LLM到底收到了什么,Select AI提供了一个叫 `showprompt` 的动作。专门用来显示最终发给LLM的完整提示词内容。

我在测试环境跑了一条:

```sql
SELECT AI showprompt how many customers from San Francisco are married;
```

返回的内容大概长这样(根据文档描述和我的测试推导,不是逐字原文):

```
System prompt:
You are an assistant that helps users query an Oracle database.
Given the following database schema, generate a SQL query to answer the user's question.
Only use the tables and columns listed in the schema below.

Database schema:
Table: SH.customers
  - customer_id (NUMBER): Primary key, unique identifier for each customer
  - cust_first_name (VARCHAR2): Customer's first name
  - cust_last_name (VARCHAR2): Customer's last name  
  - city (VARCHAR2): City where the customer lives
  - marital_status (VARCHAR2): Marital status of the customer
  - country_id (NUMBER): Foreign key to countries table

Table: SH.countries
  - country_id (NUMBER): Primary key
  - country_name (VARCHAR2): Name of the country
  - country_subregion (VARCHAR2)
  - region (VARCHAR2)

Foreign key relationships:
  - customers.country_id → countries.country_id

User question: how many customers from San Francisco are married
```

然后LLM在这个提示词的指导下,生成了一条类似这样的SQL:

```sql
SELECT COUNT (*)
FROM SH.customers c
WHERE c.city = 'San Francisco'
  AND c.marital_status = 'Married' ;
```

看到了吗?LLM收到的不是"how many customers from San Francisco are married"这孤零零的一句话。它收到了一整套"上下文":有哪些表、每张表有哪些列、列的数据类型、主外键关系、业务含义(如果有注释的话)。这样一来,LLM不需要靠"记忆"来猜——它拿到的信息足够它做出准确的判断。

这其实就是Select AI能稳定工作的核心秘密。它的精度不来自LLM本身有多强(虽然强模型确实有加成),而是来自 **数据准备** ——数据库把自己能提供的所有结构信息都喂给了LLM。

## 元数据的来源和范围控制

上面例子里的"Database schema"部分,数据是从哪里来的?来自Oracle数据字典。Select AI通过读取 `USER_TABLES` `USER_TAB_COLUMNS` `USER_CONSTRAINTS` `USER_COL_COMMENTS` 等视图,把表结构的元数据实时抓取出来,组装到提示词里。

但这里有一个明显的问题:如果数据库里有几百张表,每张表几十个字段,全部塞给LLM会怎样?

首先,token消耗会爆炸。GPT-4o的上下文窗口虽然能容纳几万个token,但把整个数据库的schema全塞进去是不现实的。其次,信息太多反而会让LLM困惑——从几百张表里找一张正确的,比从三五张表里找一张难得多。

所以Select AI提供了三个层面的范围控制:

**第一个层面:object_list**

在AI Profile里手动指定LLM可以"看到"哪些表。我在上一篇文章的配置里就是用的这个方法:

```json
"object_list" : [
  { "owner" : "SH" , "name" : "customers" },
  { "owner" : "SH" , "name" : "sales" },
  { "owner" : "SH" , "name" : "products" },
  { "owner" : "SH" , "name" : "countries" }
]
```

这样LLM的"视野"就被限定在这四张表里。用户问涉及其他表的问题,LLM要么不知道,要么生成的SQL无法执行(因为表不在object_list里——取决于 `enforce_object_list` 的设置)。

**第二个层面:enforce_object_list**

`enforce_object_list` 是一个布尔开关。设为 `true` 时,LLM生成的SQL只能引用 `object_list` 里的表。如果LLM试图使用列表外的表(比如它从训练数据里"记得"有一个 `orders` 表),生成的SQL会被拒绝或报错。

设成 `false` 时,LLM可以"自由发挥",访问它认为合适的任何表。灵活度高,但风险也大。生产环境建议设为 `true`

**第三个层面:object_list_mode = automated**

这是Oracle AI Database 26ai新增的能力。设为 `automated` 后,不需要手动指定 `object_list` ,Select AI会自动分析用户的问题,判断涉及哪些表,然后只把相关表的元数据发给LLM。

这个机制背后用到了向量索引——它会为每个对象(表/视图)生成语义向量,然后根据用户问题的语义相似度来匹配最相关的几张表。

```
用户问题:"show me total sales by region for last quarter"
    ↓
向量匹配 → 候选表:sales, customers, countries, time_dim
    ↓
只把这4张表的元数据发给LLM
    ↓
其他50张表 "不可见"
```

这个自动检测机制的好处是减少了手动维护 `object_list` 的工作量,但坏处是如果匹配不够精确,可能漏掉关键表或者带进来无关表,影响准确性。

我个人的建议是:系统刚上线阶段用 `object_list` 手动指定,等稳定运行一段时间、积累了足够多的查询日志之后,再考虑切换到 `automated` 模式。

## 评论和注释的力量

在元数据里,有一类信息常常被低估,但实际效果却非常显著——注释。

打开 `comments: true` 之后,Select AI会在元数据中包含表的 `COMMENT ON TABLE` 和列的 `COMMENT ON COLUMN` 信息。

举个例子,假设 `customers` 表长这样:

```sql
COMMENT ON TABLE customers IS '客户基本信息表,包含联系方式和人口统计信息' ;
COMMENT ON COLUMN customers.cust_income_level IS '客户年收入等级,A=高收入 B=中高 C=中低 D=低收入' ;
COMMENT ON COLUMN customers.marital_status IS '婚姻状况:Married=已婚 Single=单身 Divorced=离异' ;
```

打开 `comments: true` 后,这些注释会出现在提示词的元数据里。LLM读到这些信息,对字段的理解就从"一个叫marital_status的VARCHAR2列"升级到了"这个列存的是婚姻状况,值有Married/Single/Divorced三种"。

我在测试环境里做了一个对比实验。同样的表,同样的数据,同样的问题,关掉注释和打开注释,LLM生成的SQL质量有肉眼可见的差距:

**关掉注释** 时,问"哪些客户是高收入人群",LLM生成的SQL是:
```sql
SELECT * FROM customers WHERE cust_income_level = 'High' ;
```
但是cust_income_level的实际值是'A',不是'High'。LLM猜错了。

**打开注释** 后,同样的问法,LLM生成的SQL是:
```sql
SELECT * FROM customers WHERE cust_income_level = 'A' ;
```

这就是注释的价值。LLM不可能知道每个字段的编码规则。但如果你在注释里告诉它了,它就能做出正确的判断。

所以如果在实际使用Select AI时发现某些问题的SQL生成总是不对,第一步不是调模型,而是看看相关字段的注释写得够不够清楚。这个规律同样适用于外键约束——打开 `constraints: true` 之后,主外键关系会被包含在元数据里,LLM生成多表JOIN时就不需要"猜"关联条件了。

## LLM"看到"的只是元数据,不是真实数据

这里有一个非常重要的安全设计,需要单独拿出来说一下。

有些人担心:LLM是外部API(比如OpenAI),把数据库信息发给它会不会不安全?

那要看"数据库信息"指的是什么。

Select AI发给LLM的,是 **表结构和元数据** ——表名、列名、数据类型、注释、约束关系。绝对不是用户的真实数据行。在NL2SQL流程中,数据库不会把任何一行实际数据发送给LLM。

只有在使用 `narrate` action时,查询结果(汇总数据或少量行)才会被发送给LLM做自然语言描述。即使如此,Oracle也提供了 `enable_sources` 参数来控制是否在RAG回复中包含源文档信息。

如果需要更严格的安全控制,可以通过 `DBMS_CLOUD_AI.ENABLE_DATA_ACCESS` 开关来禁止某些用户或场景下将数据发往LLM。

这种"元数据作为提示词、数据和元数据分离"的架构,是Select AI能通过企业安全审计的关键。如果LLM能看到实际数据行,很多企业是不敢用的。

## 对话上下文的管理方式

Select AI的对话模式,背后也是一种提示词增强——只是增强的内容从"表结构"变成了"历史对话"。

开启 `conversation: true` 后,第二次及后续的查询,发给LLM的提示词会包含前几轮的对话记录:

```
(第二次查询时发给LLM的提示词,简化版)

System prompt: ...

Database schema: ...

Previous conversation:
User: how many customers do we have
Assistant: SELECT COUNT(*) FROM SH.customers → Result: 425

User: how many of them are from California
Assistant: ...

Current question: what about married customers in California
```

这个"Previous conversation"部分是自动追加的。所以用户才能在说"what about married customers"时,LLM知道"them"指的是California的客户。

不过文档里提到一个细节: `conversation` 只保留最近的10条交互历史。超过了10条,最早的会被丢弃。这个设计是出于token窗口的考虑——无限累积历史会让提示词越来越长。

如果需要更长周期的对话(比如跨天的),可以使用 `DBMS_CLOUD_AI.CREATE_CONVERSATION` 创建持久化对话,通过 `SET_CONVERSATION_ID` 来管理和切换。这种长期对话存在数据库表里,可以通过 `retention_days` 来控制保留天数。

## RAG的向量检索增强原理

Select AI的RAG(Retrieval Augmented Generation)功能,底层依赖的是Oracle 26ai的AI Vector Search能力。

传统的NL2SQL只能查结构化数据(表里的行和列)。但RAG可以查非结构化数据——操作手册、产品文档、制度文件这些。它的工作原理和NL2SQL的提示词增强是同一个思路,只是多了两步:

第一步:把文档向量化并存入索引。文档被切分成段落(默认500字符一段,50字符重叠),每段通过嵌入模型转成向量,存到Oracle数据库的向量索引里。

第二步:用户提问时,把用户的问题也转成向量,在向量索引里做相似度搜索,找到最相关的前K段文档内容。然后把"用户问题 + 相关文档段落 + schema元数据"三者拼成一个完整的提示词发给LLM。

所以RAG本质上是"提示词里多塞了几段从文档里搜出来的相关内容"。

举个例子,用户问:

```sql
SELECT AI NARRATE what is the return policy for electronics;
```

发给LLM的提示词里会包含从产品手册PDF里检索到的"电子产品退货政策"章节的内容。LLM基于这些内容来回答,而不是凭自己的训练数据猜测。

如果你之前用过Oracle的AI Vector Search,可能会觉得熟悉——是的,Select AI的RAG就是建立在Vector Search之上的,只是Oracle帮你把"切分文档→生成向量→创建索引→搜索→拼入提示词"这一整套流程封装起来了。

## Select AI Agent的ReAct原理

再往上一层,Select AI Agent的工作原理就更加复杂了。

Agent基于的是ReAct(Reasoning + Acting)模式。它的核心循环是:思考→行动→观察→再思考→再行动→……直到得出最终答案。

在Select AI Agent的场景下,每一步的"思考"都是LLM完成的,"行动"则是调用工具。Agent内置的工具包括:

- **SQL工具** :根据自然语言生成SQL并查询数据库
- **RAG工具** :检索企业文档
- **Web搜索工具** :联网搜索(需要OpenAI凭据)
- **通知工具** :发送邮件或Slack消息
- **自定义工具** :调用已有的PL/SQL过程或REST API

整个循环是这样跑的:

```
用户:检查库存并通知采购负责人
  ↓
Agent LLM 思考:我需要先查询库存数据
  ↓
Agent 行动:调用 SQL 工具 → SELECT ... FROM inventory WHERE quantity < reorder_level
  ↓
Agent 观察:返回了5条低于安全库存的产品
  ↓
Agent LLM 思考:需要通知采购部,发邮件
  ↓
Agent 行动:调用 NOTIFICATION 工具发邮件
  ↓
Agent 观察:邮件发送成功
  ↓
Agent 终止:返回给用户"已处理,通知了采购部"
```

这个循环也不是无限跑下去的——有最大迭代次数限制(安全机制防止Agent无限循环或调用太多次API)。配置Agent的时候可以设置这个上限。

和基础的NL2SQL相比,Agent多了一层"自主决策"的能力。NL2SQL是"用户说查什么就查什么",Agent是"用户说一个目标,Agent自己拆解步骤、选择工具、一步一步完成"。代价是配置更复杂——要先建Agent、建Tool、建Task、最后建Team。而且Agent的每一步都要调用LLM,token消耗和响应延迟都比单次NL2SQL大很多。

## showprompt的实际调试价值

讲了这么多原理层面的东西,来说一个对实际使用最有帮助的功能: `showprompt`

我之前调试AI Profile的时候, `showprompt` 几乎是每次必用的。它的作用就是把你精心配置的"扩充后的提示词"完整展示出来,让你能亲眼看到LLM收到的是什么。

比如你配置了一个Profile,LLM生成的SQL总是不对。你不用去猜是哪里出了问题——直接跑一次 `showprompt` ,看看提示词里有没有包含你预期的表结构,注释有没有正确加入,object_list有没有生效。

我遇到过几次这样的情况:配置了一个新Profile,跑了几条查询,结果总是报"表不存在"或者"列不存在"。跑 `showprompt` 一看, `object_list` 里的表名写错了(owner写成了大写但表名记混了)。改过来之后,一切正常。

还有一次是 `comments` 开了但某个字段的解释LLM没用到。跑 `showprompt` 才发现那个字段的注释是空的——建表的时候DBA忘了写注释。补上注释之后,相关问题的准确率就上来了。

对于所有接入Select AI的生产环境,我建议把 `showprompt` 作为调试阶段的标配工具。每个新Profile上线前,至少跑5-10个典型问题的 `showprompt` ,人工确认提示词里的元数据是完整且准确的。

## 影响SQL生成质量的因素

从原理上讲清楚了Select AI的工作机制之后,归纳一下影响SQL生成质量的关键因素——按重要性从高到低排列:

**1. object_list的准确性。** 这是最基础的因素。如果相关的表没有被包含在object_list里,LLM再强也生成不了正确的SQL。反之,如果塞了太多无关表,LLM的"选择困难症"也会影响准确率。

**2. 字段注释的质量。** 注释越详细,LLM对字段的理解越准确。特别是枚举值、编码规则、业务含义,一定要在注释里写清楚。

**3. 外键约束的完整性。** 如果表间的关联关系已经在数据库里通过外键定义好了(并且 `constraints: true` 打开了),LLM生成多表JOIN时基本不会搞错关联条件。如果外键没有定义、或者没有打开constraints参数,LLM就要靠"猜",猜就有概率出错。

**4. LLM本身的能力。** 不同LLM在SQL生成上的表现确实有差异。我测试下来,GPT-4o和Claude-3.5-Sonnet的表现比GPT-3.5-turbo好很多,特别是复杂查询(多层子查询、窗口函数)。不过好消息是,即使使用免费或便宜的小模型,只要前面1-3项做得好,基础查询的准确率也不会太差。

**5. 问题的表述方式。** 同样一个查询需求,不同的问法生成的SQL质量也不同。表述越具体、越清晰,结果越好。比如"上个月每个品类的销售额"比"上个月的销售情况"要准确得多。

这里面,1-3项是DBA或者数据建模阶段可以做好的,不需要LLM的参与。换句话说,Select AI的准确率提升,大部分功夫花在"数据库本身的质量"上,而不是花在"调LLM"上。

这个结论反过来也说明了一个问题:如果数据库本身的元数据质量很差(表名和字段名是拼音缩写、没有注释、没有外键定义),那用什么LLM来救都救不了。Select AI不是"脏数据清洗器",它是一个"好数据放大器"——你的数据质量越高,Select AI的效果就越好。

## 安全边界的多层设计

最后说说Select AI的安全和权限模型。这也是企业用户最关心的部分。

Select AI的安全防护是分层的:

**第一层:数据库权限。** 用户只能查询自己有权限的表。即使object_list里配置了某张表,如果数据库用户没有这张表的SELECT权限,查询一样会失败。这是在数据库层面做的权限控制,和传统的Oracle权限体系一致。

**第二层:object_list控制。** 限制了LLM能"看到"哪些表的元数据。结合 `enforce_object_list: true` ,LLM生成的SQL只能引用列表内的表。

**第三层:元数据不包含数据。** 如前所述,NL2SQL流程中,LLM只收到表结构信息,收不到任何真实数据行。

**第四层:网络ACL控制。** 通过数据库的网络访问控制列表,可以精确限制数据库能访问哪些外部API端点。白名单机制,只有授权的API地址才能连接。

**第五层:Data Access开关。** 管理员可以用 `DBMS_CLOUD_AI.ENABLE_DATA_ACCESS` `DISABLE_DATA_ACCESS` 来控制是否允许将数据(包括汇总结果)发往LLM。

**第六层:审计和监控。** 所有通过Select AI执行的查询都会被记录在数据库的审计日志中,可以追溯到哪个用户、在什么时间、问了什么问题、生成了什么SQL。

这六层防护叠加起来,大多数企业的安全合规要求都能满足。

## 总结

回到最开头的问题:自然语言如何精准转换SQL?

答案不是"LLM很强大"这么简单。Select AI的精度来自一个精心设计的系统工程:

数据库端负责「元数据准备」——把表结构、字段注释、约束关系整理好,打包成LLM能理解的提示词。LLM端负责「SQL生成」——基于数据库给的信息,把自然语言翻译成准确的结构化查询。这套中间层的存在,让LLM不再需要"猜测"数据库结构,而是基于真实的元数据做决策。数据和元数据分离的设计,也让安全性有了保障。

Oracle在数据库领域做了四十多年,积累了深厚的元数据管理能力。把这种能力和LLM结合起来,就是Select AI的核心思路。它不是一个"用AI替代数据库"的故事,而是一个"用AI理解数据库"的故事——让用户说人话,让LLM理解人话和数据的关联,让数据库执行精准的查询。

这三者各司其职,才实现了"自然语言精准转换SQL"这件事。



请使用浏览器的分享功能分享到微信等