这篇适合谁
适合已经在用 PostgreSQL、想给商品库加自然语言搜索,并把它交给 AI 智能体调用的开发者。读完你应该能顺着链路从生成向量走到向量查询,并知道先检查哪些参数。
为什么要做语义搜索
关键词搜索理解不了意图。比如「雨天穿的舒适跑鞋」,如果商品描述里没有这些字,关键词搜索就找不到。语义搜索把文本转成向量,按相似度给商品排序[1]。
准备什么
- 一个 Azure OpenAI 的 embedding 部署。示例用的是 text-embedding-3-small,输出 1536 维向量[1]。
- 带 pgvector 扩展的 PostgreSQL[1][2]。
- Redis,可选,用来做缓存[1]。
- 两个环境变量:
PROJECT_ENDPOINT和EMBEDDING_DEPLOYMENT_NAME,分别对应端点和部署名[1]。
第一步:生成向量
嵌入管理器里放着主要参数。示例使用 1536 维的向量,单次请求最多 8000 个 token,批大小为 100[1]:
self.embedding_dimension = 1536 # text-embedding-3-small dimension
self.max_tokens = 8000 # Maximum tokens per request
self.batch_size = 100 # Batch processing size没有命中缓存的文本按每批 100 条发送,批与批之间停 0.1 秒。缓存的键是部署名和文本拼接后的 SHA-256 哈希,缓存 24 小时[1]。示例设定的速率限制是每分钟 1000 次请求、15 万个 token[1]。
有两处要先检查。代码里用了 os.getenv 读配置,但没有导入 os,需要自己补上这一行[1]。端点和部署名则从 PROJECT_ENDPOINT 和 EMBEDDING_DEPLOYMENT_NAME 读取[1]。
第二步:为每个商品拼出待嵌入的文本
商品嵌入器用一个模板把字段拼成一段文字,模板是 {product_name} {brand} {description} {category} {tags},然后压缩多余的空白[1]:
self.text_template = "{product_name} {brand} {description} {category} {tags}"向量写入 retail.product_embeddings 表,按商品 ID 和嵌入模型做 upsert[1]。默认只为还没有向量、或者向量早于商品 updated_at 的商品重新生成。要全部重建,就把 force_regenerate 设为真[1]。
第三步:按余弦距离查询
查询文本先转成向量。向量以 [x,y,...] 的文本形式传给 PostgreSQL,再用 ::vector 转换类型[1]:
embedding_vector = f"[{','.join(map(str, query_embedding))}]"查询的主体部分如下。它按店铺和上架状态过滤,应用相似度阈值,按距离排序并限制行数[1]:
WHERE pe.store_id = $2
AND p.is_active = TRUE
AND 1 - (pe.embedding <=> $1::vector) >= $3
ORDER BY pe.embedding <=> $1::vector
LIMIT $4语义搜索工具要求传入 query 和 store_id。查询文本 1 到 500 个字符。默认返回 20 条,最多 100 条,默认相似度阈值为 0.7[1]。
第四步:混合搜索
混合搜索在语义分数之外加上关键词分数。默认权重是语义 0.7、关键词 0.3[1]:
"semantic_weight": {
"type": "number",
"description": "Weight for semantic similarity (0.0 to 1.0)",
"minimum": 0.0,
"maximum": 1.0,
"default": 0.7
},关键词部分用 PostgreSQL 的全文检索,即 to_tsvector 和 plainto_tsquery,再通过 FULL OUTER JOIN 与语义结果合并[1]。
推荐怎么做
推荐工具支持四种类型:similar_products、customer_based、trending 和 cross_sell[1]。基于客户的推荐会取该客户最近 days_back 天买过的商品向量并求平均,days_back 默认 90 天。只返回得分不低于 0.6 的相似商品[1]。
性能:索引和缓存
索引示例用的是 HNSW 索引,使用 vector_cosine_ops,参数 m = 16、ef_construction = 200[1]:
CREATE INDEX CONCURRENTLY idx_product_embeddings_vector_optimized
ON retail.product_embeddings
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 200);它还给了一些 PostgreSQL 参数示例,比如 work_mem = '256MB'、shared_buffers = '512MB'[1]。这些是起点,不是针对你机器调好的结果。
Redis 缓存默认连接 redis://localhost:6379,过期时间分别是:向量 7 天,搜索结果 1 小时,推荐结果 4 小时[1]:
self.embedding_ttl = timedelta(days=7) # Embeddings cached for 1 week
self.search_ttl = timedelta(hours=1) # Search results cached for 1 hour
self.recommendation_ttl = timedelta(hours=4) # Recommendations cached for 4 hoursRedis 连不上时,缓存管理器会打印警告,然后不带缓存继续工作[1]。
混合搜索的语义部分有一道门槛
混合查询里,语义部分只保留相似度不低于 0.5 的结果。这个门槛比单独的语义搜索工具默认的 0.7 低[1]。也就是说,混合搜索会召回更多候选,再由关键词分数和语义分数加权合并排序[1]。
缓存怎么分类,怎么清
缓存分三类,过期时间各不相同:向量 7 天、搜索结果 1 小时、推荐结果 4 小时[1]。缓存管理器还提供 invalidate_store_cache 方法,按店铺 ID 组成的模式删除相关的缓存键[1]。Redis 不可用时,缓存管理器只记录警告,并以无缓存的方式继续运行[1]。
输入约束要写进工具 schema
store_id 只能包含字母、数字、下划线和连字符[1]。查询文本限定在 1 到 500 个字符之间,返回条数限定在 1 到 100 之间,相似度阈值是 0.0 到 1.0 之间的数字[1]。把这些限制写进 schema,智能体在调用前就能按规则构造参数,出错也更早暴露。
常见问题
搜索结果为空怎么办? 默认相似度阈值是 0.7,先检查阈值,再对比几条已知相关商品的分数。后续的测试与调试内容会讲向量质量和相关性的验证[1]。
能不能直接复制代码运行? 不能原样运行。补上缺失的 os 导入,并把表名和结构改成你自己的数据库[1]。
pgvector 的文档在哪里? 在 pgvector 的 GitHub 仓库里[2]。
本文改编自 Microsoft 开源课程 *MCP for Beginners* 中的语义搜索实验[1],原文以 MIT 许可证发布。
来源
- 1Semantic Search IntegrationMicrosoft mcp-for-beginners (MIT)
- 2pgvectorpgvector project
引用本页: 用 MCP 做语义搜索:Azure OpenAI 向量加 pgvector. TellWorld, https://www.tellworld.net/zh/practice/practice-mcp-semantic-search-server. 更新于 2026-11-14