← 返回 Skill 列表
extension
分类: 开发与工程API Key 暂未确认

seekdb 混合检索 RAG

在 seekdb 上设计、实现和排查向量、全文与标量条件混合检索的 RAG 数据层。用于建表、选择 HNSW 参数、编写混合检索 SQL、验证召回率和定位检索异常;不用于与 seekdb 向量检索无关的通用 SQL 调优。

person作者: jiaozi567hubModelScope

seekdb 混合检索 RAG

目标

帮助开发者把“文档进入 seekdb、能被正确召回、性能可解释”这条链路做完整。优先给出可执行的 SQL、可验证的质量指标和明确的失败原因,不用模糊的“调大参数试试”。

适用边界

适用:

  • 新建 seekdb 知识库、语义搜索或 RAG 检索层。
  • 已有向量检索,但召回差、延迟高、过滤后结果异常。
  • 需要同时使用向量、全文和标量条件。
  • 需要在嵌入式、单机 server、Docker 或 OceanBase 分布式模式之间做选择。

不适用:

  • 与向量检索无关的一般 MySQL 慢查询优化。
  • 训练或微调 embedding 模型。
  • 把 seekdb 当成纯内存向量库,只做进程内数组检索。

先定四个参数

动手写表之前,先确认以下信息。缺一项就可能导致后面返工:

| 项目 | 必须确认 | 常见错误 | | --- | --- | --- | | 部署形态 | 嵌入式、单机 server、Docker 或 OceanBase 分布式 | 本地 demo 用了嵌入式,上线却按 server 方式连接 | | 向量维度 | embedding 模型实际输出维度 | 建表写 384,模型输出 768 | | 距离度量 | 与模型和归一化策略一致 | 索引用 l2,查询却按 cosine 理解 | | 质量目标 | Recall@K、延迟、数据量、过滤条件 | 只看单条 demo 结果,没有查询集 |

向量维度在建表时固定。距离度量和 embedding 的归一化方式必须成套使用,不能只改其中一处。

最小数据模型

以下 schema 适合作为知识库起点。字段按业务增删,但 id、content、embedding 三个核心字段不要省。

CREATE TABLE articles (
  id        BIGINT PRIMARY KEY,
  title     VARCHAR(512),
  content   TEXT,
  embedding VECTOR(384),
  author_id BIGINT,
  created_at DATETIME,
  FULLTEXT INDEX idx_fts (content) WITH PARSER ik,
  VECTOR INDEX idx_vec (embedding)
    WITH (DISTANCE=l2, TYPE=hnsw, LIB=vsag)
) ORGANIZATION = HEAP;

实施要点:

  • VECTOR(384) 中的 384 必须等于实际 embedding 维度。
  • FULLTEXT ... WITH PARSER ik 依赖实例可用的分词器。建表失败时先确认分词器是否已启用;不要假设所有部署都带 ik。
  • 向量索引的 DISTANCE 与查询函数必须一致。l2 索引配 l2_distance。
  • 文档 ID 必须稳定。不要把数据库自增 ID 当作唯一的业务定位依据,除非写入和查询共用同一映射。

写入与切片

  1. 先清洗文本,再去重;重复文档会挤占 Top-K。
  2. 切片要保留可独立回答问题的语义,并记录 doc_id、chunk_index、标题、来源和权限字段。
  3. 批量写入,避免一行一次往返。
  4. embedding 在写入前完成,确认维度、类型和空值处理一致。
  5. 写入后立即抽一条新文档做检索,确认索引刷新和可见性符合预期。

如果业务要求“写入即可查”,不要用离线重建索引的思路设计;seekdb 的增量索引链路可以让新写入尽快可检索,但仍应实测写入到可查的延迟。

混合检索

纯向量检索

SELECT id, title,
       l2_distance(embedding, '[0.12, 0.34, ...]') AS dist
FROM articles
ORDER BY dist APPROXIMATE
LIMIT 10;

向量 + 标量过滤

SELECT id, title,
       l2_distance(embedding, '[0.12, 0.34, ...]') AS dist
FROM articles
WHERE author_id = 42
  AND created_at > '2026-01-01'
ORDER BY dist APPROXIMATE
LIMIT 10;

向量 + 全文 + 标量

SELECT id, title,
       l2_distance(embedding, '[0.12, 0.34, ...]') AS dist
FROM articles
WHERE MATCH(content) AGAINST('quarterly report')
  AND author_id = 42
ORDER BY dist APPROXIMATE
LIMIT 10;

使用规则:

  • 近似向量排序使用 ORDER BY <distance> APPROXIMATE,不要漏掉 APPROXIMATE 后再把结果当成 HNSW 性能。
  • 先固定召回质量,再比较延迟。不能通过缩小候选集、降低 K 或跳过过滤条件来“优化”。
  • 过滤条件的选择性会显著影响结果。分别测试“无过滤、宽过滤、窄过滤”,不要只测最好的一条。
  • 需要同时比较语义和关键词时,优先让 seekdb 在同一查询里完成向量与全文条件,而不是客户端各查一次再手工拼结果。

HNSW 起点与调参

可以把以下值当作基线,而不是通用最优值:

M = 16
ef_construction = 256
ef_search = 200

调参顺序:

  1. 先用基线测 Recall@K 和 P95 延迟。
  2. 召回不足时,优先提高 ef_search,观察延迟代价。
  3. 构建质量不足时,再评估提高 ef_construction;它会增加构建成本。
  4. M 影响图连接密度和内存占用,不要和 ef_search 一起盲调。
  5. 每次只改一个参数,记录数据量、维度、过滤条件和构建时间。

如果数据量、维度或过滤分布发生变化,旧参数不能直接复用。

质量与性能验证

至少准备 100 条真实查询;数据量很小或上线风险高时,用 300 到 500 条更稳。

  1. 用精确近邻或离线暴力计算生成 Top-K 真值。
  2. 对同一批查询计算 Recall@K = 命中真值的数量 / K。
  3. 分别记录无过滤、宽过滤、窄过滤下的召回率和延迟。
  4. 记录 P50、P95、QPS 和数据规模。
  5. 重复运行,保留原始结果,不只报最快一次。
  6. 每次改 schema、索引参数、切片策略或 embedding 模型后重新跑同一套查询集。

最低验收组合:

| 指标 | 作用 | | --- | --- | | Recall@K | 证明没有为了速度牺牲检索质量 | | P95 延迟 | 比平均值更能暴露尾延迟 | | 过滤后召回 | 暴露条件下推或候选集缩小的问题 | | 写入到可查延迟 | 证明增量写入和索引刷新符合业务 | | 索引构建耗时 | 评估重建和扩容成本 |

常见故障

| 现象 | 优先检查 | | --- | --- | | 插入报维度错误 | VECTOR(n)、模型输出维度、空向量 | | 检索结果完全不相关 | embedding 是否同一模型、是否归一化、距离度量是否一致 | | 有数据但查不到 | 写入是否提交、索引是否刷新、过滤字段值是否正确 | | 过滤后结果变少且质量骤降 | 过滤条件选择性、候选集是否足够、执行计划是否下推 | | 延迟远高于预期 | 是否误用了非近似排序、Top-K 是否过大、是否每次重建连接 | | 建表失败 | ik 分词器是否可用、索引语法版本是否匹配 | | 召回率虚高 | 查询集与文档重叠、真值集被泄漏、只测了简单查询 |

排查时先保留最小复现:同一批向量、同一查询、同一索引参数。不要同时改模型、切片和索引。

交付检查

提交或上线前确认:

  • [ ] 部署方式、seekdb 版本和连接方式已写明。
  • [ ] embedding 模型、维度、归一化方式和距离度量已写明。
  • [ ] 建表 SQL、索引参数和分词器配置可复现。
  • [ ] 有至少一组真实查询和 Recall@K 结果。
  • [ ] 有无过滤、宽过滤、窄过滤三组延迟数据。
  • [ ] 有写入后立即可查的验证记录。
  • [ ] 已说明 CPU/内存、数据量、Top-K 和测试环境。
  • [ ] 没有通过减少数据、降低 K 或删除过滤条件来制造性能提升。

参考

  • seekdb 仓库:https://github.com/oceanbase/seekdb
  • seekdb 文档:https://docs.seekdb.ai/
  • pyseekdb:https://github.com/oceanbase/pyseekdb
<!-- OB-S:WU4yGbsHlca4tASyaGMHog -->