对话管理的核心问题
单轮交互很简单:用户提问→模型回答。但Agent需要处理多轮对话——记住之前说了什么、理解隐含意图、在适当的时候追问、在不同话题间切换。对话管理就是解决这些问题。
对话管理的三种范式
范式一:状态机(任务导向)
传统任务型对话系统采用状态机:
States: [GREETING, COLLECTING_INFO, CONFIRMING, EXECUTING, CLOSING]
GREETING → "您好,请问需要什么帮助?"
COLLECTING_INFO → "请告诉我您的出发地和目的地"
CONFIRMING → "您要从北京到上海,对吗?"
EXECUTING → [执行预订]
CLOSING → "预订成功,还有其他需要吗?"
每个state有明确的必填槽位(slot):
slots: {
origin: required,
destination: required,
date: required,
passenger: optional
}
优点: 流程清晰、可控性强、容易调试 缺点: 僵化、不自然、无法处理对话偏离
范式二:自由对话(开放域)
LLM天然擅长自由对话——用户可以随时切换话题、提出任何问题。对话管理完全交给LLM的上下文理解能力。
优点: 自然、灵活、用户体验好 缺点: 不可控、可能偏离任务、难以保证任务完成
范式三:混合管理(推荐实践)
在实践中最有效的是混合方案——LLM负责对话自然性,系统负责状态追踪:
用户: "帮我订明天去上海的票"
→ 系统状态追踪: {intent: "订票", date: "明天", destination: "上海", origin: null}
→ origin缺失,需要追问
→ LLM自然回复: "好的,去上海!请问您从哪里出发呢?"
用户: "从北京,高铁还是飞机?"
→ 系统更新: {origin: "北京"}
→ 需要决策: 交通方式
→ LLM: "从北京到上海,高铁约4.5小时,飞机约2小时。您倾向哪种?"
对话状态追踪
状态表示
class DialogState:
# 用户意图
intent: str # "book_ticket", "qa", "chitchat"
# 已知信息(槽位)
slots: dict # {"origin": "北京", "destination": "上海"}
# 对话历史摘要
summary: str # "用户要订北京到上海的高铁票"
# 缺失信息
missing_slots: list # ["date", "passenger_name"]
# 上下文
topic: str # 当前话题
topic_history: list # 话题切换历史
状态更新
每次用户输入后更新状态:
def update_state(state, user_input):
# 1. 意图识别
intent = classify_intent(user_input)
# 2. 槽位提取
new_slots = extract_slots(user_input)
state.slots.update(new_slots)
# 3. 缺失槽位检查
required = get_required_slots(state.intent)
state.missing_slots = [s for s in required if s not in state.slots]
# 4. 话题追踪
if intent != state.intent:
state.topic_history.append(state.topic)
state.topic = intent
return state
对话策略
基于状态决定下一步行动:
def get_policy_action(state):
if state.missing_slots:
# 需要追问缺失信息
return Action("ASK_SLOT", slot=state.missing_slots[0])
if state.intent and not state.missing_slots:
# 信息完整,执行任务
return Action("CONFIRM", data=state.slots)
if state.intent == "chitchat":
# 自由对话
return Action("CHAT")
if state.intent == "qa":
# 问答
return Action("ANSWER")
多话题管理
话题切换
用户可能在任务执行中切换话题:
用户: "帮我订明天去上海的高铁" [意图: 订票]
用户: "对了,上海明天天气怎么样?" [意图: 天气查询]
用户: "好的,继续订票吧" [回到订票]
处理策略:
- 保存当前任务状态
- 切换到新话题
- 对话结束后恢复之前任务
实现方案
class TopicManager:
def __init__(self):
self.active_topics = [] # 栈结构
def push_topic(self, topic, state):
self.active_topics.append((topic, state))
def pop_topic(self):
if self.active_topics:
return self.active_topics.pop()
return None
def current_topic(self):
return self.active_topics[-1] if self.active_topics else None
主动追问策略
何时追问
- 必填信息缺失
- 信息模糊(“明天"是哪天?)
- 需要确认(“您说的是北京朝阳区吗?")
- 需要选择(“高铁还是飞机?")
追问方式
直接追问: “请问您的出发地是哪里?”
选择式追问: “您从北京出发还是从天津出发?”
上下文追问: “我需要确认一下您的出发地。之前您提到在北京,是否从北京出发?”
隐式追问: “去上海的高铁有G3、G5、G7,请问您倾向哪个时间段?”
对话历史管理
摘要压缩
长对话需要摘要压缩历史:
原始历史(5000 tokens):
Turn 1: 用户问X,助手答Y
Turn 2: 用户问Z,助手答W
...
摘要(500 tokens):
用户之前咨询了X和Z问题,助手分别给出了Y和W的回答。
用户当前正在订北京到上海的票。
实现:每隔N轮调用一次小模型生成摘要。
选择性记忆
不是所有历史都需要保留:
- 关键决策点:保留
- 闲聊内容:摘要后丢弃
- 用户偏好:提取后永久保存
- 错误尝试:遗忘
对话终止判断
Agent需要判断何时结束对话:
信号
- 用户明确说"没有了”/“谢谢”
- 任务完成且无新需求
- 长时间无响应
- 对话陷入循环
处理
def should_end_conversation(state, recent_turns):
# 明确终止信号
if any(phrase in recent_turns[-1]["user"]
for phrase in ["没有了", "谢谢", "再见"]):
return True
# 任务完成
if state.intent == "completed":
return True
# 循环检测
if detect_loop(recent_turns):
return True
return False
总结
对话管理是Agent的"前台"能力——决定了用户体验。最好的体验来自"混合管理”:LLM保证对话自然性,系统保证任务可控性。状态追踪让Agent不会"忘了用户在干什么”,主动追问让对话高效推进,话题管理让多任务切换无缝衔接。对话管理的终极目标是:让用户感觉在和一个人对话,而非一个系统交互。