工作流由开发者预先编排一套完整有序的执行步骤。执行顺序、分支条件全部提前定义,运行时严格按照预设逻辑执行,属于确定性流程。适合业务场景确定、所有处理步骤可以提前梳理清楚的任务。工作流里可以包含 LLM 节点、代码、条件分支、工具调用等真实执行单元。
本章通过两个案例带你吃透 Dify 开发核心能力:工作流基础概念、Agent、Skill、知识库,同时理解 Dify 各类核心节点的作用与实操用法,包含:开始 (用户输入 / 触发器)、输出、循环、迭代、工具、变量赋值、问题分类器、参数提取器、人工介入、列表操作、文档读取器、代码执行、知识检索、模板转换、LLM、变量聚合器、条件分支。
1. 智能邮件答疑
1.1 业务目标
接收学员咨询邮件,自动区分技术问题与闲聊内容,对 C/C++ 编程问题给出专业解答,非技术问题回复能力范围提示,最终输出标准化回复文本。
输入示例(学员咨询邮件)
老师您好: 感谢您抽空阅读我的邮件。我在学习 C++ 开发的时候,对NULL和nullptr一直比较困惑,想请教您两个问题: 1. C++ 里NULL和nullptr二者的区别是什么? 2. 在实际编码的时候,我该如何选择使用NULL还是nullptr? 另外还有一个无关的小问题,想顺便问问痘痘怎么预防。 麻烦老师有空的时候帮忙解答,谢谢老师!
输出规则:如果判定内容为闲聊: 亲,除了技术问题,没有时间跟你闲聊啊。如果识别到技术咨询问题,输出结构化答疑结果:
{
"replay": "同学,你好。我已经收到你的问题,现对你的问题解答如下:\n\n\n问题:C++里NULL和nullptr二者的区别是什么?\n答案:NULL是C语言中的宏,通常定义为整数0或(void*)0,在C++中可能定义为0或0L,类型为整型,导致在重载或模板推导时可能产生歧义或类型错误;nullptr是C++11引入的关键字,专门表示空指针,类型为std::nullptr_t,可隐式转换为任何指针类型,但不能转换为整数,且语义更明确、类型安全,推荐在现代C++中使用。\n\n问题:在实际编码的时候,我该如何选择使用NULL还是nullptr?\n答案:在现代 C++ 中,推荐使用 nullptr 来表示空指针,而不是 NULL。因为 nullptr 是类型安全的,能避免函数重载或模板推导时的歧义,而 NULL 本质上是整数 0 或 (void*)0,可能导致错误匹配。\n\n问题:痘痘怎么预防? \n答案:我知识有限,暂不能为你提供解答。\n\n\n回复时间:2026-09-02 04:55:19\n回复人:老孟"
}

1.2 实操步骤
整体流程:接收邮件输入 → 提取全部问题 → 判断是否存在有效问题 → 迭代逐个处理问题 → 区分 C/C++ 技术问题 / 其他问题 → 知识库检索 + LLM 生成答案 → 聚合所有问答结果 → 获取当前时间 → 模板格式化输出 → 人工审核 → 分支输出审核结果。
(1)创建一个最简单的工作流,包含【开始】和【输出】两个节点。开始节点用于接受用户输入邮件内容。
(2)在开始节点后增加【参数提取器】,用于从输入的邮件内容中提取出所有的问题,并存储在数组中。节点指令:
你的任务:提取文本中所有需要解答的业务问题。 规则: 1.每一条问题只能包含1个疑问点,只要存在2个不同疑问,必须拆成2条,严禁合并。 2.同时提取带问号的显性问题,以及没有问号、但用户希望得到答复的隐含诉求,转为问句。 3.剔除单纯陈述、发泄情绪的内容,不生成原文没有的信息。
(3)在【参数提取器】后面增加【条件分支】,条件是:问题列表不为空。如果 True,则进行问题的后续处理。如果 False,则输出:亲,除了技术问题,没有时间跟你闲聊啊。
(4)在上一步的【条件分支】的 True 分支后面增加【迭代】,用于逐个生成问题的答案。
(5)在【迭代】内部创建【问题分类器】,如果问题属于 C/C++ 相关的问题。则进行一个分支处理,如果 不属于,另外一个分支处理。
(6)在上一步的【问题分类器】的不是 C/C++ 相关问题分支后面增加【模板转换】,用于生成对非 C/C++ 问题的回答。
问题:{{ ques }}
答案:我知识有限,暂不能为你提供解答。
(7)在上一步的【问题分类器】的是 C/C++ 相关问题的分支后面增加【知识检索】,此处需要配置构建知识库。用于从知识库中检索与问题相关的知识片段。
(8)在【知识检索】后增加【LLM】节点,来基于知识库生成问题答案,此处的 system prompt 如下:
你是一个编程问题专家,请严格依据给到的参考资料 {{#context#}},简明扼要回答用户的问题。最终输出的格式如下:
问题:原始问题
答案:问题答案
不要添加任何额外说明、推理、思考或注释。
(9)在【迭代】内增加【变量聚合器】,将前面的【模板转换】和【LLM】输出的结果都连接到【变量聚合器】,将两个分支的结果合并一处。
(10)在【迭代】后增加【循环】,在【循环】内部反复调用【获取当前时间工具】,直到获得时间为止,并通过【变量赋值】将获得到的时间字符串赋值到【循环】变量上,方便将结果传递到后续步骤。
(11)在【循环】后增加【模板转换】,用于将问题和答案、时间按照要求格式化。
同学,你好。我已经收到你的问题,现对你的问题解答如下:
{% for qa in qas %}
{{ qa }}
{% endfor %}
回复时间:{{ time }}
回复人:老孟
(12)在【模板转换】后增加【人工介入】,人工对生成的内容进行审核,具体设置如下:

这里注意:如果需要邮件的话,需要对 .env 文件新增如下配置,测试环境下不需要配置。
# 进行此项配置 APP_WEB_URL=http://localhost # 新增邮件配置 MAIL_TYPE=smtp MAIL_DEFAULT_SEND_FROM=发件人地址 SMTP_SERVER=你的 SMTP 服务器 SMTP_PORT=端口 SMTP_USERNAME=你的用户名 SMTP_PASSWORD=你的密码
(13)在【人工介入】后面增加三个【输出】分别对应,通过、拒绝、超时三个人工审核的结果输出的内容。
2. 多类型文件处理
用户上传混合文件,例如:2 个 txt 文本文件 + 2 个 webp 图片文件。文本与图片处理逻辑不同,通过工作流实现文件分类路由与解析。对于输入文件,展示文件内容,对于图片文件,则输出图片文件名。

通过这个案例,掌握列表操作、文档提取器、代码执行等节点的作用与用法。
(1)用户输入多个文件:2个 txt 文本文件、2个 webp 图片文件
(2)由于图片文件和文本文件处理方式不一样,我们通过【列表操作】按照类型将上传的文件路由到不同的数组中
(3)针对文件,通过【迭代】+【文档读取器】获得所有上传文件的内容
(4)针对图片,通过【代码执行】获得上传的所有图片名
(5)通过【输出】显示出处理结果。



冀公网安备13050302001966号