Phase 05 - Lesson 29
Seguimiento del Estado del Diálogo
"Quiero un restaurante barato en el norte... mejor que sea moderado... y agrega italiano." Tres turnos, tres actualizaciones de estado. El DST mantiene sincronizado el diccionario slot-valor para que la reserva funcione.
Tipo: Build Lenguajes: Python Prerrequisitos: Fase 5 · 17 (Chatbots), Fase 5 · 20 (Salidas Estructuradas) Tiempo: ~75 minutos
El Problema
En un sistema de diálogo orientado a tareas, el objetivo del usuario se codifica como un conjunto de pares slot-valor: {cuisine: italian, area: north, price: moderate}. Cada turno del usuario puede agregar, cambiar o eliminar un slot. El sistema debe leer toda la conversación y producir el estado actual correctamente.
Equivócate en un solo slot y el sistema reserva el restaurante equivocado, agenda el vuelo equivocado o cobra a la tarjeta equivocada. El DST es la bisagra entre lo que dijo el usuario y lo que ejecuta el backend.
Por qué sigue importando en 2026 a pesar de los LLM:
- Los dominios sensibles al cumplimiento (banca, salud, reservas aéreas) requieren valores de slot deterministas, no generación de formato libre.
- Los agentes que usan herramientas aún necesitan resolver slots antes de llamar a las APIs.
- La corrección en múltiples turnos es más difícil de lo que parece: "no, mejor que sea el jueves."
El pipeline moderno: conceptos clásicos de DST + extractores basados en LLM + guardrails de salida estructurada.
El Concepto
Estructura de la tarea. Un schema define dominios (restaurante, hotel, taxi) y sus slots (cuisine, area, price, people). Cada slot puede estar vacío, lleno con un valor de un conjunto cerrado (price: {cheap, moderate, expensive}), o un valor de formato libre (name: "The Copper Kettle").
Dos formulaciones de DST.
- Clasificación. Para cada par (slot, candidate_value), predecir sí/no. Funciona para slots de vocabulario cerrado. Estándar antes de 2020.
- Generación. Dado el diálogo, generar valores de slot como texto libre. Funciona para slots de vocabulario abierto. El estándar moderno.
Métrica. Joint Goal Accuracy (JGA) — la fracción de turnos en los que cada slot es correcto. Todo o nada. El leaderboard de MultiWOZ 2.4 alcanza alrededor del 83% en 2026.
Arquitecturas.
- Basada en reglas (regex de slot + palabra clave). Baseline sólido para dominios estrechos. Depurable.
- TripPy / BERT-DST. Generación basada en copia con codificación BERT. Estándar pre-LLM.
- LDST (LLaMA + LoRA). LLM ajustado por instrucción con prompting de dominio-slot. Alcanza calidad nivel ChatGPT en MultiWOZ 2.4.
- Sin ontología (2024–26). Omite el schema; genera nombres de slot y valores directamente. Maneja dominios abiertos.
- Prompt + salida estructurada (2024–26). LLM con schema Pydantic + decodificación restringida. 5 líneas de código, listo para producción.
Los modos clásicos de fallo
- Correferencia entre turnos. "Quedémonos con la primera opción." Necesita resolver cuál opción.
- Sobrescribir vs anexar. El usuario dice "agrega italiano." ¿Reemplazas cuisine o anexas?
- Confirmaciones implícitas. "Ok, genial" — ¿eso aceptó la reserva ofrecida?
- Corrección. "Mejor que sea a las 19h." Debe actualizar la hora sin borrar los otros slots.
- Correferencia a la enunciación anterior del sistema. "Sí, ese." ¿Cuál "ese"?
Constrúyelo
Paso 1: extractor de slot basado en reglas
Consulta code/main.py. Regex + diccionarios de sinónimos cubren el 70% de las enunciaciones canónicas en dominios estrechos:
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
Frágil fuera del vocabulario canónico. Funciona para confirmaciones deterministas de slot.
Paso 2: bucle de actualización de estado
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
Tres invariantes:
- Nunca reinicies un slot que el usuario no tocó.
- La negación explícita ("olvida la cuisine") debe borrar.
- La corrección del usuario ("mejor...") debe sobrescribir, no anexar.
Paso 3: DST guiado por LLM con salida estructurada
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 garantiza un objeto de estado válido. Sin regex, sin incompatibilidades de schema, sin slots alucinados.
Paso 4: evaluación por 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)
Calibra: ¿en qué fracción de los turnos el sistema acierta TODOS los slots? Para MultiWOZ 2.4, los mejores sistemas de 2026: 80-83%. Tu sistema dentro del dominio debería superar eso en tu vocabulario estrecho, o el baseline de LLM te gana.
Paso 5: manejo de la corrección
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)
Al detectar una corrección, sobrescribe el último slot actualizado en lugar de anexar. Difícil de acertar sin la ayuda de un LLM. El patrón moderno: deja siempre que el LLM regenere todo el estado a partir del historial en lugar de actualizar incrementalmente — esto maneja las correcciones de forma natural.
Trampas
- Costo de la regeneración del historial completo. Dejar que el LLM regenere el estado en cada turno cuesta O(n²) tokens en total. Limita el historial o resume los turnos más antiguos.
- Deriva del schema. Agregar nuevos slots después rompe los datos de entrenamiento antiguos. Versiona tu schema.
- Sensibilidad a mayúsculas. "Italian" vs "italian" vs "ITALIAN" — normaliza en todas partes.
- Herencia implícita. Si el usuario ya especificó "para 4 personas", una nueva solicitud para una hora diferente no debe borrar people. Pasa siempre el historial completo.
- Formato libre vs conjunto cerrado. Los nombres, horas y direcciones necesitan slots de formato libre; cuisines y areas son cerrados. Mezcla ambos en el schema.
Úsalo
El stack de 2026:
| Situación | Enfoque |
|---|---|
| Dominio estrecho (una o dos intenciones) | Basado en reglas + regex |
| Dominio amplio, datos etiquetados disponibles | LDST (LLaMA + LoRA con datos estilo MultiWOZ) |
| Dominio amplio, sin etiquetas, listo para prod | LLM + Instructor + schema Pydantic |
| Hablado / voz | ASR + normalizador + LLM-DST |
| Flujo de reserva multidominio | LLM guiado por schema con modelos Pydantic por dominio |
| Sensible al cumplimiento | Basado en reglas como primario, LLM como fallback con flujo de confirmación |
Entrégalo
Guarda como 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.
Ejercicios
- Fácil. Construye el rastreador de estado basado en reglas en
code/main.pypara 3 slots (cuisine, area, price). Pruébalo en 10 diálogos hechos a mano. Mide la JGA. - Medio. El mismo dataset con Instructor + Pydantic + un LLM pequeño. Compara la JGA. Inspecciona los turnos más difíciles.
- Difícil. Implementa ambos y enruta: basado en reglas como primario, LLM como fallback cuando el basado en reglas emite <2 slots con confianza. Mide la JGA combinada y el costo de inferencia por turno.
Términos Clave
| Término | Lo que dice la gente | Lo que realmente significa |
|---|---|---|
| DST | Seguimiento del estado del diálogo | Mantener el diccionario slot-valor a lo largo de los turnos del diálogo. |
| Slot | Unidad de intención del usuario | Parámetro nombrado que el backend necesita (cuisine, date). |
| Dominio | El área de la tarea | Restaurante, hotel, taxi — conjuntos de slots. |
| JGA | Joint Goal Accuracy | Fracción de turnos en los que cada slot es correcto. Todo o nada. |
| MultiWOZ | El benchmark | Dataset Wizard-of-Oz multidominio; evaluación estándar de DST. |
| DST sin ontología | Sin schema | Generar nombres de slot y valores directamente, sin lista fija. |
| Corrección | "Mejor..." | Turno que sobrescribe un slot previamente lleno. |
Lecturas Adicionales
- Budzianowski et al. (2018). MultiWOZ — A Large-Scale Multi-Domain Wizard-of-Oz — el benchmark canónico.
- Feng et al. (2023). Towards LLM-driven Dialogue State Tracking (LDST) — ajuste por instrucción con LLaMA + LoRA para DST.
- Heck et al. (2020). TripPy — A Triple Copy Strategy for Value Independent Neural Dialog State Tracking — el caballo de batalla del DST basado en copia.
- King, Flanigan (2024). Unsupervised End-to-End Task-Oriented Dialogue with LLMs — TOD no supervisado basado en EM.
- Leaderboard de MultiWOZ — resultados canónicos de DST.