1. 为什么我决定不再在画布里手动拖节点?
先交代背景。我从 Dify 0.6 时代就开始用这个平台做 LLM 应用,那时候画布上拖节点、连线、配参数是每天的必修课。后来工作流越搭越多,从简单的问答机器人到带知识库检索、条件分支、代码节点、HTTP 请求的复合流程,我发现一个问题:画布拖拽看起来直观,实际上维护成本极高。一个二十多个节点的流程,每次改动需求,光是把连线捋清楚就要花半天。更麻烦的是,想复制一套类似流程到另一个应用,只能重新拖一遍,稍微手滑连错一条线,跑起来数据流就乱了。
直到后来我开始研究 Dify 工作流的底层存储结构,才发现画布上那些花花绿绿的节点和箭头,本质上全是一份结构化文本——DSL(Domain Specific Language,领域特定语言)。它是一段 YAML 或 JSON,里面定义了每个节点的类型、参数、坐标,以及节点之间的连接关系。这意味着,只要能把这段 DSL 写对,根本不需要进画布拖节点。
用一个类比来说:画布拖拽像是用鼠标在 Word 里排版文档,而直接编写 DSL 像是用 Markdown 写文章——第一眼没那么直观,但一旦熟练,效率和可控性完全不是一个量级。而把这件事再往前推一步,就是让模型直接帮你生成这份 Markdown:你用自然语言描述流程,让大模型输出 Dify 能识别的 DSL 文件,再导入画布,校验、发布,完事。
这就是这篇文章要聊的核心思路:怎么用自然语言生成工作流,怎么排版让 DSL 可读、可维护,怎么校验避开那些坑,最后怎么发布上线。这套方式尤其适合以下三类人:
- 已经画过不少 Dify 工作流、但觉得重复劳动太多的开发者;
- 团队里有大量相似流程需要批量搭建的运维或平台负责人;
- 想让业务同学参与流程设计,但业务同学完全不想接触画布和节点的产品经理。
说句实话,这套方法并不是要彻底放弃画布。画布在调试单节点、快速验证想法时依然好使。但从 0 到 1 搭建一个流程,或者从 1 到 100 复制一套流程,自然语言生成 DSL 显然是更聪明的路径。
2. 自然语言生成工作流的关键拆解
2.1 从"描述"到"DSL"的思维转换
想让模型帮你生成工作流,你得先搞清楚 Dify 的 DSL 到底长什么样。我不建议直接拿着一句"帮我写个工作流"就去问模型,那样大概率得到一段含糊的、不能导入的代码。正确的姿势是先自己读懂 DSL 的骨架。
Dify 的工作流 DSL 分成几个层次。最外层是应用信息,包含app名称、mode(比如workflow还是chat)、描述、版本号;往内是editor部分,包含画布上的节点和连线;每个节点有唯一的id、type、title,以及该类型特有的配置字段;连线用edges定义,标明从哪个节点的哪个输出端口连到哪个节点的哪个输入端口。
我用一个最简单的工作流举例,它只有两个节点:一个 LLM 节点,一个结束节点。核心结构是这样的:
app: description: 测试流程 icon: 🤖 icon_background: '#FFEAD5' mode: workflow name: 简单问答 orientation: vertical kind: app name: 简单问答 version: 0.1.0 editor: graph: edges: - source: llm-node-1 sourceHandle: '1' target: answer-node-1 targetHandle: '1' id: edge-1 nodes: - data: type: llm title: LLM version: '1' prompt_template: - role: system text: 你是一个助手,请回答用户的问题。 model: provider: openai name: gpt-4o-mini mode: chat completion_params: {} selected: true id: llm-node-1 position: x: 200 y: 200 type: llm - data: type: end title: 直接回复 version: '1' selected: false id: answer-node-1 position: x: 480 y: 200 type: end看懂这个结构之后,你就会明白,DSL 就是一份描述"节点配置 + 连接关系 + 画布布局"的配置清单。大模型最擅长的就是结构化的文本生成,你只要把需求描述得足够清楚,它就能在这个框架内产出合法内容。
2.2 提示词该怎么写:让 LLM 稳定输出工作流
我这里说的"用自然语言生成工作流",不是真的只丢给模型一句话。"自然语言"指的是表达的入口,但提示词本身需要结构化,否则模型很容易在节点类型、字段名上报错。
我尝试过很多次之后,总结了三条心得:
第一,必须先给角色定义。让模型明白它是"Dify 工作流 DSL 专家",熟悉 Dify 的节点类型,比如start、end、llm、code、if-else、question-classifier、knowledge-retrieval、http-request、template、variable-aggregator等。不定义角色,模型会用通用的自动化概念来理解你的需求,生成的字段七零八落。
第二,必须给输出格式模板。最好的做法是像我上面那样,先给出一份最简单的 DSL 示例,然后要求模型"严格按照此格式输出,不要额外解释"。大模型有很强的模仿能力,你给它一份"高分作文"作为例子,它交出来的东西就不会跑偏太远。这在 prompt 工程里叫 one-shot 或 few-shot,针对 DSL 这类高度结构化的内容,比单纯口述格式要求靠谱得多。
第三,你必须描述清楚业务逻辑,而不是让模型猜。比如"用户输入简历后,先判断岗位要求是否匹配,如果匹配则调用 LLM 总结,不匹配则输出感谢语"。这种描述里其实包含了一个条件分支,如果你不说清楚,模型可能生成一个顺序执行的工作流,完全没达到预期。
一个我试过比较好用的提示词框架大概是这样:
你是一名 Dify 工作流 DSL 专家。请根据以下业务需求,生成一份完整的 Dify workflow DSL(YAML 格式)。 业务需求:{具体流程描述} 要求: 1. 严格使用 Dify 工作流 DSL 结构,包含 app、editor.graph.nodes、editor.graph.edges。 2. 节点类型只能使用 {列举可用节点类型}。 3. 每次生成后请自我检查:所有节点是否都有唯一的 id;所有 edges 的 source 和 target 是否引用了已存在的节点 id;所有变量引用是否都来自上游节点输出。 4. 只输出 YAML 代码块,不要附加任何说明文字。这套模板我用下来,出错的频率明显降低。尤其是"自我检查"那一段,等于让模型承担了最初级的校验工作。
2.3 生成的 DSL 为什么需要"排版"和"校验"
模型生成 DSL,其实和我们写完代码是一个道理:能跑和能维护,是两码事。模型有时会把所有节点挤在一行,有时会把长文本塞进同一条 YAML 字段里,这种文件即便能导入 Dify,后续人工检查时也会非常痛苦。
所以"排版"这一步不能省。排版有两个层面:第一是格式化排版,让 YAML 缩进正确、结构清晰,这样即使出现问题,也能一眼定位是哪个节点、哪条边的配置有误;第二是布局排版,也就是 DSL 里每个节点的position字段,这个字段决定节点在画布上显示的位置,如果不做处理,导入画布后所有节点会堆在左上角,你还是得一场"手工摆棋子"。
我自己的习惯是:在本地用脚本对模型生成的 YAML 做一次格式化,再根据节点的连接关系自动计算坐标,让节点从上到下、从左到右按层次排开,这样导入画布之后,整个流程图就是清晰的、可读的。后面会详细说这套脚本怎么落地。
至于"校验",那是因为 Dify 画布导入 DSL 时,本身就会做一轮合法性检查。常见的错误包括节点类型拼写错误、引用不存在的节点 ID、缺少系统变量引用、边连接关系不匹配等。与其等导入时报错,不如在本地提前做过一遍校验。Dify 的校验逻辑其实不复杂,完全可以自己写个简易检查器,先过滤掉低级错误,再进画布面对真正需要人判断的问题。
3. 实操:用一条提示词生成、排版、校验并发布工作流
3.1 准备一个标准 Prompt 模板:案例是简历筛选工作流
理论讲再多,不如走一遍流程。这一节我用一个真实做过的事情——简历筛选工作流——来完整演示"自然语言生成、排版、校验、发布"的全过程。
背景是这样的:团队每天会收到大量简历,我需要建一个 Dify 工作流,输入一段简历文本后,自动判断候选人是否具备"Python 开发经验",如果具备,用 LLM 模型总结候选人的亮点并打分;如果不具备,直接输出一句"暂时不匹配"的提示。
业务逻辑非常适合用分支结构表达,我把它描述成一段自然语言:
需求: 用户输入一段简历文本。 第一步,从简历文本中提取关键技能列表。 第二步,判断技能列表是否包含 Python。 第三步,如果包含 Python,调用 LLM 模型对候选人进行亮点总结,并用结构化格式输出总结结果和匹配度评分。 第四步,如果不包含 Python,直接输出文本"该候选人与当前岗位暂不匹配"。 所有中间变量需要命名清晰,并且每一步的输入输出变量都要正确连接。把这段描述放进我之前提到的提示词框架里,让一个支持较长上下文的模型来生成 DSL。模型返回的是一份 YAML 文件,包含 start、parameter-extractor、if-else、llm、end 等节点。
我实际拿到的生成结果开头大概长这样:
app: description: 简历筛选 icon: 📄 icon_background: '#E0E0E0' mode: workflow name: 简历筛选工作流 orientation: vertical editor: graph: edges: - id: edge-1 source: start-node sourceHandle: '1' target: extract-node targetHandle: '1' ... nodes: - data: type: start title: 开始 variables: - variable: resume_text label: 简历文本 required: true type: paragraph id: start-node position: x: 80 y: 200 type: start要注意,这份生成结果里有几个节点是"看起来合理但细节有问题"的,比如parameter-extractor节点的模型配置可能缺失、if-else的条件表达式写法可能不符合 Dify 的语法。所以生成之后不能直接导入,先做本地处理。
3.2 在本地用脚本生成并格式化 DSL
模型输出的是纯文本 YAML,我一般不会直接复制粘贴到 Dify 平台,而是先存成.yaml文件,用脚本处理两件事:格式化和坐标排版。
格式化方面,我用 Python 的ruamel.yaml库,它和常见的pyyaml不一样,可以最大程度保留字段顺序和注释。我的处理思路是:
pip install ruamel.yaml然后写一个非常简单的脚本,把模型输出的原始文本读进来,重新 dump 成规整的 YAML:
from ruamel.yaml import YAML yaml = YAML() yaml.preserve_quotes = True yaml.width = 4096 with open('raw_dsl.yaml', 'r', encoding='utf-8') as f: data = yaml.load(f) with open('formatted_dsl.yaml', 'w', encoding='utf-8') as f: yaml.dump(data, f)格式化之后,再处理坐标。这一步很多人会忽略,但我要强调:Dify 的画布布局是完全由 DSL 里的 position 字段决定的。如果所有节点的 position 都是 (0, 0),导入后节点的卡片会叠成一团,你还得手动一个个拉开,等于把拖拽的功夫又捡回来了。
所以我写了一个简单的层次布局算法:从 start 节点出发,按拓扑排序给每一层节点分配纵坐标,每一层内部根据节点数量均分横坐标。当然,算法本身并不需要多复杂,能用就行。大致逻辑是:
def auto_layout_nodes(nodes, edges): # 计算每个节点的入度,找到起始层 in_degree = {node['id']: 0 for node in nodes} for edge in edges: in_degree[edge['target']] += 1 # 从入度为 0 的节点开始,做简单的层分配 current_layer = [n for n in nodes if in_degree[n['id']] == 0] layer_index = {n['id']: 0 for n in current_layer} depth = 0 while current_layer: depth += 1 next_layer = [] for node in current_layer: for edge in edges: if edge['source'] == node['id']: target_id = edge['target'] if target_id not in layer_index: layer_index[target_id] = depth next_layer.append(next(t for t in nodes if t['id'] == target_id)) current_layer = next_layer # 按层设置坐标 for node in nodes: node['position'] = { 'x': 200 + layer_index.get(node['id'], 0) * 320, 'y': 200 + len([n for n in nodes if layer_index.get(n['id'], 0) == layer_index.get(node['id'], 0) and n['id'] < node['id']]) * 120, }实际做的时候,横坐标间距我一般设 300 到 400,纵坐标间距设 120 到 150,这样画布上节点间距不会太挤,也不会太疏。生成好的坐标重新写回 DSL 的node.position字段,这一步完成之后,文件才算"排版完毕"。
3.3 导入画布并处理校验提示
格式化完成后,打开 Dify 应用编辑页面,在画布右上角找到导入功能,选择刚才的 YAML 文件。导入时 Dify 会做一次解析,如果语法有问题,它会直接提示无法导入。如果导入成功,你会在画布上看到节点已经自动排好位置,这时候第一轮校验就过了,但别高兴太早,它只是说明 YAML 语法合法,不等于业务流程正确。
就拿我的简历筛选流程来说,导入后 Dify 提示了这样一个问题:
节点 "判断是否具备 Python 经验" 的条件配置无效原因是我让模型生成的if-else节点里,条件表达式写成了:
conditions: - key: '{{#extract-node#skills}}' comparison_operator: contains value: Python单独的 YAML 解析没问题,但 Dify 的校验器要求key必须引用真实的变量路径,而且contains操作符只支持字符串类型的变量。parameter-extractor输出的skills如果被模型定义成数组,这个条件就匹配不上。我当时的处理方法是:在parameter-extractor节点里增加一个text类型的输出,把技能列表先用Join拼成文本,再让if-else去判断。
这种问题在自然语言生成的时候几乎无法完全避免,因为模型对 Dify 特定节点内部字段的约束理解有限。所以我的态度是:把校验当成迭代过程。第一次导入报错很正常,看提示、改字段、再导入,两三轮之后基本就通了。
3.4 发布与版本管理
当 DSL 成功导入画布、节点调试也跑通之后,最后一步是发布。这里我特别提醒一下:Dify 的发布分两层。第一层是把草稿保存为新的 DSL 版本,第二层是把这个版本发布到线上生效。在 Dify 界面里,右上角的"发布"按钮会把当前草稿固化成一条新的版本记录,同时更新线上运行所引用的工作流。
如果你和我一样是用 API 方式管理多套环境,发布逻辑也是可以通过 API 完成的。具体来说,Dify 的控制台 API 里提供了获取草稿、更新草稿、获取发布记录等功能。我习惯将每次生成的 DSL 用 Git 管理起来,不同分支对应不同环境,合并到 main 分支后由脚本调用 API 自动发布,核心命令是:
curl -X POST https://your-dify-server/console/api/apps/{app_id}/workflows/publish \ -H "Authorization: Bearer {api_key}" \ -H "Content-Type: application/json"这样每次改动都有迹可循,出问题时可以一键对比版本。Dify 画布本身也提供版本记录和回滚功能,编辑页面左侧的"版本历史"面板可以查看每一次发布记录,点进去就能预览并恢复对应版本。但线上多环境的话,版本记录完全依赖平台不太够用,把 DSL 纳入 Git 才是真正可控的做法。
4. 常见校验错误与"看得见的坑"
4.1 高频错误速查表
自然语言生成 DSL 这件事,实操中最消磨耐心的就是反复出现的校验错误。我用表格把这段时间遇到过的高频问题整理出来,方便照着排查:
| 错误现象 | 根本原因 | 处理方式 |
|---|---|---|
| 无法导入文件,提示 YAML 解析错误 | 模型输出内容混入了 Markdown 标记、中文标点或多余的说明文字 | 用脚本提取代码块内容,并做一次 YAML 再序列化 |
| 节点不存在或类型无效 | 模型使用了 Dify 不支持的节点类型,比如decision、input | 在提示词中用枚举方式限制节点类型,导入前先检查node.type是否在合法列表内 |
| 边的 source 或 target 找不到对应节点 | 模型生成的节点 id 与连线引用的 id 不一致 | 用脚本扫描所有edges,对照nodes的 id 列表做一致性检查 |
变量引用无效,如{{#node#var}}的变量名不存在 | 上游节点并没有定义这个输出变量 | 在提示词中明确要求"所有变量引用必须来自上游节点输出";手动对照节点输出字段修正 |
| 条件表达式无效 | if-else的条件里引用了数组类型变量,或比较操作符用错 | 在 Dify 中打开节点,使用条件编辑器重新选择变量和操作符,然后导出修正后的 DSL |
| 节点坐标重叠 | 所有节点 position 都相同或相近 | 用自动布局脚本按层次分配合法坐标 |
这些错误里,百分之八十不需要人工动脑子修复,脚本就能逮住。你要做的不是一条条肉眼检查,而是把检查脚本化,固化下来每次生成完就跑一遍,把低级错误扼杀在导入之前。
4.2 两个我反复踩的错误:变量引用与节点断连
第一个高频坑是变量引用路径错误。Dify 的变量引用格式是{{#节点id#输出变量名#}},模型的记忆偶尔会出错,引用了一个上游节点根本不会输出的变量。比如简历筛选流程里,模型在 LLM 节点的输入里写了{{#extract-node#summary#}},但extract-node的实际输出里并没有summary这个字段,它叫skills和raw_text,导入时画布会飘红。
这个问题的教训是:别指望模型记住你整个流程里每个节点输出了什么。我的做法是,生成之后先让模型输出一份"变量声明清单",列出每个节点的输出变量名和类型,然后自己对照检查一次。相当于让模型给自己的代码写一份数据字典,虽然多花几十秒,但省掉后面反复导入试错的时间。
第二个坑是节点虽然存在,但根本没有被连线,也就是"断连"状态。自然语言描述流程时,模型经常生成一个节点,却在 edges 里忘了把它串起来。例如它规划了llm-node的输出要给end-node,但在 edges 里只写了 start 到 extract、extract 到 if-else,没有写 if-else 到 llm 的边。结果这个 LLM 节点成了一个孤岛,运行时永远不会被触发。
我在脚本里加了一条检查规则:从 start 节点出发做遍历,所有无法从 start 到达的节点全部报告出来。这条规则写起来很简单,但救了我好多次。如果你也想做,核心逻辑就是从 start 节点开始 BFS,遍历 edges,最终对比一下有多少节点在可达集合里,没在的就是孤岛节点。
4.3 校验之后还有运行时错误怎么办
即使 DSL 成功导入、节点也没有断连,真正跑起来可能依然会报错。最常见的有三类:
第一类是模型服务调用失败。这个牵扯到 Dify 的环境配置,比如你在 LLM 节点里选择了某个模型供应商,但平台上对应的 API Key 没有配置或已失效。界面上会提示类似 An error occurred during credentials validation 的信息。遇到这种问题,先去检查模型供应商的凭据,别在 DSL 层面浪费时间。我的经验是:自然语言生成 DSL 时,提示词里应当限定模型供应商和模型名,否则模型可能会填一个你没配置过的模型上去,导入全绿、运行必挂。
第二类是HTTP 请求节点的连接问题。比如调内部接口时connect timeout或ssl certificate错误。这里要分情况,如果是调用公网接口但服务证书有问题,可以在节点配置里检查 TLS 校验开关;如果是内网服务,先确认网络能通。有一次我发现 DSL 生成后 HTTP 节点自动配了https://,但目标服务其实是纯 http,改掉协议后立刻通了。
第三类是知识库检索为空。如果你的工作流里有knowledge-retrieval节点,经常遇到提示 "unstructured api url is not configured for doc file processing" 这类问题。这其实不是 DSL 的问题,而是 Dify 安装时没启动文档处理服务。遇到这种错误,不要改 DSL,去检查 Dify 的环境变量里有没有配置UNSTRUCTURED_API_URL和对应的 API Key,配置好之后重启相关容器就行了。
我的建议是,把"校验"分成三道关:第一道关是脚本静态检查(类型、引用、连通性),第二道关是 Dify 导入校验(平台语法检查),第三道关是模拟运行测试(真实跑一遍上线前必须做)。真正做到第三道关,一个"生成式工作流"才算是真正可用。
5. 把"自然语言生成工作流"变成团队的日常工具
5.1 模板库:沉淀自己的 Prompt 工作流
一个人用自然语言生成工作流,算是新鲜体验;整个团队都用出效率,就得靠沉淀。我发现最有效的方式是建立一套"Prompt 模板库"。模板库不是把生成结果存下来,而是把"描述业务需求 → 生成 DSL → 本地校验 → 导入发布"的标准 Prompt 保存下来。
我一般会在团队 Wiki 里维护一个页面,里面放着一套提示词模板,包含几个必备模块:
- 角色设定:统一用"Dify 工作流 DSL 专家"这个角色;
- 能力边界:明确规定只允许使用哪些节点类型,避免模型自由发挥;
- 格式要求:要求输出纯 YAML,不带解释,不出现 Markdown 代码块;
- 自检清单:让模型在输出前做一次逻辑检查;
- 示例片段:放一个最小可用的 DSL 样例作为 few-shot 参考。
这套模板的好处是,新来的同学不需要懂 Dify 节点细节,照着模板填入业务描述,就能产出一份"初步可用的 DSL"。产出之后如果导入报错,再对着第 4 节的排查表处理。这个过程本质上是把"平台使用经验"转移到了提示词里,让模型替经验不足的人补齐节点知识。
我还习惯把常用的业务模式做成"意图片段"。比如"使用知识库检索增强生成"对应knowledge-retrieval + llm + end的节点组合,"表单分类处理"对应question-classifier + 多个分支的结构。写业务需求时只描述意图,生成阶段由提示词模板把意图映射成节点组合。这样长期积累下来,模板库本身就成了团队的流程资产库,后期搭建同类工作流基本 5 分钟内出一条可用草稿。
5.2 用 API 做批量导入和发布
当工作流的数量开始多起来,比如要一次性初始化几十个应用的流程,手动操作画布导入就太慢了。这个场景更适合走 Dify Console API。
Dify 的 Console API 需要管理员权限,主要是浏览器端使用的内部 API。核心的有这么几个:
GET /console/api/apps:获取应用列表;GET /console/api/apps/{app_id}/workflows/draft:获取某个应用的当前草稿 DSL;POST /console/api/apps/{app_id}/workflows/draft:更新草稿 DSL;POST /console/api/apps/{app_id}/workflows/publish:发布当前草稿为线上版本。
基于这套接口,我写过一个批量发布脚本:先读一个"应用 ID 与 DSL 文件路径"的映射表,循环调用更新、再发布。整个过程十几行代码的事,但能省下大量机械操作。
for row in $(cat app_dsl_mapping.csv); do app_id=$(echo $row | cut -d',' -f1) dsl_file=$(echo $row | cut -d',' -f2) curl -X POST "https://your-dify-server/console/api/apps/${app_id}/workflows/draft" \ -H "Authorization: Bearer ${CONSOLE_API_KEY}" \ -H "Content-Type: application/json" \ -d "@${dsl_file}" curl -X POST "https://your-dify-server/console/api/apps/${app_id}/workflows/publish" \ -H "Authorization: Bearer ${CONSOLE_API_KEY}" done需要注意的是,Console API 的鉴权和普通 App API 不同,它要求的是登录后的管理员令牌,不是应用里生成的那个 API Key。如果你熟悉 Dify 的源码,可以找到对应的鉴权逻辑;如果不熟,最简单的办法是在浏览器登录 Dify 管理后台后,从开发者工具里复制console_token作为 Bearer Token 使用,但要注意这个 Token 有过期时间,适合临时脚本,不适合长期定时任务。
另外,批量发布之前一定要有演练环境。我吃过一次亏:脚本里漏了一个应用 ID,结果把一份没有知识库节点的工作流发布到了生产应用上,线上问答直接丢掉了知识库上下文。从那以后,所有批量发布脚本都必须支持--dry-run参数,只输出将要执行的动作,不真正调发布接口。
5.3 团队协作与版本回滚
用自然语言生成工作流带来的另一个变化是,流程的"源码"从画布转到了文本,这给协作和版本管理带来了便利。以前,大家讨论一个工作流,只能把截图发到聊天群里,指着图片说"这里改一下"。现在可以直接把 DSL 文件放在 Git 仓库里,变更记录一目了然。
我推荐的仓库结构是:
dify-flows/ ├── apps/ │ ├── resume-screening/ │ │ ├── draft.yaml │ │ ├── prompt.md │ │ └── README.md │ └── customer-support/ │ ├── draft.yaml │ └── prompt.md ├── templates/ │ ├── basic-llm-prompt.md │ └── knowledge-rag-prompt.md └── scripts/ ├── format_dsl.py ├── validate_dsl.py └── publish.py每个应用的目录下都有prompt.md,也就是生成这份 DSL 时用的自然语言描述。这样一来,半年后回来看这个流程,你打开prompt.md就知道当初做的是什么业务,打开draft.yaml看到的就是可运行的完整定义。业务同学提需求时改prompt.md,技术同学改draft.yaml,分工也变得更清晰。
版本回滚在 Git 里是一件非常自然的事:git diff看两个版本的差异,git revert回退到上一版,然后重新走一遍校验和发布流程。相比之下,在 Dify 画布上一层层点"版本历史"去恢复,效率和可追溯性都差一截。
6. 写在最后:这套流程还能怎么继续扩展
最后聊点实际的体会。用自然语言生成工作流这件事,我最大的感受是:它并没有把画布变成一个完全没用的东西,而是把画布从"工作台"降级成了"预览面板"。现在我的习惯是:先用提示词生成 DSL,导入后打开画布,不再是为了连线配参数,而是为了从头到尾走读一遍流程,确认分支逻辑、变量传递是否符合业务直觉。画布的图可视化能力依然是不可替代的,只是我不再需要它作为唯一的创作入口。
往远了说,这条路还有几个可以继续扩展的方向。一个是把 Dify DSL 生成能力接进对话助手,让用户直接在聊天框里说"帮我建一个客户投诉分类工作流",后端调用模型生成 DSL、自动创建应用、自动发布,全程无需人工登录后台。另一个是把生成逻辑做成 CI 流程的一部分,每次业务需求变更,提交 PR 触发自动生成、自动校验、自动生成预览链接,相当于给工作流开发配了一套"测试环境"。这些想法我都在逐步尝试,目前至少验证了"自然语言 → DSL → 校验 → 发布"这条链路是稳定可行的。
如果你也在 Dify 里做过不少工作流,真心建议你花一个下午把这条链路跑通。一旦跑通,你再回头看那种"在一个二十节点流程里找一条断开的连线"的日子,大概率会和我一样,不想再回去了。