MCP转CLI实战:并发问答优化揭秘
通过MCP实现了PC端应用与IMA知识库的“隐秘”交流,即在PC端应用中形成的问题通过MCP提交给IMA,由LLM调用自己在IMA的专用知识库进行解读,并把结果回传的PC端应用,全程无需人为干预均在后台完成,看不到PC端与IMA的认证、链接、选库等过程,该实现过程可参见前期公众号文章《通过IMA-Copilot-MCP实现与IMA的“隐秘”交流》。
实现了与IMA丝滑的后台交流,即解决了LLM解读专用知识库的问题,也极大地提高了应用效率。但目前与LLM交互的程序基本上是问答式、串连的,如果实际应用中问题比较多,交互IMA的效率将直接影响整个应用效率,于是接下来做了两个方面的尝试:
第一,能不能把 MCP 方式改成 CLI?
第二,如果继续使用 MCP,能不能把原来的串行问答改成并发问答?
于是围绕这两个问题,进行了一轮实际改造。
最后的结果是:
CLI 可以实现,但对于这个项目来说,把 MCP 包装成 CLI 并没有真正改变 IMA 的访问方式;真正值得优化的,是 MCP 模式下的并发问答。
一、从MCP 到 CLI:本想换一种调用方式,结果发现并没有那么简单
1. 最初的想法很简单
原来的程序是通过 MCP 调用 IMA。
所以最初的想法其实非常直接:
既然MCP 已经提供了 IMA 的问答工具,那能不能不通过原来的MCP程序调用,而是直接在命令行里调用MCP暴露的工具?
从概念上看,这似乎并不复杂。
比如:
命令行↓调用 MCP 工具↓得到 IMA 的回答
于是开始尝试做一个 CLI 版本。
2. 真正做起来才发现:MCP 工具并不是一个可以直接调用的函数
实际开始改造以后,很快发现事情没有想象中那么简单。
表面上看,我们需要调用的似乎只是一个:
ask_with_kb
但这个工具真正能够工作,并不是因为调用了一个简单的函数。
它背后实际上依赖了一整套环境和实现。
首先是IMA 的登录认证状态。
包括 Cookie、BKN、USKEY 等认证信息。
其次是知识库的配置和 ID。
程序需要知道到底访问哪个 IMA 知识库,以及知识库名称如何解析成对应的 ID。
再往下,还有MCP Client 与 MCP Server 之间的连接和初始化。
而真正向 IMA 发起问答请求时,又涉及 IMA 内部接口、SSE 数据解析、Token、重试以及并发控制等逻辑。
也就是说,所谓:
“调用一下 MCP 工具”
实际上是建立在下面这一整套链路之上的:
CLI↓MCP Client↓MCP Server↓认证信息↓知识库配置↓IMA Client↓IMA 内部问答接口
所以,MCP 工具并不是一个拿过来就可以独立运行的“黑盒函数”。
3. 这时候问题就变成了:CLI 到底是在取代 MCP,还是在包装 MCP?
如果 CLI 自己重新实现上面这些东西,那么它确实可以最终直接访问 IMA。
但这样一来,CLI 就必须自己负责:
IMA 登录态··Cookie 等认证信息··知识库 ID··MCP 原本承担的连接逻辑··IMA 接口调用··SSE 解析··Token 刷新··重试··并发控制·
这实际上已经不是简单的“把 MCP 改成 CLI”了。
而是:
把MCP Server 中原本负责 IMA 访问的核心逻辑,重新搬到 CLI 里面。如同只需一条命令行,现在需要一个批命令BAT了。
这样做当然技术上可行,但也意味着要重新维护一套与 MCP Server 高度重复的代码。
于是还有另外一种更简单的办法:
CLI 不自己实现这些功能,而是继续调用 MCP。
这就变成:
CLI↓MCP Client↓MCP Server↓IMA
CLI 只是负责:
通过命令行调用MCP 提供的工具。
4. 结论:CLI 可以做,但对于这个项目没有改变访问方式
做到这里,答案其实已经比较明确了。
如果目标只是:
“我想有一个可以在命令行中使用的 IMA 工具。”
那么 CLI 当然有价值。
但如果最初的目标是:
“我想把 MCP 换掉,不再依赖 MCP Server。”
那么简单地增加一个 CLI 并没有实现这个目标。
因为底层仍然是:
CLI↓MCP↓IMA
真正访问 IMA 的,依然是 MCP Server。
所以这次尝试最终得到的结论是:
CLI 并没有取代 MCP,只是把 MCP 工具包装成了 CLI。
而对于当前这个项目来说,这种改变并没有带来实质性的访问方式变化。
二、既然CLI 没有必要,那就回到 MCP:真正的问题是什么?
既然 CLI 只是 MCP 的包装,那么继续折腾 CLI 就没有太大意义。
与其换一种调用方式,不如回到原来的 MCP 架构,看看真正影响程序体验的问题是什么。
很快就发现:
原来的多问题问答是串行的。
1. 原来的问答方式:一个接一个
假设程序需要向 IMA 提出三个问题:
问题1↓等待答案1↓问题2↓等待答案2↓问题3↓等待答案3
也就是说:
前一个问题没有完成,后一个问题不会开始。
而且原来的程序还有一个特点:
一个问题完成以后,答案会立即显示。
所以它的特点其实是:
串行执行+即时输出
这在逻辑上没有问题,但如果三个问题彼此独立,那么显然存在进一步优化的空间。
2. 第一次尝试:让多个问题并行
既然三个问题互相独立,那么自然想到:

理想情况下:
三个问题同时开始,哪个先得到答案,就先显示哪个。
例如:
问题1:8 秒
问题2:5 秒
问题3:6 秒
串行执行需要大约:
8 + 5 + 6 = 19 秒
而真正并行时,理论上只需要等待最慢的那个:
max(8, 5, 6) = 8 秒
当然,实际速度还会受到 IMA 服务端、网络以及各种通信开销的影响,但方向是明确的:
如果服务端允许并发,多问题问答没有必要一个一个等待。
3. 但第一次并发尝试,结果反而更慢了
问题来了。
把提问改成并发以后,实际运行却发现:
并没有变快,甚至比原来的串行方式更慢。
这就说明:
客户端能够并发,不代表整个调用链能够并发。
于是继续向下检查 MCP Server。
4. 真正的瓶颈:MCP Server 把并发限制成了 1
最终在.env中找到关键配置:
IMA_ASK_CONCURRENCY_LIMIT=1
也就是说,MCP Server 默认只允许一个 IMA 问答同时执行。
因此,即使客户端同时发出三个请求:

到了服务端以后仍然会变成:
请求1 → 执行
请求2 → 等待
请求3 → 等待
所以所谓“并发”,实际上只是:
客户端并发发起,服务端排队执行。
而且之前的并发方案还额外创建了多个 MCP 会话,需要分别进行初始化。
结果不仅没有获得并发带来的收益,反而增加了额外开销。
这也解释了为什么第一次并发尝试会比串行更慢。
5. 找到瓶颈之后,真正的优化方向就明确了
问题已经不是:
CLI 还是 MCP?
而是:
MCP Server 是否允许多个 IMA 问答同时进行?
于是决定回到原来的 MCP 架构。
先调整.env文件中的:
IMA_ASK_CONCURRENCY_LIMIT
从:1 调整为:2
这一步才是真正打开并发能力的关键,如图:

6. 新版本:真正并发,而不是“假并发”
新的程序使用ThreadPoolExecutor 同时发起多个 MCP 请求。
结构变成:

MCP Server 允许两个问答同时执行。
因此,客户端的并发才真正有机会转化为服务端的并发。
7. 谁先完成,谁先显示
并发以后,还有一个问题:
三个问题的完成顺序可能不一样。
例如:
问题1 → 8 秒问题2 → 5 秒问题3 → 6 秒
那么实际显示顺序应该是:
5 秒 → 答案26 秒 → 答案38 秒 → 答案1
而不是等问题1完成以后才显示问题2。
因此新版本采用:
哪个请求先完成,就立即回调显示哪个答案。
但是最终结果仍然按照原始问题顺序归位:
问题1问题2问题3
这样既保证了实时性,也不会影响后续结果处理。
8. 最终验证:这次是真正的并发
修改以后,通过带延迟的模拟 MCP Client 对程序进行了测试。
重点验证的是:
在第一个请求完成之前,其他请求是否已经发起。
测试结果确认:

三个请求确实可以在前一个请求完成之前全部发出。
同时验证了:
·双问可以并发;··三问可以并发;··谁先完成谁先显示;··最终结果仍按照原问题顺序排列;··MCP Server 的并发限制已经调整为 2;··原来的meihua_yishu_web.py 保持不变。·
至此,真正的并发问答才算实现。
三、最后的结论
回头看这次改造,其实解决的是两个不同的问题。
第一个问题:MCP 能不能改成 CLI?
能。
但如果 CLI 只是:
CLI → MCP → IMA
那么它只是 MCP 的命令行包装。
如果真的想摆脱 MCP,就必须把 MCP Server 中的 IMA 访问、认证、SSE、Token、重试等逻辑重新搬到 CLI 中。
对于当前项目来说,这样做成本更高,而且没有明显收益。
所以:
没有必要为了CLI 而放弃 MCP。
第二个问题:MCP 能不能优化?
能,而且值得优化。
真正的问题并不是 MCP 本身,而是:
IMA_ASK_CONCURRENCY_LIMIT=1
导致多个问答只能串行执行。
把服务端并发限制提高以后,再配合客户端并发请求,就可以实现:
多个问题同时发起↓谁先完成谁先显示↓最终结果按原顺序归并
这才是这次改造真正有价值的地方。
更多推荐

所有评论(0)