[Bug] dify-agent: plugin tool observations repeat JSON output that already arrived as text

当 Dify 插件工具在一次响应中同时返回内容相同、但格式化不同的 text 与 json 消息时, dify-agent 运行时会把同一份 JSON 观察结果重复拼进模型上下文;应优先排查 dify-agent/src/dify_agent/layers/dify_plugin/tools_lay

快速结论:当 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、显卡版本,无需据此判断。

解决步骤

  1. 定位 dify-agent/src/dify_agent/layers/dify_plugin/tools_layer.py 中的 _convert_tool_response_to_text。
  2. 参考 API 侧 api/core/tools/tool_engine.py 中 ToolEngine.tool_response_to_str 在 #42196 后的实现,把去重比较从原始字符串改为 canonical JSON 序列化后的比较(对 text 与 json 两侧都做规范化)。
  3. 保留原始 text 内容作为最终输出,不要用 canonical JSON 替换用户/插件原始文本。
  4. 确保比较逻辑不把 true 与 1 视为相同,不把数组顺序不同的载荷视为相同。
  5. 不要将去重范围扩展到重复 text 消息、重复 JSON-only 消息,也不要影响 suppress_output。
  6. 在本地运行 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、不同数组顺序的结果仍被保留而非误去重;再运行上述测试目录确认相关用例通过。

参考来源

langgenius/dify #45296

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 31814

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注