对话管理的核心问题

单轮交互很简单:用户提问→模型回答。但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")

多话题管理

话题切换

用户可能在任务执行中切换话题:

用户: "帮我订明天去上海的高铁"  [意图: 订票]
用户: "对了,上海明天天气怎么样?"  [意图: 天气查询]
用户: "好的,继续订票吧"  [回到订票]

处理策略:

  1. 保存当前任务状态
  2. 切换到新话题
  3. 对话结束后恢复之前任务

实现方案

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不会"忘了用户在干什么”,主动追问让对话高效推进,话题管理让多任务切换无缝衔接。对话管理的终极目标是:让用户感觉在和一个人对话,而非一个系统交互。