十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态?

Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态? Dify 中级实验14对话变量与状态管理——如何让工作流记住多轮对话的状态Dify 实验系列 · 中级 14/20 | 实验编号DIFY-102-15基于 Dify 1.16.1 实测2026-081. 业务场景先讲一个我们实际遇到的场景。一家 SaaS 公司的产品团队要做一个「客户需求调研」用户在对话框里逐步回答 5 个问题姓名、联系方式、年龄、使用场景、确认信息系统边问边记。听起来简单但他们用普通工作流搭出来的第一版彻底翻车——用户回答完第一个问题进入第二轮时上一轮说的内容全被清空了系统像个失忆的人一遍遍重复问同样的问题。普通工作流每次执行都是「失忆」的上一轮说了什么这一轮全忘。我们第一次接这类需求时第一反应也是「把上一轮的回答存进变量不就行了」。真正动手才发现——普通工作流每次执行都是「失忆」的开始变量只在当轮有效节点变量算完即弃不引入跨轮的存储机制多轮对话根本做不成。这不是个例。任何「要跨多轮对话收集信息、且每轮都要记得前面说了什么」的场景都是这个模式客服工单要逐步收集故障信息、报名系统要分步收集报名资料、体检问卷要按步骤采集健康数据、多轮引导式销售要边聊边记录客户意向——没有状态管理多轮对话就做不成。2. 场景痛点这个流程的痛点在需求调研系统的开发过程中体现得最直接每轮都失忆用户被反复问用户答完姓名下一轮系统又问「请提供姓名」——信息没有跨轮保存用户体验极差调研根本走不完 5 步。状态不知道存哪把数据塞进前端会话用户能篡改、刷新就丢自己在服务端写存储又要搭数据库、写接口为了一个问卷功能成本失控。信息校验没有载体用户填的邮箱格式不对、年龄超出范围系统想「记住这个错误、下一轮重问」但没有地方存放校验状态和错误记录。变量作用域混乱开始变量、节点变量、对话变量、环境变量分不清改错变量导致互相覆盖数据张冠李戴排查半天。本质上多轮对话应用的骨架就是「状态」——每轮要读什么、改什么、写回什么、持久化到哪里这套机制不建立起来对话应用就永远只能是一问一答的玩具。3. 方案为什么是对话变量 变量赋值器Dify 提供了专门解决这个问题的原生能力——对话变量Conversational Variables跨轮次持久化、按 session 隔离、服务端存储类似 Web 的 Session 机制客户端无法篡改。配合**变量赋值器Assigner**节点把计算出的新值显式写回对话变量就构成了「读 → 改 → 写回 → 持久化」的规范姿势。选它的理由平台原生、零成本不用自己搭存储对话变量由 Dify 服务端管理跨轮次、按会话隔离天然可靠作用域清晰对话变量只在对话型应用advanced-chat/chatflow有效和开始变量/节点变量/环境变量各司其职不容易混写回路径显式LLM 不能写变量统一走「代码节点算值 → Assigner 落盘」状态变更可追踪、可审计。这篇文章我们就用它搭一个「多轮问卷收集系统」用户说「开始问卷」→ 系统逐步提问 5 步 → 用户回答 → 系统校验并记录 → 下一轮接着问 → 最后确认完成。4. 整体架构开始无输入变量声明 5 个对话变量分析用户回复LLM读 {{#conversation.*#}}输出 is_valid / extracted_info / next_action / message更新对话变量Code合并新信息、推进/回退步骤、越界钳制、生成进度持久化已收集信息Assignerconversation.collected_info ← cd_update.collected_info持久化当前步骤Assignerconversation.current_step ← cd_update.current_step直接回复Answer{{#cd_update.message#}}链路很清晰入口无变量 → LLM 读对话变量分析用户回复 → 代码节点算新值 → Assigner 写回对话变量 → Answer 回复用户。关键设计是「LLM 只读、代码算值、Assigner 落盘」三层分工——任何一步缺失状态就断链。5. 模块设计5.1 对话变量声明开始节点conversation_variables:-id:b9d9cc2d-2843-420e-9f8d-7f7c8a66a4a1# 必须是合法 UUIDname:collected_infovalue_type:objectvalue:{}-id:ca0ca474-becf-4284-bcde-03d30f6cf850name:current_stepvalue_type:numbervalue:0-id:1f257899-86c4-446a-bfaa-3ab23ac13fb9name:total_stepsvalue_type:numbervalue:5-id:c9bfa3d7-9300-44e6-ab61-5949d8cd92aename:conversation_historyvalue_type:array[string]value:[]-id:443a1074-97a1-41a6-bfe6-b46e1b7bbaf1name:validation_errorsvalue_type:array[string]value:[]5.2 分析用户回复LLM——三花括号读对话变量你是一个问卷助手正在收集用户信息。 当前进度第{{#conversation.current_step#}}步共{{#conversation.total_steps#}}步 已收集的信息{{#conversation.collected_info#}} 对话摘要历史{{#conversation.conversation_history#}} 历史校验错误{{#conversation.validation_errors#}} 问卷内容 步骤1收集姓名 步骤2收集联系方式邮箱或手机号 步骤3收集年龄范围 步骤4收集使用场景 步骤5确认信息 用户当前回复{{#sys.query#}} 根据对话历史和当前回复判断 1. 用户是否回答了当前问题 2. 提取新的信息extracted_info 只放本轮新出现的字段 3. 当前答案是否合理 输出 JSON { is_valid: true/false, extracted_info: {要更新的字段: 值}, next_action: ask_next / reask / confirm / complete, message_to_user: 对用户说的话 }5.3 更新对话变量Codedefmain(current_step:int,collected_info:dict,llm_text:str,total_steps:int)-dict:importjson,re stepint(current_step)ifstr(current_step).isdigit()else0totalmax(int(total_steps),1)ifstr(total_steps).isdigit()else5infocollected_infoifisinstance(collected_info,dict)else{}analysis{}mre.search(r\{.*\},(llm_textor).strip(),re.DOTALL)ifm:try:analysisjson.loads(m.group(0))exceptException:analysis{}new_infoanalysis.get(extracted_info,{})ifisinstance(new_info,dict):fork,vinnew_info.items():ifvandknotininfo:# 不覆盖已有信息info[k]v actionanalysis.get(next_action,reask)ifactionask_next:step1elifactioncomplete:steptotal stepmax(0,min(step,total))# 越界钳制progress步骤 {}/{}.format(step,total)messageanalysis.get(message_to_user)or请输入信息{}.format(progress)return{collected_info:info,current_step:step,last_action:action,progress_pct:round(step/total*100),message:message}5.4 持久化变量赋值器Assigner代码节点只是「算出了新值」必须显式写回对话变量才会跨轮生效。新版 Dify 用 Assigner 节点旧版在结束节点做输出映射——漏掉这步变量永远不更新-data:assigned_variable_selector:[conversation,collected_info]# 写回目标对话变量input_variable_selector:[cd_update,collected_info]# 值来源代码节点输出write_mode:over-writetitle:持久化已收集信息type:assignerid:asg_info答案节点直接回复{{#cd_update.message#}}一轮闭环完成。6. 运行验证输入预期实测开始问卷欢迎语询问姓名助手回复「请输入您的姓名」张三记录姓名询问联系方式current_step 1→2collected_info 含 namezhangsanemail.com记录邮箱询问年龄逐步推进信息不丢失25-30记录年龄询问场景collected_info 累积 3 项主要用于客户管理进入确认环节回显全部信息汇总展示姓名/邮箱/年龄/场景确认无误问卷完成进度 100%current_step5回复完成语刷新页面后继续同会话状态保持对话变量跨轮持久化未归零7. 实战坑坑现象修复对话变量 id 不是 UUID保存/运行报 SQL 500 错误id 必须是合法 UUID如 b9d9cc2d-2843-420e-9f8d-7f7c8a66a4a1代码节点算完不持久化每轮变量都回到初始值问卷永远停在第一步用 Assigner 节点或结束节点映射显式写回对话变量实验文档设计约束想在 LLM 节点里直接改对话变量改不动/行为异常LLM 只读写操作统一走「代码节点算值 → Assigner 落盘」实验文档设计约束在 workflow非对话型里找对话变量根本没有这个配置项对话变量仅 advanced-chat/chatflow 支持实验文档设计约束extracted_info 覆盖已有字段用户改口后旧值被新值覆盖合并逻辑加「已存在则不覆盖」保护实验文档设计约束8. 实验文档及源码获取实验文档完整操作步骤DIFY-15对话变量与状态管理.md源码可直接导入dify102_15_对话问卷系统.yml文章聚焦核心配置与采坑点实验的完整分步操作节点搭建/参数表/调试指引见实验文档原文。下一篇Dify 中级实验15条件分支高阶策略——多条件路由如何避免分支爆炸 你在这个实验的场景里踩过什么坑欢迎评论区分享你的实战经验。
返回列表