Skip to main content
Back to how-to guides Use this pattern when a supervisor must work out what the customer wants and hand the conversation to the specialist that owns that work.

Concept

Intent routing uses two ABL sections.
  • INTENTS: lists the categories and describes each one. The descriptions guide the classifier and the supervisor model. The section is optional. Without it, the compiler takes the categories from the intent.category comparisons in your WHEN rules.
  • HANDOFF: holds the routes. Each entry names a target agent, a WHEN condition, and the context the target receives.
A route reads the category through intent.category, for example WHEN: intent.category == "billing".

How the platform picks a route

The pipeline is optional. It runs only when the project enables it, the agent does not opt out, and a pipeline model is available. Without it, the model path decides. See How to use EXECUTION.pipeline for agent routing, classification, and short-circuiting.

Condition styles

Quote plain-language text. Unquoted text is read as variable names and produces undefined-variable warnings. A // outside quotes starts a comment in a WHEN. A WHEN that holds only a comment fails to compile with the message WHEN is required. The pipeline evaluates a quoted phrase as a string that is always true. A quoted route placed above an intent.category route captures every message the pipeline classifies. Put quoted routes after the intent.category routes.

Order is priority

The position of an entry in HANDOFF: sets its priority. The first entry has the highest priority. The model sees the position as [Priority N] in the route description. The PRIORITY: field is read and then ignored. Move the entry instead.

Decisions for every route

  • History. The target receives the full conversation history unless you set history. A project default can change that value. See Control conversation history.
  • Return. EXPECT_RETURN: false is the default. The target keeps the conversation. EXPECT_RETURN: true pauses the supervisor until the target returns. See Return to the supervisor with ON_RETURN.

Minimal working example

This example is a project-level set. Put the supervisor and the four target agents in the same project. The targets are short stand-ins for your real agents. agents/intent-routing-supervisor.agent.abl
agents/billing-agent.agent.abl
agents/technical-support-agent.agent.abl
agents/retention-agent.agent.abl
agents/general-service-agent.agent.abl
The platform fills each PASS field from the parent’s data when the parent holds a value, for example from caller data or a gathered field. Otherwise the supervisor model fills it from what the customer said. When neither has a value, the target must collect it. The summary text also supports {{field}} interpolation from the supervisor’s data.

How it works

  1. The supervisor receives the message and keeps the conversation until a route fires.
  2. The platform assigns an intent category. With the pipeline, the classifier assigns it. Without the pipeline, the model states it when it calls a route.
  3. The platform checks the WHEN of the selected route. An expression must be true. A refused route returns a message to the model, which then tries another route or asks the customer.
  4. The target receives the PASS values, the summary, and the history that the history setting allows.
  5. With EXPECT_RETURN: false, the target keeps the conversation. With EXPECT_RETURN: true, the supervisor pauses and resumes when the target returns.
  6. When no route fires, the supervisor stays active. The WHEN: true entry in the example is the fallback.
A chain of nested handoffs can reach a depth of 10 before the platform refuses the next one. The platform also refuses a handoff to an agent that is already in the active chain. A child that returns to the supervisor waiting for it is not a loop.

Common variations

Control conversation history with history

When the target does not need the full transcript, set history on the entry. Add it inside CONTEXT:.
Write last_n as a block. The inline form history: { mode: last_n, count: 6 } compiles without a warning and has no effect, so the target receives the full history. Use summary_only or none for a focused specialist. Use full when the target needs the whole record.

Write context as sibling keys

PASS, SUMMARY, and HISTORY can sit directly under the entry instead of inside CONTEXT:. Both forms compile to the same route.
Use CONTEXT: when you also need set or memory_grants.

Return to the supervisor with ON_RETURN

Use EXPECT_RETURN: true when a specialist does bounded work and the supervisor continues afterward. In this example, a verification agent returns before billing help starts.
map copies a child field into a parent field. Write the child field first, then the parent field: verified: verification_result copies the child’s verified into the parent’s verification_result. The child must gather or produce verified. With a map, only the mapped fields return. Without a map, the platform copies all gathered child data back. action: resume_intent runs routing again against the original request with the returned context. The replay runs one level deep. A second return inside the replay does not replay again, and the trace shows resume_intent with reasonCode: resume_intent_depth_limit. action: continue resumes the supervisor’s current flow. While a returning child is active, the platform can also send the customer back to the supervisor. With the pipeline on, the classifier checks each reply against the supervisor routes in authored order. When another agent’s route matches with enough confidence, the conversation returns to the supervisor. The trace shows a digression event with action: return_to_parent. The supervisor EXECUTION.pipeline block sets the threshold in parentReturn. See How to configure EXECUTION.pipeline models, modes, thresholds, and fallbacks. RETURN is accepted as an older name for EXPECT_RETURN. ON_RETURN without EXPECT_RETURN: true produces the warning HANDOFF_ON_RETURN_WITHOUT_RETURN.

Put specific routes before general routes

When two routes can match the same message, place the specific route first. The first entry has the highest priority.
session.identityTier is 2 after the channel verifies the identity. The route is false for an anonymous customer, so that customer reaches Billing_Agent.

Add a plain-language route

Use a quoted phrase for a request type that has no category. Place it after the intent.category routes.
Keep the phrase in lowercase words. A quoted phrase is read as an expression when it contains uppercase AND, OR, or NOT, a comparison symbol, IS SET, a word such as contains, or a word directly followed by (.

Handle dispatch failures with ON_FAILURE

ON_FAILURE runs when the handoff fails before the target accepts it, for example when validation fails. It does not handle errors that the target raises later. A route that the WHEN guard refuses does not run ON_FAILURE. The model receives the refusal instead.
A bare quoted string such as ON_FAILURE: "message" fails to compile with INVALID_HANDOFF_FAILURE_ACTION.

Verification

  1. Send one test message for each category. Open the trace. Find the handoff_condition_check events for the turn. Confirm that the expected route has result: true.
  2. Send a message that matches no category. Confirm that the WHEN: true route receives it.
  3. When the pipeline is on, find the pipeline_classify and pipeline_routing_resolve events. Confirm the category and the matched route.
  4. Check the handoff trace event. Confirm the PASS values and the summary in its context. Check seededFields in handoff_context_seed to see which fields reached the target. Check handoff_context_pass_resolved only for values taken from the customer’s message.
  5. Check the history. With full, the target sees the full parent transcript. With summary_only, the target sees the summary only.
  6. For EXPECT_RETURN: true, confirm the supervisor resumes and the mapped fields appear in the supervisor’s session data.
  7. Find the handoff_completed event for each attempt. Check outcome (success, rejected, blocked, error, or timeout) and durationMs. A route that the WHEN guard refuses shows blocked. When ON_FAILURE runs, find the handoff_failure event for the phase and the action.

Production readiness checklist

  • Every TO target exists in the project.
  • Every PASS field has a source: parent data, caller data, a gathered field, or a value the customer states.
  • Every intent.category value in a WHEN appears in INTENTS:. The compile shows no UNDECLARED_INTENT_CATEGORY warning.
  • Specific routes come before general routes. Quoted routes come after intent.category routes.
  • Each route sets history on purpose. The default full passes the whole transcript and uses more tokens.
  • A WHEN: true fallback exists, or the supervisor can answer on its own.
  • Critical routes have an ON_FAILURE policy.
  • Every field in an ON_RETURN map exists in the child.
  • Tests cover one message per category, one ambiguous message, and one message outside every category.
  • Monitoring covers handoff_completed events with the outcomes error and timeout.

Common mistakes

Troubleshooting