对话状态跟踪


文档摘要

对话状态跟踪 本节摘要:「我要一家北边便宜的餐厅……改成中等价位吧……再加意大利菜。」三轮、三次状态更新,对话状态跟踪(DST)让槽位-值字典保持同步,预订才能成。在任务型对话系统里,用户的目标被编码为一组槽位-值对 ,每一轮用户发言都可能新增、修改或清除一个槽位,系统必须读完整个对话并正确输出当前状态。错一个槽位,系统就订错餐厅、排错航班、扣错卡——DST 是「用户说了什么」与「后端执行什么」之间的铰链。2026 年尽管有了 LLM,它依然要紧:银行、医疗、航司等合规敏感领域需要确定性的槽位值而非自由生成;工具型智能体调 API 前仍需解析槽位;多轮纠正(「其实改成周四」)比看上去难。现代流水线是经典 DST 概念 + LLM 抽取器 + 结构化输出护栏。

对话状态跟踪

本节摘要:「我要一家北边便宜的餐厅……改成中等价位吧……再加意大利菜。」三轮、三次状态更新,对话状态跟踪(DST)让槽位-值字典保持同步,预订才能成。在任务型对话系统里,用户的目标被编码为一组槽位-值对 {cuisine: italian, area: north, price: moderate},每一轮用户发言都可能新增、修改或清除一个槽位,系统必须读完整个对话并正确输出当前状态。错一个槽位,系统就订错餐厅、排错航班、扣错卡——DST 是「用户说了什么」与「后端执行什么」之间的铰链。2026 年尽管有了 LLM,它依然要紧:银行、医疗、航司等合规敏感领域需要确定性的槽位值而非自由生成;工具型智能体调 API 前仍需解析槽位;多轮纠正(「其实改成周四」)比看上去难。现代流水线是经典 DST 概念 + LLM 抽取器 + 结构化输出护栏。本节从规则式槽位抽取、状态更新循环、LLM + Pydantic 的 DST,讲到联合目标准确率(JGA)评估与纠正处理,点明跨轮共指、覆盖 vs 追加、隐式确认等经典失败模式,以及全历史重生成能自然处理纠正却带来 O(n²) token 成本的取舍。

对应原课程:Phase 5 · Lesson 29 · dialogue-state-tracking(原英文 phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md)。前置依赖:第 17 节(聊天机器人)、第 20 节(结构化输出)。

学习目标

阅读完本节,你应当能够:

  1. 说清槽位-值状态如何编码用户目标,以及**联合目标准确率(JGA)**为何是全有或全无的指标。
  2. 区分 DST 的两种表述:分类(闭环词表)与生成(开放词表)
  3. 从零实现规则式槽位抽取、状态更新循环与 LLM + Pydantic 的 DST。
  4. 识别跨轮共指、覆盖 vs 追加、隐式确认、纠正等失败模式,并设计确认流程。

一、问题与直觉

在任务型对话系统里,用户的目标被编码为一组槽位-值对:{cuisine: italian, area: north, price: moderate}。每一轮用户发言都可能新增、修改或清除一个槽位。系统必须读完整个对话并正确输出当前状态。

错一个槽位,系统就订错餐厅、排错航班、扣错卡。DST 是「用户说了什么」与「后端执行什么」之间的铰链。

2026 年尽管有了 LLM,它依然要紧:

  • 合规敏感领域(银行、医疗、航司预订)需要确定性槽位值,而非自由生成。
  • 工具型智能体调 API 前仍需解析槽位。
  • 多轮纠正比看上去难:「其实不,改成周四。」

现代流水线:经典 DST 概念 + LLM 抽取器 + 结构化输出护栏。

任务结构。 一个 schema 定义领域(餐厅、酒店、出租)及其槽位(cuisine、area、price、people)。每个槽位可为空、填入闭集值(price: {cheap, moderate, expensive})或自由值(name: "The Copper Kettle")。

两种 DST 表述:

  • 分类:对每个 (槽位, 候选值) 对预测是/否。适合闭环词表槽位。2020 前的标准。
  • 生成:给定对话,以自由文本生成槽位值。适合开放词表槽位。现代默认。

💡 指标:联合目标准确率(JGA)——每一轮所有槽位都对的比例。全有或全无。MultiWOZ 2.4 排行榜 2026 顶部约 83%。

架构演进:

  1. 规则式(槽位正则 + 关键词):窄领域的强基线,可调试。
  2. TripPy / BERT-DST:BERT 编码 + 复制式生成。LLM 前的标准。
  3. LDST(LLaMA + LoRA):指令微调 LLM 配领域-槽位提示,在 MultiWOZ 2.4 上达 ChatGPT 级质量。
  4. 无本体(2024~26):跳过 schema,直接生成槽位名与值,处理开放领域。
  5. 提示 + 结构化输出(2024~26):LLM 配 Pydantic schema + 受约束解码,5 行代码,生产就绪。

经典失败模式

  • 跨轮共指:「就用第一个吧」需解析是哪个选项。
  • 覆盖 vs 追加:用户说「加意大利菜」,你是替换 cuisine 还是追加?
  • 隐式确认:「好的酷」——这算接受了提议的预订吗?
  • 纠正:「其实改成晚上 7 点」必须更新时间而不清其他槽位。
  • 对系统上文的共指:「对,就那个。」哪个「那个」?

二、从零实现

第 1 步:规则式槽位抽取器

code/main.py。正则 + 同义词词典在窄领域覆盖 70% 的规范话术:

CUISINE_SYNONYMS = { "italian": ["italian", "pasta", "pizza", "italy"], "chinese": ["chinese", "chow mein", "noodles"], } def extract_cuisine(utterance): for canonical, synonyms in CUISINE_SYNONYMS.items(): if any(syn in utterance.lower() for syn in synonyms): return canonical return None

在规范词表之外脆弱。适合确定性槽位确认。

第 2 步:状态更新循环

def update_state(state, utterance): new_state = dict(state) for slot, extractor in SLOT_EXTRACTORS.items(): value = extractor(utterance) if value is not None: new_state[slot] = value for slot in NEGATION_CLEARS: if is_negated(utterance, slot): new_state[slot] = None return new_state

三条不变式:

  • 永不清除用户没碰的槽位。
  • 显式否定(「cuisine 就算了」)必须清。
  • 用户纠正(「其实……」)必须覆盖,而非追加。

第 3 步:带结构化输出的 LLM 驱动 DST

from pydantic import BaseModel from typing import Literal, Optional import instructor class RestaurantState(BaseModel): cuisine: Optional[Literal["italian", "chinese", "indian", "thai", "any"]] = None area: Optional[Literal["north", "south", "east", "west", "center"]] = None price: Optional[Literal["cheap", "moderate", "expensive"]] = None people: Optional[int] = None day: Optional[str] = None def llm_dst(history, llm): prompt = f"""You track the slot values of a restaurant booking across turns. Dialogue so far: {render(history)} Update the state based on the latest user turn. Output only the JSON state.""" return llm(prompt, response_model=RestaurantState)

Instructor + Pydantic 保证返回合法状态对象。没有正则、没有 schema 不匹配、没有幻觉槽位。

第 4 步:JGA 评估

def joint_goal_accuracy(predicted_states, gold_states): correct = sum(1 for p, g in zip(predicted_states, gold_states) if p == g) return correct / len(predicted_states)

校准:系统在多少比例的轮次上把所有槽位弄对?MultiWOZ 2.4 上的 2026 顶级系统:80~83%。你领域内的系统应在你的窄词表上超过它,否则 LLM 基线就胜过你。

第 5 步:处理纠正

CORRECTION_CUES = {"actually", "no wait", "on second thought", "change that to"} def is_correction(utterance): return any(cue in utterance.lower() for cue in CORRECTION_CUES)

检测到纠正时,覆盖最近更新的槽位而非追加。没有 LLM 帮助很难做对。现代模式:总让 LLM 从历史重新生成整个状态,而非增量更新——这能自然处理纠正。

三、框架对比

陷阱

  • 全历史重生成的成本:让 LLM 每轮重生成状态,总 token 是 O(n²)。要封顶历史或总结老轮次。
  • schema 漂移:事后加新槽位会破坏旧训练数据。要版本化 schema。
  • 大小写敏感:「Italian」vs「italian」vs「ITALIAN」——到处归一化。
  • 隐式继承:若用户之前指定了「4 个人」,之后一个换时间的请求不应清掉 people。总要传完整历史。
  • 自由形式 vs 闭集:名字、时间、地址需自由槽位;cuisine、area 是闭集。schema 里两者混用。

2026 的栈:

情形 方法
窄领域(一两种意图) 规则 + 正则
宽领域,有标注数据 LDST(LLaMA + LoRA 在 MultiWOZ 式数据上)
宽领域,无标注,要生产就绪 LLM + Instructor + Pydantic schema
语音 / 语音 ASR + 归一化器 + LLM-DST
多领域预订流程 schema 引导的 LLM,每领域一个 Pydantic 模型
合规敏感 规则主路径,LLM 兜底配确认流程

四、可复用产物

保存为 outputs/skill-dst-designer.md:

--- name: dst-designer description: Design a dialogue state tracker — schema, extractor, update policy, evaluation. version: 1.0.0 phase: 5 lesson: 29 tags: [nlp, dialogue, task-oriented] --- Given a use case (domain, languages, vocab openness, compliance needs), output: 1. Schema. Domain list, slots per domain, open vs closed vocabulary per slot. 2. Extractor. Rule-based / seq2seq / LLM-with-Pydantic. Reason. 3. Update policy. Regenerate-whole-state / incremental; correction handling; negation handling. 4. Evaluation. Joint Goal Accuracy on a held-out dialogue set, slot-level precision/recall, confusion on the hardest slot. 5. Confirmation flow. When to explicitly ask the user to confirm (destructive actions, low-confidence extractions). Refuse LLM-only DST for compliance-sensitive slots without a rule-based secondary check. Refuse any DST that cannot roll back a slot on user correction. Flag schemas without version tags.

五、练习

  1. 基础:在 code/main.py 里为 3 个槽位(cuisine、area、price)搭规则式状态跟踪器,在 10 段手工对话上测,报 JGA。
  2. 进阶:同一数据用 Instructor + Pydantic + 小 LLM,对比 JGA,检查最难的几轮。
  3. 挑战:两者都实现并路由:规则主路径,规则只出 <2 个有置信槽位时走 LLM 兜底,测合并后的 JGA 与每轮推理成本。

本节要点回顾

  1. DST 用槽位-值字典编码用户目标:每轮可能增改清槽位,错一个就执行错。
  2. DST 是「用户说的」与「后端执行的」之间的铰链:合规领域要确定性槽位而非自由生成。
  3. 两种表述:分类(闭集词表)、生成(开放词表,现代默认)。
  4. JGA 是全有或全无:每轮所有槽位都对才算,MultiWOZ 2.4 顶部约 83%。
  5. 架构从规则到 LLM + Pydantic:TripPy 复制式是 LLM 前标准,LDST 达 ChatGPT 级。
  6. 三条不变式:不清未碰槽位、显式否定必清、纠正必覆盖而非追加。
  7. 现代模式是全历史重生成:LLM 从历史重生整个状态,自然处理纠正,代价是 O(n²) token。
  8. 经典失败模式:跨轮共指、覆盖 vs 追加、隐式确认、对系统上文的共指。
  9. schema 要版本化:事后加槽位破坏旧数据,大小写要归一化,自由 vs 闭集混用。
  10. 合规敏感走规则主路径 + LLM 兜底:破坏性动作与低置信抽取要显式确认流程。

至此,我们走完了「自然语言处理」这一章的全部 29 节。回望来路:从第 01 节的 token 化与词形还原、第 03 节的 Word2Vec,到第 10 节的注意力与第 13 节的问答;从第 14 节的混合检索、第 22 节的嵌入深潜、第 23 节的分块策略,到第 27、28 节的评估框架与长上下文;最后落到本节的对话状态跟踪——把语言从「理解」推到「行动」。本章的一条主线是:文本如何变成向量,就理解了 LLM 之前的一切 NLP;另一条主线是:每一项能力都有「从零实现 → 框架对比 → 可复用产物」的工程闭环。带上这套底座,你已为后续「LLM 工程化」「RAG 与智能体」等章节做好了准备——下一章见。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U