快速结论:当同一条 assistant 消息既有附件文件、又在流式输出 LLM 文本时,Dify 后端会把文本分片误标成 message_file 事件,导致前端把回答文本当成文件事件丢弃或渲染异常。优先排查流式响应里文本分片的 event 类型是不是变成了 message_file。
适用环境:Dify 自托管源码部署(Self Hosted / Source),跟踪 upstream main 分支;Issue 明确引用的提交为 b96ea94505e3abe00daba69faa8bba9bcf9deb0a,评论中复现相关代码位置基于 86af36429da4c5433621520e704ed9dff7be869a。Issue 未提供操作系统、Python、CUDA、显卡等环境信息。
最快修复方案:暂无确认的一步修复方案。Issue 只确认了问题仍存在于 main,并指出曾被关闭的 PR #31414 的思路(让 LLM 文本分片无条件使用 StreamEvent.MESSAGE)方向正确,但没有合并为已验证修复;已合并的 PR #32019 只是收窄了返回 MESSAGE_FILE 的条件。
注意事项:Issue 中提到的改法均为代码级推断或未合并方案,改动前务必评估对 QueueMessageFileEvent(文件元数据本身仍应以 message_file 流式发送)的影响,避免把真正的文件事件也一并改掉;同时需自行补充单元测试验证文本分片与文件事件是否正确分离。
问题场景
在 Dify 自托管源码部署中,使用会流式返回 assistant 回复、且同一条 assistant 消息还会产生附件的聊天/补全流程(例如工作流或工具节点在 LLM token 流式输出之前或期间发出 QueueMessageFileEvent)。此时 LLM 的文本分片会以文件事件类型下发,前端可能把回答文本当作文件事件处理,造成流式回答文本丢失或显示不正确。
报错原文
LLM text chunks are streamed as message_file when assistant message has files
实际表现是文本分片的 SSE 字段为 event: message_file,而预期应为 event: message;文件元数据则继续通过 QueueMessageFileEvent 以 event: message_file 单独发送。
原因分析
最可能的原因在流式管线的处理链路:
MessageCycleManager.message_file_to_stream_response()处理文件事件时,会把消息 id 缓存进_message_has_file,并正常发出message_file事件。- 之后处理
QueueLLMChunkEvent时,EasyUIBasedGenerateTaskPipeline._process_stream_response()会调用MessageCycleManager.get_message_event_type(message_id=...)预计算事件类型,并复用到 LLM 文本分片上。 get_message_event_type()在消息 id 存在于_message_has_file,或数据库中已存在该消息的 assistantMessageFile时,会返回StreamEvent.MESSAGE_FILE;于是文本分片被传给message_to_stream_response(..., event_type=StreamEvent.MESSAGE_FILE),而非StreamEvent.MESSAGE。
评论补充说明:PR #32019 合并后为 get_message_event_type() 增加了 .where(MessageFile.belongs_to == "assistant") 过滤,收窄了返回 MESSAGE_FILE 的条件,但 _precomputed_event_type 的缓存逻辑仍然存在。只要首个 QueueLLMChunkEvent 处理前数据库里已有该消息的 assistant MessageFile 记录,缓存的事件类型就会是 MESSAGE_FILE,后续所有文本分片都会被误标。本质原因是文本分片与文件元数据属于不同语义的事件类型,文本分片的类型不应取决于该消息是否带文件。
环境排查
- 确认部署方式为 Self Hosted(Source),并核对当前代码是否仍在 upstream
main上,相关提交可对照b96ea94505e3abe00daba69faa8bba9bcf9deb0a与86af36429da4c5433621520e704ed9dff7be869a。 - 检查
api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py的_process_stream_response()是否仍存在对QueueLLMChunkEvent的事件类型预计算/缓存(Issue 指出约在 L348–L358 附近)。 - 检查
api/core/app/task_pipeline/message_cycle_manager.py中get_message_event_type()的返回逻辑,以及是否包含 PR #32019 引入的belongs_to == "assistant"过滤。 - Issue 未提供 Python、CUDA、PyTorch、显卡等版本信息,无需据此排除问题。
解决步骤
- 先复现并抓取 SSE 响应,确认同一条 assistant 消息触发文件事件之后,后续文本分片的
event是否变为message_file。 - 对照当前代码,确认
_process_stream_response()中QueueLLMChunkEvent是否复用了get_message_event_type()返回的事件类型(即_precomputed_event_type缓存路径仍生效)。 - 确认该消息在首个 LLM 分片处理前是否已存在 assistant
MessageFile记录或已被写入_message_has_file,这是触发误标的前提条件。 - 可优先尝试按 PR #31414 的思路修正:让
QueueLLMChunkEvent的文本分片无条件使用StreamEvent.MESSAGE,不再依赖消息是否附带文件;QueueMessageFileEvent仍单独以message_file发送。注意该 PR 已被关闭,此方案属于 Issue 讨论中的方向性建议,尚未验证合并。 - 若暂不改动核心逻辑,可先绕开触发条件(避免同一条 assistant 消息在文本流式期间同时产生附件),但 Issue 未验证该规避方式,属推测性做法。
验证方法
修改后重新运行同一流程:同一 assistant 消息既有附件又流式返回文本时,文本分片应显示为 event: message,文件元数据仍为 event: message_file;前端能正常渲染完整回答文本,流式内容不再被丢弃。建议同时补充单元测试覆盖“消息带 assistant 文件时 LLM 分片仍为 message”的场景。
参考来源
相关讨论中提及的 PR:#31414、#32019;相关 Issue/PR:#32420、#32242(均为 Issue 评论引用,未逐一核实内容)。
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。

![[Bug]: runai_safetensors_weights_iterator yields tensors in nondeterministic order, breaking FP8 inference on some platforms](https://www.chat-gpts.plus/wp-content/uploads/2026/11/38991-8f2a5705-768x403.jpg)
![issue: [regression] [dev] Holding Shift no longer shows the Delete buttons on the Notes page after clicking a button](https://www.chat-gpts.plus/wp-content/uploads/2026/11/33714-9975cfd0-768x403.jpg)