最近在折腾把公司内部的RAG服务封装成MCP tool给Claude用,结果发现一个诡异的问题。单独调用RAG接口时,top5召回的结果还挺准的。但一旦通过MCP server转发,同样的query返回的chunk就变得很飘,经常混进一些不相关的内容。我对比了下日志,发现MCP这边好像把query做了二次处理?传过去的参数编码有问题?还是说MCP的tool description写得太长,影响了模型对query的理解?另外,有没有朋友遇到过MCP server超时导致RAG那边异步任务被中断的情况?求指点,在线等,挺急的。
把RAG接进MCP server后,召回效果反而变差了是怎么回事?
全部回复
共 51 条之前踩过类似的坑,重点查一下MCP server里tool的input schema定义,如果query字段类型或描述写得不明确,模型会自己脑补加料,导致传过去的参数和你预期的不一样。另外超时中断那个大概率是同步调用方式的问题,试试把RAG改成异步返回,或者在MCP侧调大timeout,别让Claude那边等太久。
我之前也踩过类似的坑,多半不是MCP二次处理query,而是tool description里塞了太多业务术语,Claude会自己脑补出一套“意图理解”,反而把原始query带偏了。你试试把description精简到纯功能说明,另外确认下MCP那边传参是不是用了HTTP的form编码而不是JSON,chunk里混进乱码就说明编码炸了。超时那个确实会,MCP默认超时短的话,RAG异步没跑完就被掐了,建议把MCP的timeout调大或者改同步调用,先排除这两个变量再排查其他的。
我之前也踩过类似的坑,重点查下MCP server里有没有对query做额外的拼接或改写,有些框架会默认加system prompt,把原始语义带偏了。另外tool description别写太满,Claude对超长描述反而会抓不住重点,精简到一两句核心功能试试。超时那个问题,建议把RAG调用改成同步等待模式,或者在MCP里加大timeout配置,不然异步任务被kill了确实会返回脏数据。
大概率是MCP把query序列化时动了手脚,试试打印下实际收到的参数,比对下编码前后差异。
超时中断倒是常见,把RAG的异步任务改成同步重试机制能缓解不少。
这个现象我太熟了,之前折腾过类似的东西,最后定位到是MCP的tool description里塞了太多示例导致Claude把query语义带偏了。你试试把description精简成纯函数式说明,别在里头写什么“用于检索公司内部知识库文档”这种带主观色彩的引导词。另外参数编码确实是个坑,MCP的JSON序列化有时候会把中文引号或者特殊符号转义成unicode,RAG那边分词器处理不了就全乱了。超时导致异步中断我也碰到过,如果RAG是流式返回或者要跑rerank,建议在server端把超时调大,同时让MCP这边改成同步等待但内部做重试机制。还有个小技巧,你可以把query原样打日志对比一下两边收到的字符串,如果字节数都不一样那肯定是传输层出问题了。最后别忽略MCP的采样buffer,如果Claude在调用tool前先自己判断了query意图,它可能会对原句做改写,这个在日志里能看到,但很多人没注意到。
我上周也踩过类似的坑,最后发现是MCP的tool description里塞了太多示例和参数说明,Claude在构造query时反而被带偏了,把原始问题拆得七零八落,建议你试试精简description,只保留必要的检索意图。至于参数编码,我这边遇到过特殊字符被转义后召回率暴跌的情况,你可以打印一下MCP实际收到的请求体,对比和直连时的差异。超时中断那个我倒是没碰到,但如果RAG是异步的,建议把MCP的timeout调大,或者在server端改成同步等待结果返回,省得两边状态对不上。
你日志里看到的“二次处理”大概率是MCP协议序列化时把query当普通字符串拼进prompt模板了,转义或者截断都可能让语义漂掉。我之前也踩过类似坑,建议先把MCP server收到的原始query打出来跟直连RAG时的做个diff,别只看最终结果。另外tool description太长确实会抢模型的注意力,试试压缩到一两句话描述功能就好。超时那个问题更麻烦,MCP断了RAG那边不一定能感知,最好加个request id做幂等或者取消信号的透传。
你提到的query二次处理这点很关键,我之前也踩过类似的坑。MCP server在转发前如果对参数做了JSON schema校验或者字段映射,很容易把原始query里的特殊符号、空格甚至大小写给改了,检索那边拿到的东西就已经不是你以为的那个query了。建议先把MCP实际发出的request body完整打出来,跟直连RAG时的参数做逐字节对比,大概率能看出问题。至于超时中断,MCP那边默认超时一般偏短,RAG要是走异步召回确实容易被掐掉,调大timeout或者改成同步短链路试试。
我之前也踩过这个坑,后来发现八成是tool description写太长把query带偏了,模型会拿描述里的词去重写查询。建议先把description砍到一句话,只留功能说明,参数交给schema约束。另外MCP超时确实会掐断下游异步,我们改成先返回task id再轮询才稳。你可以在MCP层把原始query和改写后的query都打出来对比一下,一眼就能看出是不是二次处理的问题。
你提到的二次处理问题我也踩过,后来发现是MCP tool的input schema里query字段被加了默认的prompt template,相当于模型又改写了一遍才发给RAG,试试把schema简化成纯string透传。tool description太长确实会干扰,我压到两行以内召回就稳了。超时那个建议在MCP侧设个略小于RAG的超时阈值,不然异步任务被掐断很难排查。
你提到的二次处理挺关键的,我之前也踩过类似的坑,MCP那边对query做了normalize或者截断,导致语义直接变了。建议先把MCP server收到的原始query和RAG直接调用的query打日志对比一下,大概率是编码或转义的问题。tool description太长确实会稀释模型对query的注意力,可以精简下描述试试。超时那个我也遇到过,异步任务被掐断后返回的是半截结果,看着就很不相关。