在 Cloudflare Workers 上用 Haiku 做查询翻译和图像识别,实际比 Sonnet 更适合短文本任务且成本更低;关键教训是向 LLM 请求「翻译」不如请求「提取2-3个关键词」来匹配电商关键词系统。
我在运营 OneFindMe,一个面向 AliExpress 的 AI 商品搜索前端。你可以用 12 种语言中的任何一种用自然语言描述你想要的东西,也可以上传一张照片,它会返回商品、相似的商品以及更便宜的替代品。整个应用完全运行在 Cloudflare Worker 上,由 LLM 完成语言处理工作。
这不是一篇发布公告。本文要讲的是三个真正棘手的问题、我最初采用的错误解决方案,以及真正有效的修复方法。如果你在市场搜索 API 前置一个 LLM,三个问题你都会遇到。
整个 API(搜索、翻译、图像理解、缓存)都跑在 Cloudflare Workers 上。
用 Claude Haiku 做查询翻译和图像识别。我也试过 Sonnet,但在这个任务上——短商品名称和图像标签——Haiku 实际上更合适,而且每次搜索都是 LLM 调用,Haiku 便宜得多。
用 Workers KV 作为缓存层。
前端静态多语言部分放在 Cloudflare Pages 上。
核心流程是:接收任意语言的自然语言查询 → 转换为一个干净的市场搜索词 → 调用联盟搜索 API → 排序过滤 → 返回。出问题的地方都在"转换为一个干净的搜索词"这一步。
第一个版本让模型"把这个购物查询翻译成英文"。它做到了——漂亮、流畅、然而毫无用处。
一个用户搜索 שמלת ערב(晚礼服),得到的是一款优雅的正式晚礼服,适合晚间场合。语法上完美无缺。但市场几乎搜不到任何结果,因为没有卖家会用流畅的散文来命名商品。市场卖家写的是 Women Elegant Evening Party Dress Sexy Backless——关键词堆砌,不是句子。
修复方法是停止要求翻译,开始要求卖家会在标题中使用的 2-3 个词的词组。提示词从"翻译"改成了"返回 AliExpress 卖家会用的短商品名称"。流畅是敌人;模型生成自然语言的本能恰恰与关键词搜索索引的需求相反。
经验:当 LLM 给关键词系统提供输入时,你不需要它最好的语言。你需要的是目标索引的语言。要明确地为它提示那种语言。
为了收窄结果,我让模型在关键词之外再建议一个 AliExpress 类目 ID。带类目约束的搜索返回结果更干净——前提是 ID 真实存在。
模型会自信地返回根本不存在的类目 ID。不经常,但足够频繁。而一个不存在的类目 ID 不会报错——它返回空结果或垃圾结果集,然后悄无声息地替换掉同一查询本可以得到的、纯关键词的优质结果。幻觉出来的约束悄悄击败了诚实的备选方案。
两件事解决了这个问题。首先,一个硬白名单:模型提议的类目 ID 要经过已知有效 ID 映射表检查,不认识的就丢弃。其次,更重要的是,关键词搜索始终要运行;类目只是一个可选的 refinement 层叠在上面,永远不是替代品。如果类目路径没有返回结果,关键词结果仍然在那里。
经验:永远不要让模型的可选 enrichment 悄无声息地覆盖你的确定性基线。要分层、要 gating、要让基线默认胜出。
未缓存的搜索要做真实的工作:LLM 调用来构建查询、市场 API 往返、排序、过滤。冷启动下需要 6-8 秒。用户不会等 6-8 秒。弹跳率最大的驱动因素不是相关性——而是首次搜索的延迟。
缓存帮助巨大:每条搜索结果都在 KV 中缓存最多 30 天,所以热搜索大约 200 ms 返回。但你无法缓存一个还没人搜过的查询,而且第一个搜这个词的人要付完全部成本。
两个动作把感知等待时间降到了几乎为零,而无需让搜索实际变快:
即时畅销榜。搜索一开始,UI 就显示该类目已知有效的畅销结果,而真正的搜索在后台运行。屏幕永远不会空;真实结果就绪后自动替换进来。
对未知词自我预热。当一个新关键词首次被翻译时,翻译结果被保存,搜索被预缓存,所以下一个人搜这个词——几乎总有下一个人——走的是 200 ms 的热路径。
两者都没有让冷路径变快。两者都让它变得不可见。区分——优化感知延迟而非实际延迟——移动了那个比任何相关性调优都更重要的指标。
经验:在搜索产品上,加载中的空状态是一个功能,不是缺口。立即展示一些东西,然后回填。
一个微妙的坑,因为它看起来像成功:不要孤立地信任市场自己的"这个商品是否有货"信号。联盟 API 会把真实可购买的商品报告为已下架。单独依赖它过滤会悄无声息地清空本应满满的搜索结果页。库存状态需要佐证,不是一个布尔值——和幻觉类目 ID 一样的教训,只是换了身衣服:单一不可靠信号不应该被允许把一个好的结果集清零。
为目标系统的语言提示,而不是用户的语言。关键词索引要的是关键词,不是散文。
模型的 optional 输出永远不能覆盖你的确定性路径。要 gating 住它、要用已知有效值校验它、要分层、要让基线胜出。
先优化感知延迟。瞬间的部分结果永远比更快的加载动画更优。
一个信号永远不应该清空结果集。过滤到空之前要先佐证。
这个引擎现在跑着 12 种语言,上面每一个 bug 在每种语言里都一模一样地出现了。如果你在构建任何在人类句子和结构化搜索索引之间放 LLM 的东西,你会遇到全部四个问题。欢迎在评论区交流经验——尤其是如果你找到了比"显示畅销榜然后祈祷"更好的冷搜索解决方案的话。
我在构建 OneFindMe——通过文本或图像在 12 种语言中进行 AliExpress 商品搜索的 AI。它是免费的;它通过联盟佣金运行,对买家不额外收费。