关于 postgres-semantic-search
PostgreSQL Semantic Search 是用于设计和评估 Postgres 搜索的技能与 SQL 构件,面向选择向量、关键词、混合检索及重排方案的开发者,并着重说明评估方法和不报错的失效情形。
主要功能
- 01向量与关键词检索指南
- 02使用 RRF 的混合检索
- 03检索用 SQL 函数
- 04评估与调优实践
- 05pgvector 索引建议
- 06重排方案权衡
适用场景
- 01构建文档语义搜索
- 02组合关键词与向量检索
- 03诊断结果缺失或质量不佳
- 04评估重排与查询变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ laguagu/ claude-code-nextjs-skills --skill postgres-semantic-search安装后在 Claude Code 中输入/postgres-semantic-search确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/evaluation.mdhybrid-search.mdkeyword-search.mdpgvector.mdreranking.mdscripts/fuzzy_search.sqlhybrid_search_bm25.sqlhybrid_search_fts.sqlindexes.sqlprefix_tsquery.sqlsemantic_search.sqlsetup.sql详细介绍
PostgreSQL 语义搜索
关于基于 Postgres 构建搜索的决策、实测结果和隐蔽故障模式,以及经过测试的 SQL 构建模块,见 scripts/。官方文档已充分说明的语法就留给它们;这里讨论的是那些出错时不会报错的问题。
构建顺序
- 了解用户实际输入的内容:标识符和代码、一到三个词的术语、完整问题,以及使用哪些语言。这决定了需要哪些检索分支。
- 从适合这些查询的最简单检索方式开始:精确/关键词查找或向量搜索。使用近似向量搜索时,保留精确向量搜索作为召回率基准。
- 调优前,先构建两组评测集:长问题和短术语(evaluation.md)。
- 仅在新增检索分支能提升需要它的查询的基准表现时,才加入该分支并用 RRF 融合(hybrid-search.md)。
- 最后再对高质量候选列表的前列结果使用重排器(reranking.md)。
一次只改一项。预先定义验收规则;默认要求受影响查询群体的提升超过实测的运行间波动,且另一组评测集没有明显损失。检查每条查询的得失,并确认达到约定的延迟和成本预算。 以下数值结果是针对特定语料的观察,并非普遍适用的性能保证。
选择
- 根据维度选择列类型,而不是根据提供方:
vector(N)最多可索引 2,000 维;最多 4,000 维可通过转换为halfvec来索引(或使用halfvec列);更高维度则使用二进制量化或 Matryoshka 截断。 - 默认选择 HNSW;当内存或构建时间限制使 HNSW 不可行时,选择 IVFFlat。行数不能决定选择:应对照精确搜索测量召回率和延迟。
- 模型每隔几个月就会变化。 根据提供方当前的文档选择嵌入和重排器模型;对于非英语文本,优先选择多语言模型,并在确定使用前针对目标语言进行评测。
- 用户既会输入问题,也会输入术语时,使用混合搜索。 在一个芬兰语转录语料库中,单独使用关键词搜索对长问题的效果较弱(首位命中率为 24 %),单独使用向量搜索对短术语的效果较弱(53 %);混合搜索在长问题上与向量搜索持平,在短术语上表现更好(分别为 53 % 和 93 %; hybrid-search.md)。
- 并非所有环境都支持 BM25:托管服务各不相同(Neon 已移除
pg_search)。结合下文的修复方法,普通 FTS 往往就足够了。
无声失败
这些情况会返回更少的行、零行或看似合理的结果,但不会报错。
pgvector(pgvector.md):
- 不启用迭代扫描时,HNSW 结果数量受
hnsw.ef_search限制(默认值为 40)。较大的LIMIT或筛选条件严格的WHERE都会悄悄返回更少的结果。启用hnsw.iterative_scan(默认关闭),或使用部分索引或分区。 - 默认的
ef_search可能会降低召回率,且不会发出警告。提高它,直到 相对于精确搜索的召回率不再变化。如果EXPLAIN显示顺序扫描, 调整参数也无济于事,而且顺序扫描可能是更快的方案。 - 将距离阈值放在
MATERIALIZEDCTE 外部,其他筛选条件放在 CTE 内部。 SET按连接生效。 使用事务池化器时,请在同一事务中使用SET LOCAL,或使用 函数级别的SET。- 相似度阈值因模型而异:为一个模型调好的阈值用于另一个模型时可能什么也返回不了。
1 - (a <#> b)并非余弦相似度。 - 客户端会弄乱 JS 数组:node-postgres 发送
{0.1,0.2},而 Drizzle 的sql模板会将数组展开为($1, $2, ...);这两种形式都会被向量输入拒绝。 请发送JSON.stringify(embedding),并用::vector转换类型。
关键词(keyword-search.md):
plainto_tsquery和websearch_to_tsquery中未加引号的普通输入会对裸词进行 AND 匹配, 因此长问题可能一个结果也匹配不到。对于普通输入检索, 可考虑使用 OR 和ts_rank_cd;保留显式网页搜索中的引号、OR 和排除词。unaccent会合并不同的词,例如芬兰语、瑞典语、德语或土耳其语中的词。 只折叠装饰性重音符号,并在文本搜索配置中进行处理。- 不需要的零宽字符导出残留可能会粘连到词元上,阻碍词干提取。请统一规范化这些字符,但不要删除有意义的 ZWNJ/ZWJ。
- ORM 迁移后,生成的 tsvector 列可能会悄悄变成普通的 NULL 列。 如果混合检索和纯向量检索返回完全相同的列表,关键词分支可能缺失或不起作用:在判断原因前,先检查 NULL/填充数量和仅关键词检索结果。
- 屈折语言中的前缀匹配必须将各项用 OR 连接,并展开带连字符的词元(prefix_tsquery.sql)。
%比较完整字符串;对于长文本中的前缀和短查询,请使用<%。
非英语文本和分块
- 使用所选嵌入模型的分词器和输入上限来限制分块大小。
不同模型和文本的比例各不相同:使用 OpenAI 的
cl100k_base(text-embedding-3)时, 芬兰语平均每个词元约 2.5 个字符,而英语为 4 个或更多,因此按英语调整的字符上限大约宽松了一倍。 端点会拒绝该分块,并且通常会导致整个批次失败。 - 非查询语言的查询可能导致词法分支不起作用,即使使用多语言嵌入模型,也会使效果降低约 12 个百分点。请将其翻译成完整句子,而非关键词列表(在一个语料库中,关键词列表的得分比不翻译低 14 个百分点),并考虑两阶段融合。
- 多词同义词扩展会以独立词语的形式进入 tsquery,其中的通用词会主导排名。请删除那些常见程度比同一短语其余部分高出一个数量级的词。
- 屈折变化:在一个芬兰语语料库中,嵌入已能处理屈折变化;词干化分支(Postgres
finnish)提升了仅关键词检索,但没有提升混合检索;词典式词形还原器在口语和领域文本上的效果不如词干提取器;复合词拆分则导致该分支结果泛滥。请在目标文本上重新测量。 前缀匹配始终无法匹配屈折查询词的原形 (keyword-search.md)。 - 转录文本:分块长度几乎不会改变找到的视频。对匹配片段中的字幕提示进行第二轮细粒度搜索,即可找到对应时刻。
详细信息和测量结果:hybrid-search.md。
重排
- 先只重排约 30 个候选项的短名单前列(前 5 个),只有当短期集合保持不变时才进一步扩大范围。交叉编码器评分的是“提到了 X”,而不是“主题是 X”,因此会让对短词的顺带提及排名靠前:在一个芬兰语转录语料库上,对全部 30 个候选项重新排序后,短期 Hit@1 从 0.93 降至 0.60–0.80;前 10 个仍会让部分模型损失一两个术语,而 6 个重排器在前 5 个中均未造成损失。请在自己的数据集上重新测量重排深度。
- 相关性问题的措辞可能比模型更重要(在一个语料库上相差约 30 个百分点)。每项标准都必须能从发送的文本中核实,并且重排器输入中应包含来源标题。
- **根据运行环境以及文本是否可以离开网络来选择。**在 2 核 CPU 上测得:约 120M 参数的多语言交叉编码器增加了约 0.3 秒延迟,而 568M–600M 参数的模型需要 10–25 秒。在数据中心 GPU 上,Qwen3-Reranker-8B 是唯一明显比该小型 CPU 模型更准确的模型(reranking.md)。托管式重排器无需基础设施,但会接收候选文本。可以先使用时有时无的 GPU 服务,并以 CPU 模型作为超时回退方案。请在目标硬件上测量 p95。
- 先测量短名单的上限:重排器无法找回第一阶段遗漏的内容。
数字、模型权衡和采用规则:reranking.md。
脚本
新建示例数据库时,请使用 setup.sql,然后使用 indexes.sql 和选定的函数文件。对于现有数据库,请将它们审阅并调整为迁移脚本:这些脚本假定使用 documents(id, title, content, metadata, embedding vector(1536)) 和 chunks(id, document_id, chunk_index, content, embedding):请在每个文件中一并更改名称和维度。部分函数文件会删除旧版签名;应用前请检查依赖对象。这些只是构建模块,并非完整的生产迁移/授权策略。所有脚本都不会设置 hnsw.ef_search;该设置由调用方负责。
| 文件 | 函数(实际参数名) |
|---|---|
| semantic_search.sql | match_documents(query_embedding, match_threshold, match_count)、match_documents_filtered(query_embedding, filter_metadata, match_threshold, match_count)、match_chunks(query_embedding, match_threshold, match_count);match_threshold 为 NULL 时 = 普通 top-k |
| hybrid_search_fts.sql | hybrid_search_fts(query_embedding, query_text, match_count, rrf_k, fts_language, vector_weight, keyword_weight);任一输入都可以为 NULL |
| hybrid_search_bm25.sql | hybrid_search_bm25(...)、hybrid_search_chunks_bm25(...):参数相同,但不含 fts_language;需要 pg_search |
| fuzzy_search.sql | fuzzy_search_trigram(query_text, similarity_threshold, max_results)、autocomplete_search(search_prefix, max_results)、hybrid_search_fuzzy_semantic(query_text, query_embedding, max_results, rrf_k) |
| prefix_tsquery.sql | prefix_tsquery(config, text [, join]) |
| setup.sql、indexes.sql | 扩展、示例表、索引 |
Supabase .rpc() 会按名称绑定参数,因此键名拼写错误会在调用时导致失败:
const { data, error } = await supabase.rpc('hybrid_search_fts', {
query_embedding: embedding, // number[]
query_text: userQuery,
match_count: 10,
fts_language: 'simple',
});
// Drizzle or node-postgres: send the vector as text and cast it
await db.execute(sql`SELECT * FROM match_documents(${JSON.stringify(embedding)}::vector, NULL, 10)`);
参考资料
- pgvector.md:类型和维度限制、HNSW 调优、规划器选择、过滤器和阈值、连接池、构建、运算符。
- keyword-search.md:tsquery 解析、重音符号和语言配置、无效关键词分支、前缀匹配、三元组、ParadeDB。
- hybrid-search.md:融合、混合搜索与向量搜索、分块、转录内容、非目标语言查询、同义词扩展。
- reranking.md:选择、深度、硬件、相关性问题、余量、采用情况。
- evaluation.md:两组评测数据、偏差、噪声,以及测量结果产生误导的四种方式。
版本
- pgvector:使用
SELECT extversion FROM pg_extension WHERE extname = 'vector'检查已安装的版本。迭代扫描需要 0.8.0+。请查看当前稳定版本:0.8.2 修复了并行 HNSW 构建中的缓冲区溢出问题,0.8.3 和 0.8.4 修复了 HNSW vacuum 损坏和错误,0.8.7 修复了 IVFFlat 构建中的缓冲区溢出问题。升级前,请阅读当前的索引修复和升级说明。版本仅以 git 标签的形式发布,因此请阅读 CHANGELOG,而不是空的 Releases 标签页。 - pg_search:0.25+ 依赖 pgvector;请先安装 pgvector。ParadeDB 的 API 变化很快;请参阅 keyword-search.md。
相关技能
| 需求 | 技能 |
|---|---|
| 通用 Postgres 模式、索引、RLS、连接池 | supabase-postgres-best-practices |
| 聊天机器人编排、会话、工具调用 | nextjs-chatbot |
| 通过 AI SDK 调用嵌入模型 | ai-sdk |