# 详解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"这件事。