← 返回文章列表

Dify 企业级实验(02):跨应用状态传递——多轮对话的状态如何跨应用不丢?

1. 业务场景

先讲一个我们实际遇到的场景。

企业客服最常见的对话长这样:用户第一轮说「我要退货」,第二轮补上订单号,第三轮才说清楚期望怎么处理。信息是分轮次到齐的——客服对话应用每轮只拿到一小块,但后台工单应用要的是一整份。

我们第一次接这类需求时,第一反应也是「状态嘛,存数据库不就行了」。真正动手才发现——Dify 的 chatflow 原生就有对话变量(conversation_variables),跨轮次保持,配合工具调用和变量写回就能组成状态闭环,根本不用自己搭存储。难的不是存,是「每一轮都在正确的时间把状态拿出来用」。

如果对话应用收集到的状态传不到后台,工单就建不起来;就算建起来了,用户第二天回来问「我的工单处理到哪了」,系统也得记得住、答得上。

这不是个例。任何「用户分步提供信息、后台分步处理」的场景都是这个模式:理赔资料收集、预约信息确认、多步表单填写——对话跨轮次,状态就必须跨轮次。

2. 场景痛点

这个流程的痛点,在客服团队身上体现得最直接:

本质上,多轮对话的价值恰恰在「跨轮次记住」——状态没地方放、没人管,用户说过的话就等于白说

3. 方案:为什么是对话变量 + 状态写回

Dify 的 chatflow 有一个原生状态容器——对话变量(conversation_variables),配合工具调用与变量写回,正好组成跨应用状态闭环。选它的理由:

这篇文章我们就用它搭一个「客服工单状态闭环」:客服对话应用收集状态、工单应用处理、工单号写回对话变量。

4. 整体架构

graph TD subgraph sub_chat["客服对话(chatflow,dify104_02_01)"] start["开始(用户消息)"] extract["提取工单信息(PE:order_id/issue_desc/expectation)"] merge["合并校验信息(code:PE 值与对话变量值取并集 + complete/ticket_exist 判断)"] persist["持久化到对话变量(assigner ×3:order_id/issue_desc/expectation)"] decide{"是否创建工单(complete=true 且 ticket_exist=false)"} create_ticket["创建工单(tool,调 dify104_02_02)"] assemble["组装工单结果(code)"] write_back["写回工单号(assigner)"] reply["工单创建回复"] ask_more["补充信息/状态回复"] start --> extract --> merge --> persist --> decide decide -- "是" --> create_ticket --> assemble --> write_back --> reply decide -- "否" --> ask_more end subgraph sub_ticket["工单应用(workflow,dify104_02_02,被调用)"] t_start["开始:order_id/issue_desc/expectation/user_id"] t_create["创建工单(code:生成 TK-xxxx)"] t_persist["持久化工单记录(http → KV)"] t_end["结束(ticket_no)"] t_start --> t_create --> t_persist --> t_end end

链路很清晰:每轮提取新信息 → 与对话变量合并 → 校验完整性 → 建单或追问 → 工单号写回。状态闭环的关键是「写回」——工单号回到对话变量,用户下一轮就能查到。

5. 模块设计

5.1 对话变量(状态容器)

chatflow 在 conversation_variables 里声明 4 个状态变量,跨轮次保持(102-15/103 实测)。id 必须是合法 UUID(非 UUID 会报 SQL 500):

conversation_variables:

- description: 已收集的订单号

  id: 53ad6c85-cc60-4d1f-b8bf-06d7e6fb90a3

  name: order_id

  selector: [conversation, order_id]

  value: ''

  value_type: string

# issue_desc / expectation / ticket_no 同结构

5.2 合并校验 code(cd_update)

PE 每轮只提取新信息,要和对话变量已有值合并,并输出两个字符串状态标志:

def main(pe_order_id, pe_issue_desc, pe_expectation,

         cv_order_id, cv_issue_desc, cv_expectation, cv_ticket_no) -> dict:

    order_id = pe_order_id or cv_order_id or ""

    issue_desc = pe_issue_desc or cv_issue_desc or ""

    expectation = pe_expectation or cv_expectation or ""

    ticket_exist = "true" if cv_ticket_no else "false"

    missing = []

    if not order_id: missing.append("订单号")

    if not issue_desc: missing.append("问题描述")

    if not expectation: missing.append("期望结果")

    complete = "true" if not missing else "false"

    if ticket_exist == "true":

        message = "您的工单 " + str(cv_ticket_no) + " 正在处理中,请耐心等待…"

    elif complete == "true":

        message = "已收集完整信息,正在为您创建工单…"

    else:

        message = "请补充以下信息:" + "、".join(missing) + "。"

    return {"order_id": order_id, "issue_desc": issue_desc, "expectation": expectation,

            "complete": complete, "ticket_exist": ticket_exist, "message": message}

5.3 触发条件(if5)

创建工单需要 complete=true ticket_exist=false——校验结果必须接 if-else 消费,否则状态不全也会触发下游(102-20 实测教训):

- id: if5

  data:

    type: if-else

    title: 是否创建工单

    cases:

    - case_id: create

      logical_operator: and

      conditions:

      - comparison_operator: is

        value: 'true'

        variable_selector: [cd_update, complete]

      - comparison_operator: is

        value: 'false'

        variable_selector: [cd_update, ticket_exist]

5.4 工单工具的参数模板

跨应用传递 = 工具调用参数。:chatflow 的 tool 节点参数模板引用 code 节点输出偶发为空(工具报参数 required);本实验状态本就存在对话变量,改用对话变量模板语义最贴合:

- id: tool5c

  data:

    type: tool

    title: 创建工单(工单应用)

    provider_type: workflow

    tool_name: dify104_02_gongdan

    tool_parameters:

      order_id:

        type: mixed

        value: '{{#conversation.order_id#}}'

      issue_desc:

        type: mixed

        value: '{{#conversation.issue_desc#}}'

      expectation:

        type: mixed

        value: '{{#conversation.expectation#}}'

    tool_configurations: *tool_parameters   # 双写同值

5.5 写回工单号(状态闭环)

工具返回 ticket_no 后,用 assigner 节点写回对话变量write_mode: over-write),用户下一轮就能查到工单号,且 ticket_exist=true 保证不重复建单。

6. 运行验证

输入 预期 结果
①「我要退货」 PE 未提取到订单号 → 追问「请补充订单号」 通过(4 轮多轮对话实测)
② 补订单号+问题描述 字段齐全 → 调用工单应用(Workflow as Tool)创建工单 → 返回工单号 通过
③④ 后续轮次问「我的工单处理到哪了」 对话变量已写回工单号 → 回复处理中,不重复建单 通过
检查 KV 工单记录 只有 1 条(不重复创建) 通过

7. 实战坑

现象 修复
chatflow 工具参数模板引用 code 节点输出 偶发为空,工具报参数 required 改用对话变量模板 {{#conversation.x#}}(实测,本实验)
工具重建时 parameters 传空数组 调用方传参校验失败 parameters 必须 = 子工作流 start 变量(实测,本实验)
code 字符串状态判断用 elif complete: 字符串 "false" 恒真,误触发「已收集完整」分支 必须 == "true" 显式比较(实测,本实验)
对话变量 id 非 UUID 报 SQL 500 错误 使用合法 UUID(实测,102-15)
校验结果不接 if-else 状态不全也触发下游(死代码) if-else 消费 complete/ticket_exist(实测,102-20)
code 节点沙箱禁写文件 PermissionError: /tmp 工单记录改用本机 KV 模拟服务持久化(实测,本批)

8. 实验文档及源码获取

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

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过