seekdb 混合检索知识库(Hybrid RAG)
用 seekdb 一个数据库同时存放「原文 + 向量 + 全文索引」,一条 SQL 完成语义召回、关键词召回与 RRF 融合排序。
不需要额外部署向量库/搜索引擎,pip install seekdb 后即为嵌入式模式,数据库就是一个目录。
什么时候用
- 给 Agent / RAG 应用做本地知识库或长期记忆,要求离线可用、无需运维。
- 检索同时需要语义相似(近义词、改写)和精确关键词(错误码、产品名、专有名词)。
- 需要
WHERE标量过滤(租户、时间、类型)和检索结果在同一事务/同一张表里。 - 不适用:千万级以上向量 + 多副本高可用集群(那需要服务器模式与专门运维)。
前置条件
pip install seekdb # 官方嵌入式 Python 包(wheel 内置 seekdb 内核,约 54MB)
- 支持 Linux x86_64 / macOS arm64;CPython 3.11+(wheel 提供 cp311 与 cp312-abi3)。
- 嵌入式模式默认数据目录
./seekdb.db,启动毫秒级,不需要端口和账号。 - 需要真实语义向量时,再准备一个 OpenAI 兼容的
/embeddings服务(本地 vLLM、ModelScope/百炼等平台均可), 通过环境变量注入:EMBEDDING_API_BASE、EMBEDDING_API_KEY、EMBEDDING_MODEL。
快速开始(3 步)
# 0) 准备知识库目录(.md / .txt);包内已带 5 篇示例文档 assets/demo-docs/ 可直接跑
# 1) 初始化(dim 必须与嵌入模型维度一致;演示用 256)
python scripts/seekdb_kb.py init --db ./kb --dim 256
# 2) 建索引:切块 → 嵌入 → 写入(向量索引 + 全文索引随表创建)
python scripts/seekdb_kb.py ingest --db ./kb --docs assets/demo-docs --dim 256 --embed hash
# 3) 混合检索
python scripts/seekdb_kb.py search --db ./kb --query "混合检索 RRF 怎么融合" --mode hybrid --topk 5
输出为 JSON:hits[] 含 id / title / content / _score / _keyword_score / _semantic_score,
_score 是 RRF 融合分(sum(1/(rank_constant+rank))),越大越相关。
把
--embed hash换成--embed api即使用真实嵌入模型(同一批文档与查询必须用同一个模型和维度)。
工作流
- 确认知识源:一个目录下的
.md/.txt;单文件也行。中文文档无需预处理,全文索引用ngram分词器。 - 定维度:
--dim与嵌入模型输出维度一致(如 1024)。向量列类型vector(DIM)固定,改维度请换数据库目录或删表。 - 建库 + 入库:脚本按
--chunk(默认 400 字符,按字符切,适合中英混合)与--overlap切块; 同一文件重复入库会先DELETE旧块,可反复增量更新。 - 检索:三选一——
--mode hybrid(默认,推荐):向量 + 全文两路召回,RRF 融合;--mode vector:只要语义相似;--mode keyword:只要关键词命中(BM25 相关度排序)。
- 接生成:把
hits[].content按 token 预算拼进提示词,交给大模型回答;需要时再加AI_RERANK/外部 rerank 精排。
三路召回怎么选
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 自然语言提问、同义改写多 | hybrid | 语义召回兜底,关键词召回补精度 |
| 精确名词、错误码、编号、版本号 | hybrid 或 keyword | 全文索引精确命中,向量模型不稳定 |
| 文档语义相似度排序 / 聚类预览 | vector | HNSW 余弦距离快且直观 |
| 关键词必须命中(强约束) | keyword + WHERE | BM25 打分 + 标量过滤 |
直接写 SQL(不经过脚本)
嵌入式模式下用 Python 直连即可(完整语法见 references/sql-cheatsheet.md):
import seekdb
inst = seekdb.open("./kb") # 打开数据库目录
conn = seekdb.connect("test")
cur = conn.cursor()
cur.execute("select id, title from kb_chunks "
"order by cosine_distance(embedding, '[...]') approximate limit 5")
conn.close(); inst.close()
混合检索用 JSON DSL:
SET @p = '{"query":{"match":{"content":"混合检索"}},
"knn":{"field":"embedding","k":10,"query_vector":[...]},
"rank":{"rrf":{"rank_constant":60,"rank_window_size":20}},
"_source":["id","title","_keyword_score","_semantic_score"],"size":5}';
SELECT json_pretty(DBMS_HYBRID_SEARCH.SEARCH('kb_chunks', @p));
已验证的行为(seekdb 1.4 嵌入式实测)
- 建表:
vector vector(N)+vector index ... with (distance=cosine, type=hnsw, lib=vsag)+fulltext ... with parser ngram同时存在,DDL 一次成功。 - 写入:向量按字符串字面量
'[0.12,0.34,...]'插入,select count(*)正常。 - 纯向量查询
ORDER BY cosine_distance(...) APPROXIMATE LIMIT k返回结果,距离单调。 - 纯全文查询
MATCH(content) AGAINST('关键词')命中并给出 BM25 分数。 - 混合查询
DBMS_HYBRID_SEARCH.SEARCH返回_score/_keyword_score/_semantic_score,RRF 融合排序正确。 DBMS_HYBRID_SEARCH.GET_SQL可看到生成的 SQL(调优/排错时很有用)。
常见坑(都踩过)
- 混合检索表不能显式建主键:
id int primary key会报table with user provided primary key not supported (1235),改成普通列。 - 向量排序必须直接在
ORDER BY里写距离函数:order by round(cosine_distance(...),4)会报Using vector index without vector_sort_expr is not supported (1235);不要在排序表达式外面套函数或改用别名。 MATCH的列集合必须与某个全文索引的列完全一致:索引建在(title, content)上就MATCH(title, content); 只建了复合索引时不要MATCH(title);本 Skill 的做法是把标题拼进content,只建fulltext kb_ft(content)。- 分词器只有
space / ngram / ik / beng:WITH PARSER simple会报Function simple is not defined。中文用ngram(最稳)或ik。 - 服务器模式建大向量索引前先调
ob_vector_memory_limit_percentage(嵌入式默认可用,无需设置)。 - 同进程重复
seekdb.open(同一个目录)会复用实例;进程退出前调用inst.close()释放锁。
目录结构
seekdb-hybrid-rag/
├── SKILL.md # 本文件
├── scripts/seekdb_kb.py # 单文件 CLI:init / ingest / search / stats
├── references/sql-cheatsheet.md # 实测可用的 seekdb 向量/全文/混合检索 SQL
├── references/troubleshooting.md# 报错码与修复
├── examples/demo.md # 端到端跑通示例(含真实输出)
├── assets/demo-docs/ # 5 篇示例文档,可直接复现示例
└── LICENSE # Apache-2.0
生产化建议
- 嵌入质量决定上限:
hash后端只用于冒烟与离线降级;正式使用请换真实嵌入模型(--embed api)。 - 切块 400~800 字符、overlap 10%~20% 起步;长文先用标题/段落切分再按长度兜底。
- 融合不一定优于单路:先用
hybrid,再用评测集(Recall@K / MRR)对比vector、keyword,按数据分布定rank_constant(常用 60)。 - 需要更强精度时把候选集交给 rerank(seekdb 的
AI_RERANK或外部 rerank 服务),召回阶段只负责覆盖率。 - 数据量增长后:
analyze table kb_chunks+optimize类维护,必要时换服务器模式与专用向量索引参数。
Scan to join WeChat group