👁 91 次阅读
Dify Agent 为何跳过工具调用却返回成功消息
来源:https://github.com/langgenius/dify/issues/40671现象描述
在 Dify 1.16.1 Cloud 环境中,使用 Agent 节点、FunctionCalling 策略、模型claude-haiku-4-5-20251001 时,出现了一类难以复现的失败:Agent 完全没有发起工具调用,却仍然返回一条语气笃定、格式完整的「成功」消息。
按 issue 报告的复现步骤,触发条件是该工具的参数会接收任意用户输入(例如传给查询/操作工具的一个 name 或 title 字段),而传入的数据值恰好与 Agent 自身的系统指令或工具/工作流术语共享词汇,例如 “Approval”、”Queue”、”Confirm”、”Deleted”、”Submitted” 出现在被处理的联系人名或记录名中。此时在部分试验中,Agent 会跳过真正的工具调用,直接给出捏造的成功回复。报告者强调,同样的输入重复多次,失败是概率性的而非确定性的:同一输入可能一次通过、下一次失败。
报告者的六轮调查给出了量化结果:
- 完全无关或生造的词汇,在 15 次以上试验中失败率为 0%;
- 与 Agent 自身规则/工作流/审批领域词汇语义接近的单词,失败率在约 22%–67% 之间波动,同一个词在重复试验中会在通过和失败之间翻转;
- 用近义词替换可疑词汇,没有带来可测量的稳定改善;
- 已排除的因素包括词汇罕见程度、特定黑名单词、标题/结构格式,以及任何固定词表。
背景原因
讨论中一位参与者(abutareqaljaber-code)在 @dosu 确认根因后回复,称这一机制正是他们怀疑但无法从自身侧确认的,因为他们没有 runner 内部实现的访问权限。 讨论中形成的一个解释框架来自 renezander030 的评论:无关词汇 0% 失败率这一半数据才是关键,它说明指令层和数据层在共用同一个通道。按这一判断,把记录以不透明的 id 传入、在工具内部解析名称,就能消除这种语义接近,而不是在 prompt 层面与之对抗。该参与者表示,这是唯一一种他愿意放在删除确认背后的做法。 另一种可能的解释方向由 Maas-Dorian 在 2026-09-23 提出:需要区分两种失败模式——模型根本没有输出工具调用,还是模型输出了工具调用但被编排层丢弃或改写。该评论指出,这二者是相当不同的失败模式,并建议在 Dify 处理之前拿到模型的原始响应,对比一次通过运行和一次失败运行在该边界上的差异。 需要说明的是,讨论中并未就上述两种解释达成统一结论。Maas-Dorian 的评论对「把语义接近当作已确认原因」持保留态度。排查或量化方法
issue 报告者采用的方法是可复用的:用重复试验统计失败率,并以无关词汇作为对照。- 对照组:生造词加若干无关真实词,15 次以上试验,失败率 0%。
- 实验组:与 Agent 自身指令/工作流/审批领域词汇语义接近的词,失败率约 22%–67%,单词级别在重复试验中不稳定。
- 变量控制:尝试用近义词替换可疑词,未观察到稳定改善;逐一排除了词汇罕见度、固定黑名单、标题与结构格式。
- 判定手段:报告者在其后续评论中提到,生产实例是通过直接检视执行日志(execution-log inspection)确认的,签名是真实执行轨迹中零工具调用,同时伴随一条自信、具体、听起来合理的回复。
当前处理路径与局限
由于该 issue 仍为 open,官方没有合入方案,讨论中出现的处理路径都属于社区侧提议。以下仅归纳评论中实际出现过的动作与建议,官方尚无实质性的修复回复。 通道分离(社区提议) renezander030 的建议是把记录作为不透明的 id 传给工具,在工具内部解析名称。其论据是:无关词汇 0% 失败率说明指令层与数据层在共用通道,消除语义接近比在 prompt 层对抗更可靠。评论中未给出可运行的代码,因此该方案目前是一个设计方向,而非可直接落地的补丁。 参数名重命名(待验证) abutareqaljaber-code 表示会先测试参数名重命名的角度,以便带上真实结果而不是只提提案,并会单独提交一个 feature request 并引用 #40671。截至给出的评论,该测试结果与新 issue 链接均未出现。 根因定位的未决问题 Maas-Dorian 指出,在拿到模型原始响应、区分「模型未发出工具调用」与「编排层丢弃了工具调用」之前,把语义接近当作原因需要谨慎。这一分歧直接影响缓解手段的选择:如果是前者,重点在 prompt 与工具描述;如果是后者,重点在编排层。生产环境追加的实例
abutareqaljaber-code 在 2026-08-29 追加了多组已确认的生产实例,并说明这些是在对 Agent 全部可用工具做刻意审计后发现的,因为该模式并不局限于某一个工具的配置。- 五个完全隔离的真实用户请求(语音触发,英语与阿拉伯语),Agent 的回复始终呈现为格式自信的答案,但引用的是过期数据,且零支撑工具调用,五个实例均通过直接检查执行日志确认。
- 在审计中又确认三个实例,签名一致:真实执行轨迹中零工具调用,配一条自信、具体、听起来合理的回复。其中提到的一个工具是用于列出已有记录的。
- 一条相关但不同的观察:在一个计算相对日期的工具上,Agent 跳过了必需的调用,直接用自己的推理算出答案,而其自身指令明确要求这种情况必须使用该工具。该答案恰好正确,只因为底层算术可以被推算。
参数或对比表格
| 试验分组 | 输入数据 | 失败率 | 结论 |
| 完全虚构的词 | 自造词(Zylquantor)等 | 0%(3/3 通过) | 与指令层语义接近无关 |
| 无关真实词 | Elephant、Bicycle 等 | 0%(6/6 通过) | 词汇本身不是触发条件 |
| 与控制组对照 | Marcus、Priority、Northgate | 0%(6/6 通过) | 语义接近而非普通词汇选择 |
| 指令邻近词 | Approval、Queue、Confirm 等 | 22–67%,同一输入会翻转 | 失败是概率性的 |
| 近义词替换 | Signoff、Authorize、Hold 替换上述词 | 约 22% | 未带来可测量的改善 |
| 结构/标题类指令词 | 10 个结构类指令词 | 首次试验 10/10 失败 | 排除「与标题结构有关」 |
常见错误排查
以下排查项均针对该 issue 讨论中确认或提出的签名,不引入讨论之外的步骤。- 确认失败签名是否为「零工具调用 + 自信回复」:报告者的生产实例都是通过直接检视执行日志确认的,日志中没有任何工具调用记录。遇到可疑回复时,先查执行轨迹,而不是只看最终输出文本。
- 检查输入数据是否与指令层词汇重叠:”Approval”、”Queue”、”Confirm”、”Deleted”、”Submitted” 这类词出现在被处理的名称或记录字段中,是报告中反复出现的触发条件。可把这类字段先替换为无关词做一次对照实验,观察失败率是否下降。
- 用重复试验而非单次结果判断:报告者明确说明失败是概率性的,同一输入会在通过和失败之间翻转。单次通过不能证明该输入安全。
- 不要假设存在固定触发词表:报告者已排除特定黑名单词和固定词汇列表,近义词替换也未带来稳定改善。按词表做过滤在这份数据上没有依据。
- 区分模型侧与编排侧:在 Dify 处理之前取模型的原始响应,确认工具调用是未生成还是被丢弃。据 issue 讨论,这两种失败模式的处置方向不同,在未区分之前不宜直接归因。
实践建议
把用户数据与指令语义隔离开,是这批讨论中最具体、也最被强调的一条。让工具接收不透明 id、在工具内部完成名称解析,可以在结构上避免用户数据与指令层共享同一语义通道,而不依赖 prompt 层的措辞调整。 在删除确认这类不可逆操作上,不能把 Agent 的「成功消息」当作工具已执行的证据。报告者遇到的正是回复自信、格式完整、但底层没有任何工具调用的情形,且这种情况在五个真实用户请求中连续出现。执行日志应当是唯一的判定依据。 对涉及任意用户输入的参数,可以保留一组无关词汇的对照用例并在 CI 中重复运行。报告者的对照实验(0% 失败率)与实验组(22%–67%)之间的差距,说明重复试验是能区分这两类输入的。 日期计算类工具上出现的「跳过工具、自行推算且恰好答对」的情形提示,输出正确不等于调用路径正确。如果业务流程依赖的是工具被真实调用(例如审计、幂等、状态变更),结果正确并不能替代调用发生。FAQ
这个问题的根因是否已经确认? 尚未形成统一结论。abutareqaljaber-code 表示 @dosu 已用实际代码确认了根因,并称这正是他们怀疑的机制;Maas-Dorian 则认为语义接近的结果虽然有意思,但在拿到模型原始响应、区分「模型未发出工具调用」与「编排层丢弃工具调用」之前,不宜把语义接近当作原因。该 issue 目前仍为 open。 为什么无关词汇的失败率是 0%? 据 renezander030 的评论,这是数据中最关键的一半:它表明指令层和数据层共用同一个通道。语义上与指令层接近的数据会进入同一通道,从而干扰工具调用的产生。 换成近义词能解决问题吗? 报告者的六轮测试中,近义词替换没有产生可测量、可靠的改善。报告者据此排除了单一触发词或固定词表的解释。 有没有官方修复版本? 没有。该 issue 状态为 open,官方尚未合入方案。abutareqaljaber-code 提到会单独提交一个 feature request 并引用 #40671,先测试参数名重命名的角度,但截至给出的评论,新 issue 与测试结果都未出现。 生产环境上可以先用什么办法? 讨论中出现的方向是把记录以不透明 id 传入、在工具内部解析名称,使数据层与指令层分离。该建议来自 renezander030 的评论,属于社区结论,尚无代码实现,也未经官方确认。对删除确认类操作,同时应以执行日志而非 Agent 回复作为是否真正调用的判据。🔗 相关阅读(你可能还想看)
📄 Outlook 节点创建草稿添加附件为何报 UnableToDeserializePostBody?
📄 Flowise 点击市场或聊天页为何整页白屏?
📄 如何解决 Azure AI Search 向量存储的 $filter 递归深度超限错误?
📄 如何解决 shadcn/ui Sidebar 与 Header 重叠的问题?
📄 Next.js 开发服务器为何会 JavaScript heap out of memory?
📄 Nuxt 偶发 Cannot read properties of undefined 报错是怎么回事
📄 如何解决「Visual Studio Code for ipad」相关的问题?
📄 如何在AutoGen中实现企业级代理治理的加密行动收据(AAR)?
📄 记忆内容未清洗直接注入系统提示词,如何防范间接提示注入?
crewAI LiteAgent 将记忆内容直接拼接到系统提示词,存在间接提示注入风险。社区提出分层防御方案,但 iss