1. 概述与核心概念
1.1 什么是短期内存
短期内存是LangChain框架中的核心组件,用于记住单次会话或线程内的先前交互。对于AI智能体而言,内存系统至关重要,它使智能体能够记住之前的交互、从反馈中学习并适应用户偏好。
当智能体处理需要大量用户交互的复杂任务时,这种记忆能力对于效率提升和用户体验优化都不可或缺。短期内存特别关注线程级别的会话历史管理,确保每个独立对话都能保持连贯性。
1.2 上下文挑战与解决方案
当今大型语言模型面临一个重要挑战:长对话处理。当完整的历史记录超出LLM的上下文窗口限制时,会导致上下文丢失或生成错误。即使模型理论上支持更长的上下文,多数LLM在长上下文场景下仍存在性能问题——它们容易受到陈旧或无关内容的干扰,同时伴随着响应时间变慢和成本增加的问题。
聊天模型通过消息列表(包含系统指令和用户输入)接受上下文。在聊天应用中,人类输入和模型响应交替出现,导致消息列表随时间不断增长。由于上下文窗口有限,许多应用需要使用特殊技术来移除或"遗忘"陈旧信息。
2. 基本使用与配置
2.1 核心配置:检查点机制
要为智能体添加短期内存(线程级持久化),您需要在创建智能体时指定检查点器(checkpointer)。LangChain通过智能体状态图来管理短期内存,并将其存储在智能体状态中,使智能体能访问完整对话上下文,同时保持不同线程的隔离性。
基础内存实现示例:
fromlangchain.agentsimportcreate_agentfromlanggraph.checkpoint.memoryimportInMemorySaver agent=create_agent("gpt-5",# 模型名称tools=[get_user_info],# 可用工具checkpointer=InMemorySaver(),# 内存检查点器)agent.invoke({"messages":[{"role":"user","content":"Hi! My name is Bob."}]},{"configurable":{"thread_id":"1"}},# 线程标识)状态更新发生在智能体调用或步骤(如工具调用)完成时,状态读取则发生在每个步骤开始时。
2.2 生产环境配置
在生产环境中,应使用数据库支持的检查点器以确保持久化和可扩展性:
pip install langgraph-checkpoint-postgresfromlangchain.agentsimportcreate_agentfromlanggraph.checkpoint.postgresimportPostgresSaver DB_URI="postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"withPostgresSaver.from_conn_string(DB_URI)ascheckpointer:checkpointer.setup()# 自动在PostgreSQL中创建表agent=create_agent("gpt-5",tools=[get_user_info],checkpointer=checkpointer,)3. 自定义智能体内存
3.1 扩展AgentState
默认情况下,智能体使用AgentState管理短期内存,特别是通过messages键管理对话历史。您可以扩展AgentState以添加额外字段,自定义状态模式通过state_schema参数传递给create_agent:
fromlangchain.agentsimportcreate_agent,AgentStatefromlanggraph.checkpoint.memoryimportInMemorySaverclassCustomAgentState(AgentState):user_id:strpreferences:dictagent=create_agent("gpt-5",tools=[get_user_info],state_schema=CustomAgentState,checkpointer=InMemorySaver(),)# 可以在invoke中传递自定义状态result=agent.invoke({"messages":[{"role":"user","content":"Hello"}],"user_id":"user_123","preferences":{"theme":"dark"}},{"configurable":{"thread_id":"1"}})4. 内存管理策略
4.1 消息修剪策略
当长对话超过LLM的上下文窗口时,最常见的解决方案之一是修剪消息。LLM通常有最大支持的上下文窗口(以令牌数计算)。决定何时截断消息的一种方法是统计消息历史中的令牌数,并在接近限制时截断。
使用@before_model中间件装饰器修剪消息历史:
fromlangchain.messagesimportRemoveMessagefromlanggraph.graph.messageimportREMOVE_ALL_MESSAGESfromlanggraph.checkpoint.memoryimportInMemorySaverfromlangchain.agentsimportcreate_agent,AgentStatefromlangchain.agents.middlewareimportbefore_modelfromlanggraph.runtimeimportRuntimefromlangchain_core.runnablesimportRunnableConfigfromtypingimportAny@before_modeldeftrim_messages(state:AgentState,runtime:Runtime)->dict[str,Any]|None:"""仅保留最近几条消息以适应上下文窗口"""messages=state["messages"]iflen(messages)<=3:returnNone# 无需更改first_msg=messages[0]recent_messages=messages[-3:]iflen(messages)%2==0elsemessages[-4:]new_messages=[first_msg]+recent_messagesreturn{"messages":[RemoveMessage(id=REMOVE_ALL_MESSAGES),*new_messages]}agent=create_agent(your_model_here,tools=your_tools_here,middleware=[trim_messages],checkpointer=InMemorySaver(),)4.2 消息删除策略
您可以从图状态中删除消息来管理消息历史。这对于想要移除特定消息或清除整个消息历史的情况很有用。要删除消息,需要使用具有add_messages减速器的状态键。
删除特定消息:
fromlangchain.messagesimportRemoveMessagedefdelete_messages(state):messages=state["messages"]iflen(messages)>2:# 移除最早的两条消息return{"messages":[RemoveMessage(id=m.id)forminmessages[:2]]}删除所有消息:
fromlanggraph.graph.messageimportREMOVE_ALL_MESSAGESdefdelete_messages(state):return{"messages":[RemoveMessage(id=REMOVE_ALL_MESSAGES)]}4.3 消息摘要策略
修剪或删除消息可能导致信息丢失,因此某些应用受益于使用聊天模型总结消息历史的更复杂方法:
fromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportSummarizationMiddlewarefromlanggraph.checkpoint.memoryimportInMemorySaverfromlangchain_core.runnablesimportRunnableConfig checkpointer=InMemorySaver()agent=create_agent(model="gpt-4o",tools=[],middleware=[SummarizationMiddleware(model="gpt-4o-mini",# 用于摘要的模型trigger=("tokens",4000),# 触发条件keep=("messages",20)# 保留的消息数)],checkpointer=checkpointer,)5. 访问与修改内存
5.1 工具中访问短期内存
通过runtime参数(类型为ToolRuntime)在工具中访问短期内存(状态)。该参数对工具签名隐藏(因此模型看不到它),但工具可以通过它访问状态:
fromlangchain.agentsimportcreate_agent,AgentStatefromlangchain.toolsimporttool,ToolRuntimeclassCustomState(AgentState):user_id:str@tooldefget_user_info(runtime:ToolRuntime)->str:"""查找用户信息"""user_id=runtime.state["user_id"]return"User is John Smith"ifuser_id=="user_123"else"Unknown user"5.2 从工具写入短期内存
要在执行过程中修改智能体的短期内存(状态),可以直接从工具返回状态更新。这对于持久化中间结果或使信息对后续工具或提示可用非常有用:
fromlangchain.toolsimporttool,ToolRuntimefromlangchain_core.runnablesimportRunnableConfigfromlangchain.messagesimportToolMessagefromlangchain.agentsimportcreate_agent,AgentStatefromlanggraph.typesimportCommandfrompydanticimportBaseModelclassCustomState(AgentState):user_name:strclassCustomContext(BaseModel):user_id:str@tooldefupdate_user_info(runtime:ToolRuntime[CustomContext,CustomState],)->Command:"""查找并更新用户信息"""user_id=runtime.context.user_id name="John Smith"ifuser_id=="user_123"else"Unknown user"returnCommand(update={"user_name":name,# 更新消息历史"messages":[ToolMessage("成功查找用户信息",tool_call_id=runtime.tool_call_id)]})6. 最佳实践与注意事项
6.1 消息历史有效性检查
删除消息时,请确保结果消息历史是有效的。检查您使用的LLM提供程序的限制,例如:
- 某些提供程序期望消息历史以用户消息开始
- 大多数提供程序要求包含工具调用的助手消息后必须跟随相应的工具结果消息
6.2 生产环境建议
- 选择合适的检查点器:开发环境可使用内存检查点器,生产环境应选择PostgreSQL或其他数据库支持的检查点器
- 监控内存使用情况:定期检查消息历史长度,防止超过模型上下文限制
- 实现适当的清理策略:根据应用需求选择修剪、删除或摘要策略
- 线程隔离:确保不同线程间的状态完全隔离,避免数据混淆
- 错误恢复机制:检查点系统应支持异常情况下的状态恢复
6.3 性能优化建议
- 批量操作:尽量减少状态更新频率,使用批量更新提高性能
- 异步处理:长时间运行的操作应使用异步机制
- 缓存策略:对频繁访问的状态数据实现缓存机制
- 定期归档:将历史对话归档到长期存储,减少活动状态数据量
结论
LangChain的短期内存系统为构建持久化、可扩展的对话应用提供了强大基础。通过合理的配置和策略选择,您可以克服LLM上下文限制,创建出真正具有记忆能力和连贯性的智能体应用。
关键成功因素包括:正确的检查点器选择、合适的内存管理策略、有效的状态访问模式以及生产环境下的监控和维护机制。随着应用复杂性增加,这些短期内存管理技术将变得更加重要。