IntentAgent Execution Flow
How an
IntentAgentroutes a user message through intent matching before reaching the LLM.
IntentAgentextendsAgentβ the first two nodes (render_system_prompt,current_active_agent) are identical. The divergence starts atcontinue_conversation, which sends the flow tomap_intentsinstead ofcall_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:
| Pass | Method | Description |
|---|---|---|
| 1st | map_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:
| Condition | Next node |
|---|---|
No result above threshold (0.85) | call_model |
Single result with score β₯ direct_intent_score (0.90) and clearly best | intent_mapping_router (fast path) |
Multiple results within threshold_neighbor (0.05) of the top score | filter_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).
| Result | Next node |
|---|---|
1 intent remaining above threshold | intent_mapping_router |
| Still multiple | entity_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:
| State | Action |
|---|---|
| 0 intents | call_model β no usable match, let the LLM handle it |
| 1 Γ RAW | Emit intent_target string directly as AIMessage β __end__ |
| 1 Γ AGENT | goto=intent_target β jump to that sub-agent node |
| 1 Γ TOOL | inject_intents_in_system_prompt β let the LLM call the tool |
| Multiple AGENT/TOOL | request_human_validation |
| Multiple with β€ 1 non-RAW | inject_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
| Type | intent_target | LLM involved? | Description |
|---|---|---|---|
RAW | A plain string | No | Short-circuit: return the string directly |
TOOL | Tool name (e.g. list_tools_available) | Yes | Inject intent β LLM calls the tool |
AGENT | Agent name (e.g. AnalyticsAgent) | No | Redirect to that sub-agent graph node |
Scope
| Scope | Meaning |
|---|---|
DIRECT | Matched only against the current message |
ALL | May be matched across the full conversation context |
Scoring Thresholds
| Parameter | Default | Role |
|---|---|---|
threshold | 0.85 | Minimum cosine similarity for any intent to be considered |
threshold_neighbor | 0.05 | Maximum score gap to be considered a βclose competitorβ |
direct_intent_score | 0.90 | Above 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):
| Category | Examples | Type |
|---|---|---|
| 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 |