mcpField
P
postgres-semantic-search

postgres-semantic-search

github.com/laguagu数据与数据库70218 次安装2 天前更新
代码仓库

关于 postgres-semantic-search

PostgreSQL Semantic Search 是用于设计和评估 Postgres 搜索的技能与 SQL 构件,面向选择向量、关键词、混合检索及重排方案的开发者,并着重说明评估方法和不报错的失效情形。

主要功能

  1. 01向量与关键词检索指南
  2. 02使用 RRF 的混合检索
  3. 03检索用 SQL 函数
  4. 04评估与调优实践
  5. 05pgvector 索引建议
  6. 06重排方案权衡

适用场景

  1. 01构建文档语义搜索
  2. 02组合关键词与向量检索
  3. 03诊断结果缺失或质量不佳
  4. 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.md
hybrid-search.md
keyword-search.md
pgvector.md
reranking.md
scripts/
fuzzy_search.sql
hybrid_search_bm25.sql
hybrid_search_fts.sql
indexes.sql
prefix_tsquery.sql
semantic_search.sql
setup.sql

详细介绍

PostgreSQL 语义搜索

关于基于 Postgres 构建搜索的决策、实测结果和隐蔽故障模式,以及经过测试的 SQL 构建模块,见 scripts/。官方文档已充分说明的语法就留给它们;这里讨论的是那些出错时不会报错的问题。

构建顺序

  1. 了解用户实际输入的内容:标识符和代码、一到三个词的术语、完整问题,以及使用哪些语言。这决定了需要哪些检索分支。
  2. 从适合这些查询的最简单检索方式开始:精确/关键词查找或向量搜索。使用近似向量搜索时,保留精确向量搜索作为召回率基准。
  3. 调优前,先构建两组评测集:长问题和短术语(evaluation.md)。
  4. 仅在新增检索分支能提升需要它的查询的基准表现时,才加入该分支并用 RRF 融合(hybrid-search.md)。
  5. 最后再对高质量候选列表的前列结果使用重排器(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 显示顺序扫描, 调整参数也无济于事,而且顺序扫描可能是更快的方案。
  • 将距离阈值放在 MATERIALIZED CTE 外部,其他筛选条件放在 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.sqlmatch_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.sqlhybrid_search_fts(query_embedding, query_text, match_count, rrf_k, fts_language, vector_weight, keyword_weight);任一输入都可以为 NULL
hybrid_search_bm25.sqlhybrid_search_bm25(...)、hybrid_search_chunks_bm25(...):参数相同,但不含 fts_language;需要 pg_search
fuzzy_search.sqlfuzzy_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.sqlprefix_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

评论

支持 Markdown