第 5 章:记忆与持久化:管理会话状态
有效的记忆管理能让智能体在多轮对话和跨会话场景中表现得更连贯与可靠。
记忆系统的作用与价值
记忆系统对于智能体至关重要,它使其能够记住过去的交互,适应用户偏好,并从反馈中学习。随着智能体处理更复杂的任务和更多的用户交互,这种能力对于效率和用户满意度变得必不可少。
短时记忆(Short-term Memory):对话历史的维持
短时记忆允许应用程序记住单个线程或对话中的先前交互。其核心形式是对话历史,智能体通过消息状态(message state)自动维护对话历史,信息存储在状态(State)中,这可以被视为智能体的短时记忆。
默认情况下,智能体使用 AgentState 来管理短时记忆,特别是通过一个名为 messages 的键来管理对话历史。
使用 Checkpointer 实现线程级持久性
为了给智能体添加短时记忆(即线程级别的持久性),需要在创建智能体时指定一个 Checkpointer。Checkpointer 负责保存和恢复智能体执行的状态。
- 生产环境建议使用由数据库支持的 Checkpointer,以实现持久性。
- 测试期间可以使用
InMemorySaverCheckpointer 来模拟多轮交互,测试依赖于状态的行为。 - LangGraph 的持久性层用于安全地暂停执行并保存图状态,例如在人工参与(HITL, Human-in-the-Loop)中断时。
以下代码展示了设置记忆的典型步骤:
在实际应用中,可以通过如下方式为智能体配置 Checkpointer:
checkpointer = InMemorySaver()
# ...
agent = create_agent(
# ... 其他配置
checkpointer=checkpointer
)
通过使用相同的 thread_id,智能体可以继续对话并维持会话状态。
自定义状态(State)模式:扩展智能体的记忆
虽然默认的 AgentState 管理对话历史,但可以扩展它以记住对话期间的额外信息。自定义状态模式必须继承 AgentState 作为 TypedDict。
扩展方式主要有两种:
- 通过中间件(Middleware):推荐方式,适用于自定义状态需要被特定中间件钩子和附加到该中间件的工具访问的场景。
- 通过
state_schema参数:在create_agent上定义自定义状态的快捷方式,主要用于只在工具中使用的自定义状态。
状态(State)或短时记忆是对话范围的数据,在智能体执行过程中是可变的(Mutable),可以被读取和修改。它包含当前消息、上传的文件、身份验证状态、工具结果等。
工具可以通过 ToolRuntime 参数访问短时记忆(状态):
- 读取:工具可以使用
ToolRuntime访问当前图状态。 - 写入:工具可以通过返回状态更新来修改智能体的短时记忆。这对于持久化中间结果或使信息可用于后续步骤非常有用。
上下文窗口管理挑战与解决方案
长对话对当前的大语言模型(LLM, Large Language Model)构成了挑战。完整的历史记录可能不适合 LLM 的上下文窗口,导致上下文丢失或错误。此外,即使模型支持完整的上下文长度,在长上下文中,模型性能仍可能下降,响应时间变慢,成本变高。
短时记忆启用后,长对话可能会超出 LLM 的上下文窗口。常见的解决方案包括移除或“遗忘”陈旧的信息。
消息截断(Trim Messages):基于 Token 限制的删除
一种管理上下文窗口的方法是截断消息。其机制是计算消息历史中的 Token 数量,并在接近限制时进行截断。可以指定要保留的 Token 数量以及用于处理边界的策略(如保留最后的 max_tokens)。
在智能体中截断消息历史,可以使用 @before_model 中间件装饰器实现。
消息摘要(Summarize Messages):保留关键信息
截断或移除消息可能会导致信息丢失。更复杂的方法是使用聊天模型对消息历史进行摘要(Summarizing)。
- 摘要中间件使用一个单独的 LLM 调用对旧消息进行总结,并用该摘要信息永久性地替换状态中的原始消息。
- LangChain 提供了内置的
SummarizationMiddleware,可在接近 Token 限制时自动摘要对话历史。 - 摘要属于生命周期上下文的一部分,属于持久性更新——它会永久地用摘要信息替换状态中的旧消息。未来的轮次将看到摘要而不是原始消息。
下方流程图展示了上下文管理的简化流程:
长期记忆(Store):跨会话数据的存储与访问
长期记忆(Store)用于持久化跨对话的用户偏好、提取的洞察、记忆或历史数据。LangGraph 将长期记忆存储为 JSON 文档。
每个记忆都组织在一个自定义命名空间(类似文件夹)和一个独特的键(类似文件名)之下。命名空间通常包括用户 ID 或组织 ID。
长期记忆(Store)通过运行时对象(Runtime)进行访问:
- 在工具内读取/写入:工具可以通过
ToolRuntime.store访问和更新长期存储。 ToolRuntime提供了对Store的访问权限。
长期存储的访问和更新示例:
# 访问长期记忆 (Store)
user_data = runtime.store.get("user_preferences", user_id)
# 写入长期记忆 (Store)
runtime.store.set("user_preferences", user_id, updated_data)
总结
合理设计记忆系统与持久化机制,能显著提升智能体的连贯性、可维护性和用户体验。短时记忆与长期记忆结合,配合灵活的状态管理与上下文窗口优化,是高效智能体架构的基础。