LangChain ToolRuntime 入门:context、state 与 store 的实际用法

1. ToolRuntime 是什么

ToolRuntime 是 LangChain 执行工具时自动传入的运行对象。

工具参数分为两类:

  • 普通参数:模型根据用户问题填写,例如 keyword;
  • runtime:框架自动注入,模型不能填写。
python 复制代码
from langchain.tools import ToolRuntime, tool

@tool
def search(keyword: str, runtime: ToolRuntime) -> str:
    # keyword 由模型填写。
    # runtime 由 LangChain 自动传入。
    return f"搜索:{keyword}"

2. 三个核心对象

对象 作用 典型内容
context 本次运行的固定条件 用户 ID、权限、数据库连接
state 当前流程的临时状态 消息、步骤、临时结果
store 跨请求保存的数据 用户偏好、长期记忆

记忆方法:context 管运行条件,state 管当前过程,store 管长期数据。

3. context:传入用户和运行依赖

context 由程序创建,再传给 Agent。需要用户身份时,应使用登录系统已经验证过的信息,不能从用户文字中读取用户 ID。

python 复制代码
from dataclasses import dataclass

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import ToolRuntime, tool


@dataclass
class RequestContext:
    # 这些值由程序传入,模型不能自行修改。
    user_id: int
    role: str


@tool
def show_user(runtime: ToolRuntime[RequestContext]) -> str:
    # 工具从 context 读取用户身份,不接受模型传入的 user_id。
    return f"用户 {runtime.context.user_id},角色 {runtime.context.role}"


# 根据实际环境配置模型。
model = init_chat_model("openai:gpt-4o-mini")

# context_schema 只声明上下文类型。
agent = create_agent(
    model=model,
    tools=[show_user],
    context_schema=RequestContext,
)

# 真实用户信息应来自登录系统。
context = RequestContext(user_id=1001, role="user")

# context 参数才是真正把用户信息传给 Agent。
result = agent.invoke(
    {"messages": [{"role": "user", "content": "查询当前用户"}]},
    context=context,
)
print(result["messages"][-1].content)

适用场景:权限判断、按用户查询数据、传入数据库会话、传入租户信息。

注意:context_schema 不会自动创建用户信息;必须先创建 RequestContext(...),再使用 context=... 传入。

4. state:当前流程的临时状态

state 是 Agent 当前正在处理的内容。最简单的例子是读取当前消息数量。

python 复制代码
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import ToolRuntime, tool


@tool
def count_messages(runtime: ToolRuntime) -> str:
    # messages 是 Agent state 中自带的消息列表。
    # get 的第二个参数 [] 是默认值:没有 messages 时返回空列表。
    messages = runtime.state.get("messages", [])
    return f"当前共有 {len(messages)} 条消息"


model = init_chat_model("openai:gpt-4o-mini")
agent = create_agent(
    model=model,
    tools=[count_messages],
)

# 模型调用工具时,ToolRuntime 会自动带上当前 state。
result = agent.invoke(
    {"messages": [{"role": "user", "content": "数一下当前消息"}]}
)
print(result["messages"][-1].content)

适用场景:读取消息、记录当前步骤、保存重试次数和临时查询结果。

实际用法:统计模型调用次数

当前代码把 model_call_count 放进 state,由中间件在模型调用完成后加 1:

python 复制代码
from typing import Any, NotRequired

from langchain.agents import AgentState
from langchain.agents.middleware import AgentMiddleware
from langgraph.runtime import Runtime


class CustomAgentState(AgentState):
    # AgentState 已经提供 messages,这里只增加调用次数。
    model_call_count: NotRequired[int]


class ModelCallLimitMiddleware(AgentMiddleware[CustomAgentState]):
    # 注册扩展后的 state 类型。
    state_schema = CustomAgentState

    def after_model(
        self, state: CustomAgentState, runtime: Runtime
    ) -> dict[str, Any]:
        # 没有记录时从 0 开始,每完成一次模型调用就加 1。
        count = state.get("model_call_count", 0)
        return {"model_call_count": count + 1}

流式调用时,updates 事件可以读到中间件更新后的 state:

python 复制代码
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model

model = init_chat_model("openai:gpt-4o-mini")
agent = create_agent(
    model=model,
    middleware=[ModelCallLimitMiddleware()],
)

async def read_model_call_count() -> None:
    input_state = {
        "messages": [{"role": "user", "content": "你好"}],
        "model_call_count": 0,
    }
    model_call_count = 0

    async for mode, data in agent.astream(
        input_state,
        stream_mode=["messages", "updates"],
    ):
        if mode != "updates":
            continue

        update = data.get("ModelCallLimitMiddleware.after_model")
        if update:
            model_call_count = update["model_call_count"]

    print(f"本轮模型调用次数:{model_call_count}")

这里的次数只属于当前 Agent 流程,适合做日志、限次和调试信息。

5. store:跨请求保存数据

store 适合保存下一次请求仍然需要的数据,例如:

  • 用户语言和主题偏好;
  • 用户主动要求记住的信息;
  • 跨会话的设置;
  • 需要复用的业务缓存。

不适合把所有聊天内容无条件放进 store。长期保存前,应先判断内容是否有价值,并按用户或租户隔离命名空间。

实际项目通常把 store.put() 和 store.get() 封装到工具中。工具从 context 获取当前登录用户 ID,外部调用时不需要重复传入用户 ID:

python 复制代码
from dataclasses import dataclass

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.tools import ToolRuntime, tool
from langgraph.store.memory import InMemoryStore


@dataclass
class UserContext:
    # 这个 ID 应由登录系统传入,不能由模型填写。
    user_id: str


@tool(description="保存用户喜欢的颜色。")
def save_color(color: str, runtime: ToolRuntime[UserContext]) -> str:
    # 工具通过 runtime.store 保存数据。
    namespace = ("user", runtime.context.user_id)
    runtime.store.put(
        namespace,
        "favorite_color",
        {"value": color},
    )
    return "颜色偏好已保存"

@tool(description="读取用户之前保存的喜欢颜色。")
def read_color(runtime: ToolRuntime[UserContext]) -> str:
    # 读取时使用相同的 namespace 和 key。
    namespace = ("user", runtime.context.user_id)
    item = runtime.store.get(namespace, "favorite_color")
    if item is None:
        return "还没有保存颜色偏好"
    return f"喜欢的颜色是:{item.value['value']}"


# store 应在应用启动时创建,多个请求才能共用它。
store = InMemoryStore()
model = init_chat_model("openai:gpt-4o-mini")

# 把 store 和 context 类型传给 Agent,工具才能使用 runtime.store 和 runtime.context。
agent = create_agent(
    model=model,
    tools=[save_color, read_color],
    context_schema=UserContext,
    store=store,
)

# 每次请求传入当前登录用户的上下文。
context = UserContext(user_id="1001")
agent.invoke(
    {"messages": [{"role": "user", "content": "记住我喜欢蓝色"}]},
    context=context,
)

# 下一次请求仍使用同一个 store,工具可以读到之前保存的内容。
next_context = UserContext(user_id="1001")
result = agent.invoke(
    {"messages": [{"role": "user", "content": "我喜欢什么颜色?"}]},
    context=next_context,
)
print(result["messages"][-1].content)

InMemoryStore 只保存在内存中,程序重启后会丢失;需要永久保存时,应换成数据库等持久化实现。

7. 如何选择

  1. 本次运行的用户、权限和依赖:使用 context。
  2. 当前流程的消息和临时结果:使用 state。
  3. 下次请求仍要使用的数据:使用 store。

总结

ToolRuntime 是工具获取运行信息的入口:

  • context 管本次运行条件;
  • state 管当前工作过程;
  • store 管跨请求数据。

先判断数据的生命周期,再选择对象,工具代码就不会混乱。

相关推荐
回家路上绕了弯19 分钟前
当 AI 越来越能干,我们如何理解自己的价值?
后端
陈随易29 分钟前
Node.js 2026上半年都有哪些性能提升?
前端·后端·程序员
hhcgchpspk40 分钟前
flask服务端页面预防被爬措施
后端·python·flask·网络爬虫·防爬
vipxieliang1 小时前
Python collections:高效容器与实战案例
后端·python
驭渊的小故事1 小时前
Spring AOP 核心概念与通知类型详解
java·后端·spring
vipxieliang1 小时前
ValidX vs 手写验证代码:开发效率对比实测
java·spring boot·后端
vipxieliang1 小时前
Go GC 性能优化实践:从 GOGC、GOMEMLIMIT 到分配优化
后端·go
海豚调度1 小时前
Apache DolphinScheduler 2026 年 10 月社区贡献总结报告
后端