Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
Dify 实验系列 · 中级 04/20 | 实验编号:DIFY-102-05
上篇:Dify 中级实验(03):模板转换实战——如何用零 Token 完成文本加工?
1. 实验目的
掌握Iteration(迭代)节点的高阶用法:嵌套迭代、逐条质量过滤、条件分流、输出结构设计,以及性能边界——迭代不是无限的,30 个元素上限、10 分钟超时、并行数限制,都是真实约束。
适合场景:批量取数、逐条审核、分页采集、批处理流水线——任何「拿到一个数组,逐条处理」的需求。
2. 场景设计
从多个数据源拉取客户反馈,每个源返回多条评论,需要:
- 外层迭代遍历数据源列表(3 个源);
- 内层迭代对每个源的评论逐条做质量过滤(内容过短、纯标点、重复字符判为低质);
- 只有质量合格的评论才送 LLM 做情感分析;
- 最终汇总「扫描总数 / 有效数 / 拦截数」。
输入(sources,JSON 数组,每项含 name/url/limit):
[{"name":"App Store","url":"https://api.example.com/reviews/app","limit":3},{"name":"Google Play","url":"https://api.example.com/reviews/android","limit":2},{"name":"官方论坛","url":"https://api.example.com/reviews/forum","limit":4}]输出:总扫描 9 条、有效分析 N 条、被拦截 M 条 + 各源明细。
3. 节点拓扑
开始(sources:JSON) ↓ 解析数据源配置(Code:json.loads + 截断 [:20]) ↓ 外层迭代:遍历数据源(iter_sources) ├─ 模拟获取数据源评论(Code:按 limit 生成 mock + 0.2s 延迟) │ ↓ │ 内层迭代:逐条质量过滤与分析(iter_reviews) │ ├─ 质量过滤(Code:quality_ok 判定) │ │ ↓ │ │ 质量合格判断(IF-ELSE) │ │ ├─ case_ok → 情感分析(LLM)→ 汇聚分析结果(Code) │ │ └─ case_bad → 汇聚分析结果(Code) ↓ 汇总统计(Code)→ 结束4. 关键配置
4.1 开始节点变量
variables:-label:数据源配置(JSON 数组,每项含 name/url/limit)required:truetype:paragraph# 长 JSON 粘贴,paragraph 而非 text-inputvariable:sources4.2 外层迭代容器
迭代输入数组有30 个元素上限,解析节点先截断留余量:
defmain(sources_text:str)->dict:importjsontry:data=json.loads(sources_textor"[]")ifnotisinstance(data,list):data=[]exceptException:data=[]data=data[:20]# 迭代上限 30,提前截断return{"source_items":data,"total":len(data)}外层迭代容器配置(节选):
-data:error_handle_mode:terminatedis_parallel:falseiterator_selector:[cd_parse,source_items]output_selector:[iter_reviews,output]# 内层迭代的输出output_type:array[string]parallel_nums:10start_node_id:itstart0title:外层迭代:遍历数据源type:iterationid:iter_sources4.3 质量过滤(Code)
低质判定逻辑输出quality_ok——注意布尔值展平为字符串:变量选择器看不到 boolean 类型,下游 if-else 也只用字符串比较:
defmain(review:dict)->dict:importre review=reviewor{}content=str(review.get("content","")or"")quality_issues=[]iflen(content)<3:quality_issues.append("内容过短")ifre.match(r"^[!!.。??,,。]+$",content):quality_issues.append("无实质内容")iflen(set(content))<=2andlen(content)>1:quality_issues.append("疑似垃圾评论")return{"quality_ok":"true"iflen(quality_issues)==0else"false",# 字符串,不是布尔"quality_issues":quality_issues,...}4.4 内层分支与 LLM
IF-ELSE 用is比较字符串(不能用=):
cases:-case_id:case_okconditions:-comparison_operator:isvalue:"true"variable_selector:[cd_quality,quality_ok]logical_operator:and-case_id:case_badconditions:-comparison_operator:isvalue:"false"variable_selector:[cd_quality,quality_ok]logical_operator:and迭代内 LLM 引用当前元素用{{#iter_reviews.item.字段#}},且必须显式reasoning_format: separated——否则思考过程混入 text,整个迭代输出数组都被污染:
prompt_template:-id:p_analyzerole:systemtext:|分析以下用户评价: 平台:{{#iter_reviews.item.platform#}} 用户:{{#iter_reviews.item.user#}} 评分:{{#iter_reviews.item.rating#}}/5 评价内容:{{#iter_reviews.item.content#}} 输出分析结论(50字以内): 1. 情感倾向(正面/中性/负面) 2. 核心关注点 3. 可采取的行动建议reasoning_format:separated5. 运行验证
输入上方 sources 测试数据,观察:
| 检查项 | 预期 | 实测 |
|---|---|---|
| 外层迭代次数 | 3(3 个数据源) | 3 |
| 内层迭代总次数 | 9(3+2+4) | 9 |
| 质量过滤拦截 | 低质评论被拦,不走 LLM | 与预期一致 |
| 汇总输出 | 扫描 9 条 / 有效 N 条 / 拦截 M 条 | 与预期一致 |
进阶对比:把外层迭代is_parallel打开(并行数 3)再跑一次,对比串行/并行耗时——本实验 mock 延迟 0.2s,数据量小看不出差距,数据量大了差异明显。
6. 采坑点
| 坑 | 现象 | 修复 |
|---|---|---|
| 迭代输入数组超 30 个 | 运行报then length of var "item" must be less than 30 elements | 上游代码节点data[:20]提前截断留余量 |
| output_selector 选 array[object] | 迭代输出为空数组,下游取不到 | 内部代码节点json.dumps序列化为 string,output_type: array[string] |
| 布尔输出给下游判断 | 变量选择器看不到 boolean,取不到值 | 输出"true"/"false"字符串,if-else 用is比较 |
| 嵌套迭代内部节点 parentId 写错层级 | 校验报「parentId=None 不是 iteration 节点」 | parentId 是节点外层字段(与 id 同级),不是 data 内 |
| 迭代内 LLM 缺 reasoning_format | 思考过程混入 text,判断逻辑全乱 | 显式reasoning_format: separated |
💡 嵌套迭代格式本身能通过校验,但 Dify 运行层对「迭代套迭代」支持有限——如果你的场景不需要逐源再逐条的两级结构,优先用「串联迭代 + 展平代码节点」更稳。
7. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-05:迭代进阶——批量处理的边界与陷阱.md
- 源码(可直接导入):dify102_05_批量反馈分析器.yml
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
下一篇:Dify 中级实验(05):并行执行——如何让多路任务同时跑?
