最近在折腾把公司内部的RAG知识库通过MCP暴露给Claude用,本来想着能直接让模型调工具查资料,省得每次手动喂上下文。结果实测发现,走MCP通道后,检索回来的chunk相关性明显不如之前在本地pipeline里的效果。我用的还是同一个embedding模型和向量库,只是把检索逻辑封装成了MCP tool。怀疑是MCP那边为了标准化,把一些过滤参数(比如top_k、相似度阈值)给简化掉了?或者是我对MCP的tool描述写得太简单,导致模型传参时不够精确?有没有大佬踩过类似的坑,指点一下调试方向,谢谢!
RAG接入MCP后检索质量反而下降了,是我姿势不对吗?
全部回复
共 15 条大概率不是MCP的锅,问题出在你tool description和参数映射上。模型调用工具时是按你写的描述去猜参数含义的,top_k和阈值这种细节它根本不会主动传,你得把默认值写进描述里甚至直接硬编码。我之前试过把检索逻辑拆成两个tool,一个精确查询一个模糊兜底,效果比一个万能tool稳很多。另外建议你在tool内部加个日志,看看实际收到的参数和你预期差多少,八成是模型自己在瞎填。
大概率是tool描述太糙,模型不知道咋传参,试试把top_k和阈值直接写进描述里让它照着调。
大概率是tool description写得不够细,模型不知道该怎么填参数,默认值一多query意图就糊了。我之前试过把top_k直接写死成20,结果相关度反而被稀释,后来改成让模型自己传动态值并配了示例,效果立刻回来。另外建议你对比下MCP走的那套输入预处理,有时候多了几步文本规范化会把原query里的关键实体给改掉。可以先在tool里加个debug输出,看模型实际传进来的参数和你本地pipeline里跑的是不是同一份。
这个现象太典型了,我怀疑问题大概率出在tool描述和参数映射上,而不是MCP本身。你本地pipeline里可能用了很多隐式的后处理逻辑,比如rerank、按时间衰减、或者针对特定域做了query改写,但封装成MCP后这些都被你“简化”掉了,模型只能拿原始query去查,相关性自然就掉了。另外,MCP的tool schema里如果只暴露了top_k和query,那模型确实没法传你之前调好的filter条件,比如metadata过滤或者相似度阈值,它只能靠猜。我建议你先在tool描述里把检索逻辑的“意图”写清楚,比如明确说“这是一个针对技术文档的语义搜索,支持按产品线过滤,默认阈值0.75”,让模型有据可依。还有一个坑是,Claude这类模型在调用工具时往往倾向于用更“通用”的表述,比如会把你的query做抽象化处理,反而丢失了具体实体词,这跟本地直接跑检索完全是两码事。你可以试试把MCP的输入参数定义得更细,比如拆成多个可选字段,然后观察模型实际传了什么值进来,对比一下日志就知道是不是参数传递丢了信息。另外,如果向量库本身支持复杂查询,不要为了标准化把所有逻辑都压进一个tool里,拆成多个专用工具可能更利于模型选择。最后,别忽略一个细节:MCP调用是有延迟的,模型可能因为等待响应而调整了后续的推理策略,有时候它会在拿到结果前就自行生成部分答案,导致你看到的最终输出质量下降,这个也得排查下。
大概率是tool描述太糙,模型不知道咋传参,试试把top_k和阈值写进description里强制约束。
这问题我遇到过,大概率不是MCP的锅,是tool的description写得太糙了。模型不知道每个参数的实际业务含义,就会瞎传值,比如把top_k传成10,但你的库可能默认5更准。你可以试试把description写详细点,明确说清楚“这个值控制返回条数,越小越精准”这类提示。另外检查下MCP那层有没有偷偷改你的filter逻辑,我之前就被框架默认的score threshold坑过。
大概率是tool description写得太糙了,模型不知道啥时候该调、该传啥参数,所以经常拿默认值去查。你可以试试把description写详细点,明确告诉它top_k和阈值该按什么逻辑设,甚至把几种典型查询场景都列进去。另外MCP这层确实会丢参数,建议直接在tool里把过滤逻辑写死,别依赖模型传。
我之前也遇到类似情况,后来发现问题多半出在tool描述上。模型对参数的理解完全依赖你的描述,写得太笼统它就会用默认值乱试,最好把top_k和阈值直接写死成推荐值,甚至给个示例query。另外MCP那层确实可能丢元数据,像日期、来源权重这些,本地pipeline能用的过滤条件到那边就失效了,建议先抓一下实际传参日志对比看看。
MCP的tool描述确实是个很容易被忽略的坑,我之前也遇到过类似情况。你想想,本地pipeline里你可以直接写死top_k=20、相似度阈值0.7这些参数,但封装成tool之后,这些逻辑全得靠模型在调用时“猜”,而模型对工具的描述理解往往很表面。我建议你先看下MCP那边实际传进来的检索参数是什么,加个日志打印一下,大概率会发现模型给的top_k特别小,或者压根没传过滤条件,导致召回范围太窄。另外,tool描述里最好把“预期用途”写清楚,比如“用于检索与用户问题直接相关的技术文档片段,返回前10条相似度高于0.75的结果”,别光写“检索知识库”,模型不知道你的默认阈值和排序逻辑。还有个思路是,干脆把后处理逻辑也封装进tool里,比如在MCP服务端强行限制top_k和阈值,不让模型自由发挥,这样至少能保证和本地效果一致。你那个embedding模型没变的话,问题大概率出在参数传递和上下文压缩上,Claude可能为了省token故意截断了长查询,导致向量化不充分。我建议你先从日志排查传参,再逐层调tool描述,别一上来就怀疑MCP协议本身。
大概率是tool描述里的参数语义没对齐,模型瞎猜top_k和阈值,试试把默认值和范围直接写死在描述里。
大概率是tool描述和参数映射的问题,MCP只是传输层,不会主动帮你改检索逻辑。你本地pipeline里那些过滤条件是不是硬编码的?封装成tool后模型拿不到这些上下文,它只会按你写的description瞎猜参数。建议把top_k和阈值直接写进tool schema里当必填项,再给个示例query让模型参考。另外检查下MCP server端有没有对输入做额外预处理,有时候框架会自动加个 rerank 之类的,反而干扰了原始向量检索。
大概率是tool description写得太糙,模型不知道该怎么填参数,试试把top_k、阈值这些直接写进描述里,比如“查资料时优先返回前5条,低于0.7相似度的直接忽略”。另外MCP那层确实可能吃掉一些自定义过滤逻辑,你可以在server端把默认参数补上,别指望模型每次都传对。我这边之前也遇到过,后来干脆在tool内部做了个二次rerank,效果就回来了。
大概率是tool描述和参数映射的锅,MCP只是个协议不会动你的检索逻辑,但模型看到模糊的描述会自己猜top_k和阈值,猜歪了相关性自然崩。你可以试试把tool描述写成带具体示例的完整prompt,比如“查2024年Q3财报时传filter=report_date”,模型传参精准度会明显提升。另外检查下MCP层有没有偷偷改你的默认参数,我之前就发现SDK会覆盖自定义的相似度阈值。
MCP那层确实容易变成黑盒,我之前也遇到过类似情况,后来发现是tool description里没写清楚检索条件的边界,模型瞎传参导致top_k被设得很小。你可以先抓一下Claude实际调tool时传的参数,看看跟本地pipeline里用的差多少,大概率是这里出的问题。另外MCP的schema会把一些字段类型收窄,比如float的相似度阈值可能被转成int,这种精度损失也会影响排序结果。还有个思路是别把整个检索逻辑塞进一个tool,拆成两个,一个负责查候选,一个负责精排,让模型分步调,反而更可控。我最后是直接在MCP server里硬编码了默认阈值,不允许模型覆盖,检索质量就稳回来了,你可以试试这种“降权”做法。
这问题我太有同感了,之前调MCP工具时也栽在参数传递上。你怀疑得很对,很多MCP封装为了通用性,确实会把top_k、score_threshold这类参数降级成固定值或者干脆暴露成字符串,模型根本不知道该怎么填。另外tool description的影响比你想的大得多,我试过把描述写成“搜索公司内部知识库,输入查询词、返回条数和相似度阈值”,效果比单纯写“检索文档”好了不止一档。建议你先在MCP server端把参数默认值打日志,看模型实际传了什么,大概率会发现它把top_k传成了字符串或者根本没传。还有个思路,别直接把原始检索逻辑暴露出去,而是封装成几个不同粒度的工具,比如“精确检索”和“宽松检索”,让模型自己选,比让它调参靠谱。最后检查下MCP那边是不是对chunk做了额外的截断或序列化,有时候多包一层就会丢metadata,排序权重就变了。