为什么我在向量搜索之上添加了 LLM 解析器(以及它带来的变化)

发布: (2026年3月9日 GMT+8 19:27)
8 分钟阅读
原文: Dev.to

Source: Dev.to

我曾以为向量搜索已经足够

我创建了 Queryra – 一个用于 WooCommerce 和 Shopify 的 AI‑search 插件。
它用语义嵌入取代了关键词匹配,这样用户就可以输入类似 “冬天的保暖衣物” 的描述,立即找到毛衣、抓绒外套、毯子等。零结果的查询变得很少。它运行良好……直到有人搜索:

“$80 以下的无线耳机,且不是 Beats”

向量搜索返回了无线耳机,但其中很多价格在 $200,且有几款是 Beats。价格上限和品牌排除在嵌入模型中完全不可见。

这时我意识到:向量搜索只是第 1 层。我遗漏了第 2 层。

纯向量搜索的问题

嵌入擅长一件事:编码 语义相似度

  • “Sneakers” 与 “trainers” 和 “running shoes” 距离很近。
  • “Gift for dad” 能找到园艺工具、烧烤套装、手表——即使查询中没有这些词。

但像 “laptop under $1000 for video editing, not Chromebook” 这样的查询包含两种根本不同的信息类型:

类型描述
语义意图客户想要的东西(用于视频工作的强大笔记本电脑)
结构约束如何过滤结果(价格上限、类别排除)

嵌入能够很好地处理 语义意图,但它们 没有处理结构约束的机制。你无法把 “under $1000” 编码为向量空间中的方向,而 “not Chromebook” 也不是语义概念——它是对搜索系统的指令。所有仅使用向量的实现都有这个盲点,且随着查询变得更具体,这一问题会愈发严重。

谁受影响最大? 意图最高的买家——那些准备立即购买的用户。

解决方案:LLM 解析器作为第二层

我添加了一个 查询解析器,它在向量搜索 之前 运行。它的工作是将查询分解为结构化组件。

示例

{
  "semantic_query": "organic shampoo",
  "price_max": 25,
  "attribute_exclude": ["sulfates"],
  "sort_by": "rating"
}

然后,每个组件会被发送到相应的子系统:

组件目标目的
semantic_query向量搜索查找语义相关的产品
price_max数据库过滤硬性上限 $25
attribute_exclude后置过滤移除含硫酸盐的产品
sort_by结果重新排序优先展示评分最高的

向量层发现 客户的意图;解析器层则执行 客户的要求

绕过问题(延迟)

解析器会增加约 700–800 ms 的延迟。对于像 “blue t‑shirt” 这样的简单查询,这种开销是没有必要的,因为仅使用嵌入就能很好地处理。

路由快捷方式

import re

def should_parse(query: str) -> bool:
    # Price signals
    if re.search(r'under \$|below \$|\$\d+|budget|cheap|premium', query, re.I):
        return True
    # Exclusion signals
    if re.search(r'\bnot\b|\bwithout\b|\bno\b|\bexclude\b', query, re.I):
        return True
    # Sorting signals
    if re.search(r'best rated|top rated|newest|cheapest|most popular', query, re.I):
        return True
    # Brand signals (capitalized words that aren't at sentence start)
    if re.search(r'(?<!^)(?<!\. )[A-Z][a-z]+(?:\s[A-Z][a-z]+)*', query):
        return True

    return False   # Simple query — go straight to vector search

简单查询会直接跳过解析器。 复杂查询则进行完整的意图提取。路由对用户是透明的——他们只会得到更好的结果。

变更内容

查询之前(仅向量)之后(向量 + 解析器)
“$80 以下的耳机”所有耳机仅 $80 以下的耳机
“非 BrandX 品牌”包含 BrandX排除 BrandX
“评分最高的咖啡机”随机顺序按评分排序
“有机且不含硫酸盐”所有有机洗发水过滤后的无硫酸盐

每个表格的第一行都是相同的——简单的语义查询的效果相同。其余每一行展示了解析器填补的差距。

一个意想不到的好处:拼写错误 + 约束

我原本期望解析器能帮助处理结构化查询,但它还解决了一个次要问题:拼写错误与约束的组合

  • 向量搜索 本身对拼写错误的容忍度很高——“moisturiser” 能找到 “moisturizer”。
  • “moisturiser under $20 without pareban”(parabens 拼写错误)导致排除失效,因为在拼写错误的词上嵌入相似度下降。

LLM 解析器一次性处理两者:它纠正拼写错误,提取价格约束,并识别排除条件。这种综合的鲁棒性让人惊喜。

权衡

解析器会产生费用,因为它在处理复杂查询时会进行一次 LLM API 调用。我使用 gpt‑4.1‑nano(在此用例下与 gpt‑4o‑mini 质量相同,成本约低 33 %)。通过绕过逻辑,只有一小部分查询会进入解析器,但费用仍随流量而增长。

  • 自托管选项:将 API 调用替换为本地模型(例如,Ollama + Mistral 7B 在意图抽取方面表现相当不错)。
  • SaaS 产品:将 LLM 使用量计入定价。

接下来会怎样

解析器目前能够提取:

  • 价格区间
  • 品牌引用
  • 属性过滤和排除
  • 排序偏好
  • 基本否定

接下来要做的: 多意图查询
示例:“适合办公室的东西和适合健身房的东西”——两个独立的语义搜索,结果合并。仅靠向量搜索无法拆分意图;解析器可以。

如果你正在构建电商搜索并遇到同样的瓶颈——向量结果会忽略前两个有意义的词之后的所有内容——这种两层方法值得增加的复杂性。

我为店主写了一个更长的、非技术性的版本,见此处:Why Vector Search Alone Isn’t Enough for Ecommerce Stores

乐意回答问题!

Queryra 是针对 WooCommerce 和 Shopify 的 AI 搜索。queryra.com

0 浏览
Back to Blog

相关文章

阅读更多 »

本周你的目标是什么? #169

这里是美国的 Spring Break,我们刚把时钟拨快了一小时。我们失去了一小时,并且在讨论是否应该取消 daylight 时会失去更多时间……