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 typing import Annotated, Any
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 typing import Annotated, Any
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 typing import Annotated
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 typing import Annotated
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

DimensionInjectedStateInjectedStore
ScopeCurrent conversation (single Agent run)Cross-session (shared across multiple Agent runs)
LifecycleDisappears when the conversation endsPersistent storage
Typical usesReading message history, intermediate results of the current conversationUser preferences, learning progress, configuration information
Injection methodInjectedState (automatically injected)InjectedStore() (parentheses required)
Data organizationFlat dictionaryNamespace + 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 typing import Annotated
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}"

InjectedToolArg 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.

Other extensions