Implementation:Run llama Llama index QueryPlanTool
Overview
The QueryPlanTool module implements a query planning and execution system that orchestrates multiple tools as a directed acyclic graph (DAG). It enables an LLM to decompose a complex question into a tree of sub-questions, each assigned to a specific tool, and then executes them in dependency order. Results from child nodes are synthesized using a response synthesizer to produce a final answer.
Source File: llama-index-core/llama_index/core/tools/query_plan.py
Module: llama_index.core.tools.query_plan
Lines of Code: 233
Dependencies
| Dependency | Type | Purpose |
|---|---|---|
llama_index.core.bridge.pydantic.BaseModel |
Internal | Base model for data classes |
llama_index.core.bridge.pydantic.Field |
Internal | Field definitions with descriptions |
llama_index.core.response_synthesizers.BaseSynthesizer |
Internal | Abstract base for response synthesizers |
llama_index.core.response_synthesizers.get_response_synthesizer |
Internal | Factory function for default synthesizer |
llama_index.core.schema.NodeWithScore |
Internal | Node with relevance score for synthesis |
llama_index.core.schema.TextNode |
Internal | Text node for child response storage |
llama_index.core.tools.types.BaseTool |
Internal | Base tool interface |
llama_index.core.tools.types.ToolMetadata |
Internal | Tool metadata |
llama_index.core.tools.types.ToolOutput |
Internal | Standard tool output wrapper |
llama_index.core.utils.print_text |
Internal | Colored text output utility |
Data Models
Class: QueryNode
class QueryNode(BaseModel)
Represents a single node in the query plan DAG. Each node is either a leaf node (answered by a tool) or a branch node (answered by synthesizing child node results).
| Field | Type | Description |
|---|---|---|
id |
int |
Unique identifier for the query node |
query_str |
str |
The question to be answered at this node |
tool_name |
Optional[str] |
Name of the tool to execute (for leaf nodes); None for branch nodes
|
dependencies |
List[int] |
List of node IDs that must be resolved before this node |
The tool_name and dependencies fields are mutually exclusive in practice:
- Leaf nodes have a
tool_nameand emptydependencies. - Branch nodes have
dependenciesreferencing child nodes.
Class: QueryPlan
class QueryPlan(BaseModel)
The top-level plan containing all query nodes.
| Field | Type | Description |
|---|---|---|
nodes |
List[QueryNode] |
All nodes in the query plan DAG |
The QueryPlan model is used as the fn_schema for the QueryPlanTool, meaning the LLM is expected to generate a QueryPlan JSON when calling this tool.
Module Constants
| Constant | Description |
|---|---|
DEFAULT_NAME |
"query_plan_tool"
|
QUERYNODE_QUERY_STR_DESC |
Description for the query string field |
QUERYNODE_TOOL_NAME_DESC |
Description for the tool name field (mutually exclusive with dependencies) |
QUERYNODE_DEPENDENCIES_DESC |
Description for the dependencies field (mutually exclusive with tool name) |
DEFAULT_DESCRIPTION_PREFIX |
Default prefix for the tool description, explaining the DAG-based query plan concept |
Class: QueryPlanTool
class QueryPlanTool(BaseTool)
Constructor
def __init__(
self,
query_engine_tools: List[BaseTool],
response_synthesizer: BaseSynthesizer,
name: str,
description_prefix: str,
) -> None
| Parameter | Type | Description |
|---|---|---|
query_engine_tools |
List[BaseTool] |
Available tools that can be assigned to leaf nodes |
response_synthesizer |
BaseSynthesizer |
Synthesizer for combining child node results |
name |
str |
Tool name |
description_prefix |
str |
Prefix text for the dynamically generated description |
Instance Attributes
| Attribute | Type | Description |
|---|---|---|
_query_tools_dict |
Dict[str, BaseTool] |
Dictionary mapping tool names to tool instances |
_response_synthesizer |
BaseSynthesizer |
Response synthesizer for combining results |
_name |
str |
Tool name |
_description_prefix |
str |
Description prefix |
_custom_metadata |
Optional[ToolMetadata] |
Optional custom metadata override |
Factory Method: from_defaults
@classmethod
def from_defaults(
cls,
query_engine_tools: List[BaseTool],
response_synthesizer: Optional[BaseSynthesizer] = None,
name: Optional[str] = None,
description_prefix: Optional[str] = None,
) -> "QueryPlanTool"
Creates a QueryPlanTool with sensible defaults:
- Defaults to
DEFAULT_NAMEif no name is provided. - Defaults to
DEFAULT_DESCRIPTION_PREFIXif no description prefix is provided. - Creates a default response synthesizer via
get_response_synthesizer().
Property: metadata
The metadata property dynamically generates a ToolMetadata that includes:
- The description prefix.
- A listing of all available tool names and descriptions.
- The
QueryPlanmodel as thefn_schema.
If _custom_metadata is set (via the setter), it is returned instead of the dynamically generated metadata.
Method: _execute_node
def _execute_node(
self, node: QueryNode, nodes_dict: Dict[int, QueryNode]
) -> ToolOutput
Recursively executes a single query node:
- If the node has dependencies:
- Retrieves child
QueryNodeobjects fromnodes_dict. - Recursively executes each child node.
- Wraps child responses as
TextNodeobjects with query/response text. - Creates
NodeWithScorewrappers (score=1.0). - Uses the response synthesizer to combine child results into a response.
- If the node also has a
tool_name, additionally executes that tool (overriding the synthesized response).
- Retrieves child
- If the node has no dependencies (leaf node):
- Looks up the tool by
node.tool_namein_query_tools_dict. - Calls the tool with
node.query_str.
- Looks up the tool by
- Prints execution details using colored output.
- Returns the
ToolOutput.
Method: _find_root_nodes
def _find_root_nodes(self, nodes_dict: Dict[int, QueryNode]) -> List[QueryNode]
Identifies root nodes in the DAG by finding nodes that are not listed as a dependency of any other node. Uses a counting approach:
- Initializes a count of zero for each node ID.
- Iterates through all nodes, incrementing the count for each dependency reference.
- Nodes with a count of zero are root nodes (no other node depends on them).
Method: __call__
def __call__(self, *args: Any, **kwargs: Any) -> ToolOutput
Entry point for tool execution:
- Constructs a
QueryPlanfrom the provided keyword arguments. - Builds a dictionary mapping node IDs to
QueryNodeobjects. - Finds root nodes via
_find_root_nodes. - Validates that exactly one root node exists (raises
ValueErrorotherwise). - Recursively executes the root node via
_execute_node.
Execution Flow
LLM generates QueryPlan JSON
|
v
QueryPlanTool.__call__(**plan_kwargs)
|
v
QueryPlan(nodes=[...]) -- parse into data model
|
v
_find_root_nodes() -- identify the root of the DAG
|
v
_execute_node(root_node)
|
+---> Has dependencies?
| |
| +---> Yes: recursively execute child nodes
| | |
| | v
| | Synthesize child results with response_synthesizer
| | |
| | v
| | (Optionally call tool_name if present)
| |
| +---> No (leaf node): call tool(node.query_str)
|
v
Return ToolOutput
Design Patterns
DAG-Based Query Decomposition
The module models complex queries as a DAG where each node is either answered by a specific tool (leaf) or by synthesizing the results of its child nodes (branch). This enables multi-hop reasoning over multiple data sources.
Recursive Execution
Node execution is implemented recursively. The _execute_node method processes dependencies depth-first before synthesizing results at the current level.
Dynamic Metadata Generation
The metadata property dynamically constructs the tool description by enumerating all registered tools. This ensures the LLM always has an up-to-date list of available tools when generating query plans.
See Also
- Run_llama_Llama_index_Tool_Calling - Tool invocation utilities used downstream
- Run_llama_Llama_index_EvalQueryEngineTool - Query engine tool with evaluation
- Run_llama_Llama_index_OnDemandLoaderTool - On-demand data loading tool