IntentAgent Execution Flow

How an IntentAgent routes a user message through intent matching before reaching the LLM.

IntentAgent extends Agent β€” the first two nodes (render_system_prompt, current_active_agent) are identical. The divergence starts at continue_conversation, which sends the flow to map_intents instead of call_model.


Graph Overview

flowchart TD
    USER([πŸ‘€ User])
    AGENT(["πŸ€– IntentAgent"])

    subgraph GRAPH["IntentAgent Graph (LangGraph StateGraph)"]
        A["render_system_prompt\n──────────────\nResolve & store system prompt\ninto graph state"]
        B["current_active_agent\n──────────────\nRouter: @mention / active agent\nor fall through"]
        C["continue_conversation\n──────────────\nRedirects to map_intents\n(overrides base Agent)"]
        MI["map_intents\n──────────────\nVector similarity search\nagainst intent registry"]
        FI["filter_out_intents\n──────────────\nLLM logical filter:\nremove false positives"]
        EC["entity_check\n──────────────\nspaCy entity validation\n+ LLM fallback"]
        IMR["intent_mapping_router\n──────────────\nDispatch based on intent\ntype and count"]
        RHV["request_human_validation\n──────────────\nAsk user to pick\namong candidates"]
        IIS["inject_intents_in_system_prompt\n──────────────\nAppend matched intents\nto system prompt"]
        CM["call_model\n──────────────\nInvoke LLM with enriched\nsystem prompt + history"]
        CT["call_tools\n──────────────\nExecute requested\ntool calls"]
    end

    SUB(["πŸ€– Sub-agent graph\n(nested StateGraph)"])
    SUP(["πŸ§‘β€βœˆοΈ Supervisor graph\n(parent StateGraph)"])
    END(["βœ… __end__\nFinal response"])

    USER -->|"send message"| AGENT
    AGENT --> A
    A --> B

    B -->|"@mention or active_agent β‰  self"| SUB
    B -->|"no redirect"| C
    C --> MI

    MI -->|"score β‰₯ direct_intent_score\n(single clear winner)"| IMR
    MI -->|"multiple close matches"| FI
    MI -->|"no match"| CM

    FI -->|"single intent left\n(score > threshold)"| IMR
    FI -->|"still multiple"| EC

    EC --> IMR

    IMR -->|"0 intents"| CM
    IMR -->|"1 Γ— RAW"| END
    IMR -->|"1 Γ— AGENT"| SUB
    IMR -->|"1 Γ— TOOL"| IIS
    IMR -->|"multiple AGENT/TOOL"| RHV

    RHV -->|"present choices to user"| END

    IIS --> CM
    CM -->|"tool_calls present"| CT
    CM -->|"no tool calls"| END

    CT -->|"if agent required, transfer_to_"| SUB
    CT -->|"request_help called"| SUP
    CT -->|"return_direct=False\nor tool error"| CM
    CT -->|"return_direct=True"| END
    END -->|"send response"| USER

    SUB -->|"update AgentSharedState"| AGENT
    SUP -->|"update AgentSharedState"| AGENT

Step-by-Step

1. render_system_prompt (inherited)

Evaluates the system prompt (static string or callable) and writes it into graph state. Identical to the base Agent.

2. current_active_agent (inherited)

Routes directly to a sub-agent via @mention or stored current_active_agent. If neither applies, falls through to continue_conversation.

3. continue_conversation (overridden)

In the base Agent this goes to call_model. Here it redirects to map_intents β€” the entry point of the intent pipeline.

4. map_intents

Core of the intent system. Two-pass vector search:

PassMethodDescription
1stmap_intent(text, k=10)Embed the raw user message, cosine similarity against intent index
2nd (fallback)map_prompt(text, k=10)Re-run the same search if pass 1 returned nothing

After scoring, three routing decisions:

ConditionNext node
No result above threshold (0.85)call_model
Single result with score β‰₯ direct_intent_score (0.90) and clearly bestintent_mapping_router (fast path)
Multiple results within threshold_neighbor (0.05) of the top scorefilter_out_intents

Special case: if the user replied with a digit ("1", "2", …) after a request_human_validation message, map_intents resolves the choice and routes directly to the selected agent.

5. filter_out_intents

Calls the LLM with a dedicated filter_intents tool. The model receives the list of candidate intents and the user message, then returns a boolean list β€” true keeps the intent, false drops it.

Filters out intents that are logically incompatible despite surface similarity (e.g. wrong named entities, over-specific conditions).

ResultNext node
1 intent remaining above thresholdintent_mapping_router
Still multipleentity_check

6. entity_check

Uses spaCy (en_core_web_sm) to extract named entities from each candidate intent value.

  • Intent with no entities β†’ kept as-is.
  • Intent with entities β†’ check if every entity appears in the user message.
    • If entity sets match β†’ kept.
    • Ambiguous β†’ LLM asked to answer "true" / "false".

Always routes to intent_mapping_router.

7. intent_mapping_router

Final dispatch based on the cleaned intent set:

StateAction
0 intentscall_model β€” no usable match, let the LLM handle it
1 Γ— RAWEmit intent_target string directly as AIMessage β†’ __end__
1 Γ— AGENTgoto=intent_target β€” jump to that sub-agent node
1 Γ— TOOLinject_intents_in_system_prompt β€” let the LLM call the tool
Multiple AGENT/TOOLrequest_human_validation
Multiple with ≀ 1 non-RAWinject_intents_in_system_prompt

8. request_human_validation

Presents a numbered list of candidate agents/tools to the user and ends the turn. On the next message, map_intents detects the numeric reply and resolves the choice.

9. inject_intents_in_system_prompt

Appends an <intents_rules> block to the system prompt containing the matched intent(s) and their targets. The LLM then knows which tool to call or which response pattern to follow.

10. call_model + call_tools (inherited)

Identical to the base Agent β€” see Agent Execution Flow.


Intent Types & Scope

Types

Typeintent_targetLLM involved?Description
RAWA plain stringNoShort-circuit: return the string directly
TOOLTool name (e.g. list_tools_available)YesInject intent β†’ LLM calls the tool
AGENTAgent name (e.g. AnalyticsAgent)NoRedirect to that sub-agent graph node

Scope

ScopeMeaning
DIRECTMatched only against the current message
ALLMay be matched across the full conversation context

Scoring Thresholds

ParameterDefaultRole
threshold0.85Minimum cosine similarity for any intent to be considered
threshold_neighbor0.05Maximum score gap to be considered a β€œclose competitor”
direct_intent_score0.90Above this with a clear lead β†’ skip filtering, route immediately

Intent Pipeline Summary

User message
    β”‚
    β–Ό
map_intents ──(no match)──────────────────────────────► call_model
    β”‚
    β”œβ”€(score β‰₯ 0.90, single winner)──────────────────► intent_mapping_router
    β”‚
    └─(multiple close)──► filter_out_intents
                                β”‚
                                β”œβ”€(1 left)────────────► intent_mapping_router
                                β”‚
                                └─(still many)──► entity_check
                                                        β”‚
                                                        └────────────► intent_mapping_router
                                                                            β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
                                    β–Ό                                       β”‚
                            0 β†’ call_model                                  β”‚
                            RAW β†’ __end__ (direct string)                   β”‚
                            AGENT β†’ sub-agent node                          β”‚
                            TOOL β†’ inject β†’ call_model                      β”‚
                            multiple β†’ request_human_validation β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Default Intents

A set of built-in intents is always injected (unless default_intents=False):

CategoryExamplesType
Identity”what’s your name?”, β€œwhat do you do?”AGENT β†’ call_model
Greetings”Hello”, β€œHi”, β€œSalut”, β€œBonjour”RAW
Thanks”Thank you”, β€œMerci”RAW
Discovery”List tools available”, β€œList sub-agents available”TOOL