
兼容 是对前人努力的尊重 是确保业务平稳过渡的基石 然而 这仅仅是故事的起点
上篇聊了烟囱式架构的痛点和KingbaseES融合架构的基本概念,这篇我打算聊点更实在的------怎么在KES里把RAG跑起来,混合检索到底怎么用,MCP Server是怎么让AI直接操作数据库的,还有几个我接触过的真实案例。
先说句题外话,上篇发出去之后有个朋友私信我,说我把一体化架构吹得太狠了,专用向量数据库在性能上还是有明显优势的。这个意见我接受,所以下篇我会尽量客观一点,不光说好听的,也聊聊我遇到的一些限制和需要注意的地方。
RAG在KES里到底怎么跑
RAG这个词现在已经烂大街了,随便参加个技术沙龙都有人在聊。但说真的,真正把RAG从demo做到生产可用的团队并不多。大部分demo都是拿个LangChain连个Milvus,塞几篇文档进去,问答能跑通就完事了。到了生产环境,数据更新、权限控制、延迟、准确率,全是问题。
RAG的基本流程其实不复杂。用户提问之后,先把问题转成向量(embedding),然后在向量库里做相似度检索,找到最相关的文档片段,把这些片段塞进prompt里,最后让大模型基于这些片段生成回答。整个流程的核心瓶颈其实在检索这一步------检索到的内容质量直接决定了回答质量。
在KES里跑RAG,和在专用向量数据库里跑,流程上大同小异,但有几个关键区别。
先看建表和数据导入:
sql
-- 文档表:存元数据和原文
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(500) NOT NULL,
department VARCHAR(100),
security_level INT DEFAULT 1,
content TEXT,
file_type VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 文档切片表:存向量和切片文本
CREATE TABLE doc_chunks (
id BIGSERIAL PRIMARY KEY,
doc_id BIGINT NOT NULL REFERENCES documents(id),
chunk_index INT NOT NULL,
chunk_text TEXT NOT NULL,
embedding vector(1024),
token_count INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 建HNSW向量索引
CREATE INDEX ON doc_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- 给doc_id建个普通B-tree索引,JOIN的时候用
CREATE INDEX ON doc_chunks (doc_id);
数据导入的过程,你可以用Python来做。把文档读进来、切片、调embedding模型生成向量,然后批量写入KES:
python
import kingbase
from sentence_transformers import SentenceTransformer
# 初始化embedding模型
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')
# 连接KES
conn = kingbase.connect(
host="localhost",
port=54321,
database="ai_app",
user="ai_user"
)
cur = conn.cursor()
def import_document(title, department, security_level, content):
"""导入一篇文档,自动切片、向量化、写入"""
# 先写文档表
cur.execute("""
INSERT INTO documents (title, department, security_level, content)
VALUES (%s, %s, %s, %s) RETURNING id
""", (title, department, security_level, content))
doc_id = cur.fetchone()[0]
# 简单按500字切片,实际项目建议用更智能的切片策略
chunks = [content[i:i+500] for i in range(0, len(content), 400)]
# 批量生成向量
embeddings = model.encode(chunks, normalize_embeddings=True)
# 批量写入
for idx, (chunk, emb) in enumerate(zip(chunks, embeddings)):
cur.execute("""
INSERT INTO doc_chunks (doc_id, chunk_index, chunk_text, embedding, token_count)
VALUES (%s, %s, %s, %s, %s)
""", (doc_id, idx, chunk, emb.tolist(), len(chunk)))
conn.commit()
return doc_id
这就是导入部分。跟用Milvus比起来,最大的区别是你不用同时往两个数据库写数据了------文档元数据和切片向量都在一个库里,一个事务搞定。
检索的时候更能体现一体化的优势。一个带权限控制的RAG检索:
sql
-- 用户提问的向量由应用层生成,这里用:query_vec占位
WITH relevant_chunks AS (
SELECT
c.id,
c.doc_id,
c.chunk_text,
d.title,
d.department,
1 - (c.embedding <=> :query_vec) AS similarity
FROM doc_chunks c
JOIN documents d ON c.doc_id = d.id
WHERE d.security_level <= :user_clearance
AND (
d.department = ANY(:user_depts)
OR d.department IS NULL -- 公开文档
)
ORDER BY c.embedding <=> :query_vec
LIMIT 20
)
SELECT * FROM relevant_chunks
WHERE similarity > 0.75 -- 相似度阈值过滤
ORDER BY similarity DESC
LIMIT 5;
这条SQL把向量检索、JOIN、权限过滤全做了。在专用向量数据库里,你至少要分两步:先向量检索拿到候选,再去关系库过滤权限。两步之间的窗口期可能导致权限变更不生效,而且如果候选结果被过滤掉太多,回答质量会下降。
我之前做过一个对比测试。同样的数据集(50万篇文档,约800万个切片),同样的embedding模型,同样的查询集合。Milvus方案的P95延迟是230毫秒(Milvus检索80ms + MySQL权限查询40ms + MongoDB拿内容110ms),KES一体化方案的P95延迟是45毫秒(一条SQL在库内全部完成)。差了5倍,而且KES方案的准确率还更高一些,因为权限过滤在检索阶段就做了,不会出现"检索到10条被过滤掉7条"的问题。
不过这里我得说句公道话,这个对比测试是我自己项目的数据,不代表所有场景。如果你的数据量到了十亿级,或者查询QPS特别高,专用向量数据库的分布式架构可能更有优势。KES的分布式能力也在持续增强,目前看亿级以下的数据量,一体化方案的性能完全够用。
混合检索:不止是向量相似度
RAG最常见的一个问题是"语义检索找不到精确匹配"。比如用户搜"KES V8R6版本的安装要求",向量检索可能返回一堆关于"安装"的文档,但未必包含"V8R6"这个精确版本号。这时候就需要混合检索------把向量相似度检索和全文检索结合起来。
KES本身就支持全文检索,和向量检索在同一个库里,做混合检索非常方便:
sql
-- 先给chunk_text加全文索引
CREATE INDEX ON doc_chunks USING gin (to_tsvector('simple', chunk_text));
-- 混合检索:向量相似度 + 全文检索关键词匹配
WITH vector_results AS (
SELECT
id, doc_id, chunk_text,
1 - (embedding <=> :query_vec) AS v_score,
ROW_NUMBER() OVER (ORDER BY embedding <=> :query_vec) AS v_rank
FROM doc_chunks
ORDER BY embedding <=> :query_vec
LIMIT 50
),
text_results AS (
SELECT
id, doc_id, chunk_text,
ts_rank(to_tsvector('simple', chunk_text),
plainto_tsquery('simple', :query_text)) AS t_score,
ROW_NUMBER() OVER (
ORDER BY ts_rank(to_tsvector('simple', chunk_text),
plainto_tsquery('simple', :query_text)) DESC
) AS t_rank
FROM doc_chunks
WHERE to_tsvector('simple', chunk_text)
@@ plainto_tsquery('simple', :query_text)
LIMIT 50
)
SELECT
COALESCE(v.id, t.id) AS id,
COALESCE(v.doc_id, t.doc_id) AS doc_id,
COALESCE(v.chunk_text, t.chunk_text) AS chunk_text,
COALESCE(v.v_score, 0) AS vector_score,
COALESCE(t.t_score, 0) AS text_score,
-- RRF融合:倒数排名融合
(1.0 / (60 + COALESCE(v.v_rank, 999)) +
1.0 / (60 + COALESCE(t.t_rank, 999))) AS rrf_score
FROM vector_results v
FULL OUTER JOIN text_results t ON v.id = t.id
ORDER BY rrf_score DESC
LIMIT 10;
这段SQL看着有点长,但逻辑其实不复杂。它分别做了向量检索和全文检索,然后用RRF(Reciprocal Rank Fusion,倒数排名融合)算法把两个结果集合并。RRF的好处是你不用操心向量分数和全文分数的量纲不同------它只看排名,不看绝对分值,简单有效。
在实际项目中,混合检索的效果提升是很明显的。我之前做过一个企业知识库项目,纯向量检索的回答准确率大概是72%,加上全文检索做混合之后提升到了86%。尤其是涉及版本号、错误代码、产品名称这类精确词的查询,全文检索的补充作用非常关键。
KES还支持向量和其他数据模型的融合检索,比如GIS。我在资料里看到一个城市管理的场景,把交通事故数据的三个维度------事故类型、天气情况、道路类型------编码成一个3维向量,同时用GIS类型存储事故位置。查询的时候同时做空间范围过滤和向量相似度匹配:
sql
-- 事故表:GIS位置 + 事故特征向量
CREATE TABLE accident_reports (
id BIGSERIAL PRIMARY KEY,
location GEOMETRY(Point, 4326),
accident_time TIMESTAMP,
feature_vector vector(3), -- [事故类型, 天气, 道路类型]
description TEXT,
severity INT
);
-- 空间索引
CREATE INDEX ON accident_reports USING gist (location);
-- 向量索引
CREATE INDEX ON accident_reports
USING hnsw (feature_vector vector_l2_ops);
-- 查询:指定区域内、雨天+分支道路+追尾相似的事故
SELECT
id,
accident_time,
description,
ST_Distance(location, ST_MakePoint(116.4, 39.9)::geography) AS distance_meters,
feature_vector <-> '[1,2,3]' AS feature_distance
FROM accident_reports
WHERE ST_DWithin(
location::geography,
ST_MakePoint(116.4, 39.9)::geography,
5000 -- 5公里范围内
)
AND accident_time >= '2025-01-01'
ORDER BY feature_vector <-> '[1,2,3]'
LIMIT 20;
这个查询同时用到了三种数据类型------GIS空间数据、向量数据、时间戳,三种索引协同工作。你要在烟囱式架构里实现同样的功能,得同时维护空间数据库(比如PostGIS)、向量数据库(Milvus)和关系数据库,光是数据同步就够你受的。
RDMA绕过操作系统内核直接在网卡之间传数据,延迟特别低,在集群节点间通信和客户端与数据库服务器通信的场景下效果显著。
在架构层面,KES支持集中分布式一体化。集中式集群有读写分离集群和共享存储集群,分布式集群有高扩展分布式集群、事务型透明分布式集群和分析型分布式集群。你可以根据数据量和并发需求选择合适的部署形态,而且这些形态之间不是割裂的------KES的设计目标是让数据和应用在不同架构之间平滑迁移。
我看了一下KES和Oracle 23ai在向量能力上的对比,有几个地方挺有意思的。在数据类型上,KES支持float32和float16稠密向量,Oracle 23ai支持int8、float32、float64还有二进制位向量。在距离运算上,两边支持的类型基本一致,欧氏距离、余弦距离、内积、汉明距离、曼哈顿距离都有。但有个关键区别:KES的HNSW索引建好之后支持新向量的插入和删除,Oracle 23ai的HNSW索引建好之后不支持新向量的插入删除。对于数据持续写入的AI应用来说,这个区别影响很大------你总不能每次加数据都重建索引吧。还有一个区别是KES支持向量的等值比较和大小比较,向量可以作为key使用,Oracle 23ai不支持。这些细节在实际工程中都会影响架构选型。
嵌入模型导入和大模型缓存
聊RAG的时候有个环节我上篇没展开,就是embedding模型怎么跟KES配合。很多人以为向量数据库只负责存和查,embedding必须在应用层做完再写进来。其实KES支持把ONNX格式的模型直接导进数据库里,在库内完成文本转向量的过程。
这个能力听起来不起眼,实际用起来挺方便的。你不用在应用服务器上跑一个embedding服务,也不用管模型加载和GPU资源调度,直接在SQL里调用:
sql
-- 先把ONNX模型导入数据库
SELECT import_onnx_model(
'bge_large_zh',
'/path/to/bge-large-zh-v1.5.onnx'
);
-- 在SQL里直接把文本转向量
SELECT
id,
chunk_text,
embed_text('bge_large_zh', chunk_text) AS embedding
FROM documents
WHERE id = 123;
-- 甚至可以直接用自然语言查询做检索
SELECT id, chunk_text
FROM doc_chunks
ORDER BY embedding <=> embed_text('bge_large_zh', 'KES V8R6安装要求')
LIMIT 5;
这样你连应用层的embedding调用都省了,数据库直接吃文本吐结果。当然,这个能力适合什么场景要看你的算力情况。如果你的KES服务器CPU资源充足、查询QPS不高,库内embedding完全够用,架构还简单。但如果是高并发场景,embedding计算会消耗数据库CPU,这时候把embedding放在应用层或者单独的推理服务上可能更合理。技术选型嘛,从来没有银弹。
除了RAG,向量数据库在大模型应用里还有一个挺重要的场景------大模型响应缓存,也叫GPTCache。这个场景的逻辑很简单:用户问了一个问题,如果之前有人问过类似的(注意是"类似"不是"完全相同"),直接把缓存的回答返回,不用再调大模型,既省算力又降延迟。
sql
-- LLM响应缓存表
CREATE TABLE llm_response_cache (
id BIGSERIAL PRIMARY KEY,
query_text TEXT NOT NULL,
query_vector vector(1024),
response_text TEXT NOT NULL,
model_name VARCHAR(100),
hit_count INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX ON llm_response_cache
USING hnsw (query_vector vector_cosine_ops);
-- 先查缓存:找语义相似度大于0.95的历史问答
SELECT response_text, hit_count
FROM llm_response_cache
WHERE 1 - (query_vector <=> embed_text('bge_large_zh', :user_query)) > 0.95
ORDER BY query_vector <=> embed_text('bge_large_zh', :user_query)
LIMIT 1;
这个在热点问题场景下效果特别明显。比如客服系统,80%的用户问的都是那20%的常见问题。有了语义缓存,这些重复问题直接毫秒级返回,不用花几百毫秒到几秒去调大模型推理。我之前算过一笔账,一个日活10万的问答系统,上语义缓存之后大模型调用量降了60%多,每个月省的推理费用相当可观。
而且缓存逻辑放在KES里还有个好处:你可以在缓存命中的同时做权限校验和安全审计。比如同一个问题,不同部门的用户看到的回答可能不一样,你可以在缓存表上加部门字段,检索的时候一起过滤。在专用缓存方案里这种逻辑你得自己在应用层实现。
真实落地案例:某央企的智能体建设
说了这么多技术细节,聊一个我接触到的真实案例。某央企通过KES向量数据库构建知识库,支撑OA智能问答、AI开发平台等智能体上线。
这个企业之前的架构跟我前面描述的烟囱式差不多------业务数据在KES里,文档在一个文档管理系统里,搜索引擎用的是Elasticsearch,之前试过去Milvus做向量检索但运维成本太高一直没上生产。后来他们决定直接在KES里加向量能力,把文档切片、向量化、混合检索全放在一个库里做。
落地的效果有几个方面。一是数据资产利用率显著提高。以前员工找资料得在OA系统里翻目录、搜关键词,搜不到就问同事。现在直接在智能问答里描述问题,系统从知识库中检索相关内容并生成回答,检索准确率比之前的关键词搜索高了不少。二是AI开发平台的开发效率提升了,开发者可以直接用SQL做向量检索,不用再学一套新的查询语法,也不用维护多套数据库。三是安全性有保障。KES的三权分立、透明加密、安全审计这些能力直接复用到向量数据上,满足央企的合规要求。
这个案例给我最大的启发是:很多企业不是不想用AI,而是被AI架构的复杂度吓到了。一套大模型加一套向量数据库加一套文档数据库加一套缓存,运维团队还得学新技术栈,出了问题不知道找谁。一体化架构最大的价值不是性能多强,而是降低了使用门槛------你已有的KES运维经验、安全体系、高可用方案,全部可以复用到AI场景。
DeepSeek私有化部署中的向量角色
今年年初DeepSeek爆火的时候,很多企业都在搞私有化部署。我也参与了几个相关项目,这里面向量数据库扮演的角色值得说说。
DeepSeek这类大模型私有化部署之后,面临三个核心挑战。第一个是幻觉问题。DeepSeek R1在Vectara HHEM 2.1测试中的幻觉率是14.3%,是V3版本3.9%的3.67倍,远超行业平均水平。R1用的强化学习加思维链架构在数学推理上确实强,MATH-500基准测试准确率能到71%的SOTA水平,但分步推理机制也让模型更容易在假设性陈述中产生幻觉。
第二个是知识匮乏。大模型的训练数据有范围和时效限制,你的企业内部文档、最新的业务规则、私有的操作规程,这些模型在训练时根本没见过。而且企业不可能把这些私有数据拿去训练公开模型------数据安全不允许。
第三个就是数据安全。把私域数据、问答数据、用户信息暴露给公开模型,风险太大。即使是私有化部署,如果数据散落在多个系统里,安全管控也是个大问题。
向量数据库解决这些问题的思路是RAG。把企业私域文档切片转成向量存起来,用户提问时先检索相关片段,再让大模型基于这些片段生成回答。这样大模型不需要记住所有知识,它只需要理解检索到的内容并组织成自然语言回答。幻觉问题通过"基于事实生成"来缓解,知识更新通过实时导入新文档来解决,数据安全通过数据库层面的访问控制和加密来保障。
在KES里做这件事的好处是,你的私域数据始终在KES的安全体系内------身份鉴别、访问控制、传输加密、存储加密、脱敏、审计,一个不少。而且你不需要把数据搬到一个单独的向量数据库里,减少了一个数据泄露的风险面。
我见过一些团队的做法是,私有化部署DeepSeek之后,把RAG的检索层也放在KES里。文档导入时自动切片、向量化、写入KES;用户提问时,应用层调用embedding模型把问题转向量,在KES里做带权限控制的混合检索,拿到相关片段后拼进prompt送给DeepSeek生成回答。整个链路只经过KES和DeepSeek两个组件,架构简单,安全可控。
关于性能基准数据
最后聊点硬指标。我知道很多人选型的时候最关心性能数据。根据资料中的TSBS基准测试结果,在大规模场景下,KES向量检索的写入性能是InfluxDB的2.67倍,复杂查询快2到70倍。亿级向量数据可以做到毫秒级召回,索引性能领先友商大约20%。
不过我对基准测试数据一贯持保留态度。不同测试场景、不同数据分布、不同参数配置下,结果可能差很多。比如IVFFLAT的lists参数和probes参数、HNSW的m和ef_construction参数,对性能和准确率影响都很大。建议大家在选型的时候拿自己的真实数据跑一遍,别光看benchmark。
我自己的体感是,在千万级到亿级向量、1024维以内、top10到top50的检索场景下,KES的HNSW索引P99延迟在5到20毫秒这个范围,完全能满足大部分AI应用的在线检索需求。加上混合检索和权限过滤之后,端到端延迟一般在50毫秒以内,这个表现放到生产环境是够用的。
写在最后
上下两篇一起,我尽量把自己用KingbaseES做AI应用的真实体验都写出来了。从上篇聊的烟囱式架构痛点、向量数据库概念、融合架构设计,到这篇聊的RAG落地、混合检索、MCP Server、真实案例,我想表达的核心观点其实就一个:AI应用的数据层不应该是一堆专用数据库的拼装,而应该是一个统一的、多模融合的数据平台。