给知识库做中文检索时,一个常见的岔路是:向量检索用一套组件,全文检索再用一套组件,最后还得想办法把结果拼起来。其实在 PostgreSQL 里,pgvector 加一个中文分词扩展,就能在一条栈上同时拿到两路检索。
这篇文章讲清楚这条路径,以及为什么它比「向量库 + 搜索引擎」的组合更省事。
中文全文检索的坑
PostgreSQL 自带的全文检索能力是完整的,但默认解析器按空格和标点切词。中文没有空格,于是一整段话会被当成一个词,倒排索引形同虚设。
解法是换一个能分词的中文解析器。zhparser 是 PostgreSQL 的中文分词扩展,基于 SCWS(简易中文分词系统)实现。装上它并配置好解析器后,「中文混合检索」才能被切成「中文 / 混合 / 检索」这样的词项。
pgvector 管向量这一路
pgvector 给 PostgreSQL 增加向量类型和相似度索引。它支持 vector、halfvec(半精度,省一半存储)等类型,索引有 HNSW 和 IVFFlat 两种。
在知识库场景里,halfvec 加 HNSW 是常用的组合:召回质量基本不受影响,存储占用明显下降,而且向量和业务数据在同一个事务里,不需要跨系统同步。
一张表同时存向量与全文
关键是把两路检索落在同一张表、同一行上。每一行同时保存:
- 用于向量检索的
halfvec列,建 HNSW 索引; - 用于全文检索的
tsvector列,用 zhparser 的配置生成。
查询时分别走两条路,得到两个带排名的结果集,再用 RRF 合并。RRF 是确定性融合(1 / (k + rank)),不需要训练或调权重。
为什么是 PG 而不是专用组件
我的判断是:当知识库本身就以 PostgreSQL 为事实来源时,把向量和全文都放在同一库里,比「pgvector + 独立的 Elasticsearch / 专用向量库」更划算。理由有三:
- 一套事务:文档、版本、向量、全文索引在同一份存储里一致更新,不存在跨系统的一致性问题。
- 一套运维:少一个要部署、监控、升级的组件。
- 可追溯:检索命中的 chunk 能直接关联到同一行上的源文档、版本与位置信息,排障时不用跨系统拼证据。
这不是说专用向量库不好。数据量巨大、或检索延迟要求极苛刻时,专用组件有它的位置。但对大多数知识库,单库方案先把复杂度降下来,往往就够了。
落地入口
琅嬛就是按这条路径实现的:pgvector(halfvec + HNSW)+ PostgreSQL 全文检索(zhparser)+ 确定性 RRF,每个 chunk 带回来源锚点。表结构、索引与检索链可以在 琅嬛仓库 的 架构文档 里核对。
自 v1.0.0 起,琅嬛还提供一条零配置单机路径:SQLite + sqlite-vec 向量 + FTS5 全文 + gse 中文分词,无需 PostgreSQL 也能跑,适合本地开发与演示——上面说的「单库方案」依然成立,只是把数据库换成了 SQLite。
如果你已经在用 PostgreSQL,中文混合检索未必需要再引入一套新组件——先看看 pgvector + zhparser 能不能解决。