LangChain Tool Access -- InjectedState and InjectedStore
Sometimes tools need access to more contextual information, such as the current conversation state, the user's persistent data, etc.
LangChain uses a dependency injection mechanism to let tool functions automatically obtain this information.
InjectedState — Accessing Agent State in Tools
By default, tools can only receive model-provided data through parameters. But sometimes tools need to know the context of the current conversation — such as previous conversation history, information already confirmed by the user, etc.
InjectedStateLets tools directly read the full state of the Agent.
Example
from dotenv import load_dotenv
load_dotenv()
from langchain.tools import tool, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
@tool
def remember_preference(
preference: str,
state: Annotated[dict[str, Any], InjectedState],
) -> str:
"""Remember the user's preference settings.
Args:
preference: the user's preference content
state: the current Agent state automatically injected by the system
"""
# Get the previous message history from the state
messages = state.get("messages", [])
message_count = len(messages)
# Can read any field in the state
previous_prefs = state.get("user_preferences", "none")
return (
f"Preference remembered: {preference}."
f"(The current conversation has {message_count} messages,"
f"Previous preference: {previous_prefs})"
)
# InjectedState is automatically injected, the Agent does not need to pass this parameter
result = remember_preference.invoke({
"preference": "dark theme",
# state does not need to be passed, it is automatically injected by the framework
})
print(result)
Run result:
已记住偏好: 暗色主题。(当前对话共 0 条消息,之前偏好: 无)
Using InjectedState in an Agent
Example
from langchain.tools import tool, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
@tool
def conversation_stats(
state: Annotated[dict[str, Any], InjectedState],
) -> str:
"""Get statistical information of the current conversation, such as message count, conversation length, etc.
No parameters are needed; the statistics are automatically read from the current state.
"""
messages = state.get("messages", [])
human_msgs = [m for m in messages if m.type == "human"]
ai_msgs = [m for m in messages if m.type == "ai"]
tool_msgs = [m for m in messages if m.type == "tool"]
return (
f"Conversation stats: {len(messages)} messages total | "
f"Human messages: {len(human_msgs)} | "
f"AI replies: {len(ai_msgs)} | "
f"Tool calls: {len(tool_msgs)}"
)
InjectedState lets you access all fields in AgentState. If you extend state_schema (adding custom fields), these fields can also be read by InjectedState.
InjectedStore — Accessing Persistent Storage in Tools
Agent state is conversation-level; it disappears when the conversation ends. WhereasStoreit is cross-session persistent storage, which can be used to save long-term information such as user preferences, learning progress, etc.
InjectedStoreLets tools directly read and write the Store.
Example
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore, InjectedState
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# Create a persistent store (in real projects, a database can be used)
store = InMemoryStore()
# Preset some data
store.put(("users", "user_001"), "profile", {
"data": {
"name": "Xiao Ming",
"level": "Beginner",
"completed_courses": ["HTML Basics Tutorial"]
}
})
@tool
def get_user_profile(
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""Get the current user's learning profile information.
Read user data from the persistent store.
"""
# Read data from the Store
# Store uses namespace (namespace, key) to organize data
item = store.get(("users", "user_001"), "profile")
if item is None:
return "User profile not found"
profile = item.value["data"]
return (
f"User profile: name={profile['name']},"
f"level={profile['level']},"
f"completed courses={', '.join(profile['completed_courses'])}"
)
@tool
def save_course_progress(
course_name: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""Save the user's learning progress to the persistent store.
Args:
course_name: the name of the completed course
store: the persistent store automatically injected by the system
"""
# Read existing data
item = store.get(("users", "user_001"), "profile")
profile = item.value["data"] if item else {"name": "Xiao Ming", "level": "Beginner", "completed_courses": []}
# Update the course list
if course_name not in profile["completed_courses"]:
profile["completed_courses"].append(course_name)
# Write back to the Store
store.put(("users", "user_001"), "profile", {"data": profile})
return (
f"Learning progress updated! Completed {len(profile['completed_courses'])} courses:"
f"{', '.join(profile['completed_courses'])}"
)
# Test the tool
print(get_user_profile.invoke({}))
print(save_course_progress.invoke({"course_name": "Python3 Basic Tutorial"}))
print(get_user_profile.invoke({}))
Run result:
用户档案:姓名=小明,水平=入门,已完成课程=HTML 基础教程 学习进度已更新!已完成 2 门课程:HTML 基础教程, Python3 基础教程 用户档案:姓名=小明,水平=入门,已完成课程=HTML 基础教程, Python3 基础教程
Using Store in an Agent
Pass the Store to create_agent(), and all tools in the Agent can access it through InjectedStore:
Example
from langgraph.store.base import BaseStore
from langgraph.store.memory import InMemoryStore
from langchain.tools import tool, InjectedStore
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
# Create Store and preset data
store = InMemoryStore()
store.put(("example", "courses"), "catalog", {
"data": {
"Python3 Basic Tutorial": {"price": "Free", "duration": "20 hours"},
"Python Data Analysis": {"price": "Member", "duration": "30 hours"},
"HTML Basics Tutorial": {"price": "Free", "duration": "15 hours"},
}
})
@tool
def query_course_price(
course_name: str,
store: Annotated[BaseStore, InjectedStore()],
) -> str:
"""Query the price information of a specified course on EXAMPLE.
Args:
course_name: the course name
"""
item = store.get(("example", "courses"), "catalog")
catalog = item.value["data"] if item else {}
if course_name in catalog:
info = catalog[course_name]
return f"{course_name} - Price: {info['price']}, Learning duration: {info['duration']}"
return f"Course not found: {course_name}"
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
agent = create_agent(
model=model,
tools=[query_course_price],
store=store, # Pass the Store into the Agent
system_prompt="You are a course consultant at EXAMPLE.",
)
result = agent.invoke({
"messages": [HumanMessage(content="How much do Python3 Basic Tutorial and Python Data Analysis cost respectively?")]
})
print(result["messages"][-1].content)
Run result:
根据查询结果: - 《Python3 基础教程》是免费的,学习时长约 20 小时 - 《Python 数据分析》是会员课程,学习时长约 30 小时 建议先从免费的 Python3 基础教程开始学习。
InjectedState vs InjectedStore Comparison
| Dimension | InjectedState | InjectedStore |
|---|---|---|
| Scope | Current conversation (single Agent run) | Cross-session (shared across multiple Agent runs) |
| Lifecycle | Disappears when the conversation ends | Persistent storage |
| Typical uses | Reading message history, intermediate results of the current conversation | User preferences, learning progress, configuration information |
| Injection method | InjectedState (automatically injected) | InjectedStore() (parentheses required) |
| Data organization | Flat dictionary | Namespace + key hierarchy |
InjectedToolArg — Marking Generic Injected Parameters
In addition to the dedicated InjectedState and InjectedStore, you can also useInjectedToolArgto mark any parameter that needs to be injected by the framework:
Example
from langchain.tools import tool, InjectedToolArg
@tool
def my_tool(
normal_param: str,
injected_param: Annotated[str, InjectedToolArg],
) -> str:
"""An example tool containing an injected parameter.
Args:
normal_param: this parameter is provided by the model
injected_param: this parameter is injected by the framework (the Agent does not need to provide it)
"""
return f"normal={normal_param}, injected={injected_param}"
Other extensionsInjectedToolArg is a generic injection marker; InjectedState, InjectedStore, and InjectedToolCallId are all implemented based on it. In most cases, using the dedicated injection markers is sufficient; InjectedToolArg is used to extend custom injection logic.