Chatbots and Dialogue State Tracking
Route turns, run the agent loop, track slots across turns and budget the context window. Python from the lessons.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Defaults and numbers
- Troubleshooting
- Questions and tips
Hybrid routing#
The agent loop#
Tasks in code#
| Task | Code |
|---|---|
FAQ answer or None | faq_respond(user_input, |
| Detect a destructive request | is_destructive_action(text) |
| Agent with a step budget | agent_loop(user_message, |
| Score a tracker | joint_goal_accuracy(predicted_states, |
| Budget the window | ContextBudget(max_tokens= |
| Add a component with a cap | budget.allocate("tools", |
| Prune tools by intent | select_tools(query, |
| Best docs at start and end | reorder_lost_in_middle(items, |
Rule-based state update#
SLOT_EXTRACTORS = {"cuisine": extract_cuisine, "area": extract_area, "price": extract_price, "people": extract_people}def update_state(state, utterance): new_state = dict(state)never reset untouched slots for slot, extractor in SLOT_EXTRACTORS.items(): value = extractor(utterance) if value is not None: new_state[slot] = valuea correction overwrites continue if is_negation(utterance, slot): new_state[slot] = None"never mind the cuisine" return new_stateLLM tracker with a schema#
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] = Nonefree-form slotdef llm_dst(history, llm): prompt = f"""...{render(history)}...Output only the JSON state.""" return llm(prompt, response_model=RestaurantState)whole state each turnAssemble context per query#
Paradigms and DST terms#
- Rule-based
- patterns, intents and slot filling. Fails outside its scope.
- Retrieval-based
- nearest stored response. No generation, so no hallucination.
- Neural seq2seq
- fluent, but generic replies and factual drift.
- LLM agent
- plan, call tool, observe result, decide next step.
- Slot
- a named parameter the backend needs, such as cuisine or date.
- Classification DST
- yes or no for each slot and candidate value. Closed vocabulary.
- Generation DST
- generate slot values as text. Open vocabulary. The modern default.
- JGA
- fraction of turns where every slot is correct. All or nothing.
Defaults and numbers#
| Setting | Default | Note |
|---|---|---|
max_tokens | 128000 | Window size of ContextBudget |
generation_reserve | 4000 | Reserve 2,000-4,000 for the answer |
max_history_tokens | 5000 | Above it, summarize the 2 oldest turns |
token_budget | 2000 | Tool definitions for one query |
| Relevance threshold | 0.05 | The lesson text uses 0.1 |
| Token estimate | 1.3 | Tokens for each whitespace word |
max_steps | 5 | Agent step budget |
FAQ threshold | 0.5 | Hybrid route uses 0.6 |
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Agent claims an action it did not do | Confident fabrication | Trust only a successful tool return |
| Input overrides the system prompt | Prompt injection | Untrusted input, PVE, confirm destructive |
| Same tool called again and again | Infinite loop | Step budget, dedupe tool calls |
| Earliest turns lost | Context window full | Summarize or retrieve old turns |
| Fact in the middle ignored | Lost in the middle | Key facts first or last |
| Correction clears other slots | Reset of untouched slots | Overwrite only the corrected slot |
| "Italian" and "italian" differ | Case | Normalize everywhere |
| Old training data breaks | New slots added later | Version the schema |
Abbreviations#
- DST
- Dialogue State Tracking. Keeps the slot-value dict across turns.
- JGA
- Joint Goal Accuracy. MultiWOZ 2.4 top systems reach 80-83%.
- MultiWOZ
- Multi-domain WOZ dataset. The standard DST benchmark.
- LDST
- LLaMA plus LoRA, instruction-tuned with domain-slot prompts.
- TripPy
- copy-based DST with BERT encoding. Pre-LLM standard.
- PVE
- Plan-Verify-Execute. Check each action against the plan first.
- RAG
- retrieve relevant docs, then ground the reply in them.
- Lost-in-the-middle
- 85-90% accuracy at the ends, 60-70% in the middle.
Questions#
- Why route instead of one model?
- No single architecture handles every request well.
- Why rules for payments?
- Destructive actions need deterministic, confirmed flows.
- Incremental update or regenerate?
- Regenerate the whole state from history. It handles corrections.
- What does regeneration cost?
- O(n^2) total tokens. Cap or summarize old turns.
- Is a bigger window enough?
- No. A curated 10K context often beats a dumped 100K.
Tips#
- Below the FAQ threshold, return
Noneand escalate. - Log every agent step. Measure off-task rate.
- Mix free-form slots (names, times) with closed slots (cuisine, area).
- Always pass the full history so earlier slots stay set.
- Tool pruning cuts tool tokens by 60-80%.
- Two or three good few-shot examples often beat long instructions.