多轮对话的本质:状态管理、上下文理解、记忆机制

说实话,很多做Prompt的人,把多轮对话想简单了。

他们以为就是“你一句,我一句”,把历史消息拼在一起就行。

嗯,我刚开始也这么干过。结果呢?模型聊着聊着就“失忆”了,或者答非所问。

后来我花了大量时间调优线上对话系统,才真正搞明白——多轮对话的核心,其实是三个东西:状态管理、上下文理解、记忆机制。 这三者缺一不可,而且顺序不能乱。

1. 状态管理:对话的“方向盘”

什么叫状态?说白了,就是当前对话进行到哪一步了

你想想看,一个客服对话,用户刚说完“我要退货”,下一秒又说“我的订单号是123”。如果系统没有状态管理,它可能以为用户要重新开一个话题。

我个人习惯,在设计多轮Prompt时,会先定义一个状态机。每个状态对应一个意图或一个槽位。

核心思路: 状态决定了模型下一步该做什么,而不是让模型自由发挥。

举个例子,一个订餐机器人的状态流转:

状态定义:
- INIT: 初始状态,等待用户输入
- COLLECTING_ADDRESS: 收集地址
- COLLECTING_ORDER: 收集菜品
- CONFIRMING: 确认订单
- COMPLETED: 完成

Prompt设计(简化版):
当前状态:{{state}}
用户输入:{{user_input}}
历史槽位:{{slots}}

请根据当前状态处理用户输入:
- 如果状态是INIT,判断用户意图(点餐/查询/取消)
- 如果状态是COLLECTING_ADDRESS,提取地址信息并更新槽位
- 如果状态是COLLECTING_ORDER,提取菜品名称和数量
- 如果状态是CONFIRMING,确认用户是否要提交

我在项目中遇到过一个问题:状态定义得太细,导致模型频繁切换,反而容易出错。后来我学乖了,状态数量控制在5个以内,每个状态只做一件事。

避坑指南: 我曾经把“确认地址”和“确认菜品”分成两个独立状态,结果模型在确认地址时,用户突然说“再加一个菜”,系统就卡住了。后来我把所有确认逻辑合并成一个“CONFIRMING”状态,反而更灵活。

3. 上下文理解:对话的“地图”

状态管理解决了“走到哪一步”,但上下文理解解决的是“刚才说了什么”

这里有个常见的误区:很多人以为把历史消息全部塞进Prompt里就行。其实不是的。

为什么?因为Token有限,而且模型对长上下文的注意力会衰减。你想想看,如果用户聊了20轮,你把所有内容都堆进去,模型很可能只关注最后几轮,前面的关键信息就丢了。

我建议的做法是:对上下文进行结构化压缩

上下文类型 处理方式 示例
当前轮次 完整保留 用户最新输入
最近3轮 保留关键信息 提取意图、实体、槽位
历史轮次 摘要压缩 “用户已确认地址,正在选择支付方式”
全局信息 持久化存储 用户ID、偏好、历史订单

说白了,就是分层管理。近的详细,远的概括。这样既不会丢失关键信息,也不会让模型被噪音淹没。

一个小技巧: 我习惯在Prompt里加一个“上下文摘要”字段,每次对话结束后,让模型自动生成一句摘要。比如:“用户想订一份披萨,已确认地址是北京市朝阳区,正在等待支付确认。” 下一轮对话直接把这个摘要放进去,效果比堆历史消息好得多。

3. 记忆机制:对话的“长期存储”

状态管理和上下文理解,解决的是单次对话内的问题。但真正的多轮对话,往往跨越多天、多次会话。

比如用户今天问“我的订单到哪了”,明天又问“上次那个订单能退款吗”。如果系统没有记忆,它就会把用户当成一个全新的人。

嗯,这里要注意:记忆不等于历史消息。记忆是经过提炼的、结构化的信息。

我一般把记忆分为三类:

  • 短期记忆: 当前对话内的关键信息,比如用户刚说的地址、订单号。对话结束后可以丢弃。
  • 长期记忆: 跨会话的持久信息,比如用户偏好、历史投诉记录、会员等级。需要存储到数据库。
  • 工作记忆: 当前正在处理的任务状态,比如“用户正在申请退款,已进入审核流程”。

在Prompt设计上,我通常会这样处理长期记忆:

用户画像(长期记忆):
- 用户ID: 12345
- 会员等级: 黄金
- 历史偏好: 喜欢辣味、偏好微信支付
- 最近投诉: 2024-03-01 订单延迟

当前对话上下文(短期记忆):
- 当前状态: REFUND_PROCESSING
- 已收集信息: 订单号 #8888,退款原因“商品破损”
- 最近3轮摘要: 用户提供了订单号,确认了退款原因

请基于以上信息,回答用户的问题。

核心原则: 记忆要“主动遗忘”。不是所有信息都值得记住。我一般只保留对后续对话有影响的信息,比如用户明确表达的偏好、未完成的任务、重要的状态变更。那些“嗯”、“好的”、“谢谢”之类的,直接丢掉就好。

我曾经犯过一个错误:把用户所有历史对话都存下来,然后一股脑塞进Prompt。结果模型被大量无关信息干扰,回答质量反而下降。后来我改成只保留最近5轮对话 + 结构化摘要,效果立竿见影。

总结一下

状态管理、上下文理解、记忆机制,这三者其实是层层递进的关系:

  • 状态管理 告诉你“现在该做什么”
  • 上下文理解 告诉你“刚才发生了什么”
  • 记忆机制 告诉你“用户是谁、想要什么”

你想想看,如果一个对话系统能把这三件事做好,那它基本就不会“失忆”了。我见过太多失败的案例,都是因为只关注了其中一点,忽略了其他两点。

嗯,下一章我会讲具体的Prompt模板设计,到时候会结合这三个机制,给出可以直接用的代码。今天就先到这里。

课后思考: 如果你现在要设计一个“多轮客服对话”系统,你会先定义状态,还是先设计记忆结构?我个人建议先定义状态,因为状态决定了对话的骨架,记忆只是填充血肉。但如果你做的是“闲聊型”对话,那记忆可能比状态更重要。没有标准答案,看场景。