用迭代节点批量处理多份资料
课程归属:Dify自动化实战|第 21/32 节
一、场景与验收目标
当“资料报告助手”一次接收多份文件时,直接把所有文本塞进一个 LLM 节点,容易遇到上下文过长、单份资料遗漏、结果难以追溯等问题。Dify Workflow 的迭代(Iteration)节点可以把一个数组逐项交给内部子流程处理,再把每次迭代的指定输出收集成数组。本文用三份教学资料演示“逐份提取要点—汇总成统一报告”的流程,并验证空内容、单份失败和输出映射错误等边界。完成后,读者能区分文件列表、单份文件文本、迭代项和迭代结果数组,搭出可检查的批处理工作流。
二、案例范围与输入约定
本篇只聚焦“批量处理多份资料”,不在同一篇里展开知识库检索、OCR 或 Word 文件生成。上传文件如果是扫描 PDF,普通文本提取可能得不到正文,需要单独接入 OCR 服务;本例使用可提取文本的教学文件。
表 1:本例输入、处理对象与输出
| files | Dify 多文件输入 | 接收用户上传的多个文件,不是路径字符串数组 |
| report_topic | String | 告诉工作流本次报告关注什么 |
| 单份文本 | String | 文档提取节点针对当前文件得到的文本 |
| 单份结果 | Object 或可解析的结构化结果 | 包含来源名称、摘要、要点与警告 |
| key_points | 字符串数组 | 汇总各份资料的关键要点 |
| source_refs | 对象数组 | 保存来源名称、定位信息和原文片段 |
| warnings | 字符串数组 | 记录空文本、提取失败或字段不完整 |
| report_markdown | String | 最终报告正文 |
| download_url | String | 本篇不生成文件时为空字符串 |
注意:Dify 不同发行版本的节点输出字段和变量选择器显示可能略有差异。搭建时以你部署版本的节点面板和运行详情为准;本文给出的是稳定的数据流设计,而不是声称存在一个可直接导入的 DSL 文件。
三、先理解迭代节点如何工作
迭代节点的输入必须是数组。它会为数组中的每个元素运行内部子流程,并将配置为输出的变量收集起来。只在迭代内部生成、却没有配置为迭代输出的变量,通常不能被后续节点直接使用。
图 1:批量资料处理的数据流
#mermaid-svg-EZHcAhj80BKYxQC7{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EZHcAhj80BKYxQC7 .error-icon{fill:#552222;}#mermaid-svg-EZHcAhj80BKYxQC7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EZHcAhj80BKYxQC7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EZHcAhj80BKYxQC7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EZHcAhj80BKYxQC7 .marker.cross{stroke:#333333;}#mermaid-svg-EZHcAhj80BKYxQC7 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EZHcAhj80BKYxQC7 p{margin:0;}#mermaid-svg-EZHcAhj80BKYxQC7 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster-label text{fill:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster-label span{color:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster-label span p{background-color:transparent;}#mermaid-svg-EZHcAhj80BKYxQC7 .label text,#mermaid-svg-EZHcAhj80BKYxQC7 span{fill:#333;color:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 .node rect,#mermaid-svg-EZHcAhj80BKYxQC7 .node circle,#mermaid-svg-EZHcAhj80BKYxQC7 .node ellipse,#mermaid-svg-EZHcAhj80BKYxQC7 .node polygon,#mermaid-svg-EZHcAhj80BKYxQC7 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-EZHcAhj80BKYxQC7 .rough-node .label text,#mermaid-svg-EZHcAhj80BKYxQC7 .node .label text,#mermaid-svg-EZHcAhj80BKYxQC7 .image-shape .label,#mermaid-svg-EZHcAhj80BKYxQC7 .icon-shape .label{text-anchor:middle;}#mermaid-svg-EZHcAhj80BKYxQC7 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-EZHcAhj80BKYxQC7 .rough-node .label,#mermaid-svg-EZHcAhj80BKYxQC7 .node .label,#mermaid-svg-EZHcAhj80BKYxQC7 .image-shape .label,#mermaid-svg-EZHcAhj80BKYxQC7 .icon-shape .label{text-align:center;}#mermaid-svg-EZHcAhj80BKYxQC7 .node.clickable{cursor:pointer;}#mermaid-svg-EZHcAhj80BKYxQC7 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-EZHcAhj80BKYxQC7 .arrowheadPath{fill:#333333;}#mermaid-svg-EZHcAhj80BKYxQC7 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-EZHcAhj80BKYxQC7 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-EZHcAhj80BKYxQC7 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EZHcAhj80BKYxQC7 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-EZHcAhj80BKYxQC7 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EZHcAhj80BKYxQC7 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster text{fill:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 .cluster span{color:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-EZHcAhj80BKYxQC7 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-EZHcAhj80BKYxQC7 rect.text{fill:none;stroke-width:0;}#mermaid-svg-EZHcAhj80BKYxQC7 .icon-shape,#mermaid-svg-EZHcAhj80BKYxQC7 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-EZHcAhj80BKYxQC7 .icon-shape p,#mermaid-svg-EZHcAhj80BKYxQC7 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-EZHcAhj80BKYxQC7 .icon-shape .label rect,#mermaid-svg-EZHcAhj80BKYxQC7 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-EZHcAhj80BKYxQC7 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-EZHcAhj80BKYxQC7 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-EZHcAhj80BKYxQC7 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
开始:files、report_topic
文档提取:得到文件列表文本
整理为资料数组
Iteration:逐项处理
当前资料:提取摘要与要点
设置迭代输出变量
收集为结果数组
模板或代码:汇总与校验
结束:report_markdown 等字段
图中的关键是“当前资料”和“结果数组”不是同一个变量。迭代内部每次只处理一项;迭代节点外部拿到的是各次输出组成的数组,后续 LLM 节点通常需要字符串,因此可先用模板或代码节点把数组转换成可读文本,再进行总汇总。
四、准备教学资料与工作流
1. 准备三份小型文本资料
在本地建立 dify_lab/data/input/ 目录,准备三份可复制到文本文件中的教学资料。它们是模拟数据,不代表真实企业业务。
资料一.txt
资料名称:资料解析服务
内容:资料解析服务负责读取用户上传的可提取文本文件,并输出文本内容。遇到扫描版 PDF 时,普通文本提取可能无法得到正文,需要额外的 OCR 处理。
资料二.txt
资料名称:报告归档服务
内容:报告归档服务保存最终报告正文、任务主题和来源信息。归档前应检查报告是否为空,并确认来源字段与正文内容对应。
资料三.txt
资料名称:运行验收规范
内容:工作流验收应检查正常输入、空内容和节点失败。只有实际输出字段齐全且来源可追溯,才能判定本次任务通过;模型口头声称完成不算验收证据。
在 Dify 的“资料报告助手”应用中,选择 Workflow 类型。在开始节点定义 files(多文件输入)与 report_topic(String)。运行时通过平台上传控件选择三份文件;不能把 dify_lab/data/input/资料一.txt 这类本机路径作为 files 的值,除非你另行开发了负责读取这些路径的服务。
2. 节点规划
表 2:节点配置与职责
| 1 | 开始 | files、report_topic | 接收用户输入 |
| 2 | 文档提取 | files | 从上传文件中提取可用文本;确认当前版本是否支持文件列表输入 |
| 3 | 代码或结构整理节点 | 提取结果 | 整理为明确的资料数组,每项包含 source_name、text、可选 locator |
| 4 | Iteration | 资料数组 | 每次循环处理一个资料对象 |
| 5 | 迭代内部 LLM/模板 | 当前 item | 生成当前资料的摘要、要点和来源信息 |
| 6 | Iteration 输出选择 | 内部节点输出变量 | 显式选择要收集的变量 |
| 7 | 汇总节点 | 迭代结果数组、report_topic | 合并要点、核对缺失项、生成报告 |
| 8 | 结束 | 统一公共字段 | 返回 report_markdown、key_points、source_refs、warnings |
文档提取节点在不同版本中对单文件和多文件的输入支持可能不同。如果你的版本只接受单个文件,应先确认该版本支持的文件列表处理方式,或使用受控的文件解析服务将多文件逐一转为数组;不要把文件对象、路径字符串和文本数组混为一谈。
五、配置迭代内部的单份处理流程
1. 整理每项数据结构
迭代前应把上游输出整理成一维数组。建议每个元素使用相同结构:
[
{
"source_name": "资料解析服务",
"text": "资料解析服务负责读取用户上传的可提取文本文件……",
"locator": "资料一.txt"
},
{
"source_name": "报告归档服务",
"text": "报告归档服务保存最终报告正文、任务主题和来源信息……",
"locator": "资料二.txt"
}
]
这只是数据结构示例,不是 Dify 上传文件的原始协议格式。真实工作流里,需根据文档提取节点的实际输出,在代码节点或其他可用节点中映射出该数组,并检查数组元素是否都包含必需字段。若上游输出已经是合适的数组,就不必重复包装。
图 2:单份资料在迭代内部的处理
#mermaid-svg-ReGd7nFym235oYk6{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ReGd7nFym235oYk6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ReGd7nFym235oYk6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ReGd7nFym235oYk6 .error-icon{fill:#552222;}#mermaid-svg-ReGd7nFym235oYk6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ReGd7nFym235oYk6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ReGd7nFym235oYk6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ReGd7nFym235oYk6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ReGd7nFym235oYk6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ReGd7nFym235oYk6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ReGd7nFym235oYk6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ReGd7nFym235oYk6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ReGd7nFym235oYk6 .marker.cross{stroke:#333333;}#mermaid-svg-ReGd7nFym235oYk6 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ReGd7nFym235oYk6 p{margin:0;}#mermaid-svg-ReGd7nFym235oYk6 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ReGd7nFym235oYk6 .cluster-label text{fill:#333;}#mermaid-svg-ReGd7nFym235oYk6 .cluster-label span{color:#333;}#mermaid-svg-ReGd7nFym235oYk6 .cluster-label span p{background-color:transparent;}#mermaid-svg-ReGd7nFym235oYk6 .label text,#mermaid-svg-ReGd7nFym235oYk6 span{fill:#333;color:#333;}#mermaid-svg-ReGd7nFym235oYk6 .node rect,#mermaid-svg-ReGd7nFym235oYk6 .node circle,#mermaid-svg-ReGd7nFym235oYk6 .node ellipse,#mermaid-svg-ReGd7nFym235oYk6 .node polygon,#mermaid-svg-ReGd7nFym235oYk6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ReGd7nFym235oYk6 .rough-node .label text,#mermaid-svg-ReGd7nFym235oYk6 .node .label text,#mermaid-svg-ReGd7nFym235oYk6 .image-shape .label,#mermaid-svg-ReGd7nFym235oYk6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ReGd7nFym235oYk6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ReGd7nFym235oYk6 .rough-node .label,#mermaid-svg-ReGd7nFym235oYk6 .node .label,#mermaid-svg-ReGd7nFym235oYk6 .image-shape .label,#mermaid-svg-ReGd7nFym235oYk6 .icon-shape .label{text-align:center;}#mermaid-svg-ReGd7nFym235oYk6 .node.clickable{cursor:pointer;}#mermaid-svg-ReGd7nFym235oYk6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ReGd7nFym235oYk6 .arrowheadPath{fill:#333333;}#mermaid-svg-ReGd7nFym235oYk6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ReGd7nFym235oYk6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ReGd7nFym235oYk6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ReGd7nFym235oYk6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ReGd7nFym235oYk6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ReGd7nFym235oYk6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ReGd7nFym235oYk6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ReGd7nFym235oYk6 .cluster text{fill:#333;}#mermaid-svg-ReGd7nFym235oYk6 .cluster span{color:#333;}#mermaid-svg-ReGd7nFym235oYk6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ReGd7nFym235oYk6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ReGd7nFym235oYk6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ReGd7nFym235oYk6 .icon-shape,#mermaid-svg-ReGd7nFym235oYk6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ReGd7nFym235oYk6 .icon-shape p,#mermaid-svg-ReGd7nFym235oYk6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ReGd7nFym235oYk6 .icon-shape .label rect,#mermaid-svg-ReGd7nFym235oYk6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ReGd7nFym235oYk6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ReGd7nFym235oYk6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ReGd7nFym235oYk6 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
当前 item
text 是否非空?
输出单份警告
按 report_topic 提取信息
整理摘要、要点、来源
输出统一结构
每次迭代都应返回一致的结构。即使某一份资料没有文本,也尽量返回包含来源名称和警告的结果对象,而不是让字段忽隐忽现。这样外部汇总节点才能稳定地处理所有结果。
2. 配置内部 LLM 节点提示词
在 Iteration 内添加 LLM 节点,将当前迭代项的 source_name、text 和外部的 report_topic 通过变量选择器放进提示词。不要直接照抄一个猜测的变量引用语法;应从当前节点的变量选择器选择对应字段。
可使用下面的提示词内容:
你是资料报告助手中的单份资料分析器。
报告主题:{{由变量选择器插入 report_topic}}
资料名称:{{由变量选择器插入当前 item 的 source_name}}
资料正文:
{{由变量选择器插入当前 item 的 text}}
请只根据资料正文提取与报告主题相关的信息。
返回 JSON 对象,字段固定为:
{
"source_name": "资料名称",
"summary": "不超过 120 字的摘要",
"key_points": ["要点一", "要点二"],
"locator": "资料定位标识",
"excerpt": "支持要点的原文短片段",
"warnings": []
}
资料没有相关内容时,明确说明未找到,不要补造事实、数据或引用。
提示词中的花括号说明是为了表达变量插入位置;实际配置时通过 Dify 变量选择器插入变量,不要把中文占位说明原样当作变量引用提交。如果当前版本的 LLM 节点支持结构化输出,按它的字段配置定义对象;否则要求输出 JSON 后,在下游用代码或参数提取能力解析并校验。不能只凭模型输出“看起来像 JSON”就认为结构有效。
3. 显式设置 Iteration 输出
打开 Iteration 节点,检查两项配置:
- 输入数组:选择整理节点输出的资料数组。
- 输出变量:选择迭代内部需要收集的节点输出,例如经过解析的单份结果对象。
当前 Dify 源码的迭代节点配置包含输入变量选择、输出变量选择、并行设置和错误处理模式等字段,可参考官方仓库中的迭代节点实现;实际界面文字以已部署版本为准。
表 3:迭代节点最容易配置错的地方
| 输入 | 选择数组类型变量 | 选择普通字符串或整个文件对象 |
| 当前项 | 在迭代内部引用当前 item 对应字段 | 引用另一个节点的旧变量 |
| 输出 | 显式选择内部节点输出 | 认为内部所有变量会自动导出 |
| 输出类型 | 每次迭代返回相同结构 | 有的项返回字符串,有的项返回对象 |
| 后续汇总 | 先检查数组,再转换成文本或字段 | 直接把对象数组塞进只接受字符串的提示词 |
六、汇总迭代结果并生成统一报告
Iteration 输出通常是数组,不能假定下游 LLM 的普通文本输入会自动把数组转换成自然语言。推荐先用模板节点或代码节点做一次规范化:确认每项结构,提取 summary、key_points、source_name、locator、excerpt 与 warnings,再生成便于汇总的文本。
图 3:汇总与报告输出
结束节点
汇总节点
Iteration
Workflow
用户
结束节点
汇总节点
Iteration
Workflow
用户
#mermaid-svg-dBNG3SqOUcc89b86{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dBNG3SqOUcc89b86 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dBNG3SqOUcc89b86 .error-icon{fill:#552222;}#mermaid-svg-dBNG3SqOUcc89b86 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dBNG3SqOUcc89b86 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dBNG3SqOUcc89b86 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dBNG3SqOUcc89b86 .marker.cross{stroke:#333333;}#mermaid-svg-dBNG3SqOUcc89b86 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dBNG3SqOUcc89b86 p{margin:0;}#mermaid-svg-dBNG3SqOUcc89b86 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBNG3SqOUcc89b86 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-dBNG3SqOUcc89b86 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dBNG3SqOUcc89b86 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-dBNG3SqOUcc89b86 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-dBNG3SqOUcc89b86 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-dBNG3SqOUcc89b86 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-dBNG3SqOUcc89b86 .sequenceNumber{fill:white;}#mermaid-svg-dBNG3SqOUcc89b86 #sequencenumber{fill:#333;}#mermaid-svg-dBNG3SqOUcc89b86 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-dBNG3SqOUcc89b86 .messageText{fill:#333;stroke:none;}#mermaid-svg-dBNG3SqOUcc89b86 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBNG3SqOUcc89b86 .labelText,#mermaid-svg-dBNG3SqOUcc89b86 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-dBNG3SqOUcc89b86 .loopText,#mermaid-svg-dBNG3SqOUcc89b86 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-dBNG3SqOUcc89b86 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-dBNG3SqOUcc89b86 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-dBNG3SqOUcc89b86 .noteText,#mermaid-svg-dBNG3SqOUcc89b86 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-dBNG3SqOUcc89b86 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBNG3SqOUcc89b86 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBNG3SqOUcc89b86 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-dBNG3SqOUcc89b86 .actorPopupMenu{position:absolute;}#mermaid-svg-dBNG3SqOUcc89b86 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-dBNG3SqOUcc89b86 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-dBNG3SqOUcc89b86 .actor-man circle,#mermaid-svg-dBNG3SqOUcc89b86 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-dBNG3SqOUcc89b86 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
loop
[每份资料]
上传多份资料并填写主题
传入资料数组
提取当前项并生成单份结果
返回结果数组
校验字段、合并要点与警告
设置统一输出字段
返回报告与来源
该时序强调:汇总发生在所有迭代项产生结果之后。不要让某一轮的局部变量被误当作所有资料的最终结果。
1. 公共字段映射
表 4:从单份结果到统一报告
| source_name | 固定为“多资料汇总”或实际来源集合的描述 | 不应冒充某一份资料 |
| summary | 由汇总节点概括所有有效单份摘要 | 只归纳已有结果 |
| key_points | 合并各份有效要点 | 可去重,但不要丢失来源关联 |
| source_refs | 每份有效资料至少保留一个来源对象 | source_name、locator、excerpt 均为字符串 |
| warnings | 合并空文本、解析失败、字段缺失等提示 | 无异常时返回空数组 |
| report_markdown | 由模板或 LLM 生成最终 Markdown | 报告中标出各资料来源 |
| download_url | 空字符串 | 本篇未生成下载文件 |
建议在最终报告中按“报告主题、综合摘要、分资料要点、来源片段、警告”组织内容。来源引用应使用真实的文件名或上游提供的定位信息;没有页码时不要伪造页码。source_refs 是课程约定的对象数组,每项固定包含 source_name、locator 和 excerpt 三个字符串。
2. 汇总提示词
如果使用 LLM 做最终归纳,输入应是汇总节点已经整理好的字符串,而不是未经处理的原始对象数组。提示词可以写成:
你是资料报告助手的汇总器。
报告主题:{{通过变量选择器插入 report_topic}}
以下是逐份资料处理后的结果:
{{通过变量选择器插入汇总文本}}
请生成 Markdown 报告,包含:
1. 报告主题与综合摘要;
2. 按来源分组的关键要点;
3. 能支持结论的原文片段与来源标识;
4. 未检索到信息、文本为空或字段不完整等警告。
不得把推测写成事实,不得补造来源、页码或数字。
汇总节点完成后,仍应由结束节点显式选择输出变量。工作流结束节点返回的字段取决于配置中选择的变量;仅仅在中间节点生成了 report_markdown,不代表它一定会自动成为最终 API 输出。
七、运行与验收
在 Workflow 的调试/运行界面上传三份教学文本,report_topic 填写“提炼资料处理与报告交付的验收要求”。执行后分别查看文档提取、数组整理、Iteration、汇总和结束节点的运行详情。以下是验收目标,不是已在你的 Dify 实例中实测的输出。
表 5:测试用例与判定标准
| 正常批处理 | 三份非空文本 | 得到多份单独结果和一份汇总报告 | 来源数量与上传资料对应;要点有出处 |
| 空文本 | 一份文件没有可提取文本 | 该项产生警告,其余资料继续处理(取决于错误策略) | 不编造空文件内容;警告进入最终输出 |
| 结构不完整 | 某项缺少 text 或 source_name | 校验节点标记异常 | 不把缺字段对象直接交给汇总 LLM |
| 输出未映射 | Iteration 内部结果存在,但外部拿不到 | Iteration 输出配置缺少对应变量或类型不匹配 | 补选输出变量后,外部能看到结果数组 |
| 单份节点失败 | 某轮 LLM 或提取节点失败 | 按配置终止或继续,并记录异常 | 运行详情能定位失败项;最终警告与策略一致 |
验收不能只看最终报告是否流畅。至少要核对:输入了几份资料、迭代结果数组有几项、每项是否有来源字段、最终报告是否保留原文片段,以及警告是否正确传递。如果只得到一条摘要,可能是输入数组实际上只有一项,也可能是迭代输出或汇总映射配置错误。
八、异常处理与排错顺序
表 6:常见故障定位
| Iteration 输入选择器中看不到数组 | 上游输出类型被定义成 String,或数组嵌在更深层对象里 | 检查上游输出 schema;必要时先整理为顶层数组 |
| 每次循环都处理同一份资料 | 内部节点引用了固定变量,而非当前迭代项 | 在迭代内部重新用变量选择器绑定当前项字段 |
| 迭代内部有结果,外部为空 | 未设置输出选择器,或选错节点/字段 | 明确选择内部输出变量并重跑 |
| 下游 LLM 无法使用迭代输出 | 下游需要字符串,但拿到的是数组/对象 | 用模板或代码把结果转成字符串,并保留结构校验 |
| 空资料导致整体失败 | 文档提取为空或错误处理模式为终止 | 为空文本设计明确输出;按业务需求设置终止/继续策略 |
| 并行时输出表现异常 | 节点状态或版本行为不同,或者内部有不适合并行的副作用 | 先关闭并行做串行基线验证,再按部署版本测试并行 |
排错时按“上游类型—输入数组—当前项引用—迭代输出—下游类型—结束节点映射”的顺序检查。不要一开始就反复改提示词:如果变量根本没有传入,提示词写得再详细也无法解决。
九、并行、成本与安全边界
Iteration 的并行能力可以缩短部分批处理的等待时间,但不能保证总耗时一定按文件数线性下降。实际表现受模型服务限流、节点耗时、并行数配置、网络和文件大小影响。建议先用串行执行确认结果正确,再用少量教学文件测试并行,并观察运行详情和服务端限流响应。
- 限制输入规模:为单次上传的文件数量和文本长度设置合理边界,避免把大量资料一次性送入模型。
- 隔离单份结果:每次迭代只返回当前资料的结果,不依赖轮次之间共享临时变量。
- 保留来源:每条摘要或要点尽可能对应来源文件与原文片段,便于人工复核。
- 保护敏感信息:上传前确认资料有权处理;在发送给模型前按需要脱敏。
- 慎用继续执行:继续模式适合允许部分成功的批处理;如果任何一份资料缺失都会让报告失真,则应选择终止或在汇总阶段明确标记未完成。
- 区分文件类型:扫描件 OCR、受密码保护文件和复杂版式文档可能需要独立处理能力,不能把提取不到文本当作“资料没有内容”。
本篇只设计并说明工作流配置与验收标准;没有连接读者的 Dify 实例,也没有声称已经运行三份文件。上线前应在实际部署版本中完成正常、空文本、单份失败和输出映射测试。



