跳到内容

内存

LangGraph 支持构建对话代理所需的两种基本内存类型

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

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

image

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

术语

在 LangGraph 中

  • 短期记忆 也被称为 线程级别记忆
  • 长期记忆 也被称为 跨线程记忆

一个 线程 代表按相同的 thread_id 分组的一系列相关运行。

短期记忆

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

  1. 创建代理时提供一个 checkpointercheckpointer 能够持久化代理的状态。
  2. 运行代理时在配置中提供一个 thread_idthread_id 是会话的唯一标识符。

API 参考:create_react_agent | InMemorySaver

from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver() # (1)!


def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"


agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_weather],
    checkpointer=checkpointer # (2)!
)

# Run the agent
config = {
    "configurable": {
        "thread_id": "1"  # (3)!
    }
}

sf_response = agent.invoke(
    {"messages": [{"role": "user", "content": "what is the weather in sf"}]},
    config
)

# Continue the conversation using the same thread_id
ny_response = agent.invoke(
    {"messages": [{"role": "user", "content": "what about new york?"}]},
    config # (4)!
)
  1. InMemorySaver 是一个将代理状态存储在内存中的 checkpointer。在生产环境中,您通常会使用数据库或其他持久化存储。请查阅checkpointer 文档以了解更多选项。如果您正在使用 LangGraph Platform 进行部署,该平台将为您提供生产就绪的 checkpointer。
  2. checkpointer 被传递给代理。这使代理能够跨调用持久化其状态。请注意
  3. 在配置中提供了一个唯一的 thread_id。此 ID 用于标识会话。该值由用户控制,可以是任何字符串。
  4. 代理将使用相同的 thread_id 继续对话。这将允许代理推断用户正在专门询问纽约的天气

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

LangGraph Platform 提供生产就绪的 checkpointer

如果您正在使用LangGraph Platform,在部署期间,您的 checkpointer 将被自动配置为使用生产就绪的数据库。

管理消息历史记录

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

  • 总结:维护对话的持续摘要
  • 修剪:删除历史记录中的前 N 条或后 N 条消息

这使得代理可以在不超出 LLM 上下文窗口的情况下跟踪对话。

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

总结消息历史记录

image

长对话可能会超出 LLM 的上下文窗口。一个常见的解决方案是维护对话的持续摘要。这使得代理可以在不超出 LLM 上下文窗口的情况下跟踪对话。

要总结消息历史记录,您可以将 pre_model_hook 与预构建的 SummarizationNode 一起使用

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

from langchain_anthropic import ChatAnthropic
from langmem.short_term import SummarizationNode
from langchain_core.messages.utils import count_tokens_approximately
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.checkpoint.memory import InMemorySaver
from typing import Any

model = ChatAnthropic(model="claude-3-7-sonnet-latest")

summarization_node = SummarizationNode( # (1)!
    token_counter=count_tokens_approximately,
    model=model,
    max_tokens=384,
    max_summary_tokens=128,
    output_messages_key="llm_input_messages",
)

class State(AgentState):
    # NOTE: we're adding this key to keep track of previous summary information
    # to make sure we're not summarizing on every LLM call
    context: dict[str, Any]  # (2)!


checkpointer = InMemorySaver() # (3)!

agent = create_react_agent(
    model=model,
    tools=tools,
    pre_model_hook=summarization_node, # (4)!
    state_schema=State, # (5)!
    checkpointer=checkpointer,
)
  1. InMemorySaver 是一个将代理状态存储在内存中的 checkpointer。在生产环境中,您通常会使用数据库或其他持久化存储。请查阅checkpointer 文档以了解更多选项。如果您正在使用 LangGraph Platform 进行部署,该平台将为您提供生产就绪的 checkpointer。
  2. context 键被添加到代理的状态中。该键包含用于总结节点的簿记信息。它用于跟踪上次总结信息,并确保代理不会在每次调用 LLM 时都进行总结,因为这可能效率低下。
  3. checkpointer 被传递给代理。这使代理能够跨调用持久化其状态。
  4. pre_model_hook 被设置为 SummarizationNode。此节点将在将消息历史记录发送给 LLM 之前对其进行总结。总结节点将自动处理总结过程,并使用新的总结更新代理的状态。如果您愿意,可以用自定义实现替换它。请参阅 create_react_agent API 参考了解更多详细信息。
  5. state_schema 被设置为 State 类,这是一个包含额外 context 键的自定义状态。

修剪消息历史记录

要修剪消息历史记录,您可以将 pre_model_hooktrim_messages 函数一起使用

API 参考:trim_messages | count_tokens_approximately | create_react_agent

from langchain_core.messages.utils import (
    trim_messages,
    count_tokens_approximately
)
from langgraph.prebuilt import create_react_agent

# This function will be called every time before the node that calls LLM
def pre_model_hook(state):
    trimmed_messages = trim_messages(
        state["messages"],
        strategy="last",
        token_counter=count_tokens_approximately,
        max_tokens=384,
        start_on="human",
        end_on=("human", "tool"),
    )
    return {"llm_input_messages": trimmed_messages}

checkpointer = InMemorySaver()
agent = create_react_agent(
    model,
    tools,
    pre_model_hook=pre_model_hook,
    checkpointer=checkpointer,
)

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

在工具中读取

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

API 参考:InjectedState | create_react_agent

from typing import Annotated
from langgraph.prebuilt import InjectedState, create_react_agent

class CustomState(AgentState):
    user_id: str

def get_user_info(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """Look up user info."""
    user_id = state["user_id"]
    return "User is John Smith" if user_id == "user_123" else "Unknown user"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_user_info],
    state_schema=CustomState,
)

agent.invoke({
    "messages": "look up user information",
    "user_id": "user_123"
})

请参阅上下文指南了解更多信息。

从工具写入

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

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

from typing import Annotated
from langchain_core.tools import InjectedToolCallId
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import ToolMessage
from langgraph.prebuilt import InjectedState, create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState
from langgraph.types import Command

class CustomState(AgentState):
    user_name: str

def update_user_info(
    tool_call_id: Annotated[str, InjectedToolCallId],
    config: RunnableConfig
) -> Command:
    """Look up and update user info."""
    user_id = config["configurable"].get("user_id")
    name = "John Smith" if user_id == "user_123" else "Unknown user"
    return Command(update={
        "user_name": name,
        # update the message history
        "messages": [
            ToolMessage(
                "Successfully looked up user information",
                tool_call_id=tool_call_id
            )
        ]
    })

def greet(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """Use this to greet the user once you found their info."""
    user_name = state["user_name"]
    return f"Hello {user_name}!"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_user_info, greet],
    state_schema=CustomState
)

agent.invoke(
    {"messages": [{"role": "user", "content": "greet the user"}]},
    config={"configurable": {"user_id": "user_123"}}
)

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

长期记忆

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

要使用长期记忆,您需要

  1. 配置一个存储来持久化跨调用的数据。
  2. 使用 get_store 函数从工具或提示内部访问存储。

读取

代理可以用来查找用户信息的一个工具
from langchain_core.runnables import RunnableConfig
from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore

store = InMemoryStore() # (1)!

store.put(  # (2)!
    ("users",),  # (3)!
    "user_123",  # (4)!
    {
        "name": "John Smith",
        "language": "English",
    } # (5)!
)

def get_user_info(config: RunnableConfig) -> str:
    """Look up user info."""
    # Same as that provided to `create_react_agent`
    store = get_store() # (6)!
    user_id = config["configurable"].get("user_id")
    user_info = store.get(("users",), user_id) # (7)!
    return str(user_info.value) if user_info else "Unknown user"

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[get_user_info],
    store=store # (8)!
)

# Run the agent
agent.invoke(
    {"messages": [{"role": "user", "content": "look up user information"}]},
    config={"configurable": {"user_id": "user_123"}}
)
  1. InMemoryStore 是一个将数据存储在内存中的存储。在生产环境中,您通常会使用数据库或其他持久化存储。请查阅store 文档以了解更多选项。如果您正在使用 LangGraph Platform 进行部署,该平台将为您提供生产就绪的存储。
  2. 对于此示例,我们使用 put 方法向存储写入一些示例数据。请参阅 BaseStore.put API 参考了解更多详细信息。
  3. 第一个参数是命名空间。它用于将相关数据分组。在本例中,我们使用 users 命名空间来分组用户数据。
  4. 命名空间内的键。本例使用用户 ID 作为键。
  5. 我们要为给定用户存储的数据。
  6. get_store 函数用于访问存储。您可以在代码的任何地方调用它,包括工具和提示。此函数返回创建代理时传递给代理的存储。
  7. get 方法用于从存储中检索数据。第一个参数是命名空间,第二个参数是键。这将返回一个 StoreValue 对象,其中包含值和有关值的元数据。
  8. store 被传递给代理。这使得代理在运行工具时可以访问存储。您还可以使用 get_store 函数从代码的任何地方访问存储。

写入

一个更新用户信息的工具示例
from typing_extensions import TypedDict

from langgraph.config import get_store
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore

store = InMemoryStore() # (1)!

class UserInfo(TypedDict): # (2)!
    name: str

def save_user_info(user_info: UserInfo, config: RunnableConfig) -> str: # (3)!
    """Save user info."""
    # Same as that provided to `create_react_agent`
    store = get_store() # (4)!
    user_id = config["configurable"].get("user_id")
    store.put(("users",), user_id, user_info) # (5)!
    return "Successfully saved user info."

agent = create_react_agent(
    model="anthropic:claude-3-7-sonnet-latest",
    tools=[save_user_info],
    store=store
)

# Run the agent
agent.invoke(
    {"messages": [{"role": "user", "content": "My name is John Smith"}]},
    config={"configurable": {"user_id": "user_123"}} # (6)!
)

# You can access the store directly to get the value
store.get(("users",), "user_123").value
  1. InMemoryStore 是一个将数据存储在内存中的存储。在生产环境中,您通常会使用数据库或其他持久化存储。请查阅store 文档以了解更多选项。如果您正在使用 LangGraph Platform 进行部署,该平台将为您提供生产就绪的存储。
  2. UserInfo 类是一个 TypedDict,它定义了用户信息结构。LLM 将使用它根据模式格式化响应。
  3. save_user_info 函数是一个工具,允许代理更新用户信息。这对于用户想要更新其个人资料信息的聊天应用程序可能非常有用。
  4. get_store 函数用于访问存储。您可以在代码的任何地方调用它,包括工具和提示。此函数返回创建代理时传递给代理的存储。
  5. put 方法用于在存储中存储数据。第一个参数是命名空间,第二个参数是键。这将在存储中存储用户信息。
  6. user_id 在配置中传递。它用于标识正在更新其信息的用户。

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

预构建的内存工具

LangMem 是 LangChain 维护的一个库,它提供了用于管理代理中长期记忆的工具。请参阅LangMem 文档了解使用示例。

额外资源

评论