https://item.jd.com/15350592.html
在LangGraph构建的AI智能体系统中,记忆模组是支撑智能体实现"类人交互"的核心组件,它打破了传统对话模型"单次交互即失忆"的局限,让智能体能够通过记录历史信息、沉淀经验知识,实现连贯的多轮对话与复杂任务处理。不同于简单的信息缓存,LangGraph的记忆模组是一套结构化的系统架构,其核心设计思路围绕智能体的交互场景需求,精准区分了短期记忆与长期记忆两大核心类型,确保信息的存储、调用与更新既高效又贴合实际应用场景。
短期记忆与长期记忆的协同工作构成了LangGraph智能体记忆能力的基础框架。短期记忆聚焦于当前会话的即时信息管理,如同智能体在单一对话线程中的"即时记事本",实时跟踪交互过程中的上下文动态;而长期记忆则承担着跨场景、跨会话的信息沉淀功能,像是智能体的"永久知识库",让分散对话中的关键信息得以留存和复用。这两种记忆类型并非孤立存在,而是通过LangGraph的结构化设计形成互补,共同为智能体的决策与响应提供全面的信息支撑。
作为智能体状态系统的重要组成部分,记忆模组的设计直接决定了智能体的交互连贯性与任务适应性。短期记忆通过与智能体状态深度绑定实现实时更新与持久化,确保当前对话的上下文不中断;长期记忆则借助灵活的命名空间与存储组件,打破对话线程的限制,让用户偏好、任务规则等核心信息能够跨场景调用。深入理解这套记忆架构的设计逻辑与实现方式,是掌握LangGraph智能体开发的关键环节。
6.1.1 基于内存的短期记忆
基于内存的短期记忆是LangGraph中短期记忆最基础、最常用的实现形式,其核心特征是将记忆数据直接存储在智能体运行进程的内存中,完全依赖当前会话的生命周期完成数据的创建、更新与销毁。这种设计模式天然契合短期记忆"即时性"与"临时性"的需求,无须依赖外部数据库等存储介质,从根本上保障了信息读取与写入的极低延迟,让智能体能够快速调用上下文信息响应用户需求。
首先,我们使用一个无记忆功能的智能体来帮助用户完成任务,代码如下:
from langchain_core.tools import tool
1. 定义工具
@tool("get_weather_info")
def get_weather_info(city: str) -> str:
"""根据城市名称查询当前天气状况(含温度、天气状况)"""
weather_data = {
"上海": "城市:上海 | 天气:晴 | 温度:18℃ | 风向:东南风 | 湿度:65%",
"南京": "城市:南京 | 天气:多云 | 温度:15℃ | 风向:北风 | 湿度:70%",
"杭州": "城市:杭州 | 天气:小雨 | 温度:12℃ | 风向:西北风 | 湿度:80%",
}
return weather_data.get(city, f"未查询到{city}的天气信息")
@tool
def get_weather_tips(city: str) -> str:
"""根据城市名称返回天气相关生活建议(不包含温度和天气状况)"""
tips_data = {
"上海": "天气晴朗,适合户外活动;紫外线较强,注意防晒;早晚温差大",
"南京": "多云天气,适宜出行;风力不大,适合晾晒衣物;空气较干燥",
"杭州": "有小雨,出门记得带伞;路面湿滑,注意行车安全;气温较低,适当添衣",
}
return tips_data.get(city, f"暂无{city}的相关天气建议")
tools = [get_weather_info,get_weather_tips]
import bigmodel
llm = bigmodel.llm
from langchain.messages import AIMessage
from langchain.agents.middleware.types import AgentMiddleware, AgentState, ModelResponse
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=tools,
system_prompt="你是一个有用的智能体,可以借助工具帮助用户完成任务。",
)
def chatbot(state:bigmodel.BaseAgentState):
messages = state.messages
reply = agent.invoke({"messages": messages})
reply_content = (reply["messages"][-1].content)
return {"messages": reply_content}
from langgraph.graph import StateGraph,add_messages
graph_builder = StateGraph(bigmodel.BaseAgentState)
graph_builder.add_node(chatbot)
graph_builder.set_entry_point("chatbot")
graph = graph_builder.compile()
if name == 'main':
inps = ["帮我查一下南京的天气","你能给什么建议吗?"]
for inp in inps:
reply = graph.invoke(input={"messages": inp})
print(reply["messages"][-1].content)
print("-"*50)
输出结果如下:
南京当前天气为多云,温度是15℃,风向为北风,湿度为70%。
请告诉我您所在的城市或您关心的城市名称,这样我才能为您提供相关的天气建议!
在上面的输出结果中,我们仅提供了对当前内容的查询,而没有加载前期的查询内容,即使当前的智能体运行在同一个状态空间State中。
这显然不是我们所需要的,想要解决这个问题,我们需要提供一个额外的内存空间。在LangGraph的开发框架中,基于内存的短期记忆通常与智能体的"状态(State)"紧密绑定,通过定义结构化的状态类来承载记忆数据,即作为一个载体对当前状态空间的内容建立整体联系,从而使得智能体具有一个"短期记忆"。
例如,开发者可以在State中声明一个"对话历史"列表字段,每当智能体完成一次交互(接收用户输入、生成响应),就会通过状态更新逻辑将本次交互的关键信息(如用户提问内容、智能体回答要点、交互时间戳等)追加到该列表中。当智能体需要生成下一次响应时,只需从当前状态的"对话历史"字段中读取数据,即可快速掌握前序交互脉络,避免出现"上下文断裂"的问题。
下面是一个添加记忆功能到智能体的示例,代码如下:
...
from langgraph.checkpoint.memory import InMemorySaver
memory = InMemorySaver()
graph = graph_builder.compile(checkpointer=memory)
...
此时可以看到,我们在智能体图的构建过程中,主动传入了一个初始化的memory,这是基于内存的存储空间,供智能体上下文使用。完整代码如下:
from langchain_core.tools import tool
1. 定义工具
@tool
def get_weather_info(city: str) -> str:
"""根据城市名称查询当前天气状况(含温度、天气状况)"""
weather_data = {
"上海": "城市:上海 | 天气:晴 | 温度:18℃ | 风向:东南风 | 湿度:65%",
"南京": "城市:南京 | 天气:多云 | 温度:15℃ | 风向:北风 | 湿度:70%",
"杭州": "城市:杭州 | 天气:小雨 | 温度:12℃ | 风向:西北风 | 湿度:80%",
}
return weather_data.get(city, f"未查询到{city}的天气信息")
@tool
def get_weather_tips(city: str) -> str:
"""根据城市名称返回天气相关生活建议(不包含温度和天气状况)"""
tips_data = {
"上海": "天气晴朗,适合户外活动;紫外线较强,注意防晒;早晚温差大",
"南京": "多云天气,适宜出行;风力不大,适合晾晒衣物;空气较干燥",
"杭州": "有小雨,出门记得带伞;路面湿滑,注意行车安全;气温较低,适当添衣",
}
return tips_data.get(city, f"暂无{city}的相关天气建议")
tools = [get_weather_info,get_weather_tips]
import bigmodel
llm = bigmodel.llm
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=tools,
system_prompt="你是一个有用的智能体,可以借助工具帮助用户完成任务。",
)
def chatbot(state:bigmodel.BaseAgentState):
messages = state.messages
reply = agent.invoke({"messages": messages})
reply_content = (reply["messages"][-1].content)
return {"messages": reply_content}
from langgraph.graph import StateGraph,add_messages
graph_builder = StateGraph(bigmodel.BaseAgentState)
graph_builder.add_node(chatbot)
graph_builder.set_entry_point("chatbot")
from langgraph.checkpoint.memory import InMemorySaver
memory = InMemorySaver()
graph = graph_builder.compile(checkpointer=memory)
if name == 'main':
config = {
"configurable": {
"thread_id": "wx929" # 每个用户/对话用唯一ID,多轮对话依赖此保存历史
}
}
inps = ["帮我查一下南京的天气","你能给什么建议吗?"]
for inp in inps:
reply = graph.invoke(input={"messages": inp},config=config)
print(reply["messages"][-1].content)
print("-"*50)
输出结果如下:
南京当前天气为多云,温度是15℃,风向为北风,湿度为70%。
根据南京当前的天气情况,以下是一些建议:
-
多云天气适宜出行,可以安排户外活动。
-
风力不大,适合晾晒衣物。
-
空气较干燥,注意适当补水,保持皮肤湿润。
对比可以看到,我们通过在智能体的构建过程中显式地传入一个基于内存的存储空间,可以对当前对话进行保留,从而使得智能体具有一个短期的记忆功能。
可以看到,这种记忆形式的优势不仅体现在高效性上,还在于其极强的易用性和灵活性。对于快速原型开发、简单对话场景或轻量级智能体而言,基于内存的短期记忆无须额外配置存储服务,开发者仅需通过几行代码定义状态结构和记忆更新规则,就能实现基础的上下文管理功能。同时,其支持根据场景需求灵活定义记忆数据的格式------既可以存储完整的对话文本,也可以仅保留经过提炼的关键信息(如用户核心诉求、任务进度节点等),从而在"上下文完整性"与"内存占用效率"之间找到平衡。
6.1.2 基于硬存储的长期记忆存储
基于硬存储的长期记忆的核心目标是将人机交互过程中产生的每一次交流结果持久化保存,摆脱内存存储的临时性限制,实现对话数据的长期留存、追溯与复用。在具体使用上,我们可以直接对节点进行操作,也可以使用after_model中间件作为模型推理流程的后置钩子(Hook)。钩子是实现这一目标的核心载体------它能在模型生成并返回响应后,自动触发数据处理与存储逻辑,确保每次交流结果以标准化的格式落地到硬存储介质(本地文件、数据库等)中。
本以节将依托智能体状态空间State,之后以JSON格式的形式对需要存储的记忆进行定义。
1. 状态空间State格式定义
在具体使用上,我们为保证数据的结构化和可解析性,需先定义统一的状态空间State,覆盖交流全量关键信息,简单的格式如下:
from datetime import datetime
from pydantic import BaseModel,Field
class ChatMemoryState(BaseModel):
human_message: str = Field(default="",description="用户提出的问题和交流")
AI_message:str = Field(default="",description="AI对当前问题进行回复的时间")
datetime_now:datetime = (datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
2. 交流Agent的定义与使用示例
下面需要完成Agent的初始化和使用,仿照前面章节的内容,我们定义一个Agent的实例,通过传入工具完成用户的查询,示例如下:
from langchain.agents.middleware import before_model
from datetime import datetime
from pydantic import BaseModel,Field
class ChatMemoryState(BaseModel):
human_message: str = Field(default="",description="用户提出的问题和交流")
AI_message:str = Field(default="",description="AI对当前问题进行回复的时间")
datetime_now:datetime = (datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
from langchain_core.tools import tool
1. 定义工具
@tool
def get_weather_info(city: str) -> str:
"""根据城市名称查询当前天气状况(含温度、天气状况)"""
weather_data = {
"上海": "城市:上海 | 天气:晴 | 温度:18℃ | 风向:东南风 | 湿度:65%",
"南京": "城市:南京 | 天气:多云 | 温度:15℃ | 风向:北风 | 湿度:70%",
"杭州": "城市:杭州 | 天气:小雨 | 温度:12℃ | 风向:西北风 | 湿度:80%",
}
return weather_data.get(city, f"未查询到{city}的天气信息")
@tool
def get_weather_tips(city: str) -> str:
"""根据城市名称返回天气相关生活建议(不包含温度和天气状况)"""
tips_data = {
"上海": "天气晴朗,适合户外活动;紫外线较强,注意防晒;早晚温差大",
"南京": "多云天气,适宜出行;风力不大,适合晾晒衣物;空气较干燥",
"杭州": "有小雨,出门记得带伞;路面湿滑,注意行车安全;气温较低,适当添衣",
}
return tips_data.get(city, f"暂无{city}的相关天气建议")
tools = [get_weather_info,get_weather_tips]
import bigmodel
from langchain.agents import create_agent
agent = create_agent(
model=bigmodel.llm,
tools=[get_weather_info, get_weather_tips],
system_prompt="""你需要回答用户的提问,必要时调用工具完成任务。回答需清晰准确,并以JSON的格式回复。""",
response_format=ChatMemoryState,
)
6.调用智能体
result = agent.invoke({"messages": "南京的天气是什么?"})
print(result["structured_response"])
读者可以自行运行代码验证。
3.核心after_model中间件函数
after_model函数作为模型响应后的钩子,接收用户输入、模型输出、会话时间数据等核心参数,从而完成存储构建与存储,代码如下:
from langgraph.runtime import Runtime
from langchain.agents.middleware import after_agent
from pathlib import Path
import json
@after_agent
def log_after_agent(state: ChatMemoryState | dict, runtime: Runtime):
state = state["structured_response"]
try:
human_msg = state.human_message
ai_msg = state.AI_message
dt_now = state.datetime_now
#构造存储数据(确保datetime转为字符串)
storage_data = {
"human_message": human_msg,
"AI_message": ai_msg,
"datetime_now": dt_now.strftime("%Y-%m-%d %H:%M:%S") if isinstance(dt_now, datetime) else dt_now
}
4.标准JSON数组存储(支持正常解析)
storage_file = Path("./memory.json")
if storage_file.exists():
读取已有数据(JSON数组)
with open(storage_file, "r", encoding="utf-8") as f:
try:
memory_list = json.load(f) # 加载已有数组
except json.JSONDecodeError:
memory_list = [] # 若文件损坏,重置为空数组
else:
memory_list = [] # 新文件初始化数组
追加新记录并写入(保持JSON数组格式)
memory_list.append(storage_data)
with open(storage_file, "w", encoding="utf-8") as f:
json.dump(memory_list, f, ensure_ascii=False, indent=2) # indent=2 格式化显示,便于阅读
print(f"✅ 交互记录已追加到 {storage_file}")
print(f"📝 新增记录:{json.dumps(storage_data, ensure_ascii=False, indent=2)}\n")
except Exception as e:
print(f"❌ 存储失败:{str(e)}")
return
4. 可对交互内容进行存储的智能体完整实现
下面我们实现对实时交互内容进行完整存储的智能体,其核心目标是在响应用户需求的同时,通过after_agent中间件自动持久化交互记录,确保每一次用户提问与AI回复都以标准JSON格式存储,支持后续追溯、历史查询与长期复用。该实现兼顾了结构化响应、稳定存储与灵活扩展,适用于需要留存交互轨迹的场景(如客服对话、工具使用记录等)。
from langchain.agents.middleware import before_model
from datetime import datetime
from pydantic import BaseModel,Field
class ChatMemoryState(BaseModel):
human_message: str = Field(default="",description="用户提出的问题和交流")
AI_message:str = Field(default="",description="AI对当前问题进行回复")
datetime_now:datetime = (datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
from langgraph.runtime import Runtime
from langchain.agents.middleware import after_agent
from pathlib import Path
import json
@after_agent
def log_after_agent(state: ChatMemoryState | dict, runtime: Runtime):
state = state["structured_response"]
try:
human_msg = state.human_message
ai_msg = state.AI_message
dt_now = state.datetime_now
#构造存储数据(确保datetime转为字符串)
storage_data = {
"human_message": human_msg,
"AI_message": ai_msg,
"datetime_now": dt_now.strftime("%Y-%m-%d %H:%M:%S") if isinstance(dt_now, datetime) else dt_now
}
标准JSON数组存储(支持正常解析)
storage_file = Path("./memory.json")
if storage_file.exists():
读取已有数据(JSON数组)
with open(storage_file, "r", encoding="utf-8") as f:
try:
memory_list = json.load(f) # 加载已有数组
except json.JSONDecodeError:
memory_list = [] # 若文件损坏,重置为空数组
else:
memory_list = [] # 新文件初始化数组
追加新记录并写入(保持JSON数组格式)
memory_list.append(storage_data)
with open(storage_file, "w", encoding="utf-8") as f:
json.dump(memory_list, f, ensure_ascii=False, indent=2) # indent=2 格式化显示,便于阅读
print(f"✅ 交互记录已追加到 {storage_file}")
print(f"📝 新增记录:{json.dumps(storage_data, ensure_ascii=False, indent=2)}\n")
except Exception as e:
print(f"❌ 存储失败:{str(e)}")
return
from langchain_core.tools import tool
定义工具
@tool
def get_weather_info(city: str) -> str:
"""根据城市名称查询当前天气状况(含温度、天气状况)"""
weather_data = {
"上海": "城市:上海 | 天气:晴 | 温度:18℃ | 风向:东南风 | 湿度:65%",
"南京": "城市:南京 | 天气:多云 | 温度:15℃ | 风向:北风 | 湿度:70%",
"杭州": "城市:杭州 | 天气:小雨 | 温度:12℃ | 风向:西北风 | 湿度:80%",
}
return weather_data.get(city, f"未查询到{city}的天气信息")
@tool
def get_weather_tips(city: str) -> str:
"""根据城市名称返回天气相关生活建议(不包含温度和天气状况)"""
tips_data = {
"上海": "天气晴朗,适合户外活动;紫外线较强,注意防晒;早晚温差大",
"南京": "多云天气,适宜出行;风力不大,适合晾晒衣物;空气较干燥",
"杭州": "有小雨,出门记得带伞;路面湿滑,注意行车安全;气温较低,适当添衣",
}
return tips_data.get(city, f"暂无{city}的相关天气建议")
tools = [get_weather_info,get_weather_tips]
import bigmodel
from langchain.agents import create_agent
agent = create_agent(
model=bigmodel.llm,
tools=[get_weather_info, get_weather_tips],
system_prompt="""你需要回答用户的提问,必要时调用工具完成任务。回答需清晰准确,并以JSON的格式回复。""",
middleware=[log_after_agent],
response_format=ChatMemoryState,
)
调用智能体
result = agent.invoke({"messages": "南京的天气是什么?"})
print(result["structured_response"])
此时可以看到,memory.json以标准JSON数组格式存储所有交互记录,可直接用记事本、JSON 编辑器打开,读者可以自行查阅。
对于有一定基础的读者,可以将本地文件替换为数据库(如MongoDB、SQLite、MySQL),只需修改中间件的读写逻辑,示例如下:
替换为MongoDB存储(需安装pymongo)
from pymongo import MongoClient
client = MongoClient("mongodb://localhost:27017/")
db = client["chat_memory_db"]
collection = db["interaction_records"]
#中间件中替换文件读写为数据库插入
collection.insert_one(new_record)
读者可以自行尝试完成。
