Principle:Langchain ai Langgraph Tool Definition
| Attribute | Value |
|---|---|
| Concept | Defining tools that an LLM agent can invoke during execution |
| Workflow | ReAct_Agent_Creation |
| Type | Principle |
| Repository | Langchain_ai_Langgraph |
| Source | libs/prebuilt/langgraph/prebuilt/tool_node.py
|
Overview
Tool definition in LangGraph establishes the contract between a language model and external capabilities it can invoke. A tool is any callable function or BaseTool instance that an LLM can select and execute during its reasoning loop. LangGraph's tool system extends beyond simple function calling by supporting state injection, persistent store access, and runtime context -- enabling tools to participate as first-class citizens within a stateful agent graph.
The ToolNode class serves as the execution engine for tools within the graph, accepting a sequence of tools at initialization and managing their parallel execution, error handling, and argument injection. Two key annotation types -- InjectedState and InjectedStore -- allow tools to transparently receive graph state and persistent storage without exposing these dependencies to the language model's tool-calling schema.
Description
The Tool-Use Paradigm
Modern LLM agents operate on a tool-use paradigm where the language model reasons about which tools to call, generates structured tool call arguments, and receives tool outputs to continue reasoning. This creates a cycle:
- The model receives a prompt and message history.
- The model decides to invoke one or more tools, producing
tool_callsin its response. - The
ToolNodeexecutes the selected tools and returnsToolMessageresults. - The model incorporates the results and either calls more tools or produces a final answer.
Tools are defined as Python functions decorated with @tool (from langchain_core.tools) or as BaseTool subclasses. The ToolNode automatically converts plain callables into BaseTool instances, inferring schemas from function signatures and docstrings.
Tool Calling Protocol
The tool calling protocol in LangGraph operates through a well-defined message structure:
- The model produces an
AIMessagecontaining atool_callslist. - Each tool call specifies a
name,argsdictionary,id, andtype. - The
ToolNodematches tool calls to registered tools by name. - Execution produces
ToolMessageobjects that reference the originaltool_call_id.
This protocol ensures that tool results are correctly associated with their corresponding requests, enabling parallel tool execution and multi-turn conversations.
State and Store Injection
A distinguishing feature of LangGraph's tool system is the ability to inject runtime context into tool parameters without the model knowing about these parameters:
- InjectedState: Annotating a tool parameter with
Annotated[dict, InjectedState]causes the full graph state to be injected at execution time. UsingInjectedState("field_name")injects only a specific field. These parameters are automatically excluded from the tool schema presented to the model.
- InjectedStore: Annotating a parameter with
Annotated[Any, InjectedStore()]injects the persistentBaseStoreinstance, enabling cross-session data persistence. The store must be provided when compiling the graph.
- ToolRuntime: A parameter typed as
ToolRuntimereceives a dataclass bundlingstate,context,config,stream_writer,tool_call_id, andstore. NoAnnotatedwrapper is needed -- simply declaringruntime: ToolRuntimetriggers injection.
The injection mechanism works by inspecting tool function signatures during ToolNode initialization. The _get_all_injected_args function builds a mapping of which parameters need injection, and at execution time, the ToolNode strips injected parameters from the model-facing schema while supplying them internally.
Error Handling Strategies
ToolNode provides configurable error handling through the handle_tool_errors parameter:
- Boolean:
Truecatches all errors and returns them asToolMessagecontent. - String: A custom error message returned for any error.
- Exception type(s): Only catches specified exception types.
- Callable: A function that receives the exception and returns a custom error string.
- False: Disables error handling, letting exceptions propagate.
This allows the model to see and recover from tool errors during its reasoning loop rather than crashing the entire agent execution.
Usage
Tools are defined using the @tool decorator and assembled into a ToolNode:
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode, InjectedState, InjectedStore
from typing import Annotated, Any
@tool
def search(query: str) -> str:
"""Search for information."""
return f"Results for: {query}"
@tool
def stateful_tool(x: int, state: Annotated[dict, InjectedState]) -> str:
"""Tool that accesses graph state."""
msg_count = len(state["messages"])
return f"Processed {x} with {msg_count} messages"
@tool
def persistent_tool(key: str, store: Annotated[Any, InjectedStore()]) -> str:
"""Tool that accesses persistent storage."""
result = store.get(("data",), key)
return result.value if result else "Not found"
tool_node = ToolNode(
[search, stateful_tool, persistent_tool],
handle_tool_errors=True,
)
Theoretical Basis
The tool-use paradigm draws from the ReAct (Reasoning + Acting) framework introduced by Yao et al. (2022), which demonstrated that interleaving reasoning traces with tool actions significantly improves LLM performance on complex tasks. The tool definition layer in LangGraph implements the "Act" component of this framework, providing a structured interface for the model to interact with external systems.
The injection pattern (InjectedState, InjectedStore, ToolRuntime) follows the dependency injection principle from software engineering, where dependencies are supplied externally rather than being created by the consumer. This enables loose coupling between tool logic and the execution environment, making tools portable across different graph configurations while still having access to runtime context.
The parallel execution model within ToolNode is grounded in the observation that tool calls within a single model response are typically independent, enabling concurrent execution without coordination overhead.