The previous articles covered labels, corpora, models, state, task dependencies, and services. Now we're putting all these components together to build a hotel assistant capable of continuous conversation: it can answer policy questions, query mock orders, complete booking parameters, handle modifications and conditional cancellations, and write mock results after confirmation.
This article explains how each module divides its responsibilities, along with methods for installation, running, switching recognizers, and testing. Download the complete project v3.0.
1. Running Dialogue Examples
After extracting, enter the intent-recognition-lab directory. The default demo uses only Python's standard library:
python3 batch3/assistant.py
It runs scenarios including booking, policy, conditional cancellation, order selection, modification, and out-of-scope requests, outputting each turn's input, routing, state, and results. The main variations in the booking scenario are as follows:
| User Input | Current State | Returned Result |
|---|---|---|
| I want to book a room | Booking target established, missing city and dates | clarify: complete parameters |
| Beijing 2026-10-01 2026-10-03 | Parameters completed, generating pending confirmation | confirm: show plan |
| Confirm | Mock order written, session task ends | mock_write: return order number |
"Completing parameters" and "finishing booking" are two separate steps. When the user provides dates, the program only forms a plan; only after explicit confirmation does it execute the mock operation.
All order data resides in local SQLite. DEMO-A has a cancellation fee of 0, DEMO-B has 50, and DEMO-X belongs to another demo user. They're used to observe conditional and permission branches—no real hotel or payment system is connected.
2. Module Responsibilities and Request Flow
Taking "cancel order DEMO-A" as an example: the HTTP or CLI entry first provides session ID, request ID, state version, and text. The service reads the current state, checks if it's a retry of a completed request, then passes the new request to dialogue processing.
The recognizer gives the cancellation target, and the slot extractor finds the order number. The state module binds the target with the object, and the routing layer queries the order's ownership, status, fee, and version, forming a pending confirmation plan. At this point it returns confirm, and the order in the database remains in booked state.
The next turn's "confirm" is interpreted by the pending confirmation state, rather than being re-treated as a classification problem without context. Before execution, it again verifies the order version and conditions; on success, it updates the mock order, session state, and deduplication results within the same local transaction.
You can map component responsibilities to files:
| File | Responsibility |
|---|---|
| core.py | First batch output contracts and single-task state updates |
| llm.py | Optional structured LLM recognition interface |
| batch2/baselines.py | Rule, TF-IDF, and sentence vector candidate scores |
| batch2/policy.py | Score and score difference reception, rejection, and clarification decisions |
| batch2/task_graph.py | Multi-task structure and dependency validation |
| batch3/assistant.py | Context bridging, business routing, mock tools, and persistence |
| batch3/planner.py | Advances task graph based on mock query facts |
| batch3/service.py | Local HTTP request contracts |
| batch3/evaluate.py | Reads prediction results and computes classification and rejection metrics |
The first batch's recognition output contracts and second batch task graph are preserved separately. Single-task state and multi-task plans carry different responsibilities, connected through clear entry points—easier to understand than stuffing all fields into an ever-expanding JSON.
3. Short Replies and Context Handling
When the user says "cancel order" but doesn't provide an order number, the demo system lists two options: "1 DEMO-A; 2 DEMO-B." The next turn's "1" is first interpreted within the current valid options, getting DEMO-A, then continuing with the cancellation flow.
It doesn't enter a global rule like "number 1 means cancel order." Options belong to the current session and current task; "1" in another session doesn't have the same meaning.
Date and room type supplementation works similarly. During an ongoing booking task, "Beijing 2026-10-01 2026-10-03" can be added to current parameters. When the user explicitly raises a new booking target, the task should switch—the new requirement shouldn't be mistaken for parameter supplementation of the old task.
Therefore, the program first processes confirmation replies, option selections, and parameter supplements, then calls the general-purpose intent recognizer. Using existing context information reduces unnecessary inference.
4. Cross-Turn Preservation of Conditional Constraints
Running "if free, cancel order DEMO-B"—the system finds the demo fee is 50, returns condition_false, doesn't enter confirmation, and doesn't cancel the order.
If the user initially only said "if free, cancel order," the condition needs to follow the current task. After selecting DEMO-B in the next turn, it must still check the "free" constraint—it can't lose the condition just because this turn only has a number.
Conditions that were true when the plan was proposed may change before confirmation. The attachment saves the order version used in the plan, and re-reads the fee and status before submission. Version mismatch or condition changes return a conflict, letting the upper layer re-present the plan.
For more complex multi-task requests, run:
python3 batch3/planner.py
This command receives the task graph defined in article six of the series. In real execution results, the order query and room search nodes complete, and the cancellation node stops at requires_business_validation. It demonstrates how dependencies advance while preserving confirmation entry points for write operations. Converting free-form expressions to arbitrary task graphs remains work for the recognition and planning modules; this command starts from an explicit structured plan.
5. Switching Intent Recognizers
Default rules are suitable for direct reading and running, covering expressions used in the tutorial. When sentence vector candidates are needed, install model experiment dependencies and start:
python -m pip install -r batch2/requirements.txt
python batch3/service.py --recognizer vector --db /tmp/intent-vector.sqlite3
The vector branch uses the fixed model and validation parameters from before—it may send under-supported inputs to clarification. Changing the candidate method doesn't automatically relax order verification, parameter checks, or confirmation flows.
When choosing --recognizer llm, the adapter reads OPENAI_API_KEY and OPENAI_MODEL, calling the existing structured interface. The template is in batch3/.env.example; the service doesn't auto-load it—variables need to be injected by the runtime environment. After the LLM returns a known single target, it goes to the same state and business constraints.
The current demo separates candidate recognition from simplified slot extraction, using explicit ISO format for dates. When extending natural dates, complex references, or full LLM slot results, add normalization and field validation in the interface adapter layer—don't bypass the state module to execute tools directly. Rules, sentence vectors, and optional LLM shouldn't be assumed to have the same recognition coverage.
This validation includes local rule chains and sentence vector access; the LLM entry is kept runnable without consuming online APIs. The bank's eight-class student model is used to demonstrate the training process—its labels differ from the hotel domain and weren't directly connected to hotel routing.
6. Completing Dialogues via HTTP Interface
python3 batch3/service.py --db /tmp/intent-demo.sqlite3 --port 8765
First turn, make a cancellation request:
curl http://127.0.0.1:8765/turn \
-H 'Content-Type: application/json' \
-d '{"session_id":"cancel-demo","request_id":"r1","expected_version":0,"text":"取消订单 DEMO-A"}'
After receiving version 1's pending confirmation plan, submit:
curl http://127.0.0.1:8765/turn \
-H 'Content-Type: application/json' \
-d '{"session_id":"cancel-demo","request_id":"r2","expected_version":1,"text":"确认"}'
If the response is lost, simply retransmit the second request to read the saved result. Don't generate a new request ID just for retrying. The same ID with different text returns an idempotent conflict; a new request carrying an old state version returns a state conflict.
When running the example again, you can use a different session ID; mock orders are persistent objects in the database, and cancelled orders won't automatically restore for new sessions. For a complete reset, use a new database file path.
7. Running Tests and Model Experiments
From the project root:
python3 -m unittest discover -s tests -v
python3 batch2/tests.py
python3 batch3/tests.py
python3 batch3/http_smoke.py
The first three check contracts, state, task graph, and complete workflows; the last one starts a real round-trip HTTP service, sends requests and verifies responses, then terminates the service.
Key test cases include: booking after slot filling, inquiry without triggering operations, condition preservation across turns, order changes before confirmation, fee changes, duplicate requests without duplicate writes, session isolation, service timeout rollback, and task continuation after process restart.
Model and metric experiments run separately:
python batch3/evaluate.py
python batch3/train_small.py
python batch3/infer_small.py "My card was charged twice"
Functional tests check program behavior; classification experiments check model judgments on data. Both types of results are saved in the project—when reading, you can trace a failed record to its corresponding component.
8. Integrating Your Own Business
First replace the capability directory and label definitions, then adapt your slots and business objects. When replacing mock order tools with real services, clarify authentication identity, tool idempotency keys, result queries after timeout, fee changes, and operation confirmation.
The knowledge branch currently uses a small number of sourced policy entries—the purpose is to see routing and evidence return clearly. After integrating document retrieval, you can continue adding chunking, hybrid retrieval, reranking, and answer organization, but reading current user orders should remain in permission-constrained business tools.
Concurrent services should move time-consuming recognition outside short transactions, then use version checking for commits; remote operations recover through their own idempotency and reconciliation mechanisms. Models, prompts, labels, thresholds, and routing versions should be saved together to reproduce an online judgment.
When integrating new business, preserve user goals, context, and conditional constraints, and choose the next step based on this information. Whether an operation is complete should be determined by the business system's saved result.
9. Series Reading Navigation
| Problem to Solve | Corresponding Article |
|---|---|
| What does intent recognition do in a system? | Overview |
| How are labels divided? | Label Design |
| How to annotate complex dialogues? | Corpus and Download |
| How to choose between rules, vectors, and small models? | Basic Methods |
| How does LLM return stable interfaces? | Structured Recognition |
| How to understand short replies and corrections? | Multi-Turn State |
| How to represent multi-intents and conditions? | Task Dependencies |
| When to reject and ask clarifying questions? | Unknown Intents and Clarification |
| Is the improvement effective? | Evaluation Methods |
| How to train a lighter recognizer? | Classification Head and Distillation |
| Which processing path after recognition? | RAG and Agent Routing |
| How to run services reliably? | Services and Error Closure |
After reading and running the project, looking back at the independent concepts in each article will make it easier to understand why they needed to be separated and how they jointly affect the final outcome of a dialogue.