在 Dify 里搭应用,很多人第一版就卡在一个选择上:把每一步都画成节点连起来,还是给个提示词加几个工具让模型自己决定怎么走。画多了发现改需求要重连一遍,放太松又怕它绕圈、乱调工具、账单失控。这篇不讲概念科普,只给判断口径、拆分方式和上线前要做的几步检查。
在 Dify 里搭应用,第一版最容易卡住的不是提示词写得好不好,而是结构选型:把每一步都画成节点连起来,还是给个提示词加几个工具,让模型自己决定怎么走。前者改个需求要重连一遍图,后者跑起来又怕它绕圈、乱调工具、调用次数失控。
下面不讲概念科普,只给判断口径、两种结构各自的拆分方式、混用的落点,以及上线前必须做的几项工程检查。看完你应该能对着自己的场景直接选一个方向,而不是两个都试一遍再拍脑袋。
先定判断口径:这活到底该编排还是该自主
先别急着打开编排画布,拿两个问题过一遍:整条链路的步骤能不能提前枚举出来,每一步的输出能不能被校验。两个都成立就偏工作流,只要有一项不成立,就该考虑把这段交给 Agent 自主处理。
| 判断维度 | 偏工作流 | 偏 Agent |
|---|---|---|
| 步骤可枚举 | 能画出完整节点图 | 分支多到画不全 |
| 输出可校验 | 每步有固定结构或字段 | 结果是自由文本或半开放 |
| 出错代价 | 错了直接对外发出去 | 错了能重跑或人工兜底 |
| 输入形态 | 表单、固定字段、结构化数据 | 自然语言、意图不定 |
- 步骤可枚举、每步输出可校验,两者都成立,就走工作流;缺一项再考虑 Agent。
- 失败代价高的环节必须收紧成确定性节点,别让模型临场发挥。
- 输入是表单或固定字段进来的,工作流更省事;自然语言、意图不定的入口更适合 Agent。
- 反例提醒:流程虽长但分支很少、每步都确定,硬做成 Agent 只是把稳定性换成不确定性。
工作流这一侧的画法与拆分
工作流最容易画成一团乱麻,是因为把取数、判断、生成、写回全塞进一个节点。正确的做法是先分段,段内可以并行,段与段之间保持串行,这样哪一段慢、哪一段错,一眼能定位。
- 按四段拆取数、判断、生成、落库各成一段;段内允许并行节点,段与段之间保持串行链路,方便单点排查。
- 把模型节点当纯函数输入字段明确、输出结构固定,别让一个节点既检索又决策又写文案。
- 分支条件落在规则上能用代码节点或规则判断走向的,就别用模型判断;模型只负责它擅长的文字加工。
- 循环先想批量遇到要循环的场景,先问能不能用一次批量调用替代,很多循环其实是没设计好的批处理。
这套拆法的好处是回归成本低:改一段不影响别段。反过来,一旦图长到几十个节点还互相嵌套,每次改需求都要重跑一遍全链路验证,维护成本会迅速超过它省下的那点灵活性。
Agent 这一侧该放开什么、该锁什么
Agent 的自由度不是越多越好。真正该交给模型的是「选哪个工具、按什么顺序调」,而「工具能干什么」必须用参数和权限卡死。放开的是路径,锁住的是边界。
- 放开路径
- 让模型决定调哪个工具、调几次、什么顺序。
- 锁住参数
- 工具的入参范围、可写字段、权限级别在工具定义里写死。
- 收敛工具集
- 只保留少数几个语义不重叠的工具,越多越容易选错。
- 约定返回格式
- 每个工具返回结构化结果,让模型下一步有依据,而不是拿一大段自由文本再猜。
- 终止条件至少写三种出口:达到目标、连续无进展、触达步数上限。
- 只靠模型自己喊停,容易在边界情况下无限循环。
- 工具返回里带上状态码或错误字段,方便模型判断要不要换路。
- 给每个工具写清什么场景用、什么场景别用,提示词里一句话能省掉很多误调。
混用:把 Agent 塞进工作流的哪个位置
大多数真实应用最后既不是纯工作流,也不是纯 Agent,而是外层固定、中间留一个自主节点。外层负责入口校验、结果落库和对外响应,自主节点只处理意图不明或需要多轮工具调用的那一段。
| 位置 | 该做什么 | 不该做什么 |
|---|---|---|
| 自主节点输入 | 收敛成固定结构,字段明确 | 直接把整段对话历史塞进去 |
| 自主节点输出 | 转成固定字段再流向下一节点 | 让自由文本直接流向下游 |
| 调用次数 | 设上限,超了走兜底分支 | 不限次数放它一直跑 |
| 外层工作流 | 固定入口、固定出口、固定落库 | 把整条链路都做成不可预知 |
什么情况下不适用:整条链路都依赖实时外部状态、每步都不可预知时,外层硬套工作流只会增加维护成本,这时候反而应该以 Agent 为主体,只在关键节点插校验。
状态与上下文怎么在两种模式间传
工作流靠变量传递,Agent 靠对话历史,这两套东西不会自动对齐。中间必须有一层显式转换:进自主节点前把变量拼成提示词,出自主节点后把自由文本解析回固定字段。
- 长会话进 Agent 前先裁剪或摘要,丢掉无关历史,否则选工具的判断会被噪声带偏。
- 需要跨轮记住的信息落到会话变量或外部存储,别塞在提示词里靠模型记。
- 上下文塞太满既慢又贵,具体基线可以到 量一下你自己的延迟再定。
- 变量命名和字段类型在编排图里统一,别一个节点叫 user_input、另一个叫 query。
还有一点容易忽略:摘要本身也要定格式。如果摘要只是自由文本,Agent 每次读到的重点可能都不一样,多轮之间行为就会漂移。把摘要固定成几个字段,稳定性会好很多。
上线前必做的护栏:超时、并发、降级、日志
功能跑通不等于能上线。超时、并发、降级、日志这四样缺一项,出问题时就只能靠猜。它们的设置方式不用抄别人的数字,但要按自己链路的最坏情况估。
- 超时分开设每个模型节点和工具调用分别设超时,别用一个总超时兜着,否则定位不到是谁慢。
- 并发按最坏估一个请求可能触发多次模型调用,限流要按整条链路算,不是按入口请求数算。
- 降级分三档换备用模型、走缓存结果、退化成模板回复,三档按顺序触发,提前写好别临时决定。
- 日志记五项请求 id、节点名、耗时、token 用量、工具参数与返回摘要,缺一项复盘就得靠猜。
怎么验证改动没把效果改坏
改提示词、换模型、调节点顺序之后,最容易犯的错是凭感觉说「好像变好了」。稳妥的做法是固定一组用例反复跑,同一批输入分别走工作流和 Agent 路径,比成功率、耗时和花费。
- 用例覆盖典型输入、边界输入和已知会翻车的输入三类。
- 改动后必须重跑这组用例,别跳步直接上线。
- 用不上的节点及时删掉,编排图越长每次回归成本越高。
- 达标线看业务能接受的底线,没有统一数字,跑几轮拿到自己的基线再谈优化。
这组用例本身也要维护:线上发现的新翻车案例,回头补进用例集,下次改动就能防住同一个坑。参考资料方面,模型价格和接口能力这些随时会变的数字,到 核对当时的数字,别拿旧截图做决策。
下一步选型定下来之后,把结构和你实际跑的延迟、花费对齐一遍,比空想更靠谱。到站内跑一轮再看数字,能省掉不少来回改图的时间。→常见问题
Dify 里选了工作流之后,还能加 Agent 吗?
可以。典型做法是外层用工作流保证主干稳定,中间放一个自主节点处理意图不明或需要多轮工具调用的部分,但要求这个节点的输入输出都收敛成固定结构,并且给它设好步数上限和兜底分支,不然它会把整条链路的稳定性拖下来。
是不是 Agent 更高级,所以能用 Agent 就别用工作流?
不是。Agent 的价值在于处理步骤不可枚举、需要临场判断的任务,代价是结果波动大、调试难、成本不可预估。如果流程本身分支很少、每步都能校验,用工作流反而更稳更省,硬上 Agent 只是拿确定性换不确定性。
Dify 工作流跑一轮会不会触发很多次模型调用?
会,取决于你有几个模型节点和是否循环。做并发和成本估算时要按一条用户请求可能触发的最坏调用次数来算,而不是按节点数量平摊,否则上线后很容易撞限流。具体额度口径可以到 对一下你用的模型。
Agent 老是绕圈或者调错工具,先从哪里改?
先砍工具数量,只保留语义不重叠的几个;再检查工具描述是不是写得含糊,模型靠描述选工具;最后补终止条件,把连续无进展和步数上限都设好。这三步做完还不行,才考虑是不是这个任务根本不该交给自主决策。
两种模式的效果好坏怎么比?
准备同一批输入用例,分别跑工作流和 Agent 路径,记录成功率、平均耗时和单次花费,跑到结果稳定为止再下结论。别用单次体验判断,也别凭感觉说哪个更好,几轮数据放在一起才看得出差别在哪。