A user who asks, “Is there a cancellation fee?” needs a policy lookup; a user who says, “Check my booking” needs access to their personal reservation; and a user who says, “Cancel it if it’s free” needs the facts checked first, the conditions evaluated, and only then the operation started. All three requests concern hotel bookings and cancellations, but each requires a different path.
Intent routing selects the appropriate service or workflow based on the recognition result. It must take the user’s goal, current state, parameters, and business constraints into account together. Using a hotel assistant as an example, this article introduces four handling paths: knowledge retrieval, read-only tools, write operations, and clarification. See the complete project for the code.
1. The Roles of Intent Recognition and Routing
A classifier returning cancel_booking only indicates that the user intends to cancel something. The routing layer still needs to determine which booking to cancel, who owns it, whether cancellation is allowed, what fee applies, and whether the user has confirmed the current proposal.
If all these questions were handled through labels, we would keep adding categories such as “cancel but missing booking ID,” “cancel with a fee,” and “cancel already confirmed.” The number of labels would balloon as workflow states accumulated, and annotation would become difficult to maintain consistently.
A clearer structure separates concerns: intent describes the goal, slots describe the parameters, state describes the current progress, and business rules determine the available actions. The routing layer uses all of this information to decide the next step; it cannot simply look up a handler by label.
2. Defining Business Capabilities and Intent Mappings
The capability catalog specifies which services the system can actually provide. A single intent may map to a multi-step workflow, and multiple informational intents may share the same retrieval capability.
| User goal | Label | Handling capability | Basis for response |
|---|---|---|---|
| Breakfast, check-in, or cancellation policies | policy_query | Knowledge retrieval | Document content and source |
| Search room options and prices | room_search | Read-only room search tool | Currently available information |
| Look up a personal booking | order_query | Permission-controlled booking tool | Facts about the specific booking |
| Book, modify, or cancel | Corresponding business label | Write workflow | Parameters, validation, confirmation, and execution result |
| Missing parameters or an unclear target object | The goal may already be known | Clarification strategy | Current state and candidate options |
| Not supported by current capabilities | Out-of-scope state | Explain the scope or hand off | Published capability catalog |
Whether chitchat needs its own route depends on the product. A general-purpose assistant can send chitchat to a generic conversational capability; the hotel assistant in this series instead exposes descriptions of its capabilities. Don’t disguise chitchat as a hotel-related intent simply because every input needs a response.
3. Knowledge Retrieval and Booking Lookup
Knowledge bases are a good fit for relatively stable rules and guidance, such as breakfast times, check-in policies, and document requirements. Personal booking status comes from operational systems, where it is tied to ownership, timeliness, and access permissions.
Sending “Has my booking been canceled?” to ordinary document retrieval may surface “How to cancel a booking” without answering the actual status of that booking. A semantically relevant result does not necessarily contain the facts needed to answer the question.
It can therefore help to ask two questions first: Does the answer come primarily from public guidance, or from the current user’s business object? If it comes from the latter, locate the object and call a permission-controlled tool; if necessary, retrieve the relevant policy to explain the tool result.
For example, a booking lookup might return “Cancellation fee: 50,” while policy retrieval explains why the fee applies. Together, they form the answer, but the knowledge base’s broad statement that “some room types can be canceled free of charge” must not override the specific fee on the booking.
4. Rewriting Queries with Context
If a user asks, “Can that be refunded?”, retrieving those words directly usually does not provide enough information. First, use the conversation state to determine whether “that” refers to a particular booking or product; then generate an explicit retrieval query.
One common sequence is:
But this is not a fixed pipeline that every system must follow. Some questions need a lightweight retrieval pass to determine whether the required capability exists, while some classifiers themselves use retrieved examples of labels. The key is to distinguish retrieval’s purpose: Is it helping select a label, or gathering evidence for the final answer? These uses should have independent inputs and evaluations.
When rewriting a query, preserve negation and conditions. “Don’t cancel if there’s a fee” must not be reduced to “Cancel the booking.” This is especially important for write operations: never mistake a terse query generated during retrieval optimization for the user’s complete authorization.
5. Validating and Confirming Write Operations
When canceling a booking, the system must recognize the cancellation intent, identify the booking, retrieve its status and fee, and then present a proposal and wait for confirmation.
A confirmation record should at least bind the operation type, key parameters, object version, and validity conditions. For example, if the user confirms “Cancel DEMO-A; current fee: 0,” the old confirmation should not remain valid after the fee or booking object changes.
The mock flow in the attached project saves a proposal pending confirmation; after the user replies “Confirm,” it reads the booking version and fee. If the conditions have changed, it returns a conflict so the application can display a new proposal. This creates a clear mapping among the recognition result, the user’s confirmation, and the actual submission.
“Confirm the parameters” and “confirm the action” also need to be distinguished. A response such as “Yes, that’s the booking” may confirm only the object, not the cancellation fee or the operation itself. The product should phrase its confirmation question fully enough that the user knows what their reply will trigger.
6. Workflows and Agents
For known, stable operations that require strict constraints, a fixed Workflow is a good fit. The checks required to cancel a booking and the conditions that must hold should be expressed in inspectable code.
An Agent is better suited to goals with uncertain paths, such as supplementing queries across multiple information sources, comparing candidate options, and organizing materials returned by different tools. It can propose the next step, but tool permissions, parameter validation, and write-operation constraints must still be enforced by the runtime.
The two can also work together: the Agent handles complex requirements and gathers evidence, while a fixed Workflow performs the actual booking or cancellation. This preserves flexibility while keeping critical business constraints concentrated in a small set of interfaces.
When choosing an approach, consider where the uncertainty lies. If only the way users express themselves varies, a stronger recognizer may be enough. If the processing path itself needs to be explored, dynamic planning is necessary. Don’t turn every booking modification into open-ended planning merely because the entry point uses an LLM.
7. Dependencies and Execution Order in Multi-Task Requests
The request “Check my booking, cancel it if it’s free, and also check rooms for tomorrow” contains multiple goals. The routing layer should preserve a separate task ID, parameters, and dependencies for each goal rather than compressing the entire input into one label.
The diagram in Task Dependencies can execute the booking lookup first and turn its result into a trusted fact. The cancellation node checks that fact, while the room-search node proceeds according to its own dependencies.
If the cancellation condition does not hold, skip only the cancellation node. Room search does not depend on the cancellation succeeding and should not stop with it. Conversely, if a task must depend on the previous step’s success, it cannot proceed merely because that step has returned.
The attached batch3/planner.py advances this graph using simulated booking results: read-only nodes return results, while write nodes return proposals pending confirmation. The program chooses downstream nodes based on the query results; write operations requiring confirmation pause and wait.
8. Access Control and Prompt Injection Defense
Knowledge documents may contain text such as “ignore the previous instructions,” and users may claim administrator privileges in their requests. These can be treated as data to analyze, but they must never alter server-side access controls or tool capabilities.
Prompt injection is dangerous because a model processing natural language may confuse data with instructions. The routing layer should keep identity, permissions, the capability allowlist, and preconditions for write operations in trusted code instead of relying on the model to comply on its own. OWASP guidance on prompt injection
For example, when looking up a booking, permission checks should use the identity authenticated by the server. A user’s chat message saying, “This booking belongs to me,” is not grounds for access. Nor does the model returning a valid booking ID prove that the user is authorized to access it.
9. Error Handling and Graceful Degradation
When retrieval finds no evidence, return “There isn’t enough information available” and prompt for more details; when the booking tool times out, enter a limited retry or graceful degradation path; and when the goal is unclear, ask a specific question. These three outcomes must not all be collapsed into “I didn’t understand.”
Graceful degradation should not arbitrarily change the goal. If the cancellation tool is unavailable, save the pending state and explain that the outcome remains undecided; do not substitute a smooth-sounding cancellation policy explanation and declare the task complete.
Typical routing outcomes in the project include knowledge, read_tool, clarify, confirm, condition_false, and mock_write. Only the last one means the simulated business write has been completed; each of the others points to a specific next step.
Run the following from the project root:
python batch3/assistant.py
python batch3/planner.py
For a more complete treatment of retrieval, Agent execution, and streaming service design, see Production-Grade RAG and Agent Systems. This article focuses on why a request is routed to a particular capability; the next installment, Going Live with the Service, continues with how the same request can be completed reliably.