Skip to content

内存

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

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

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

image

无论是 短期 还是 长期 内存,都需要持久化存储以维持 LLM 交互之间的连续性。在生产环境中,这些数据通常存储在数据库中。

术语

在 LangGraph 中:

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

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

短期记忆

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

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

API Reference: 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:
    """获取给定城市的天气信息。"""
    return f"{city} 的天气总是晴朗!"


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

# 运行代理
config = {
    "configurable": {
        "thread_id": "1"  # (3)
    }
}

sf_response = agent.invoke(
    {"messages": [{"role": "user", "content": "旧金山的天气如何"}]},
    config
)

# 使用相同的 thread_id 继续对话
ny_response = agent.invoke(
    {"messages": [{"role": "user", "content": "纽约呢?"}]},
    config # (4)
)
  1. InMemorySaver 是一个检查点存储器,它将代理的状态存储在内存中。在生产环境中,通常会使用数据库或其他持久存储。请查看 检查点文档 获取更多选项。如果你使用的是 LangGraph 平台,平台将为你提供一个生产就绪的检查点存储器。
  2. checkpointer 传递给代理。这使得代理可以在调用之间持久化其状态。
  3. 配置中提供了唯一的 thread_id。这个 ID 用于识别会话。值由用户控制,可以是任何字符串。
  4. 代理将使用相同的 thread_id 继续对话。这将允许代理推断出用户正在询问 天气 在纽约的具体情况。

当代理第二次使用相同的 thread_id 被调用时,第一次对话的消息历史记录将自动包含在内,使代理能够推断出用户正在询问 天气 在纽约的具体情况。

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

如果你使用 LangGraph 平台,在部署期间你的检查点存储器将被自动配置为使用一个生产就绪的数据库。

管理消息历史

长时间的对话可能会超出 LLM 的上下文窗口。常见的解决方案包括:

  • 摘要: 保持对话的滚动摘要
  • 修剪: 删除历史中的前 N 条或后 N 条消息

这允许代理在不超出 LLM 上下文窗口的情况下跟踪对话。

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

摘要消息历史

image

长时间的对话可能会超出 LLM 的上下文窗口。一个常见解决方案是保持对话的滚动摘要。这允许代理在不超出 LLM 上下文窗口的情况下跟踪对话。

要摘要消息历史,你可以使用 pre_model_hook 和预构建的 SummarizationNode:

API Reference: 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: 我们添加这个键来跟踪之前的摘要信息
    # 以确保我们不是每次 LLM 调用都进行摘要
    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 是一个检查点存储器,它将代理的状态存储在内存中。在生产环境中,通常会使用数据库或其他持久存储。请查看 检查点文档 获取更多选项。如果你使用的是 LangGraph 平台,平台将为你提供一个生产就绪的检查点存储器。
  2. 添加了 context 键到代理的状态中。该键包含摘要节点的簿记信息。它用于跟踪最后的摘要信息,并确保代理不会在每次 LLM 调用时都进行摘要,这可能效率不高。
  3. checkpointer 传递给代理。这使得代理可以在调用之间持久化其状态。
  4. 设置 pre_model_hookSummarizationNode。此节点将在将消息历史发送给 LLM 之前对其进行摘要。摘要节点将自动处理摘要过程并更新代理的状态以包含新的摘要。如果需要,你可以将其替换为自定义实现。请参阅 create_react_agent API 参考以获取更多细节。
  5. state_schema 设置为 State 类,这是包含额外 context 键的自定义状态。

修剪消息历史

要修剪消息历史,你可以使用 pre_model_hooktrim_messages 函数:

API Reference: 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

# 每次在调用 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 Reference: 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:
    """查找用户信息。"""
    user_id = state["user_id"]
    return "用户是 John Smith" if user_id == "user_123" else "未知用户"

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

agent.invoke({
    "messages": "查找用户信息",
    "user_id": "user_123"
})

有关更多信息,请参阅 Context 指南。

写入工具

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

API Reference: 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:
    """查找并更新用户信息。"""
    user_id = config["configurable"].get("user_id")
    name = "John Smith" if user_id == "user_123" else "Unknown user"
    return Command(update={
        "user_name": name,
        # 更新消息历史
        "messages": [
            ToolMessage(
                "成功查找了用户信息",
                tool_call_id=tool_call_id
            )
        ]
    })

def greet(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """一旦找到用户信息,使用此方法向用户打招呼。"""
    user_name = state["user_name"]
    return f"你好 {user_name}!"

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

agent.invoke(
    {"messages": [{"role": "user", "content": "问候用户"}]},
    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:
    """查找用户信息."""
    # 与提供给 `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)!
)

# 运行代理
agent.invoke(
    {"messages": [{"role": "user", "content": "look up user information"}]},
    config={"configurable": {"user_id": "user_123"}}
)
  1. InMemoryStore 是一个将数据存储在内存中的存储。在生产环境中,通常会使用数据库或其他持久化存储。请查看 存储文档 以获取更多选项。如果您使用 **LangGraph 平台**进行部署,平台将为您提供一个生产就绪的存储。
  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)!
    """保存用户信息."""
    # 与提供给 `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
)

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

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

语义搜索

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

预构建的记忆工具

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

其他资源