> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowx.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Intent classification and routing

> Classify user input into intent categories and route to specialized handlers. Use the dedicated Intent Classification node (5.6+) for most cases, or the TEXT_UNDERSTANDING + Condition pattern for advanced scenarios.

<Warning>
  **Preview**

  Agent Builder is currently in preview and may change before general availability.
</Warning>

## When to use

Use this pattern when your workflow must handle multiple types of user input and respond differently depending on what the user wants. Intent classification is the foundation of any conversational AI app on FlowX — it determines what happens next.

Common scenarios:

* A chatbot that must distinguish greetings from product inquiries from data submissions
* An email triage pipeline that routes messages to different processing branches
* Any multi-turn conversation where the next step depends on user intent

Without intent classification, a workflow can only follow a single linear path regardless of what the user says.

***

## Recommended: Intent Classification node (5.6+)

```
User message
    |
    v
Intent Classification node
(one output port per intent + built-in If No Intent Matches fallback)
    |
    +--> Handler A (e.g., greeting response)
    +--> Handler B (e.g., product inquiry)
    +--> Handler C (e.g., data input)
    +--> If No Intent Matches (fallback, always present)
```

Benefits over the legacy pattern:

* **No Condition node** — each intent gets its own output port automatically
* **Built-in fallback** — `If No Intent Matches` is always-present and can't be accidentally omitted
* **Use Memory** (Chat Driven workflows) — the classifier can factor in the last few turns when resolving ambiguous messages (e.g., "yes", "tell me more")
* **Include Reason for Selection** — the classifier can emit its rationale, useful for tuning labels during development
* **Up to 10 intents per node** — chain multiple Intent Classification nodes for deeper taxonomies

See the [Intent Classification node reference](../agent-builder/intent-classification) for full configuration details.

### When to use the legacy pattern below

The TEXT\_UNDERSTANDING + Condition pattern remains valid for advanced cases the dedicated node does not cover:

* **Confidence-based routing** — branching on a numeric confidence score, not just the winning label
* **Multi-label classification** — one message may match several intents simultaneously; the Condition node can fire multiple downstream paths
* **Custom post-processing** — applying business rules on the raw LLM output before routing (e.g., demoting an intent below a business-defined threshold)

***

## Legacy pattern: TEXT\_UNDERSTANDING + Condition

The pattern follows a detect-then-route structure:

```
User message
    |
    v
TEXT_UNDERSTANDING node
(classifies intent, returns structured JSON)
    |
    v
Condition node (Python expressions)
    |
    +--> Handler A (e.g., greeting response)
    +--> Handler B (e.g., product inquiry)
    +--> Handler C (e.g., data input)
    +--> Fallback (unrecognized intent)
```

The TEXT\_UNDERSTANDING node analyzes the user message and returns a structured JSON object with the detected intent. The Condition node evaluates that output and forks the workflow to the appropriate handler branch.

### Implementation

#### 1. Configure the TEXT\_UNDERSTANDING node

Add a TEXT\_UNDERSTANDING node to your workflow. This node sends the user message to an LLM with a classification prompt and enforces a structured response via a Response Schema.

#### Prompt

The prompt should instruct the LLM to classify the input into one of your defined intent categories. Be explicit about the categories and expected output format.

```text theme={"system"}
You are an intent classifier. Analyze the user message and classify it into exactly one
of the following intent categories:

- GREETING_SMALL_TALK — greetings, pleasantries, general conversation
- PRODUCT_OFFER_INQUIRY — questions about products, services, pricing, or features
- DATA_INPUT_UPDATE — the user is providing or updating personal data, documents, or form fields
- OTHER — anything that does not fit the above categories

Return a JSON object with:
- detected_intent: one of the categories above (exact string)
- confidence_score: a number between 0 and 1
- thought_process: a brief explanation of why you chose this category
- extracted_entities: (optional) any relevant entities found in the message

Do NOT include any text outside the JSON object.
```

<Tip>
  Keep the prompt focused on classification only. Resist adding response generation here — that belongs in the handler branches downstream.
</Tip>

#### Response Schema

Define a Response Schema on the TEXT\_UNDERSTANDING node to enforce structured output. This guarantees the LLM returns valid JSON matching your expected shape.

```json theme={"system"}
{
  "type": "object",
  "properties": {
    "detected_intent": {
      "type": "string",
      "enum": [
        "GREETING_SMALL_TALK",
        "PRODUCT_OFFER_INQUIRY",
        "DATA_INPUT_UPDATE",
        "OTHER"
      ]
    },
    "confidence_score": {
      "type": "number",
      "minimum": 0,
      "maximum": 1
    },
    "thought_process": {
      "type": "string"
    },
    "extracted_entities": {
      "type": "object"
    }
  },
  "required": ["detected_intent", "confidence_score", "thought_process"]
}
```

#### Configuration summary

| Setting             | Value                                                                          |
| ------------------- | ------------------------------------------------------------------------------ |
| **Node type**       | `TEXT_UNDERSTANDING`                                                           |
| **Input**           | User message (from Chat Input node or upstream variable)                       |
| **Output key**      | Configure an output key (e.g., `routesKey`) to store the classification result |
| **Response Schema** | JSON schema as shown above                                                     |
| **Temperature**     | `0` or very low — classification should be deterministic                       |

***

#### 2. Configure the Condition node

After the TEXT\_UNDERSTANDING node, add a Condition node to fork the workflow based on the detected intent. Each branch uses a Python expression that evaluates to `True` or `False`.

#### Condition expressions

Use substring matching with the `in` operator for resilience. This handles cases where the LLM wraps the intent in extra whitespace or slightly different formatting.

```python theme={"system"}
# Branch: Greeting / small talk
"GREETING" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))
```

```python theme={"system"}
# Branch: Product inquiry
"PRODUCT_OFFER" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))
```

```python theme={"system"}
# Branch: Data input / update
"DATA_INPUT" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))
```

```python theme={"system"}
# Branch: Fallback (default)
# No condition needed — this is the default/else branch
```

<Warning>
  Always include a fallback branch. Even with a Response Schema, edge cases can produce unexpected intent values. The fallback branch prevents the workflow from stalling.
</Warning>

<Tip>
  Use substring matching (`in` operator) instead of exact equality (`==`). This makes the condition resilient to minor LLM output variations without sacrificing accuracy.
</Tip>

#### Condition node summary

| Branch              | Expression                                                                                        | Routes to                                |
| ------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| **Greeting**        | `"GREETING" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))`      | Greeting handler                         |
| **Product inquiry** | `"PRODUCT_OFFER" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))` | Product info handler                     |
| **Data input**      | `"DATA_INPUT" in str(input.get("routesKey", {}).get("output", {}).get("detected_intent", ""))`    | Data processing handler                  |
| **Fallback**        | Default branch                                                                                    | Generic response or clarification prompt |

***

#### 3. Build handler branches

Each handler branch is a self-contained sub-workflow tailored to the classified intent. Examples:

* **Greeting handler** — responds with a welcome message or continues small talk using a text generation node
* **Product inquiry handler** — queries a knowledge base (see the [Knowledge base RAG](./knowledge-base-rag) pattern) and returns product information
* **Data input handler** — validates and stores user-provided data, then confirms receipt
* **Fallback handler** — asks the user to rephrase or provides a generic help message

After all branches complete their work, they converge back to a shared endpoint (typically a Closing Gateway or End node).

***

## Real-world example

The [Mortgage advisor chatbot](../tutorials/mortgage-advisor) tutorial implements this pattern as its core routing mechanism. User messages are classified into intents such as greeting, product inquiry (mortgage offers), data submission (income, employment), and general questions — then routed to specialized handlers that combine knowledge base lookups, financial calculations, and conversational responses.

***

## Variations

### Binary classification

When you only need to distinguish between two outcomes (e.g., yes/no, relevant/irrelevant), simplify the schema to a single boolean or two-value enum. Use a single Condition branch instead of multiple forks.

```json theme={"system"}
{
  "type": "object",
  "properties": {
    "is_relevant": {
      "type": "boolean"
    },
    "confidence_score": {
      "type": "number"
    }
  },
  "required": ["is_relevant", "confidence_score"]
}
```

### Multi-label classification

Some inputs may belong to multiple categories simultaneously (e.g., a message that both provides data and asks a question). Modify the schema to return an array of intents, and use a ForEach or parallel branching strategy to handle each detected intent.

### Confidence-based routing

Add a confidence threshold check before routing. If the confidence score falls below a threshold (e.g., 0.7), route to a human review queue or ask the user to clarify instead of proceeding with low-confidence automation.

```python theme={"system"}
# High confidence — route normally
float(input.get("routesKey", {}).get("output", {}).get("confidence_score", 0)) >= 0.7
```

```python theme={"system"}
# Low confidence — route to human review or clarification
float(input.get("routesKey", {}).get("output", {}).get("confidence_score", 0)) < 0.7
```

***

## Related resources

<CardGroup cols={2}>
  <Card title="AI patterns overview" icon="puzzle-piece" href="./overview">
    All available patterns and how to combine them
  </Card>

  <Card title="AI node types" icon="diagram-project" href="../agent-builder/node-types">
    Reference for TEXT\_UNDERSTANDING and other node types
  </Card>

  <Card title="Mortgage advisor tutorial" icon="building-columns" href="../tutorials/mortgage-advisor">
    End-to-end implementation using intent classification
  </Card>

  <Card title="Conversational workflows" icon="comments" href="../conversational-workflows">
    Multi-turn chat with session memory and intent routing
  </Card>
</CardGroup>
