Dify 插件开发实验(03):工具接入工作流与Agent——插件工具如何在工作流和 Agent 中使用?
1. 业务场景
先讲一个我们实际遇到的场景。
客服工单 SaaS 有两个入口在用之前做好的工具:一边是工单流程——用户提交工单时,系统要自动记下建单时间戳、并校验这张工单在订单系统里的状态,这是固定动作,不能乱;另一边是门户对话——用户在网页上直接问「现在几点」「我的工单 WO-20260805 到哪一步了」,模型要自己判断该不该调工具、调哪个工具。
同一个插件,两种用法:一个要「确定性地执行」,一个要「智能地选择」。工具造出来只是第一步,怎么接进业务应用,才是插件真正产生价值的地方。
我们第一次做这种接入时,第一反应也是「工具节点拖进工作流不就行了」。真正动手才发现——「能被工作流调用」和「能被 Agent 智能调用」,是两套完全不同的消费逻辑:工作流要确定性,参数绑死上游变量;Agent 要自主性,参数靠模型按描述生成——同一个插件,要同时伺候好这两种形态,工具描述和调用形态都得重新设计。
这不是个例。任何「一个工具既要在固定流程里跑、又要能被对话智能调用」的场景都是这个模式:电商的物流查询、银行的账户查询、SaaS 的工单查询——确定性编排和模型自主调用,是插件消费的两种基本形态。
2. 场景痛点
这个流程的痛点,在接入业务时体现得最直接:
- 同一能力要接两套形态:工单流程要固定调用,门户对话要智能调用——不会接,工具就只能躺在插件列表里吃灰。
- 模型乱调工具:不加约束时,用户闲聊「你好」模型也可能去调一次时间工具,白白浪费 token,还显得很傻。
- 参数瞎编:工具描述写得含糊,模型就生成非法参数(比如把工单号编成
abc123),调用必然失败。 - 出错就中断:工具调用失败如果直接抛错,整个工作流就断了——下游业务无感知、无降级。
本质上,「怎么被消费」决定工具的价值——工作流要的是确定性,Agent 要的是可引导的自主性,两者都需要把工具描述和调用形态设计好。
3. 方案:为什么是双形态接入
选双形态接入,我们实际对比过:
- workflow 工具节点 = 确定性编排:参数显式绑定上游变量,固定调用,适合「必须执行」的步骤(建单打时间戳);
- Agent function calling = 模型自主选择:模型按 query 意图决定调不调、调哪个,适合开放对话(门户问答);
- 同一插件实例两种形态共用:插件装一次,工作流和 Agent 都能挂,插件日志统一出口,排障只看一处。
这篇文章我们就用它把 01/02 的工具(get_current_time +
get_order_status)分别接进工单流程(workflow)和门户对话(agent),并对比两种形态的差异、掌握插件运行日志的定位方法。
4. 整体架构
链路很清晰:同一插件 → 双形态挂载 → 各自消费。workflow 走工具节点、参数绑定上游变量;agent 走工具列表、参数由模型按描述生成——两种形态的关键差异就在「参数从哪来、怎么校验」。
5. 模块设计
5.1 workflow 工具节点 DSL(实测格式)
- id: time_tool
data:
type: tool
title: 获取当前时间
provider_type: builtin
provider_id: dify106/dify106_01_time_tool/time_tool
tool_name: get_current_time
tool_configurations: {} # 必填字段,无凭证配置也保留空对象
tool_parameters:
timezone:
type: constant
value: UTC+8第二个工具节点(order_tool)的 order_id 参数用
type: mixed + value: '{{#start.order_id#}}'
绑定上游变量。多工具串行时各节点独立配
tool_configurations: {}。
5.2 agent 工具挂载(agent-chat DSL)
agent_mode:
enabled: true
strategy: function_call
max_iteration: 5
tools:
- enabled: true
provider_id: dify106/dify106_01_time_tool/time_tool
provider_type: builtin
tool_name: get_current_time
tool_parameters:
timezone: ''5.3 工具描述撰写规范(决定 agent 调用成功率)
- llm description 给参数规则 +
示例:
order_id must match format WO- followed by 8 digits, e.g. WO-20260805——实测模型据此正确生成工单号。 - pre_prompt 里写工具边界:「时间类问题只调时间工具;工单类问题只调工单工具,不要混用」「闲聊不调用工具」——实测负例不触发。
- 格式错误时模型行为可控:pre_prompt 写「工单号格式不对时先请用户核对」→ 实测 abc123 不调工具直接提示格式(合理行为)。
- 可选参数给合法取值集合:模型会自主选择时区(问时间 → timezone=UTC+8)。
5.4 双形态对照表(核心交付物)
| 维度 | workflow 形态 | agent 形态 |
|---|---|---|
| 参数来源 | 显式绑定上游变量(tool_parameters mixed 模板) | 模型自主生成(tool_input JSON,按工具描述规则) |
| 校验时机 | 工具内校验(param_invalid 直接返回) | 模型先按描述规则判断(格式不对时不调工具) |
| 错误处理 | 工具返回结构化 error → 下游节点处理 | 工具返回 error → 模型解读并转述给用户 |
| 调试方式 | node-executions 看工具节点输入/输出 | SSE 事件 agent_thought 的 tool/tool_input 字段 |
| 工具选择 | 固定调用(编排决定) | 模型按 query 意图选择(描述质量决定成功率) |
6. 运行验证
| 输入 | 预期 | 结果 |
|---|---|---|
| workflow(order_id=WO-20260805) | 时间戳 + 工单状态均正确输出 | 通过(实测 17:08:53 UTC+8 + processing) |
| workflow 错误路径(bad-id) | param_invalid 输出,流程不中断 | 通过 |
| agent 问「现在几点」 | 触发 get_current_time,回答精确时间 | 通过(timezone=UTC+8 模型自选) |
| agent 问工单状态 | 触发 get_order_status,回答真实数据 | 通过(模型正确生成 order_id) |
| agent 闲聊「你好」 | 不触发任何工具 | 通过 |
| 排障演练:base_url 改不可达 | upstream_error 结构化返回不中断;daemon 日志可见 dispatch/tool/invoke 链路 | 通过 |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| 工具描述差导致 agent 乱调/不调 | 描述不含参数规则 → 模型生成非法参数 | 描述含规则+示例(WO- 8 位数字)→ 模型正确生成(实测) |
| agent_thought 事件结构 | 按 tool_calls 字段解析不到工具调用 | 工具调用在 tool/tool_input 字段(不是 tool_calls);每个工具调用出现 2 条 agent_thought(实测) |
| workflow 未发布直接跑 | 400 "Workflow not published" | 验证脚本必须含 publish 步骤(实测) |
| 日志层级分不清 | 排障找不到插件调用证据 | daemon 日志 dispatch/tool/invoke 是插件调用链路证据;故障信息在工具结构化 error 里(实测) |
| agent 格式错误不调工具 | 提问 abc123 模型不调工具直接提示格式 | 合理行为(pre_prompt 规则生效),非缺陷(实测) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-106-03:工具接入工作流与Agent.md
- 源码(可直接导入):dify106_03_验证应用.yml(workflow)、dify106_03_验证对话.yml(agent)
- 全部源码目录:dify-106/dsl
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 插件开发实验(01):开发环境与首个工具插件——从零开发第一个 Dify 插件需要什么?
- Dify 插件开发实验(02):参数与凭证体系——插件参数和凭证如何声明、配置与管理?
- Dify 插件开发实验(03):工具接入工作流与Agent——插件工具如何在工作流和 Agent 中使用?
- Dify 插件开发实验(04):企业系统对接工具——如何用插件对接企业 ERP/CRM?
- Dify 插件开发实验(05):有状态与幂等——插件如何安全地保持状态和处理重复调用?
- Dify 插件开发实验(06):通知渠道插件——如何把 Dify 推送到钉钉/企业微信等渠道?
- Dify 插件开发实验(07):私有模型网关接入——如何让 Dify 用上私有模型网关?
- Dify 插件开发实验(08):外部知识库插件——如何把外部检索能力做成插件?
- Dify 插件开发实验(09):Agent策略插件——如何控制 Agent 的工具使用策略?
- Dify 插件开发实验(10):自定义节点扩展——不改平台代码,插件如何补节点能力?
- Dify 插件开发实验(11):打包分发与离线安装——插件如何打包签名、分发与离线安装?
- Dify 插件开发实验(12):企业级交付验收——插件交付如何做验收?