← 返回博客
David4 分钟阅读

用 pgvector + PostgreSQL 全文检索实现中文混合检索

pgvector 存向量、zhparser 做中文分词全文检索,一条 SQL 栈即可搭建可追溯的中文混合检索,不额外引入专用向量库。

pgvector全文检索PostgreSQL

给知识库做中文检索时,一个常见的岔路是:向量检索用一套组件,全文检索再用一套组件,最后还得想办法把结果拼起来。其实在 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 / 专用向量库」更划算。理由有三:

  1. 一套事务:文档、版本、向量、全文索引在同一份存储里一致更新,不存在跨系统的一致性问题。
  2. 一套运维:少一个要部署、监控、升级的组件。
  3. 可追溯:检索命中的 chunk 能直接关联到同一行上的源文档、版本与位置信息,排障时不用跨系统拼证据。

这不是说专用向量库不好。数据量巨大、或检索延迟要求极苛刻时,专用组件有它的位置。但对大多数知识库,单库方案先把复杂度降下来,往往就够了。

落地入口

琅嬛就是按这条路径实现的:pgvector(halfvec + HNSW)+ PostgreSQL 全文检索(zhparser)+ 确定性 RRF,每个 chunk 带回来源锚点。表结构、索引与检索链可以在 琅嬛仓库 的 架构文档 里核对。

自 v1.0.0 起,琅嬛还提供一条零配置单机路径:SQLite + sqlite-vec 向量 + FTS5 全文 + gse 中文分词,无需 PostgreSQL 也能跑,适合本地开发与演示——上面说的「单库方案」依然成立,只是把数据库换成了 SQLite。

如果你已经在用 PostgreSQL,中文混合检索未必需要再引入一套新组件——先看看 pgvector + zhparser 能不能解决。

Related · 延伸阅读