LangGraph支持两种对于构建对话代理至关重要的内存类型:

  • 短期内存:通过在会话中维护消息历史来跟踪正在进行的对话。
  • 长期内存:在不同会话之间存储用户特定或应用程序级别的数据。

本指南演示了如何在LangGraph中将这两种内存类型与代理结合使用。要更深入地了解内存概念,请参阅LangGraph内存文档

image

短期内存长期内存都需要持久存储,以在LLM交互中保持连续性。在生产环境中,这些数据通常存储在数据库中。

术语

在LangGraph中

  • _短期内存_也称为线程级内存
  • _长期内存_也称为跨线程内存

线程表示由相同thread_id分组的一系列相关运行。

短期内存使代理能够跟踪多轮对话。要使用它,您必须:

  1. 在创建代理时提供checkpointercheckpointer可以实现代理状态的持久性
  2. 在运行代理时在配置中提供thread_idthread_id是对话会话的唯一标识符。

API参考:create_react_agent | InMemorySaver

<span id="__span-0-1">from langgraph.prebuilt import create_react_agent
<span id="__span-0-2">from langgraph.checkpoint.memory import InMemorySaver
<span id="__span-0-3">
<span id="__span-0-4">checkpointer = InMemorySaver() 
<span id="__span-0-5">
<span id="__span-0-6">
<span id="__span-0-7">def get_weather(city: str) -&gt; str:
<span id="__span-0-8">    """Get weather for a given city."""
<span id="__span-0-9">    return f"It's always sunny in {city}!"
<span id="__span-0-10">
<span id="__span-0-11">
<span id="__span-0-12">agent = create_react_agent(
<span id="__span-0-13">    model="anthropic:claude-3-7-sonnet-latest",
<span id="__span-0-14">    tools=[get_weather],
<span id="__span-0-15">    checkpointer=checkpointer 
<span id="__span-0-16">)
<span id="__span-0-17">
<span id="__span-0-18"># Run the agent
<span id="__span-0-19">config = {
<span id="__span-0-20">    "configurable": {
<span id="__span-0-21">        "thread_id": "1"  
<span id="__span-0-22">    }
<span id="__span-0-23">}
<span id="__span-0-24">
<span id="__span-0-25">sf_response = agent.invoke(
<span id="__span-0-26">    {"messages": [{"role": "user", "content": "what is the weather in sf"}]},
<span id="__span-0-27">    config
<span id="__span-0-28">)
<span id="__span-0-29">
<span id="__span-0-30"># Continue the conversation using the same thread_id
<span id="__span-0-31">ny_response = agent.invoke(
<span id="__span-0-32">    {"messages": [{"role": "user", "content": "what about new york?"}]},
<span id="__span-0-33">    config 
<span id="__span-0-34">)

当代理第二次使用相同的thread_id被调用时,第一次对话的原始消息历史会自动包含在内,从而允许代理推断用户是在特别询问纽约的天气

LangGraph平台提供生产就绪的检查点

如果您使用LangGraph平台,在部署期间,您的检查点将自动配置为使用生产就绪的数据库。

长对话可能超出LLM的上下文窗口。常见的解决方案是:

  • 摘要:维护对话的运行摘要。
  • 裁剪:删除历史记录中的前N条或后N条消息。

这使得代理能够跟踪对话而不会超出LLM的上下文窗口。

要管理消息历史,请指定pre_model_hook——一个始终在调用语言模型之前运行的函数(节点)。

image

长对话可能超出LLM的上下文窗口。一个常见的解决方案是维护对话的运行摘要。这使得代理能够跟踪对话而不会超出LLM的上下文窗口。

要总结消息历史,您可以将[pre_model_hook](https://langgraph.com.cn/reference/agents.1.html#langgraph.prebuilt.chat_agent_executor.create_react_agent create_react_agent")与预构建的SummarizationNode一起使用。

API参考:ChatAnthropic | count_tokens_approximately | create_react_agent | AgentState | InMemorySaver

<span id="__span-1-1">from langchain_anthropic import ChatAnthropic
<span id="__span-1-2">from langmem.short_term import SummarizationNode
<span id="__span-1-3">from langchain_core.messages.utils import count_tokens_approximately
<span id="__span-1-4">from langgraph.prebuilt import create_react_agent
<span id="__span-1-5">from langgraph.prebuilt.chat_agent_executor import AgentState
<span id="__span-1-6">from langgraph.checkpoint.memory import InMemorySaver
<span id="__span-1-7">from typing import Any
<span id="__span-1-8">
<span id="__span-1-9">model = ChatAnthropic(model="claude-3-7-sonnet-latest")
<span id="__span-1-10">
<span id="__span-1-11">summarization_node = SummarizationNode( 
<span id="__span-1-12">    token_counter=count_tokens_approximately,
<span id="__span-1-13">    model=model,
<span id="__span-1-14">    max_tokens=384,
<span id="__span-1-15">    max_summary_tokens=128,
<span id="__span-1-16">    output_messages_key="llm_input_messages",
<span id="__span-1-17">)
<span id="__span-1-18">
<span id="__span-1-19">class State(AgentState):
<span id="__span-1-20">    # NOTE: we're adding this key to keep track of previous summary information
<span id="__span-1-21">    # to make sure we're not summarizing on every LLM call
<span id="__span-1-22">    context: dict[str, Any]  
<span id="__span-1-23">
<span id="__span-1-24">
<span id="__span-1-25">checkpointer = InMemorySaver() 
<span id="__span-1-26">
<span id="__span-1-27">agent = create_react_agent(
<span id="__span-1-28">    model=model,
<span id="__span-1-29">    tools=tools,
<span id="__span-1-30">    pre_model_hook=summarization_node, 
<span id="__span-1-31">    state_schema=State, 
<span id="__span-1-32">    checkpointer=checkpointer,
<span id="__span-1-33">)

要裁剪消息历史,您可以将[pre_model_hook](https://langgraph.com.cn/reference/agents.1.html#langgraph.prebuilt.chat_agent_executor.create_react_agent create_react_agent")与trim_messages函数一起使用。

API参考:trim_messages | count_tokens_approximately | create_react_agent

<span id="__span-2-1">from langchain_core.messages.utils import (
<span id="__span-2-2">    trim_messages,
<span id="__span-2-3">    count_tokens_approximately
<span id="__span-2-4">)
<span id="__span-2-5">from langgraph.prebuilt import create_react_agent
<span id="__span-2-6">
<span id="__span-2-7"># This function will be called every time before the node that calls LLM
<span id="__span-2-8">def pre_model_hook(state):
<span id="__span-2-9">    trimmed_messages = trim_messages(
<span id="__span-2-10">        state["messages"],
<span id="__span-2-11">        strategy="last",
<span id="__span-2-12">        token_counter=count_tokens_approximately,
<span id="__span-2-13">        max_tokens=384,
<span id="__span-2-14">        start_on="human",
<span id="__span-2-15">        end_on=("human", "tool"),
<span id="__span-2-16">    )
<span id="__span-2-17">    return {"llm_input_messages": trimmed_messages}
<span id="__span-2-18">
<span id="__span-2-19">checkpointer = InMemorySaver()
<span id="__span-2-20">agent = create_react_agent(
<span id="__span-2-21">    model,
<span id="__span-2-22">    tools,
<span id="__span-2-23">    pre_model_hook=pre_model_hook,
<span id="__span-2-24">    checkpointer=checkpointer,
<span id="__span-2-25">)

要了解更多关于使用pre_model_hook管理消息历史的信息,请参阅此操作指南

LangGraph允许代理在其工具内部访问其短期内存(状态)。

API参考:InjectedState | create_react_agent

<span id="__span-3-1">from typing import Annotated
<span id="__span-3-2">from langgraph.prebuilt import InjectedState, create_react_agent
<span id="__span-3-3">
<span id="__span-3-4">class CustomState(AgentState):
<span id="__span-3-5">    user_id: str
<span id="__span-3-6">
<span id="__span-3-7">def get_user_info(
<span id="__span-3-8">    state: Annotated[CustomState, InjectedState]
<span id="__span-3-9">) -&gt; str:
<span id="__span-3-10">    """Look up user info."""
<span id="__span-3-11">    user_id = state["user_id"]
<span id="__span-3-12">    return "User is John Smith" if user_id == "user_123" else "Unknown user"
<span id="__span-3-13">
<span id="__span-3-14">agent = create_react_agent(
<span id="__span-3-15">    model="anthropic:claude-3-7-sonnet-latest",
<span id="__span-3-16">    tools=[get_user_info],
<span id="__span-3-17">    state_schema=CustomState,
<span id="__span-3-18">)
<span id="__span-3-19">
<span id="__span-3-20">agent.invoke({
<span id="__span-3-21">    "messages": "look up user information",
<span id="__span-3-22">    "user_id": "user_123"
<span id="__span-3-23">})

有关更多信息,请参阅上下文指南。

要在执行期间修改代理的短期内存(状态),您可以直接从工具返回状态更新。这对于持久化中间结果或使信息可供后续工具或提示访问非常有用。

API参考:InjectedToolCallId | RunnableConfig | ToolMessage | InjectedState | create_react_agent | AgentState | Command

<span id="__span-4-1">from typing import Annotated
<span id="__span-4-2">from langchain_core.tools import InjectedToolCallId
<span id="__span-4-3">from langchain_core.runnables import RunnableConfig
<span id="__span-4-4">from langchain_core.messages import ToolMessage
<span id="__span-4-5">from langgraph.prebuilt import InjectedState, create_react_agent
<span id="__span-4-6">from langgraph.prebuilt.chat_agent_executor import AgentState
<span id="__span-4-7">from langgraph.types import Command
<span id="__span-4-8">
<span id="__span-4-9">class CustomState(AgentState):
<span id="__span-4-10">    user_name: str
<span id="__span-4-11">
<span id="__span-4-12">def update_user_info(
<span id="__span-4-13">    tool_call_id: Annotated[str, InjectedToolCallId],
<span id="__span-4-14">    config: RunnableConfig
<span id="__span-4-15">) -&gt; Command:
<span id="__span-4-16">    """Look up and update user info."""
<span id="__span-4-17">    user_id = config["configurable"].get("user_id")
<span id="__span-4-18">    name = "John Smith" if user_id == "user_123" else "Unknown user"
<span id="__span-4-19">    return Command(update={
<span id="__span-4-20">        "user_name": name,
<span id="__span-4-21">        # update the message history
<span id="__span-4-22">        "messages": [
<span id="__span-4-23">            ToolMessage(
<span id="__span-4-24">                "Successfully looked up user information",
<span id="__span-4-25">                tool_call_id=tool_call_id
<span id="__span-4-26">            )
<span id="__span-4-27">        ]
<span id="__span-4-28">    })
<span id="__span-4-29">
<span id="__span-4-30">def greet(
<span id="__span-4-31">    state: Annotated[CustomState, InjectedState]
<span id="__span-4-32">) -&gt; str:
<span id="__span-4-33">    """Use this to greet the user once you found their info."""
<span id="__span-4-34">    user_name = state["user_name"]
<span id="__span-4-35">    return f"Hello {user_name}!"
<span id="__span-4-36">
<span id="__span-4-37">agent = create_react_agent(
<span id="__span-4-38">    model="anthropic:claude-3-7-sonnet-latest",
<span id="__span-4-39">    tools=[update_user_info, greet],
<span id="__span-4-40">    state_schema=CustomState
<span id="__span-4-41">)
<span id="__span-4-42">
<span id="__span-4-43">agent.invoke(
<span id="__span-4-44">    {"messages": [{"role": "user", "content": "greet the user"}]},
<span id="__span-4-45">    config={"configurable": {"user_id": "user_123"}}
<span id="__span-4-46">)

有关更多详细信息,请参阅如何从工具更新状态

使用长期内存来跨对话存储用户特定或应用程序特定的数据。这对于聊天机器人等应用程序非常有用,您可能希望记住用户偏好或其他信息。

要使用长期内存,您需要:

  1. 配置一个存储以在调用之间持久化数据。
  2. 使用[get_store](https://langgraph.com.cn/reference/config/index.html#langgraph.config.get_store get_store")函数从工具或提示中访问存储。

代理可以用来查找用户信息的一个工具

<span id="__span-5-1">from langchain_core.runnables import RunnableConfig
<span id="__span-5-2">from langgraph.config import get_store
<span id="__span-5-3">from langgraph.prebuilt import create_react_agent
<span id="__span-5-4">from langgraph.store.memory import InMemoryStore
<span id="__span-5-5">
<span id="__span-5-6">store = InMemoryStore() 
<span id="__span-5-7">
<span id="__span-5-8">store.put(  
<span id="__span-5-9">    ("users",),  
<span id="__span-5-10">    "user_123",  
<span id="__span-5-11">    {
<span id="__span-5-12">        "name": "John Smith",
<span id="__span-5-13">        "language": "English",
<span id="__span-5-14">    } 
<span id="__span-5-15">)
<span id="__span-5-16">
<span id="__span-5-17">def get_user_info(config: RunnableConfig) -&gt; str:
<span id="__span-5-18">    """Look up user info."""
<span id="__span-5-19">    # Same as that provided to `create_react_agent`
<span id="__span-5-20">    store = get_store() 
<span id="__span-5-21">    user_id = config["configurable"].get("user_id")
<span id="__span-5-22">    user_info = store.get(("users",), user_id) 
<span id="__span-5-23">    return str(user_info.value) if user_info else "Unknown user"
<span id="__span-5-24">
<span id="__span-5-25">agent = create_react_agent(
<span id="__span-5-26">    model="anthropic:claude-3-7-sonnet-latest",
<span id="__span-5-27">    tools=[get_user_info],
<span id="__span-5-28">    store=store 
<span id="__span-5-29">)
<span id="__span-5-30">
<span id="__span-5-31"># Run the agent
<span id="__span-5-32">agent.invoke(
<span id="__span-5-33">    {"messages": [{"role": "user", "content": "look up user information"}]},
<span id="__span-5-34">    config={"configurable": {"user_id": "user_123"}}
<span id="__span-5-35">)

一个更新用户信息的工具示例

<span id="__span-6-1">from typing_extensions import TypedDict
<span id="__span-6-2">
<span id="__span-6-3">from langgraph.config import get_store
<span id="__span-6-4">from langgraph.prebuilt import create_react_agent
<span id="__span-6-5">from langgraph.store.memory import InMemoryStore
<span id="__span-6-6">
<span id="__span-6-7">store = InMemoryStore() 
<span id="__span-6-8">
<span id="__span-6-9">class UserInfo(TypedDict): 
<span id="__span-6-10">    name: str
<span id="__span-6-11">
<span id="__span-6-12">def save_user_info(user_info: UserInfo, config: RunnableConfig) -&gt; str: 
<span id="__span-6-13">    """Save user info."""
<span id="__span-6-14">    # Same as that provided to `create_react_agent`
<span id="__span-6-15">    store = get_store() 
<span id="__span-6-16">    user_id = config["configurable"].get("user_id")
<span id="__span-6-17">    store.put(("users",), user_id, user_info) 
<span id="__span-6-18">    return "Successfully saved user info."
<span id="__span-6-19">
<span id="__span-6-20">agent = create_react_agent(
<span id="__span-6-21">    model="anthropic:claude-3-7-sonnet-latest",
<span id="__span-6-22">    tools=[save_user_info],
<span id="__span-6-23">    store=store
<span id="__span-6-24">)
<span id="__span-6-25">
<span id="__span-6-26"># Run the agent
<span id="__span-6-27">agent.invoke(
<span id="__span-6-28">    {"messages": [{"role": "user", "content": "My name is John Smith"}]},
<span id="__span-6-29">    config={"configurable": {"user_id": "user_123"}} 
<span id="__span-6-30">)
<span id="__span-6-31">
<span id="__span-6-32"># You can access the store directly to get the value
<span id="__span-6-33">store.get(("users",), "user_123").value

LangGraph还允许您通过语义相似性在长期内存中搜索项目。

LangMem是一个LangChain维护的库,提供用于管理代理中长期内存的工具。请参阅LangMem文档以获取使用示例。