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):条件分支高阶策略------多条件路由如何避免分支爆炸?

相关推荐
william_yangshun3 小时前
【AI Agent 实战】eve 中文版:Vercel 官方「文件系统优先」Agent 框架上手指南
ai agent
新知图书6 小时前
14.5 AI试驾预约系统的完整实现
人工智能·agent·ai agent·智能体
零号栈帧8 小时前
Codex 手机控制台——AgentDeck
windows·cloudflare·codex·pwa·ai agent
deepseek238 小时前
Anthropic MHS 拆解:MCP 管软件,MHS 管硬件,AI 第一次安全操作显微镜、机械臂和激光器
硬件·ai agent·anthropic·mcp
新知图书9 小时前
14.1 多模态试驾预约Agent系统概述
人工智能·agent·ai agent·智能体
今天的砖头有点烫手啊12 小时前
用 Agent 做 Excel 报表自动化:从每月加班到一键生成
人工智能·ai agent
DanaAI1 天前
从会说到会做:智能体与工具调用如何重构硬件交互能力
ai agent
小七-七牛开发者1 天前
拆解 dsh 系列:从源码和版本变化看 DeepSeek Harness 的设计取舍
ai·大模型·claude·token·工作流·skill·claudecode·ai coding
XLYcmy1 天前
PDF论文处理器 - 完整使用指南
pdf·llm·json·agent·token·csv·dify
ylj_dev1 天前
从 0 构建 AI Workload Platform(九):真实场景、最小控制台与开源发布
go·react·开源项目·工作流·ai agent