Original source
---
id: dialogue-systems
title: Chatbots and Dialogue State Tracking
code: CS
subtitle: Route turns, run the agent loop, track slots across turns and budget the context window. Python from the lessons.
hue: 170
sources: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking, phases/11-llm-engineering/05-context-engineering
source_hash: e4e39898a4e411b5
---
## Hybrid routing
type: architecture
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md
```arch
node | in | 1,1 | User input | | client
node | rule | 2,1 | Destructive check | danger words | process
node | flow | 3,1 | Structured flow | confirm first | process
node | faq | 2,2 | FAQ retrieval | threshold 0.6 | store
node | agent | 3,2 | LLM agent loop | RAG and tool calls | model
node | tools | 4,2 | Tools | callable functions | process
node | out | 4,1 | Reply | | client
edge | in | rule |
edge | rule | flow | destructive
edge | rule | faq | else
edge | faq | out | match
edge | faq | agent | None
edge | agent | tools | tool_call
edge | flow | out |
```
## The agent loop
type: flow
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md
1. **Start**: `history` holds one user message. The loop runs `max_steps` times at most.
2. **LLM**: `llm(history, tools=tools)` returns content or a `tool_call`.
3. **Check**: unknown tool name or non-dict arguments add an error message. The loop continues.
4. **Tool**: `fn(**args)` runs. The call and its result go into `history`.
5. **Stop**: a reply without a `tool_call` is the final answer.
6. **Budget**: after `max_steps`, return "I could not complete the task in the step budget."
## Tasks in code
type: task-table
span: 2
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md, phases/11-llm-engineering/05-context-engineering/code/main.py
| Task | Code |
|---|---|
| FAQ answer or `None` | `faq_respond(user_input, threshold=0.5)` |
| Detect a destructive request | `is_destructive_action(text)` |
| Agent with a step budget | `agent_loop(user_message, tools, llm, max_steps=5)` |
| Score a tracker | `joint_goal_accuracy(predicted_states, gold_states)` |
| Budget the window | `ContextBudget(max_tokens=128000, generation_reserve=4000)` |
| Add a component with a cap | `budget.allocate("tools", tool_text, max_tokens=2000)` |
| Prune tools by intent | `select_tools(query, token_budget=2000)` |
| Best docs at start and end | `reorder_lost_in_middle(items, scores)` |
## Rule-based state update
type: annotated-code
lang: python
from: phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/code/main.py
```python
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] = value # a correction overwrites
continue
if is_negation(utterance, slot):
new_state[slot] = None # "never mind the cuisine"
return new_state
```
## LLM tracker with a schema
type: annotated-code
lang: python
from: phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md
```python
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 # free-form slot
def llm_dst(history, llm):
prompt = f"""...{render(history)}...Output only the JSON state."""
return llm(prompt, response_model=RestaurantState) # whole state each turn
```
## Assemble context per query
type: flow
from: phases/11-llm-engineering/05-context-engineering/docs/en.md, phases/11-llm-engineering/05-context-engineering/code/main.py
1. **System prompt**: goes first. Cap `max_tokens=1000`.
2. **Tools**: `classify_intent` then `select_tools`. Cap 2000.
3. **Retrieval**: `score_relevance` drops docs below the threshold. Cap 3000.
4. **Reorder**: `reorder_lost_in_middle` puts the best docs at the start and the end.
5. **History**: summaries plus recent turns. Cap 5000.
6. **Query**: goes last. Cap 500. The rest is the generation reserve.
## Paradigms and DST terms
type: concepts
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md
- **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
type: defaults
from: phases/11-llm-engineering/05-context-engineering/code/main.py, phases/11-llm-engineering/05-context-engineering/docs/en.md, phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md
| 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
type: troubleshooting
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md, phases/11-llm-engineering/05-context-engineering/docs/en.md
| 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
type: glossary
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md, phases/11-llm-engineering/05-context-engineering/docs/en.md
- **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
type: qa
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md, phases/11-llm-engineering/05-context-engineering/docs/en.md
- **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
type: tips
from: phases/05-nlp-foundations-to-advanced/17-chatbots-rule-to-neural/docs/en.md, phases/05-nlp-foundations-to-advanced/29-dialogue-state-tracking/docs/en.md, phases/11-llm-engineering/05-context-engineering/docs/en.md
- Below the FAQ threshold, return `None` and 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.