最近在折腾把公司内部的RAG知识库通过MCP暴露给Claude用,本来想着能直接让模型调工具查资料,省得每次手动喂上下文。结果实测发现,走MCP通道后,检索回来的chunk相关性明显不如之前在本地pipeline里的效果。我用的还是同一个embedding模型和向量库,只是把检索逻辑封装成了MCP tool。怀疑是MCP那边为了标准化,把一些过滤参数(比如top_k、相似度阈值)给简化掉了?或者是我对MCP的tool描述写得太简单,导致模型传参时不够精确?有没有大佬踩过类似的坑,指点一下调试方向,谢谢!
RAG接入MCP后检索质量反而下降了,是我姿势不对吗?
全部回复
共 32 条这个方向我试过,大概率不是MCP的锅,而是tool描述和参数映射的问题。模型在调用工具时,如果描述里没明确写清楚top_k和阈值的作用,它就会按自己的理解传一个通用值,比如默认取5条,但本地pipeline里你可能是按余弦相似度0.7过滤后再排序的,这俩逻辑完全不是一回事。建议你先把tool描述写得像接口文档一样细,把每个参数的范围、单位、影响都交代清楚,再在服务端把缺省值强制设成和本地一致,看看能不能找回效果。另外也可以打印一下模型实际传参的日志,对比下和本地调用的差异,基本能定位问题。
大概率是tool描述的问题,模型不知道每个参数该怎么填,就会按自己的理解瞎传,跟本地pipeline固定逻辑肯定没法比。你可以试试在描述里把top_k写成具体业务场景,比如“检索前20条最相关的,但只返回相似度大于0.7的”,让模型有明确依据。另外MCP那层如果真把阈值砍了,不如自己包装一层,把过滤逻辑留在tool内部,而不是依赖模型传参。我之前也遇到过类似情况,最后是直接给tool加了个默认参数兜底才救回来。
大概率是tool描述和参数映射的问题,MCP只是传输层,不会动你检索逻辑的。你试试把top_k和阈值写死在tool描述里,让模型只能选query,别让它自由发挥传参。另外检查下Claude调用时是不是把query自动做了改写,有时候模型会为了“理解意图”把原问题扩写,反而跟知识库里的chunk对不上。我之前也遇到过类似情况,最后是强制让模型传原始query不加任何润色才恢复效果的。
这问题我太熟了,十有八九是MCP把query预处理那层给吞了,比如本地pipeline里可能做了query改写或者去噪,封装成tool后模型直接拿原始问题去检索了。你可以先抓一下MCP实际传进检索函数的参数,跟本地跑的对比下,大概率能找到差异。另外tool描述里最好把检索意图、关键词权重这些写明确,不然模型瞎猜参数确实会掉点。
这个坑我太熟了,MCP封装检索不是简单把函数包一层就完事。你怀疑的方向大概率没错,尤其tool description写得太简陋时,模型根本不知道该怎么填参数,最后经常拿默认值或者瞎传一个模糊query进去,相关性自然就崩了。我建议你先在tool描述里把“什么时候用这个工具”“应该传什么格式的检索词”写得像给实习生看一样具体,甚至可以把历史有效query的例子直接塞进去。另外top_k和阈值被简化掉这个推测也合理,很多MCP实现为了让schema通用会砍掉自定义过滤字段,你可以试着在描述里暗示模型“如果结果不够精准,请降低相似度阈值重试”,让模型自己调参来弥补。还有个容易忽略的点是,本地pipeline里你可能有rerank或者关键词混合检索,但MCP工具里只暴露了向量检索,这一步降质往往比参数丢失更致命。我最后是直接在MCP server内部把rerank逻辑也封装进去,而不是只暴露一个裸的相似度搜索,效果才回到原来的水平。你可以先抓一下Claude实际传过来的参数日志,看看它到底是怎么调你的工具的,往往一眼就能看出问题。
先看看MCP tool的description里有没有把top_k和阈值写清楚,模型传参不准很常见,我当初也是描述太糊导致召回跑偏。
先查查MCP传参时top_k有没有被默认值覆盖,我踩过这坑,tool描述里没写清模型就乱猜。
这个坑我踩过,确实挺隐蔽的。你本地的pipeline里大概率是硬编码了top_k和阈值,模型直接按固定参数走,但换成MCP tool调用之后,这些参数变成了模型自己填,它经常拍脑袋给个top_k=5或者干脆不传,检索面一下就窄了。而且MCP的tool schema如果描述得含糊,模型很容易把query改写成它以为的样子,跟你原始问题偏离挺多,召回的自然就不对味。建议先把tool的input schema写死,把top_k和阈值设成默认值加范围限制,别让模型自由发挥。另外你可以在MCP server那层加个日志,把每次进来的query和参数都打出来,对比一下本地pipeline的输入,基本一眼就能看出差在哪。还有一点容易忽略,MCP返回的chunk如果被截断或者格式变了,模型对相关性的判断也会受影响,最好保持和本地一致的返回结构。调完这些再试试,大概率能拉回来。
我也遇到过类似情况,不过是在把内部检索服务封装成function call的时候发现的。问题大概率不在MCP本身,而是中间那层tool schema把你的意图压缩了。你想想,本地pipeline里你能传top_k=5、score_threshold=0.72、还要过滤掉某个source,但MCP tool的入参如果只定义了一个query字符串,那模型就只能靠猜,它根本不知道你背后有这些旋钮可以调。
另一个容易忽略的点是,模型在决定调用tool的时候会自己改写query。本地pipeline里你喂进去的是原始问题或者你精心构造的检索query,但走MCP之后,Claude会先理解一遍再生成调用参数,这一层改写很可能把关键词丢了或者加了它自己脑补的限定词。你可以把tool description写得更细,明确告诉它什么场景该传什么参数,甚至把top_k和阈值都暴露成可选参数。
还有个排查方向是看MCP返回的原始chunk和本地pipeline返回的是不是同一批。有时候不是检索质量下降,而是模型拿到chunk之后的二次筛选或者引用方式变了,让你感觉相关性差。建议先把MCP tool的返回结果打日志,跟本地直接调的top结果做对比,确认到底是召回阶段的问题还是后续处理的问题。
我最近也踩过类似的坑,最后发现是tool的description写得太笼统,模型压根不知道要传top_k和阈值,全用默认值在查。建议先把MCP tool的参数schema写细一点,把默认值也标清楚,再在描述里说明啥时候该调阈值。另外可以对比一下MCP实际发过来的请求参数和你本地pipeline的差异,大概率问题就出在传参这一步。
我也遇到过类似情况,排查下来大概率是MCP tool的参数schema设计问题。你如果把top_k和阈值写成了可选参数,模型经常就默认不传,走的是服务端兜底逻辑,跟本地pipeline的参数对不上。建议先把tool描述里每个参数的语义和推荐值写死一点,然后开日志对比一下模型实际传了什么。另外MCP那层如果做了结果截断或重排序,也会让相关性看起来变差,这个容易被忽略。
我之前也遇到过类似情况,后来发现是MCP tool的description写得太笼统,模型压根没把top_k和阈值当回事,传参全凭感觉。建议先把tool描述里每个参数的含义和推荐值写清楚,尤其是啥时候该调大调小。另外可以在tool返回里带上召回的score,方便对比本地pipeline和MCP链路的差异。有时候问题不在MCP本身,而是模型压根没按你预期的方式去调这个工具。