Dify 里搭一条多阶段 Workflow ——把"复杂任务流水线"平移成多阶段 Workflow

举报
yd_280927530 发表于 2026/10/29 20:56:00 2026/10/29
【摘要】 一、蓝本:课程第八章的流水线在干什么先把课程那套组装回忆一遍(细节见第八章 08 篇导读):一次"开始制作" → 备料入库(brief 制作要求 / skills 方法手册 / contracts 交付契约) → Graph 定阶段(世界观 → 剧情大纲 → 场景……) → 每个阶段:应用生成任务单 → 总导演 story-director 用 task 委派一次 → 角色 Agent ...

一、蓝本:课程第八章的流水线在干什么

先把课程那套组装回忆一遍(细节见第八章 08 篇导读):

一次"开始制作" → 备料入库(brief 制作要求 / skills 方法手册 / contracts 交付契约)
→ Graph 定阶段(世界观 → 剧情大纲 → 场景……)
→ 每个阶段:应用生成任务单 → 总导演 story-director 用 task 委派一次 → 角色 Agent 干活
→ 产物写 /staging/<任务ID>/(候选)→ 审核通过后转正 → 后续角色按 readFiles 读正式版

世界观设计师那份任务单(课程示例,四字段是主角):

{
 "assignee": "world-designer",
 "goal": "设计世界规则与人物档案",
 "readFiles": ["/revisions/1/brief.md", "/contracts/world.schema.json", "…"],
 "writeFiles": ["/staging/<任务ID>/world.json", "…"],
 "skillPath": "/skills/world-building/SKILL.md",
 "acceptanceCriteria": ["符合世界规则", "角色数量与制作要求一致"]
}

配套护栏:总导演全局 write deny(只委派不动手)、委派边界三查(不是 task 不行、委派对象不对不行、已委派过再委派也不行)、角色只能写任务单的 writeFiles。

本篇的任务:把这张图逐块搬到 Dify。


二、总装:主 Workflow 当总导演,三个子流程当角色

课程的三层分工,在 Dify 上各归各位:

课程的层 Dify 里变成
LangGraph Graph 定阶段 主 Workflow 的画布连线
Agent 应用准备任务单 开始节点输入变量 + 各子流程的输入定义
总导演 task 委派 工具节点调用子流程("工作流"当工具)

总装图(本篇要搭的东西):

主 Workflow(总导演)
开始(brief 文本、角色数量、场景数量)
  → [工具] 世界观设定子流程
      输入:brief、方法说明、角色数量 → 输出:world_json(世界规则 + 人物档案)
  → 代码节点:契约校验(= acceptanceCriteria 的结构版)
  → 条件分支:不达标 → 失败支路;达标 ↓
  → [工具] 大纲生成子流程
      输入:world_json、方法说明、场景数量 → 输出:outline_json(含 scenes 数组)
  → 代码节点:契约校验 → 条件分支
  → 迭代节点:遍历 scenes
      每次迭代体 → [工具] 场景生成子流程 → 单个场景 JSON
  → 迭代输出:场景数组(迭代节点的输出本身就是列表)
  → 代码节点:终检(拼装完整产物包)
  → 结束

子流程怎么建(配置意图): 每个角色单独建一个 Workflow 应用——开始节点声明输入变量、结束节点声明返回什么;再回主流程,用"工具-工作流"把它当工具节点调用。填进去的输入 = 任务单,返回的输出 = 交付物。

⭐ 子流程天然带着课程 mode: isolated 的消息隔离:总导演只拿得到输出变量,看不到子流程内部的提示词与中间步骤。

先做一个选择:总导演用普通流程连线,还是 Agent 节点? 课程的总导演是"拿着 task 工具的 Agent";本篇选前者——连线顺序确定,Agent 节点的自主性这一版用不上,还要搭上"护栏变软"的代价(第七节展开)。


三、任务单平移:四字段各换什么形状

任务单字段 课程里 Dify 里 档位
readFiles 路径列表,角色自己 read_file 子流程输入变量:资料正文以文本参数或知识库检索结果传入 换形状
skillPath SKILL.md 按需加载 方法说明文本参数传入,或方法文档进知识库检索 换形状(丢渐进披露)
writeFiles staging 候选路径 子流程输出变量:约定返回结构化 JSON 换形状(路径没了)
acceptanceCriteria contracts Schema + Validator 主流程代码节点校验输出 JSON 换形状

readFiles:从"自己去读"变成"填表给你"。 课程里路径只是位置,读不读由角色决定,所以有"备料 ≠ 读到"的失效点。Dify 里资料以变量值进入,进不进上下文由主流程决定——失效点跟着变形:传了参数 ≠ 模型用了,验收要看输出,别信输入。

skillPath:方法传入的两条近似路。 课程 Skill 的精髓是渐进式披露(先给简介,要用再读全文)。Dify 没有原生对应:固定步骤干脆封进子流程——方法从"模型读的说明"变成"画布上的固定序列",软建议变硬步骤;可变的方法写成文档放知识库按需检索——但检索不命中就不进上下文,披露层级只能靠分段设计近似(02 篇差距清单②)。

writeFiles:从"写到哪"变成"返回什么"。 在子流程里约定输出变量的 JSON 形状。本篇自定一份"世界产物契约",代替课程的 world.schema.json:

{
 "worldRules": "……世界规则……",
 "characters": [
  { "name": "……", "identity": "……", "goal": "……" }
]
}

acceptanceCriteria:从契约文件变成校验代码。 见下一节。


四、验收:代码节点当 Validator

主流程在每个子流程调用之后接一个代码节点做结构校验——这就是课程的 JSON Schema / Zod 校验换的家。示例(Python,检查上节那份契约,含"角色数量与制作要求一致"这条验收标准):

import json
​
def main(world_text: str, required_characters: int) -> dict:
   # ① 先保证是合法 JSON:LLM 偶尔会在外面包一层说明文字
   try:
       data = json.loads(world_text)
   except json.JSONDecodeError as e:
       return {"valid": False, "errors": f"不是合法 JSON:{e}"}
   if not isinstance(data, dict):
       return {"valid": False, "errors": "world 产物必须是 JSON 对象"}
​
   errors = []
   # ② 结构检查:worldRules 必须是非空文本
   if not isinstance(data.get("worldRules"), str) or not data["worldRules"].strip():
       errors.append("缺 worldRules(世界规则)")
​
   # ③ 结构检查:characters 是非空数组,每个角色三字段齐全
   characters = data.get("characters")
   if not isinstance(characters, list) or not characters:
       errors.append("characters 必须是非空数组")
   else:
       for i, c in enumerate(characters):
           for field in ("name", "identity", "goal"):
               if not str(c.get(field, "")).strip():
                   errors.append(f"第 {i + 1} 个角色缺 {field}")
​
   # ④ 数量线:acceptanceCriteria 里的"角色数量与制作要求一致"
   if isinstance(characters, list) and len(characters) != required_characters:
       errors.append(f"角色数量 {len(characters)},要求 {required_characters}")
​
   return {"valid": len(errors) == 0, "errors": ";".join(errors)}

两个课程味的分工提醒:

  • 代码节点只查结构。 "worldRules 非空"查得了;"符合世界规则"这种语义项查不了——先用数量线这类可硬化的检查顶住,语义审查(Judge)是下一篇的正题。能直接比较的交给代码,需要理解含义的交给模型——课程第十章 03 篇的分工原样成立。

  • 校验失败走条件分支的失败支路。 本篇先做"记录并终止";带问题报告返工的完整闭环,下一篇补上。


五、迭代节点:scenes 数组变成一串场景

大纲子流程输出的 outline_json 里约定一个 scenes 数组(本篇自定:每个元素含 title、summary 两个文本字段)。主流程用迭代节点遍历:

  • 迭代输入:outline 的 scenes 数组;

  • 迭代体:一次"场景生成子流程"调用——输入是当前元素的 summary、上游的 world_json、方法说明;输出是单个场景的 JSON;

  • 迭代结束,节点输出的数组本身就是场景列表,交给终检(变量聚合器不是干这活的:它汇合的是多路分支的变量,05 篇审核分支的汇合才是它的用法)。

课程对应:场景编剧按大纲逐场景干活;Dify 把这个 for 循环画在画布上。注意迭代体之间的隔离与课程子 Agent 一致——第 2 个场景默认看不到第 1 个场景生成了什么。课程里角色靠 Workspace 共享上下文;这里若要场景衔接,必须把"已生成内容"显式传给下一轮——这正是下一节差距的现场。


六、最大的差距:没有 Workspace,产物在变量里流动

课程流水线的三样东西——staging(候选暂存)、promote(审核转正)、readFiles(后续角色读正式版)——全都建立在"有文件系统"上。Dify 没有:

  • 中间产物以变量传递:world_json 这个变量本身就是"候选品"。

  • "候选 → 转正"的近似:过没过代码节点校验,就是候选与正式的分界线。画布上只从"校验通过"支路往下连线,下游引用到的 world_json 必然是过了校验的版本——用结构保证"没转正不被消费"。

  • 要跨次运行留产物:变量出了这次运行就没了。需要持久化,就用 HTTP 节点把校验通过的产物写到外部存储——候选与正式版用不同接口或路径区分,把 staging / releases 的语义搬到存储服务那一侧。

⚠️ 课程警告"模型输出 ≠ 文件已落盘",在 Dify 变形为"变量有值 ≠ 产物已持久化":LLM 说写好了、代码节点验过了,都还只在内存里。落盘永远是一次显式的 HTTP 写入;关键产物写完记得读回核对——课程"写后读回"的习惯原样平移。


七、护栏平移:从"运行时拦截"退到"结构约束"

课程三道护栏,在 Dify 里各剩多少:

课程的护栏 课程实现 Dify 近似 剩多少
总导演只委派不动手(全局 write deny) permissions + 提示词 总导演用普通流程节点——根本没有工具可乱调 结构上更硬
只委派一次 委派边界三查(运行时拦截) 画布一条调用边只执行一次;迭代体内按数组长度执行 结构保证;次数=数组长度要靠输入校验兜住
角色只写 writeFiles 白名单 文件路径级权限 子流程只声明约定的输出变量;节点不挂多余工具;敏感操作收进固定参数的代码 / HTTP 节点 粒度粗:路径白名单退成接口白名单

如果总导演改用 Agent 节点(第二节的选择题):自主性回来一点,护栏就软一截——挂着的子流程工具都可能被它选、选几次由策略决定,"只委派一次"只剩提示词约束,要靠后续代码节点校验"产物只有一份世界观"来兜底。

差距明说:课程那种"参数级的运行时拦截",Dify 没有——拦不住"这一次调用的参数想写哪里",只能在两层设防:结构层(连了什么节点、子流程声明了什么变量)与授权层(工具允不允许用,部分工具需授权)。所以越敏感的操作,越该从"模型决定参数"改成"代码节点固定参数"——02 篇第 11 条的结论,在这里落地成做法。


平移检查清单

课程机制 这一篇搭出来的 Dify 形态
总导演 story-director 主 Workflow 的普通流程连线
task 委派 + isolated 消息隔离 工具节点调用子流程(只见输出变量)
任务单四字段 输入变量(readFiles / skillPath)+ 输出变量(writeFiles)+ 校验代码(acceptanceCriteria)
contracts Schema + Validator 代码节点结构校验 + 条件分支
staging / promote 候选变量 / 校验通过后才被下游引用;持久化走 HTTP 节点写外部存储
只委派一次护栏 一条边一次执行的结构保证 + 代码节点兜底
按大纲逐场景生成 迭代节点(输出的数组即场景列表)

如果只记 3 句话

  1. 任务单从"给模型看的说明书"变成"给子流程填的表单":readFiles / skillPath 是输入变量,writeFiles 是输出变量,acceptanceCriteria 是代码节点——接口变硬,交接更稳。

  2. 没有 Workspace,产物在变量里流动:"过没过校验"就是候选与转正的分界;要跨次运行留产物,就显式用 HTTP 节点写外部存储。

  3. 护栏从"运行时拦截"退到"结构约束 + 工具授权":粒度变粗是明账,补救是输入白名单化、不挂多余工具、敏感操作固定参数。


一个自检题

世界观设计师任务单的四个字段,在这条 Dify 流水线里各落成什么形状?哪个字段"形状变了但更硬了",哪个丢了最核心的机制?

参考答案:

  • readFiles → 子流程输入变量(资料正文/知识库检索结果直接传入);skillPath → 方法说明文本参数,或方法文档进知识库检索;writeFiles → 子流程输出变量(约定返回的 JSON);acceptanceCriteria → 主流程代码节点校验。

  • 更硬的是 writeFiles 与 acceptanceCriteria:任务单从"给模型看的说明书"变成"给子流程填的表单 + 代码验收",交接是硬接口。

  • 丢得最狠的是 skillPath:渐进式披露没了——检索不命中就不进上下文(02 篇差距清单②),只能靠分段设计或子流程封装近似。


承上启下

本篇的失败支路只做了"记录并终止"——课程 runReviewLoop 被砍掉了一半。完整的闭环:带着失败原因回到生成重做、再审、最多 N 次、超限转人工。下一篇用代码节点当 Validator、LLM 节点当 Judge,加上条件分支与循环节点,补上审核返工这半圈。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。