Intent Classifier

August 21, 2026 ยท View on GitHub

The Intent Classifier is a single orchestration node that performs three roles in one LLM call: intent classification, meta response generation, and depth routing. It is the entry point for every query in the AI-Q workflow.

Location: src/aiq_agent/agents/chat_researcher/nodes/intent_classifier.py

Purpose

Rather than using separate classifiers for intent and depth, the Intent Classifier combines all routing decisions into a single LLM invocation. This minimizes latency for the common case (meta queries get an instant response) and avoids an extra round-trip for research queries.

GSF-enabled profiles use the context-aware variant in nodes/context_aware_intent_router.py. In addition to interaction and depth routing, that variant performs one bounded catalog coverage probe. For a mixed enterprise-and-public request, the probe is the smallest contiguous span that contains the complete enterprise-data question, copied verbatim from the user request. It is not a second research decomposition: no public subquery or plan is generated, and the complete original request continues to the selected research workflow.

The classifier outputs structured JSON with:

  • Intent -- meta or research
  • Meta response -- a direct reply when intent is meta
  • Depth decision -- shallow or deep when intent is research

Internal Flow

graph TD
    A[Receive ChatResearcherState] --> B[Extract latest user query]
    B --> C[Render intent_classification.j2]
    C --> D[Build message list:<br/>SystemMessage + trimmed history]
    D --> E[Invoke LLM with timeout]

    E -->|success| F[Extract JSON from response]
    E -->|timeout| G[Return timeout error message]
    E -->|API unavailable| H[Return unavailability message]

    F --> I{intent == meta?}
    I -->|yes| J[Set user_intent = meta<br/>Add AIMessage to messages]
    I -->|no| K[Set user_intent = research<br/>Set depth_decision = shallow or deep]

    J --> L[Return state update]
    K --> L
    G --> L
    H --> L

    style A fill:#e1f5fe
    style L fill:#e8f5e9
    style E fill:#fff3e0

State Model

The Intent Classifier reads from and writes to ChatResearcherState:

Inputs read:

FieldUsage
messagesConversation history; the last message is the current query
user_infoOptional user info injected into the prompt for personalization
data_sourcesUsed to build the tools info list shown to the LLM

Outputs written:

FieldTypeCondition
user_intentIntentResult(intent="meta" or "research")Always
messagesAppended AIMessage with meta responseWhen intent is meta
depth_decisionDepthDecision(decision="shallow" or "deep")When intent is research

IntentResult

:language: python
:pyobject: IntentResult
:caption: IntentResult model

DepthDecision

:language: python
:pyobject: DepthDecision
:caption: DepthDecision model

Configuration

Configured through IntentClassifierConfig (NeMo Agent Toolkit type name: intent_classifier):

ParameterTypeDefaultDescription
llmLLMRefrequiredLLM to use for classification
toolslist[FunctionRef | FunctionGroupRef][]Tool references; their names and descriptions are shown to the LLM so it can assess query complexity
llm_timeoutfloat90Timeout in seconds for the LLM call

Example YAML:

functions:
  intent_classifier:
    _type: intent_classifier
    llm: nemotron_llm
    tools:
      - web_search_tool
    llm_timeout: 90

Prompt Template

The classifier uses intent_classification.j2 located in src/aiq_agent/agents/chat_researcher/prompts/.

Template variables:

VariableSource
queryContent of the last user message
current_datetimeCurrent date and time string
user_infoUser info dict (name, email) or empty
toolsList of {name, description} dicts for available tools

The prompt instructs the LLM to respond with a JSON object containing intent, meta_response (when meta), and research_depth (when research).

Error Handling

The classifier handles two failure modes gracefully:

  • LLM API unavailable (404, model not found): Returns a user-friendly message asking to check the API key and model configuration.
  • Timeout (asyncio timeout, 504 gateway): Returns a timeout message. The timeout is configurable through llm_timeout (default 90 seconds).

In both cases, the error message is returned as an AIMessage so the orchestrator routes to END and the user sees the error.

Example I/O

Meta query:

Input:  "Hello! What can you do?"
Output: user_intent = IntentResult(intent="meta")
        messages += [AIMessage("Hi! I'm a research assistant...")]
        -> routes to END

Research query (shallow):

Input:  "What is CUDA?"
Output: user_intent = IntentResult(intent="research")
        depth_decision = DepthDecision(decision="shallow")
        -> routes to shallow_research

Research query (deep):

Input:  "Compare the economic impacts of renewable energy adoption across G7 nations"
Output: user_intent = IntentResult(intent="research")
        depth_decision = DepthDecision(decision="deep")
        -> routes to clarifier