为什么我停止使用语义嵌入进行工具选择并切换回BM25 [D]

Reddit r/MachineLearning 工具

摘要

作者分享了他们在agent中从语义嵌入切换到BM25进行工具选择的经验,发现在200个查询-工具对的数据集上,BM25的Top-1准确率达到81%,而嵌入只有64%,因为工具描述简短且关键词驱动,不像文档那样语义丰富。

我构建agent大约一年了,最近为客户部署了一个在高峰期运行约140个MCP暴露工具的系统。一路上我犯了一个经典错误——使用工具描述嵌入的余弦相似度来选择每轮模型可以看到的工具。在演示中表现完美,在生产中却存在风险。问题在于:在基本的语义排序设置中,你嵌入用户查询,预先嵌入每个工具描述,然后在运行时按余弦相似度排序。这适用于一般的文档检索,其中段落长度、语义丰富且形式大致相同。但工具描述并非如此——它们简短(通常少于50个词符)、结构相似(动词-名词、参数列表),且判别信息往往只是一个关键词。例如,「从磁盘读取文件」和「从频道读取消息」的嵌入都接近「读取」+「文件/频道」。对于「读取最新的提交」这样的查询,余弦相似度将它们放在一起,因为三个词共享动词嵌入空间,而实际的判别词(名词「提交」)被稀释了。我在评估中看到了这一点:要求agent「列出此仓库的开放议题」,语义排序器首先返回了`slack_search_messages`,因为其描述中包含「列出」「开放」「议题」作为接近的嵌入邻居。而实际的`github_list_issues`工具排在第四,因为GitHub MCP作者写了简短描述「列出仓库中的议题」,在每个模糊关键词上得分都更低。如果模型先看到`slack_search_messages`再看到`github_list_issues`,它经常会选错。 所以我构建了三种检索策略,并在200个查询→正确工具对的固定语料库上进行了测试。 **语义嵌入(text-embedding-3-small)**:Top-1准确率64%。隐蔽的失败模式:出错时信心十足,常常将完全不相关的工具排在第一。 **BM25(基于工具名称+描述+模式遍历的纯文本投影)**:Top-1准确率81%。失败几乎全是词法问题(工具用「fetch」而用户说「get」),通过轻量查询重写即可恢复。 **混合(0.7语义+0.3BM25归一化)**:78%。比单纯BM25差。语义噪声拖累了BM25的干净信号。 我对着这个结果思考了一段时间。明显的答案是混合——自2023年以来每篇RAG论文都说混合更好。但针对工具选择,混合输了。原因是工具存在于比文档更小、更结构化的空间里。判别信号是关键词形状的,而BM25正是为此设计。 另一个学到的是:索引模式字段很重要。BM25的干净胜利来自于投影「名称+描述+遍历input_schema和output_schema(仅语义词符,去除JSON Schema结构)」。属性名如`repo_id`或`branch`正是将「列出开放议题」命中GitHub而非Slack的判别器。如果只索引「名称+描述」就浪费了一半信号。 我最终采用了Ratel的索引方法(他们的ADR-0004记录了精确的投影),因为自己重建是多余的。开源、进程内Rust、通过NAPI-RS绑定到TS SDK、无需基础设施。语义+重排序的故事在他们的路线图上,但目前BM75-only的默认设置正是我想要的。如果有人想尝试,我很乐意在评论中分享。 给构建工具选择或agent网关的人的建议:不要假设文档RAG的默认设置可迁移。工具是不同形状的数据。BM25不是无聊的备选——对于这个问题它是正确的主要方案,语义只是可选的补充。在你的具体语料库上测试,然后再诉诸嵌入。
查看原文

相似文章

介绍上下文检索

Anthropic Engineering

Anthropic 推出了上下文检索,这是一种结合了上下文嵌入和 BM25 的技术,通过减少检索失败的情况,显著提高了 RAG 的准确性。