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

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

Dify 实验系列 · 中级 14/20 | 实验编号:DIFY-102-15

上一篇:Dify 中级实验(13):多 Agent 协作------如何编排多个智能体分工干活?

1. 实验目的

普通工作流每次执行都是「失忆」的:上一轮说了什么,这一轮全忘。本实验引入对话变量(Conversational Variables) ,构建一个跨轮次有状态的「多轮问卷收集系统」:逐步收集姓名/联系方式/年龄/使用场景,边问边记、按进度推进。练的是 Dify 变量体系(开始变量/节点变量/对话变量/环境变量)的作用域认知,以及「读 → 改 → 写回 → 持久化」的规范姿势。适合要做表单收集、多轮引导、客服工单类对话应用的同学。

2. 场景设计

「多轮问卷系统」(Chatflow / advanced-chat):用户说「开始问卷」→ 助手逐步提问 5 步 → 用户回答 → 系统校验并记录 → 下一轮接着问 → 最后确认完成。这不是一次查询,而是一个有状态的对话流程。

三个关键认知:

  • 对话变量只在对话型应用(advanced-chat/chatflow)有效,workflow 模式没有;
  • 对话变量跨轮次持久化、按 session 隔离、服务端存储(类似 Web 的 Session 机制,客户端无法篡改);
  • LLM 不能写变量------正确姿势是「代码节点读 → 改 → 返回 → 变量赋值器(Assigner)持久化」。

3. 节点拓扑

jinja2 复制代码
开始(无输入变量;声明 5 个对话变量)
  ↓
分析用户回复(LLM:读 {{#conversation.*#}},输出 is_valid / extracted_info / next_action / message)
  ↓
更新对话变量(Code:合并新信息、推进/回退步骤、越界钳制、生成进度)
  ↓
持久化已收集信息(Assigner:conversation.collected_info ← cd_update.collected_info)
  ↓
持久化当前步骤(Assigner:conversation.current_step ← cd_update.current_step)
  ↓
直接回复(Answer:{{#cd_update.message#}})

4. 关键配置

4.1 对话变量声明(开始节点)

yaml 复制代码
conversation_variables:
- id: b9d9cc2d-2843-420e-9f8d-7f7c8a66a4a1   # 必须是合法 UUID!
  name: collected_info
  value_type: object
  value: {}
- id: ca0ca474-becf-4284-bcde-03d30f6cf850
  name: current_step
  value_type: number
  value: 0
- id: 1f257899-86c4-446a-bfaa-3ab23ac13fb9
  name: total_steps
  value_type: number
  value: 5
- id: c9bfa3d7-9300-44e6-ab61-5949d8cd92ae
  name: conversation_history
  value_type: array[string]
  value: []
- id: 443a1074-97a1-41a6-bfe6-b46e1b7bbaf1
  name: validation_errors
  value_type: array[string]
  value: []

4.2 分析用户回复(LLM)------三花括号读对话变量

jinja2 复制代码
你是一个问卷助手,正在收集用户信息。

当前进度:第{{#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": "对用户说的话"
}

4.3 更新对话变量(Code)

python 复制代码
def main(current_step: int, collected_info: dict, llm_text: str, total_steps: int) -> dict:
    import json, re
    step = int(current_step) if str(current_step).isdigit() else 0
    total = max(int(total_steps), 1) if str(total_steps).isdigit() else 5
    info = collected_info if isinstance(collected_info, dict) else {}
    analysis = {}
    m = re.search(r'\{.*\}', (llm_text or "").strip(), re.DOTALL)
    if m:
        try:
            analysis = json.loads(m.group(0))
        except Exception:
            analysis = {}
    new_info = analysis.get("extracted_info", {})
    if isinstance(new_info, dict):
        for k, v in new_info.items():
            if v and k not in info:          # 不覆盖已有信息
                info[k] = v
    action = analysis.get("next_action", "reask")
    if action == "ask_next":
        step += 1
    elif action == "complete":
        step = total
    step = max(0, min(step, total))          # 越界钳制
    progress = "步骤 {}/{}".format(step, total)
    message = analysis.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}

4.4 持久化:变量赋值器(Assigner)

代码节点只是「算出了新值」,必须显式写回对话变量才会跨轮生效。新版 Dify 用 Assigner 节点(旧版在结束节点做输出映射------漏掉这步,变量永远不更新):

yaml 复制代码
- data:
    assigned_variable_selector: [conversation, collected_info]   # 写回目标(对话变量)
    input_variable_selector: [cd_update, collected_info]          # 值来源(代码节点输出)
    write_mode: over-write
    title: 持久化已收集信息
    type: assigner
  id: asg_info

答案节点直接回复 {``{#cd_update.message#}},一轮闭环完成。

5. 运行验证

输入 预期 实测
开始问卷 欢迎语,询问姓名 助手回复「请输入您的姓名」
张三 记录姓名,询问联系方式 current_step 1→2,collected_info 含 name
zhangsan@email.com 记录邮箱,询问年龄 逐步推进,信息不丢失
25-30 记录年龄,询问场景 collected_info 累积 3 项
主要用于客户管理 进入确认环节,回显全部信息 汇总展示姓名/邮箱/年龄/场景
确认无误 问卷完成,进度 100% current_step=5,回复完成语
(刷新页面后继续同会话) 状态保持 对话变量跨轮持久化,未归零

6. 采坑点

现象 修复
对话变量 id 不是 UUID 保存/运行报 SQL 500 错误 id 必须是合法 UUID(如 b9d9cc2d-2843-420e-9f8d-7f7c8a66a4a1)
代码节点算完不持久化 每轮变量都回到初始值,问卷永远停在第一步 用 Assigner 节点(或结束节点映射)显式写回对话变量(实验文档设计约束)
想在 LLM 节点里直接改对话变量 改不动/行为异常 LLM 只读,写操作统一走「代码节点算值 → Assigner 落盘」(实验文档设计约束)
在 workflow(非对话型)里找对话变量 根本没有这个配置项 对话变量仅 advanced-chat/chatflow 支持(实验文档设计约束)
extracted_info 覆盖已有字段 用户改口后旧值被新值覆盖 合并逻辑加「已存在则不覆盖」保护(实验文档设计约束)

7. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。


下一篇:Dify 中级实验(15):条件分支高阶策略------多条件路由如何避免分支爆炸?

相关推荐
OmniGoAI3 小时前
用账户分组做内容矩阵:同平台多账号如何发
ai agent·多账号管理·内容分发·内容矩阵
deepseek234 小时前
Meta Muse Code 拆解:不拼跑分拼恢复,事件日志如何让编程智能体 24 小时不翻车
meta·ai agent·编程智能体
minhuan4 小时前
大模型应用CI/CD全流程解析:打通模型训练、评估、部署自动化,应用持续迭代实践23.7
ci/cd·自动化·大模型应用·大模型应用ci/cd全流程解析·大模型应用工程体系
鬼手点金5 小时前
Hermes‑Agent 使用教程
python·ubuntu·powershell·cmd·ai agent·opencode·hermes
中间件XL17 小时前
ai-agent框架spring ai/alibaba 原理源码分析(十)沙箱
沙箱·ai agent·智能体·spring ai·spring ai graph
doiito19 小时前
【Agent Harness】Gliding Horse 最新升级:把“智能闭环”真正跑通的一次深度优化
rust·架构设计·系统设计·ai agent
minhuan1 天前
向量数据库选型、向量化与检索调优,结合大模型搭建私有文档检索增强完整应用实践23.6
大模型应用·向量数据库介绍·rag系统调优·rag应用实践
前端技术官1 天前
OpenClaw 跟病毒的区别是什么?
ai agent·ai安全·openclaw·devops风险·chmod 777
k4m7v2pz1 天前
34 天 33 个版本:rvs(rust-verb-shell)如何用版本号对抗 AI 误判
rust·shell·命令行工具·ai agent·系统信息·跨平台开发