欢迎光临
我们一直在努力

别只让数据路过:用变量给 Dify 应用打地基 | Dify 系列 · EP4

城北 · 2026-09-04

一条 Dify 工作流刚搭出来时,很容易给人一种错觉:节点已经连上,数据自然就会流过去;用户连续说了几句话,应用自然就知道这些话来自同一场对话。

深夜书房里一张地图,多条光路从地图节点汇入中央的透明容器,容器下方发散出根系状金色光纹,象征变量的汇聚与留存

地图上的每一条路径,最终都要汇进同一处地基。

可节点只负责执行,连线只负责指路。数据从哪里来、最后汇到哪里,要靠变量说明;哪些信息应该在一轮结束后留下来,也要靠变量决定。

没有变量的工作流,像一栋已经装好门窗、却没有铺设水电的房子。远看结构完整,真正住进去以后才会发现:每个房间都各自为政,水流不到该去的地方,灯一关,刚才发生过什么也全忘了。

这一集,我们来铺这栋房子的管线。

前情提要 EP2 结尾说过,Start、LLM、Answer 连在一起,只说明谁先运行;变量引用才说明数据具体从哪里来、交给谁。EP3 给"国家信息小助手"装出了四条各自独立的分支,却还没有回答数据怎样收拢、怎样留住。想看完整讲解,可以回看 EP2《从零搭出第一个 Dify 应用:用云版完成一次 Chatflow 上线》 的「一次最小案例,真正应该学会什么」一节,以及 EP3《给 Dify 应用装上"分岔路"》 里刚建好的四条分支——不看也不影响读完这一集。

#主题一句话状态
1 AI 应用平台通识与选型 认知/选型判断力:这个领域是什么、要不要学、怎么定位 已发布
2 云版搭第一个 Chatflow 应用 5 种应用类型、Start/LLM/Answer、Prompt 分层、发布分发 已发布
3 给应用装上"分岔路" If/Else、问题分类器:写死的规则 vs 交给模型判断 已发布
4 变量,这个系列真正的地基 会话变量、环境变量、变量聚合器/赋值器 ← 你在这里
5 把外部世界接进来 HTTP Request、Code 节点、参数提取器 未发布
6 知识库与 RAG 实战 文档处理/分段/检索策略/Rerank 未发布
7 批量处理与循环 Iteration、Loop、列表操作 未发布
8 工具与 Agent 工具市场、自定义工具、Function Calling/ReAct、双向 MCP 未发布
9 从 Demo 到生产:运维篇 DSL 版本管理、执行日志、标注、可观测性、权限 未发布
10 私有化部署实战 Docker Compose、.env、报错排查 未发布

01前三集的变量,都只是匆匆过客

"国家信息小助手"其实早就在使用变量。

用户输入"法国"时,Dify 把本轮消息放进系统变量 sys.query。问题分类器读取它,判断这句话属于"国家名称""问候或闲聊"还是"无法识别";If/Else 也读取它,检查内容是否为空;LLM 再接收这份输入,生成"法国的首都是巴黎"之类的回答。

模型生成的文字同样是变量。LLM 节点产生 text 输出,直接回复节点引用它,用户才真正看到答案。

这条链路可以简化成:

sys.query → 问题分类器/If/Else/LLM → LLM.text → 直接回复

这里的变量都属于一次运行中的临时数据。

sys.query 代表这一轮用户说的话。下一轮用户再发"日本",流程会重新运行,sys.query 也会变成"日本"。LLM 的 text 输出则属于本次模型调用,它被直接回复节点引用后,这一轮任务也就结束了。

这并没有问题。绝大多数中间结果本来就只需要活一轮。

问题在于,一旦工作流出现分支,或者产品需要跨轮保存状态,临时变量就不够用了。

EP3 结束时,应用有四条互斥路径:

  • 国家名称 → 空输入兜底;
  • 国家名称 → LLM 回答;
  • 问候或闲聊 → 问候引导;
  • 无法识别 → 无法识别兜底。

它们每次只会运行一条,却各自通向自己的出口。应用能够决定"这轮往哪里走",却不能把分散的结果重新收回来。

与此同时,用户这一轮问"法国",下一轮问"日本",应用也没有保存查询历史。两次输入发生在同一个聊天窗口里,对工作流来说却依然像两次互不相干的运行。

所以,这一集要解决两个不同的问题:

  • 同一轮运行中,互斥分支产生的结果怎样收敛成一个统一出口;
  • 不同轮运行之间,应用怎样保留需要继续使用的状态。
  • 前一个问题交给变量聚合器,后一个问题交给会话变量和变量赋值器。

    02第一次改造:先把岔路重新收回来

    先看"国家名称"分支。

    EP3 留下的结构是:

    • IF:sys.query 为空 → 空输入兜底直接回复;
    • ELSE:sys.query 不为空 → LLM → 直接回复。

    两个出口都能把文字交给用户,功能上没有错误。真正的问题是,它们把同一种下游责任复制了两遍。

    假设以后希望每条国家查询回复前都加一句:

    以下信息由 AI 生成,请以权威资料为准。

    现在至少要修改两个直接回复节点。

    如果接下来还要统一记录日志、进行内容审核、包装输出格式,所有操作都得在两个出口分别配置。分支越多,重复越多;漏改其中一条,用户在不同路径上就会得到不一致的体验。

    这类问题不能靠"记得同步修改"解决。更可靠的做法,是让不同分支只负责产生各自的结果,再把结果汇入同一个下游出口。

    这正是变量聚合器的职责。

    03先让固定文案变成可以流动的变量

    目前,ELSE 路径上的 LLM 会产生 text 输出变量,但 IF 路径上的"空输入兜底"是一个直接回复节点。

    直接回复节点的任务是把内容展示给用户。它不是一个适合继续向下游提供中间结果的处理节点。如果想让两条分支在后面重新汇合,就要先把兜底文案变成一个可以被后续节点引用的变量。

    因此,把原来的"空输入兜底"直接回复节点改造成模板转换节点。

    这个节点只做一件很简单的事:输出固定字符串。

    我没有收到有效内容,请输入一个国家名称,例如"法国"。

    模板转换节点的完整能力远不止输出一句固定文字,但那是另一个话题。这里使用它,只是因为我们需要把固定文案包装成一个字符串变量,让它能够像 LLM.text 一样继续流向下游。

    改造之后,两条路径都会产生同一类型的结果:

    • IF 路径:模板转换节点输出字符串;
    • ELSE 路径:LLM 节点输出 text 字符串。

    这一步很关键。变量聚合器不是把任意节点强行粘在一起,而是从不同互斥分支中选择一组同类型变量,再对外提供一个统一输出。

    国家名称分支的 If/Else 画布,IF 连接已改造成模板转换节点的空输入兜底,ELSE 仍连接 gpt-4o-mini 的 LLM 节点,两条路径都指向右侧的变量聚合器

    IF 和 ELSE 走的是两条完全不同的路,但都要在这里交出一个字符串。

    04用变量聚合器建立统一出口

    接下来,在模板转换节点和 LLM 节点之后添加变量聚合器。

    把下面两个变量加入同一组:

    • 空输入兜底模板转换节点的字符串输出;
    • LLM 节点的 text 字符串输出。

    它们都属于字符串类型,符合变量聚合器要求输入变量类型一致的规则。

    Dify 的变量聚合器支持字符串、数字、对象、布尔值、数组和文件等类型,但同一组里不能混合不同数据类型。如果一条分支交出字符串,另一条分支交出对象,聚合器无法假装它们是同一种下游数据。

    节点运行时也不是把两个字符串拼在一起。

    因为 If/Else 的两条路径互斥,每次运行只会有一条真正执行。sys.query 为空时,模板转换节点产生值,LLM 不运行;sys.query 非空时,LLM 产生 text,模板转换节点不运行。

    所以,聚合器配置了两个候选输入,实际每轮只有其中一个有值。那个真正产生出来的值,就成为聚合器的统一输出。

    可以把它理解成一个接线盒:

    • 上游可能从不同管线送来结果;
    • 每次只有一根管线真的有水;
    • 下游不再关心这次走的是哪条管线,只从同一个出口取水。

    如果一条复杂流程需要同时收敛多项数据,例如分别聚合"回答正文"和"来源信息",还可以在同一个变量聚合器里建立多个分组,各自独立聚合。不过这个案例只有一份最终回复,不需要增加额外复杂度。

    变量聚合器配置面板,变量赋值列表里有两项:LLM 的 text 字符串输出,以及空输入兜底的 output 字符串输出,两者都标注为 String 类型

    两个候选输入,同一种类型,聚合器只关心谁真的交了值。

    05让直接回复只认一个上游结果

    原来的"直接回复"节点继续保留,但不再只引用 LLM.text。

    把它的回复内容改为引用变量聚合器的输出变量,再让变量聚合器连接到这个直接回复节点。

    改造后的"国家名称"分支变成:

    条件分支 → 模板转换/LLM → 变量聚合器 → 直接回复

    当 IF 成立时,固定兜底文案经过模板转换节点进入聚合器,再由统一的直接回复节点发给用户。

    当 ELSE 成立时,gpt-4o-mini 生成首都回答,LLM.text 进入同一个聚合器,最后仍由同一个直接回复节点发给用户。

    上游仍然有两条路,下游却只剩一个出口。

    改造后的国家名称分支完整画布:条件分支的 IF/ELSE 两条路径分别经过模板转换节点和 LLM,一起汇入变量聚合器,再经过变量赋值节点,最终只连接一个直接回复节点

    上游还是两条路,下游已经收回成一个出口——画面里那个"变量赋值"节点是下一节才会讲到的东西,先眼熟一下。

    这次改造没有增加新的业务能力。用户问"法国",看到的答案和以前可能完全一样;空输入触发兜底时,文案也没有改变。

    但工作流的可维护性已经不同了。

    以后想在回复前统一增加免责声明,只改一个直接回复节点;想在输出前统一经过内容审核,只在聚合器后面接一次;想记录这一分支最终交付了什么,也只需要读取聚合器的统一输出。

    变量聚合器的价值,不是让结果"能出来",而是避免所有下游处理随着上游分支一起复制。

    分支负责制造差异,聚合器负责结束差异。

    "问候或闲聊"和"无法识别"目前仍然保留各自独立的直接回复。读者可以尝试沿用同一种模式,把它们也收敛到统一出口;这一集为了控制改造范围,只处理"国家名称"分支。

    06聚合器只能收互斥分支,不能合并并行结果

    变量聚合器有一条必须讲清楚的边界:它面向的是互斥分支,不是并行计算。

    If/Else 和问题分类器都符合这个条件。一次运行只会命中一条路径,所以聚合器面对的是"多个候选变量,只有一个实际有值"。

    并行分支完全不同。

    假设一条流程同时调用天气接口和汇率接口,两个分支都会执行,而且两个结果都需要保留。此时我们要做的是把多个真实结果组合起来,而不是从多个候选值里接住唯一存在的那个值。

    变量聚合器不负责这种并行结果合并。并行场景应该使用代码节点或模板节点,根据需要组织多个结果,这一集暂不展开。

    判断能不能使用变量聚合器,可以先问一句:

    这些上游结果,是"每次只会出现一个",还是"这一次可能同时出现多个"?

    只有前一种情况,才是聚合器设计出来解决的问题。

    还有一个容易造成混淆的历史命名:Dify 官方文档曾把变量聚合器称作"变量赋值节点"。它和接下来要用的"变量赋值器"不是同一个节点。

    两者的区别其实很明确:

    • 变量聚合器:从互斥分支中收拢一个已有值;
    • 变量赋值器:修改一个允许写入的变量。

    一个负责汇流,一个负责落笔。名字相近,数据动作完全不同。

    07第二次改造:让一轮数据活到下一轮

    现在,"国家名称"分支已经拥有统一出口。

    但用户连续问两个问题时,应用依然没有状态:

    用户:法国 用户:日本的首都是什么?

    第二轮运行时,sys.query 只包含"日本的首都是什么?"。上一轮的"法国"不会自动成为一个可供工作流读取的查询记录。

    这里要区分两件经常被统称为"记忆"的能力。

    LLM 可以使用对话上下文,让模型理解用户上一句话说过什么;工作流也可以保存结构化状态,让后续节点明确知道某个变量当前是什么值。

    前者更像把聊天记录交给模型阅读,后者更像应用自己维护一份状态。

    这一集要做的是后者:建立一个查询历史数组,并由工作流明确决定什么时候往里面追加内容。

    这就是会话变量。

    08会话变量不是更长寿的普通变量

    在 Chatflow 中新增一个会话变量:

    • 变量名:queried_countries
    • 含义:查询历史
    • 类型:Array[String]
    • 初始值:空数组

    添加会话变量对话框,名称填写 queried_countries,类型选择 array[string],右侧会话变量面板的说明写着会话变量用于存储 LLM 需要的上下文信息,是可读写的

    名称、类型、默认值——会话变量的配置和普通变量没什么两样,特殊的只是它的生命周期。

    普通节点输出通常属于这一轮运行。会话变量的生命周期则跨越同一个对话的多轮消息。

    用户先问"法国",工作流可以把"法国"写入数组;同一对话里再问"日本的首都是什么?",数组可以继续保留上一轮内容,并追加本轮输入。

    理想状态下,它会经历这样的变化:

    新对话开始

    queried_countries = []

    第一轮输入"法国"后

    queried_countries 保存一条查询记录。

    第二轮输入"日本的首都是什么?"后

    queried_countries 在原有记录后继续追加本轮原始输入。

    这里强调"同一个对话",因为会话变量不是永久用户档案。

    只要用户仍在当前对话中,它就可以跨轮保存;用户开启新对话后,会话变量重新回到初始值。它不会天然跨对话追踪用户,也不应该被当成数据库使用。

    会话变量只存在于 Chatflow。普通 Workflow 是一次任务式执行,没有 Chatflow 的对话生命周期,因此没有同样的会话变量概念。

    Chatflow 还提供 sys.conversation_id 和 sys.dialogue_count 等系统变量。前者标识当前会话,后者记录对话轮次并在每轮自动增加。它们能帮助我们识别"这是哪场对话、现在到了第几轮",但不会替我们维护查询历史。

    要让应用真正记住"问过什么",仍然需要自定义会话变量。

    09会话变量不能自己变化

    声明 queried_countries 只是准备了一个容器。

    如果没有节点向里面写数据,它会一直是空数组。用户问十次"法国",变量也不会因为名字叫"查询历史"就自动理解我们的意图。

    根据 Dify 的变量模型,会话变量只能通过变量赋值器节点修改。

    变量赋值器的任务,是把本轮工作流中已经产生的数据写进可写变量。它在这里要执行的动作是:

    把本轮的 sys.query 追加到 queried_countries。

    于是,临时变量和会话变量第一次发生了交接:

    • sys.query 保存这一轮的原始输入;
    • 变量赋值器读取 sys.query;
    • queried_countries 接住这份输入;
    • 下一轮运行时,sys.query 会改变,queried_countries 仍保留此前追加的记录。

    这就是"状态"产生的地方。

    10变量赋值器应该插在哪里

    这次把变量赋值器放在变量聚合器之后、统一的直接回复之前。

    改造后的主路径是:

    条件分支 → 模板转换/LLM → 变量聚合器 → 变量赋值器 → 直接回复

    这样安排有两个原因。

    第一,只有上游分支已经完成、聚合器拿到本轮最终结果以后,才把这次查询记入历史。变量赋值器由此成为"本轮处理已经走到交付前"的明确标记,而不是用户输入刚进入流程就抢先写入状态。

    第二,写入发生在最终回复之前。只要用户看到了这次回答,工作流就应该已经完成对应的状态更新,而不是先把消息发出去,再依赖后续节点补记。

    变量赋值节点配置面板,变量选择 queried_countries(Array[String]),赋值方式为追加,赋值内容引用用户输入的 query(String);背景画布可见该节点位于变量聚合器与直接回复之间

    追加,不是覆盖——这是这一步唯一不能选错的地方。

    这里沿用 EP3 的教学结构:空输入兜底位于问题分类器后的"国家名称"分支,正常情况下几乎不会被触发。我们真正需要验证的是"法国""日本的首都是什么?"这类有效查询会被追加到历史。

    如果以后把空值检查移到问题分类器之前,或者实际日志表明空值确实可能进入这条公共路径,就应该让写入动作只发生在有效查询路径上,避免把空字符串也记进数组。节点位置不是固定仪式,它应该服从"什么事件发生后,状态才算成立"这条业务语义。

    也可以把变量赋值器放在直接回复之后,让用户先看到答案再更新状态,但这会让"回答已交付"和"状态已落下"分成两个阶段。本例没有异步副作用,也不需要抢那一点展示顺序,因此放在回复之前更容易理解和检查。

    11这次存原始问题,不假装已经提取出国家名

    变量名叫 queried_countries,但我们实际追加的是 sys.query 原文。

    用户输入"法国",数组里记录"法国"。

    用户输入"日本的首都是什么?",数组里记录的也是"日本的首都是什么?",而不是经过清洗后的"日本"。

    这不是疏忽,而是一次有意控制范围的取舍。

    从自然语言中稳定提取国家实体,需要另一种能力。我们可以让节点把"日本的首都是什么?"解析成结构化字段,也可以处理一句话里出现多个国家、别名和歧义,但那已经进入参数提取器的范围。

    这一集只解决"怎样跨轮保存数据",不同时解决"怎样把数据清洗到最理想的形式"。

    先保存原始问题有三个现实好处:

    • 数据来源明确,就是当前轮的 sys.query;
    • 不需要增加额外模型调用或解析节点;
    • 即使提取逻辑尚未建立,应用也已经拥有可用的历史记录。

    它的代价也很清楚:这份历史是"用户问过什么",而不是一份严格的"国家名列表"。

    所以,queried_countries 是便于理解的教学命名。若用于生产,query_history 可能更准确。这里继续使用前者,是为了让变量目的和当前案例保持直观,同时明确它保存的是粗粒度原始问题。

    变量结构不必一步到位,但变量里究竟存了什么,必须说实话。

    下一集引入参数提取器以后,我们再有能力把原始问题变成更干净、可计算的国家实体。

    12让问候分支第一次感知历史

    会话变量真正有价值,不是因为调试面板里多了一个数组,而是因为后续轮次能够使用它。

    可以在"问候或闲聊"分支中引用 queried_countries,让问候回复根据当前查询历史表达类似这样的意图:

    如果用户此前查过国家,就提示他已经进行过哪些查询,并引导继续提问;如果历史为空,则使用普通的首次问候。

    写完设计意图,剩下的就不能再靠猜了——数组变量塞进一段中文回复文本里,最终到底长什么样,只有实际跑一遍才知道。

    实测流程是这样的:在同一场对话里,先后输入"德国""日本的首都是什么?",queried_countries 依次追加了这两条记录;再输入"你好",触发"问候或闲聊"分支,回复文本正确读到了这个数组。

    真实呈现出来的格式有点意外:

    你已经问过我这些国家啦:- 德国

    • 日本的首都是什么?

    数组的第一项被当成了紧跟在冒号后面的行内文本、用短横线引出,后面的项却各自换行、前面带着一个实心圆点——这是 Dify 把数组变量转成文本时套用的默认 Markdown 列表语法,前端再把 Markdown 渲染成这个不算工整的混合样式。它不是我们配置错了,就是这个界面版本的真实行为。

    Dify 预览面板:先后输入德国、日本的首都是什么?,均由直接回复正确返回首都答案;再输入你好,问候引导分支读取 queried_countries 并展示为德国和日本的首都是什么?两条记录

    数组变量真实的渲染效果——第一项内联、后面几项换行加圆点,没有想象中工整,但历史确实被记住了。

    这段实测结果值得记一条经验:会话变量存进去的是干净的数据结构,但它变成用户能读的文字之后,格式主动权并不完全在你手上。如果最终要交付给用户的是一份整齐的列表,几乎肯定需要在这一步之后再接一个模板转换节点,自己把数组拼成想要的样子——这一集不做这层加工,留一个明确的入口给读者:模板转换节点的完整用法,正好是这个格式问题的答案,也是下一集会用到的工具。

    当这一步跑通后,应用的行为就发生了本质变化。

    过去,用户问完"德国"再说"你好",问候分支只会机械地返回一条固定引导语。现在,它至少可以知道当前对话里已经发生过查询,并据此调整回复。

    这还不是永久记忆,也不是完整的用户画像。它只是同一场对话中的运行时状态。

    但从工程角度看,这已经比"让模型看起来像记得"更扎实:历史存在哪个变量里、什么时候追加、什么时候重置、哪些分支可以读取,全部能够在工作流中明确说明。

    13别把三类变量混成一个概念

    走到这里,"国家信息小助手"里已经出现了三类用途不同的数据。

    变量类别本例生命周期主要用途
    系统变量 sys.query、sys.conversation_id、sys.dialogue_count 由系统按运行或对话提供 读取本轮输入、识别会话和轮次
    节点输出变量 LLM.text、模板转换输出、聚合器输出 主要服务于当前轮工作流 在节点之间传递中间结果
    会话变量 queried_countries 跨越同一对话的多轮消息 保存需要后续轮次继续读取的动态状态

    系统变量由 Dify 提供。节点输出由某次节点运行产生。会话变量则由应用设计者声明,并通过变量赋值器主动修改。

    这三者不是寿命从短到长的同一种容器,而是各自承担不同责任。

    看到一个值下一轮还要使用,不要指望普通节点输出自动留下来;看到一个系统变量,也不要因为它带有 conversation 字样,就误以为它已经保存了业务历史。

    变量设计的第一步,从来不是起名字,而是判断:

    这份数据属于当前节点、当前一轮、当前对话,还是整个应用的固定配置?

    最后一种情况,就是环境变量。

    14环境变量保存的不是记忆,而是配置

    环境变量经常和会话变量一起出现在 Dify 的变量说明里,但它们解决的不是同一类问题。

    环境变量适合存放 API Key 等敏感信息,让凭证和应用编排分离。分享应用 DSL 文件时,密钥不必跟着流程配置一起泄露。

    它也可以保存多个节点共同使用的配置。例如,多个 LLM 节点需要引用同一份模型配置时,可以让它们读取环境变量;以后修改一次,所有引用位置都能同步使用新值。

    环境变量的本质是应用级配置。

    它由开发者设置,与某个用户无关,与某场对话无关,也不会因为用户多问一句而变化。对于所有使用该应用的人,它都是一份共享常量。

    会话变量则是运行时状态。

    它随对话产生,在同一对话的多轮运行之间变化,用户开启新对话后重置。不同对话拥有各自的状态,不应该共享一份 queried_countries。

    可以用一句话区分:

    环境变量回答"这个应用依赖什么配置",会话变量回答"这场对话已经发生了什么"。

    这一集不实操环境变量,因为"国家信息小助手"目前还没有调用需要密钥的外部服务。为了演示而随便塞进一个假 API Key,只会让概念显得比实际用途更抽象。

    下一集加入 HTTP Request 节点、真正访问外部 API 时,环境变量才会从一项配置功能变成必要的工程边界。

    15调试时,不要只看最后一句回答

    变量改造以后,测试重点也要随之变化。

    以前只要检查"法国的首都是不是巴黎",就能大致判断最小链路是否跑通。现在即使最后回复正确,内部状态也可能没有按预期更新。

    至少应该验证下面几组行为。

    验证聚合器的两条输入路径

    先输入正常国家问题,确认:

    • 流程进入 ELSE;
    • gpt-4o-mini 产生 text;
    • 聚合器输出等于本轮 LLM 输出;
    • 统一直接回复正确展示聚合结果。

    再以能够触发兜底的方式测试 IF 路径,确认:

    • 模板转换节点产生固定字符串;
    • LLM 没有在该路径运行;
    • 聚合器输出等于模板转换结果;
    • 最终仍由同一个直接回复节点交付。

    测试的重点不是两个答案都"看起来对",而是它们确实从两个上游变量进入了同一个下游出口。

    验证会话变量跨轮追加

    在同一场对话中依次输入:

    • 法国
    • 日本的首都是什么?
    • 德国

    每一轮之后检查 queried_countries 是否保留此前记录,并追加本轮的 sys.query,而不是覆盖旧值。

    然后开启一场新对话,确认它重新回到空数组。

    只有"旧对话能累积、新对话会重置"同时成立,才能证明这是一份会话状态,而不是全局共享数据或单轮临时输出。

    验证不同分支读取同一份状态

    完成两次国家查询后,再发送一条问候,让流程进入"问候或闲聊"分支。

    检查这个分支能否读取查询历史,并观察数组的实际呈现。若格式不适合直接交给用户,先记录真实行为,再决定后续怎样格式化,不要为了让截图好看而跳过数据语义。

    最后再测一场全新对话中的首次问候。此时历史为空,回复不应该假装用户已经查询过任何内容。

    16变量真正建立的是责任边界

    国家信息小助手改造后的完整工作流全景:开始节点连接问题分类器,国家名称分支经条件分支、模板转换/LLM、变量聚合器、变量赋值节点汇入直接回复,问候或闲聊分支引用查询历史,无法识别分支保持不变

    这一集结束时,"国家信息小助手"长这样——比 EP3 结束时多了三个节点,但每一个都在做一件明确的事。

    这次我们没有接知识库,没有调用外部 API,也没有换更强的模型。

    但"国家信息小助手"的结构已经向真正可维护的应用迈了一大步。

    变量聚合器解决的是空间上的分散:同一轮运行里,多个互斥分支可以产生不同结果,再汇入一个统一出口。它减少下游重复配置,却不负责合并并行执行产生的多个真实结果。

    会话变量解决的是时间上的断裂:这一轮产生的信息可以跨越工作流结束,继续服务同一场对话的后续轮次。它只属于 Chatflow 的对话生命周期,用户开启新对话后便会重置。

    变量赋值器则把两者之间最容易忽略的动作说清楚:状态不会自动产生,必须由工作流在明确的位置主动写入。会话变量负责"保存在哪里",变量赋值器负责"什么时候、把什么写进去"。

    环境变量又站在另一条轴线上。它不保存用户经历,而是保存开发者为应用准备的共享配置,尤其适合 API Key 等不该进入 DSL 的敏感信息。

    如果只记住这一集的两句话,可以记住:

    多条互斥分支需要共享同一套下游处理,用变量聚合器把出口收回来。

    一份数据需要跨越同一对话的多轮运行,用会话变量保存,再由变量赋值器明确更新。

    节点决定流程往哪里走,变量决定数据怎样流、在哪里汇合、能活多久。直到这一步,Dify 画布才不再只是一张连线图,而开始拥有真正的应用状态。

    下一集,我们会把外部世界接进来:用 HTTP Request 调用外部 API,用环境变量保存 API Key,再让 Code 节点和参数提取器处理从外部返回的数据。


    本文首发于我的个人站 chengbei.org,同步转载于此。

    赞(0)
    未经允许不得转载:171主机测评 » 别只让数据路过:用变量给 Dify 应用打地基 | Dify 系列 · EP4
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址