快速结论:当 Dify 插件工具在一次响应中同时返回内容相同、但格式化不同的 text 与 json 消息时,dify-agent 运行时会把同一份 JSON 观察结果重复拼进模型上下文;应优先排查 dify-agent/src/dify_agent/layers/dify_plugin/tools_layer.py 中 _convert_tool_response_to_text 的去重比较逻辑。
适用环境:Dify main 分支(问题复现提交 a79e2116),dify-agent 1.18.0;问题发生在 agent 运行时路径,不依赖正在运行的插件守护进程。Issue 未提供确切的 Python、CUDA、显卡版本信息。
最快修复方案:暂无确认的一步修复方案。可优先尝试参照 API 侧 #42196 的修复思路,在比较前把两侧内容规范化为 canonical JSON(保持一致键序、空白与编码处理),再将相同逻辑同步到 dify-agent 的对应函数。
注意事项:该修复属于代码层面改动,Issue 中未给出可直接执行的命令或配置。移植时需注意:不能直接用解析后的 Python 对象比较,否则会把 true 与 1 误判为相同;数组顺序不同的载荷应视为不同内容;仅去重 text 与 json 之间的重复,不要扩大到重复 text 或重复 json 消息的去重;同时保留 JSON-only 输出和 suppress_output 行为。
问题场景
用户在 Dify agent 运行时中调用插件工具,该工具返回了多条消息,其中同一条 JSON 数据既以 text 消息出现(如 {"a":1,"b":2}),又以 json 消息出现(如 {"a": 1, "b": 2})。dify-agent 通过 _convert_tool_response_to_text 把这些消息拼成模型可见的工具观察结果时,两段渲染由于空格、键序、Unicode 转义或尾部空白不同而无法匹配,导致相同内容被追加两次。
报错原文
[Bug] dify-agent: plugin tool observations repeat JSON output that already arrived as text
Observed on main (a79e2116, dify-agent 1.18.0):
'{"a":1,"b":2}{"a": 1, "b": 2}'
原因分析
可能原因是 _convert_tool_response_to_text 的去重实现基于原始字符串集合比较:
if json_parts:
existing_parts = set(parts)
parts.extend(part for part in json_parts if part not in existing_parts)
而 JSON 部分由本侧 json.dumps(..., ensure_ascii=False) 生成,通常与插件侧格式化的 text 载荷在空白、键序、Unicode 转义上不一致,因此字符串不相等,重复内容被保留。API 侧对应函数 ToolEngine.tool_response_to_str 曾在 #42195 报告同类问题,并通过 #42196 改为先规范化为 canonical JSON 再比较;dify-agent 中的副本早于该修复,未同步。
环境排查
- 确认运行的 Dify 是否为
main分支及提交 a79e2116 附近,问题在该版本可复现。 - 确认
dify-agent版本为 1.18.0 或包含旧版去重逻辑的版本。 - 检查所用插件工具是否会在单次响应中同时返回
text与json消息且内容语义相同。 - Issue 中本地验证使用 Windows 与
uv run python -m pytest tests/local/dify_agent/layers/dify_plugin -q;该路径为纯列表转字符串转换,无需运行插件守护进程。 - Issue 未提供 Python、CUDA、PyTorch、显卡版本,无需据此判断。
解决步骤
- 定位
dify-agent/src/dify_agent/layers/dify_plugin/tools_layer.py中的_convert_tool_response_to_text。 - 参考 API 侧
api/core/tools/tool_engine.py中ToolEngine.tool_response_to_str在 #42196 后的实现,把去重比较从原始字符串改为 canonical JSON 序列化后的比较(对 text 与 json 两侧都做规范化)。 - 保留原始 text 内容作为最终输出,不要用 canonical JSON 替换用户/插件原始文本。
- 确保比较逻辑不把
true与1视为相同,不把数组顺序不同的载荷视为相同。 - 不要将去重范围扩展到重复 text 消息、重复 JSON-only 消息,也不要影响
suppress_output。 - 在本地运行
uv run python -m pytest tests/local/dify_agent/layers/dify_plugin -q验证;可参照api/tests/unit_tests/core/tools/test_tool_engine.py中已有的两种消息顺序与格式差异用例补充测试。
验证方法
运行 Issue 中的复现代码,确认 _convert_tool_response_to_text([text, data]) 只输出一次 JSON 内容,而不是 '{"a":1,"b":2}{"a": 1, "b": 2}'。同时覆盖带空白、不同键序、Unicode 转义与 JSON 数组等用例,并确认 true 与 1、不同数组顺序的结果仍被保留而非误去重;再运行上述测试目录确认相关用例通过。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


