← 返回文章列表

Dify 插件开发实验(03):工具接入工作流与Agent——插件工具如何在工作流和 Agent 中使用?

1. 业务场景

先讲一个我们实际遇到的场景。

客服工单 SaaS 有两个入口在用之前做好的工具:一边是工单流程——用户提交工单时,系统要自动记下建单时间戳、并校验这张工单在订单系统里的状态,这是固定动作,不能乱;另一边是门户对话——用户在网页上直接问「现在几点」「我的工单 WO-20260805 到哪一步了」,模型要自己判断该不该调工具、调哪个工具。

同一个插件,两种用法:一个要「确定性地执行」,一个要「智能地选择」。工具造出来只是第一步,怎么接进业务应用,才是插件真正产生价值的地方。

我们第一次做这种接入时,第一反应也是「工具节点拖进工作流不就行了」。真正动手才发现——「能被工作流调用」和「能被 Agent 智能调用」,是两套完全不同的消费逻辑:工作流要确定性,参数绑死上游变量;Agent 要自主性,参数靠模型按描述生成——同一个插件,要同时伺候好这两种形态,工具描述和调用形态都得重新设计。

这不是个例。任何「一个工具既要在固定流程里跑、又要能被对话智能调用」的场景都是这个模式:电商的物流查询、银行的账户查询、SaaS 的工单查询——确定性编排和模型自主调用,是插件消费的两种基本形态。

2. 场景痛点

这个流程的痛点,在接入业务时体现得最直接:

本质上,「怎么被消费」决定工具的价值——工作流要的是确定性,Agent 要的是可引导的自主性,两者都需要把工具描述和调用形态设计好。

3. 方案:为什么是双形态接入

选双形态接入,我们实际对比过:

这篇文章我们就用它把 01/02 的工具(get_current_time + get_order_status)分别接进工单流程(workflow)和门户对话(agent),并对比两种形态的差异、掌握插件运行日志的定位方法。

4. 整体架构

graph TD subgraph plugin["工具插件(106-01/02 成果:time_tool + order_tool)"] wf["workflow 形态:工具节点(参数显式绑定上游变量)"] ag["agent 形态:工具列表(模型按需选择,参数由模型生成)"] shared["两者共用同一插件实例,插件日志统一出口"] end wf --> shared ag --> shared subgraph app["workflow 验证应用(dify106_03_验证应用)"] start["开始(order_id + issue_text)"] t1["get_current_time(建单时间戳)"] t2["get_order_status(状态校验)"] sum["输出汇总"] end1["结束"] end start --> t1 --> t2 --> sum --> end1 subgraph chat["agent 验证对话(dify106_03_验证对话)"] tools["工具列表 = get_current_time + get_order_status(function_call 策略)"] end

链路很清晰:同一插件 → 双形态挂载 → 各自消费。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 调用成功率)

  1. llm description 给参数规则 + 示例order_id must match format WO- followed by 8 digits, e.g. WO-20260805——实测模型据此正确生成工单号。
  2. pre_prompt 里写工具边界:「时间类问题只调时间工具;工单类问题只调工单工具,不要混用」「闲聊不调用工具」——实测负例不触发。
  3. 格式错误时模型行为可控:pre_prompt 写「工单号格式不对时先请用户核对」→ 实测 abc123 不调工具直接提示格式(合理行为)。
  4. 可选参数给合法取值集合:模型会自主选择时区(问时间 → 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. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过