
导读:给评论表做情感分析、给工单做摘要,传统做法是"导出数据 → 写 Python 脚本 → 回写结果",链路长、一致性差。OpenTenBase 的
opentenbase_ai扩展把大模型调用变成了一条 SQL。本文讲清三件事:① 模型注册背后的三个设计决策(复制表 / json_path 模板 / GUC 默认模型);② 跑通摘要生成 + 文本分类的最小闭环;③ 叠加 pgvector 实现"embedding 入库 + 相似度检索"的库内 RAG。文中 SQL 语义已与 master 源码及回归测试逐句核对;涉及真实端点的运行效果均标注"待验证",以实测为准。演示环境基于本组 PR #302 交付的 PostgreSQL 18.6 + pgvector v0.8.6 增强版。
假设你有一张用户反馈表 feedback,想给每条评论生成摘要、判断正负面。常规路径是:导出数据 → 写脚本调 LLM API → 把结果写回数据库。这条路有三个老问题:
opentenbase_ai 的思路很直接:LLM 调用 = SQL 函数。数据不出库,UPDATE / SELECT 可以直接加工整列文本,权限、事务、一致性全部复用数据库本身的能力。
先交代一下上游现状:社区由 Issue #304 牵头为 opentenbase_ai 补文档,PR #305(611 行完整版)与 PR #306(228 行精简版)都在提交 README。本文不重复通用函数参考,聚焦最小闭环 + RAG 案例,与二者互补。
环境说明:本文演示环境为本组 PR #302 交付的 PostgreSQL 18.6 + pgvector v0.8.6 增强版 + OpenTenBase master 源码树。扩展依赖
http(pgsql-http)、libcurl 与可达的模型端点,安装前置清单不在本文展开,详见文末参考链接中的 README 与交付文档。
opentenbase_ai 的全部配置都存在一张表里,全部调用都收敛到两个函数。读一遍 opentenbase_ai--1.0.sql,有三个决策值得拿出来讲。
决策 1:配置表为什么是复制表。 ai_model_list 的建表语句收尾是 DISTRIBUTE BY REPLICATION——在 OpenTenBase 的 CN/DN 分布式架构下,模型配置需要在所有节点上保持一致:在 CN 上注册一次,全局生效,任何节点执行 SQL 函数都能本地读到配置,避免跨节点查询配置表。
决策 2:json_path 不是 JSONPath,是 SQL format 模板。 这是最容易望文生义的地方。invoke_model 拿到 HTTP 响应体后,执行的是 EXECUTE format(json_path, 响应体)。三个内置注册函数分别写死了 OpenAI 兼容协议的提取模板,例如 completion 模型的是:
'SELECT %L::jsonb->''choices''->0->''message''->>''content'''即把响应体作为字面量代入模板,用 jsonb 运算符取出 choices[0].message.content。想接任何非 OpenAI 兼容的协议,不需要改代码——用 8 参数的 ai.add_model 传一个自定义模板即可。这个"模板即适配层"的设计,把协议差异收敛成了一行配置。
决策 3:默认值与合并。 GUC ai.completion_model 决定默认模型(embedding / image 各有对应的 ai.embedding_model / ai.image_model);每次调用时 default_args || user_args 做 jsonb 浅合并,顶层键覆盖——注册时写好 temperature: 0.3,某条 SQL 想临时调高,传个 config 参数就行。
整体调用链路如下(后续会替换为正式配图):
[图 1] 调用链路示意图(占位,发布前替换为配图)
SQL (ai.summarize ...)
└─> ai.invoke_model(model_name, user_args)
├─> 读 ai_model_list(复制表,全节点一致)
├─> default_args || user_args 合并参数
├─> pgsql-http 发起 POST(Bearer token)
└─> EXECUTE format(json_path, 响应体) 提取结果以 DeepSeek 为例(任何 OpenAI 兼容端点都可以):
CREATE EXTENSION opentenbase_ai; -- 依赖 http 扩展,需先装好 pgsql-http
SELECT ai.add_completion_model(
'my-chat',
'https://api.deepseek.com/v1/chat/completions',
'{"model":"deepseek-chat","temperature":0.3}'::jsonb,
'sk-***', -- token,自动构造 Authorization: Bearer 头
'deepseek'
);
SET ai.completion_model = 'my-chat';
SELECT * FROM ai.models; -- 视图查验:model_name / provider / uri / default_args注册与管理函数速查(函数签名已与 master 源码逐行核对):
函数 | 作用 |
|---|---|
| 注册对话模型(内置 chat 提取模板) |
| 注册向量模型(内置 embedding 提取模板) |
| 注册多模态模型 |
| 通用 8 参注册,适配任意协议 |
| 修改单个配置项 |
| 删除模型 |
⚠️ 生产安全提示:源码中
GRANT USAGE ON SCHEMA ai TO PUBLIC,且ai_model_list 表对 PUBLIC 授予了 SELECT——而request_header 里存着 API token,等于全库用户可读。同时 PL/pgSQL 函数创建后默认 PUBLIC 有 EXECUTE 权限。生产环境请务必REVOKE,只授权给管理员角色,这一点请当作上线前检查项。
注册好模型之后,高层函数就是一句话的事:
-- 摘要
SELECT ai.summarize('这款数据库安装简单,文档清晰,就是初始化集群时端口配置踩了点坑……');
-- 文本分类(布尔判断)
SELECT ai.generate_bool('好评返回 true,差评返回 false:物流很快,包装也不错');
-- 情感分析
SELECT ai.sentiment('客服响应很及时,但问题没彻底解决');全文最想给你看的是这一句——整列文本库内加工:
UPDATE feedback
SET summary = ai.summarize(content),
is_positive = ai.generate_bool('好评返回 true:' || content);一条 UPDATE 扫完整张表,摘要和情感标签直接落列,没有导出、没有脚本、没有回写。想限流就加 WHERE 分批跑,想重跑就事务回滚,全部是老 DBA 熟悉的武器。
ai.summarize / ai.generate_bool / ai.sentiment 三条调用的真实返回(待验证,实测后回填)UPDATE 前后 feedback 表对比(待验证,实测后回填)状态标注:以上 SQL 的语义已与扩展回归测试逐句核对;真实端点的运行结果与截图待环境实测后回填,本文发布前补齐。
没有真实 API Key 时,可以把 URI 指向 httpbin.org 这类回显服务做 mock 测试。但请清醒——mock 的验证边界非常明确:
✅ 能验证 | ❌ 不能验证 |
|---|---|
请求构造(method / headers / body 被正确发出) | 真实推理质量 |
| json_path 与真实响应结构是否匹配 |
| token 鉴权能否通过真实服务 |
错误路径(模型不存在 / http_code ≠ 200) | 超时、限流等真实网络行为 |
一句话结论:mock 通过 ≠ 真实调用通过。接真实模型前,请用真实端点把第四章完整重跑一遍。这也是本文所有演示结果坚持标注"待验证"的原因——没跑过真实端点,就不写"已实测"。
有了对话模型,再加上 pgvector,检索增强生成(RAG)的检索半边也能搬进库里:
-- 1. 注册 embedding 模型(OpenAI 兼容端点均可)
SELECT ai.add_embedding_model(
'my-emb',
'https://api.openai.com/v1/embeddings',
'{"model":"text-embedding-3-small"}'::jsonb,
'sk-***'
);
SET ai.embedding_model = 'my-emb';
-- 2. embedding 直接入库(::vector 由 pgvector 提供)
CREATE TABLE docs (
id serial PRIMARY KEY,
content text,
embedding vector(1536)
);
INSERT INTO docs (content, embedding)
SELECT content, ai.embedding(content)::vector
FROM raw_docs;
-- 3. Top-K 相似度检索
SELECT id, content
FROM docs
ORDER BY embedding <=> ai.embedding('如何初始化 OpenTenBase 集群?')::vector
LIMIT 5;到这里是标准玩法。接下来是本文独有素材——本组 PR #302 为 pgvector v0.8.6 补齐的索引自省与参数推荐能力:
-- 4. 查看索引构建参数(PR #302 新增)
SELECT * FROM hnsw_index_info('docs_hnsw_idx'::regclass);
-- m | ef_construction | ef_search | dimensions | opclass
-- ---+-----------------+-----------+------------+---------------
-- 16 | 64 | 40 | 1536 | vector_l2_ops
-- 5. 按目标 recall 推荐检索参数(PR #302 新增)
SELECT * FROM hnsw_recommend_ef_search('docs_hnsw_idx'::regclass, 5, 0.95);
SELECT * FROM ivfflat_recommend_probes('docs_ivf_idx'::regclass, 0.99);推荐函数按目标 recall 给参数倍率:< 0.9 → 0.7x,< 0.95 → 1.0x,≤ 0.99 → 1.5x,> 0.99 → 2.5x。这两个函数的意义在于:线上"向量检索 recall 异常低"的排障,从拼 pg_stat_* 加元数据,变成直接看索引本身的构建参数,RAG 检索质量可解释、可调参。上述诊断与推荐函数已经 make installcheck 14/14 全部通过(PostgreSQL 18.6 + pgvector v0.8.6)。
hnsw_index_info / 推荐函数的输出(待验证,复用 PR #302 环境回填)⚠️ 已知限制:PG18 + pgvector 0.8.6 在 2 万行以上构建 HNSW 索引存在段错误(上游 bug,已复现),演示数据量请控制在 ≤ 1 万行;另 PR #302 的诊断函数使用了 PG12+ 的
InitMaterializedSRF API,OpenTenBase 的 PG10 内核暂未适配,改动计划已列入其设计文档 v0.2。
症状 | 原因与解法 |
|---|---|
| 模型未注册或名字打错, |
调用报 GUC 未设置 |
|
返回 | 用 |
报 | 该模型的 |
设了 GUC 但不生效 | 自定义 GUC 需扩展库已加载,确认会话内已 |
模型不是 OpenAI 兼容协议 | 别硬套便捷函数,用 8 参 |
回顾一下:库内 AI 的价值 = 数据不出库 + 免 ETL + 可组合进任意 SQL;设计上能学两招——用复制表保证分布式配置一致,用 SQL format 模板把协议差异收敛为配置。再叠上 pgvector 与 PR #302 的索引自省能力,检索质量也从玄学变成了可观测的工程问题。
后续计划:真实端点实测并回填本文全部截图;向 Issue #304 提交与 #305 / #306 互补的文档 PR;PR #302 方向继续推进距离计算 SIMD 优化与 PG10 内核适配。欢迎到仓库交流。
环境声明:本文演示环境基于 PR #302 交付的 PostgreSQL 18.6 + pgvector v0.8.6 增强版;文中函数签名与 SQL 语义已与 OpenTenBase master 源码及回归测试逐句核对,标注"待验证"的演示结果将于真实端点实测后回填截图,回填前本文不发布。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。