一文说明白为什么Dify的MCP策略插件不支持流式返回
摘要:文章分析了Dify与MCP服务集成时无法流式输出的问题。通过前端代码发现只有event为"message"或"agent_message"时才会渲染回答,而MCP策略实现中全程使用日志方式跟踪执行步骤,未返回相应event消息。调试验证了问题根源在于MCP策略未生成符合前端渲染条件的消息格式。全文简要说明了问题定位过程和技术细节。
·
说在前面
最近mcp大火,相信很多人已经搭建了自己的mcp服务,然后通过dify或者cherry studio这种工具调用过mcp服务了,不知道大家是否遇到一个问题,通过dify + mcp策略插件接入的方式,回答的内容无法流式输出,也就是需要等到所有结果生成完成后一并返回,这是为什么呢?
定位问题
首先看一下前端代码
这个比较直观,主要关注何种情况下会渲染回答。
# 相对路径web\service\base.ts
... existing code ...
if (bufferObj.status === 400 || !bufferObj.event) {
onData('', false, {
conversationId: undefined,
messageId: '',
errorMessage: bufferObj?.message,
errorCode: bufferObj?.code,
})
hasError = true
onCompleted?.(true, bufferObj?.message)
return
}
if (bufferObj.event === 'message' || bufferObj.event === 'agent_message') {
// can not use format here. Because message is splitted.
onData(unicodeToChar(bufferObj.answer), isFirstMessage, {
conversationId: bufferObj.conversation_id,
taskId: bufferObj.task_id,
messageId: bufferObj.id,
})
isFirstMessage = false
}
else if (bufferObj.event === 'agent_thought') {
onThought?.(bufferObj as ThoughtItem)
}
else if (bufferObj.event === 'message_file') {
onFile?.(bufferObj as VisionFile)
}
... existing code ...
大概是可以看出来一些端倪,只有当回复消息的event是 message或者agent_message时,才会赋值answer,进而渲染回答的textarea。
然后我们看一下mcp策略的代码,
Github地址在这里。
# 相对路径strategies\ReAct.py
... existing code ...
while run_agent_state and iteration_step <= max_iteration_steps:
# continue to run until there is not any tool call
run_agent_state = False
round_started_at = time.perf_counter()
round_log = self.create_log_message(
label=f"ROUND {iteration_step}",
data={},
metadata={
LogMetadata.STARTED_AT: round_started_at,
},
status=ToolInvokeMessage.LogMessage.LogStatus.START,
)
yield round_log
... existing code ...
通过查看策略的实现,发现全程都是通过日志的方式打印,用于跟踪智能体执行的步骤、耗时,详情等信息,并没有返回event为message的信息。
最后看一下实际返回

通过打印调试,证明确实当event:message的消息返回之后,才会渲染回复信息。
如果希望得到流式回复,需要对message这个生成器对象进行遍历:
大概是这么个逻辑:
if isinstance(chunk.delta.message.content, list):
for content in chunk.delta.message.content:
response += content.data
if (
not function_call_state
or iteration_step == max_iteration_steps
):
yield self.create_text_message(content.data)
说到最后
打包插件的offline版本。
以上。
更多推荐

所有评论(0)