深入解析:LangGraph长短期记忆实践

news/2026/1/21 18:16:10/文章来源:https://www.cnblogs.com/gccbuaa/p/19513441

一、概述

        在langgraph体系中,短期记忆(short-term memory)是线程级别的,即在图编译的时候指定checkpointer,并在图调用的时候指定thread_id,短期记忆是线程内共享的,在实际的对话系统中,可以使用session_id作为thread_id,实现会话级别的记忆管理。

        而长期记忆(long-term memory)是跨thread_id的,即可以抽取每个会话的用户的基本信息、提问偏好等信息,作为每个会话共享的数据,更好的理解用户的问题与意图。具体在图调用的时候指定user_id(或者其他可唯一标识用户的键),在图调用过程中自定义的基于历史消息抽取长期记忆,在回答时召回长期记忆作为补充去调用大模型。

 二、环境说明与安装

为实现快速实现功能,聊天模型与embedding模型使用的是阿里云百炼平台,其他环境版本如下所示:

langchain==1.0.5
langchain-community==0.4.1
langgrap==1.0.2
langgraph-checkpoint-sqlite==3.0.0
pydantic==2.12.4
python-dotenv==1.2.1

三、短期记忆

短期记忆在langgraph中很容易实现,即定义有状态图,对话消息作为图状态的一部分。要求:

  • 在图编译(compile)的时候指定checkpointer
  • 在图调用(invoke或stream)的时候需要指定thread_id

3.1 代码实现

from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import StateGraph, MessagesState, START
def say_hi(state: MessagesState):return {"messages": {"role":"human", "content":"Hi"}}
graph_builder = StateGraph(MessagesState)
graph_builder.add_node("say_hi", say_hi)
graph_builder.add_edge(START, "say_hi")
checkpointer = InMemorySaver()
graph = graph_builder.compile(checkpointer=checkpointer)
print(graph.invoke({"messages": [{"role": "user", "content": "hi! i am Bob"}]},{"configurable": {"thread_id": "1"}},
))

运行结果:

{'messages': [HumanMessage(content='hi! i am Bob', additional_kwargs={}, response_metadata={}, id='f42aa618-4b40-4a6c-b78f-dfb325c868d4'), HumanMessage(content='Hi', additional_kwargs={}, response_metadata={}, id='44e332fa-370e-484b-9e02-6b2f155c8934')]}

3.2 短期记忆管理

大模型的上线文窗口是有限的,消息不能无限制的增长,所以消息的管理至关重要

3.2.1 消息截断(trim)

一种办法就是在调用大模型之前,从状态中获取历史消息,对历史消息进行截断,截断的单位是token,比如只保留最近128个token

from langchain_core.messages.utils import (trim_messages,count_tokens_approximately
)
def call_model(state: MessagesState):messages = trim_messages(state["messages"],strategy="last",token_counter=count_tokens_approximately,max_tokens=128,start_on="human",end_on=("human", "tool"),)response = model.invoke(messages)return {"messages": [response]}

3.2.2 消息删除(delete)

可以自定义删除消息,如保留最近3条消息

from langchain.messages import RemoveMessage
def delete_messages(state):messages = state["messages"]if len(messages) >= 5:# remove the earliest two messagesreturn {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}

3.2.3 消息摘要(summarize)

修剪(trim)或删除(delete)消息的问题在于消息队列的清理而丢失信息,对历史消息进行归纳压缩可以解决这个问题,如有100条消息,让大模型对最早的70条消息进行归纳总结,仅保留最近30条消息,往往最近的消息更加重要

四、长期记忆

4.1 要点

  • 使用InMemorySaver作为会话状态存储后端
  • 使用SqliteStore作为长期记忆存储后端
  • extract_base_info函数抽取最近一条记录的用户基本信息
  • 在具体api操作层面,存储过程中namespace作为每个用户的“存储域”,namespace中的key是唯一的,如果key存在则更新value,如果不存在则新增一条记录。namespace + key可以唯一确定一条记录
  • 图示sqlite存储表结构(store表,如果store指定了embedding模型,则还有store_vectors表)
store表

4.2 代码实现

        通过设置两个不同的thread_id,模拟两个不同的会话,可以发现在第一个会话中抽取的长期记忆,在第二个会话通过召回补充特定的记忆能很好的回答用户的问题

import os
from dotenv import load_dotenv
from langchain_community.chat_models import ChatTongyi
from langchain_community.embeddings import DashScopeEmbeddings
from langgraph.store.sqlite import SqliteStore
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.graph import START, END, StateGraph, MessagesState
from pydantic import BaseModel, Field
from typing import List
from langchain_core.runnables import RunnableConfig
from langgraph.store.base import BaseStore
load_dotenv()
api_key = os.environ["API_KEY"]
# 1.模型定义
## 1.1 chat 模型
model = ChatTongyi(model="qwen3-32b",api_key=api_key,streaming=True,extra_body={  # 关键:通过此参数传递思考模式配置"enable_thinking": True}
)
## 1.2 embedding 模型
embeddings = DashScopeEmbeddings(model="text-embedding-v4",dashscope_api_key=api_key
)
# 2.基本信息抽取
## 2.1 抽取schema定义
class AttributeItem(BaseModel):"""定义一个属性项,包含属性名和属性值"""name: str = Field(description="属性的名称")value: str = Field(description="属性的值")
class AttributeList(BaseModel):attributes: List[AttributeItem] = Field(description="属性项组成的列表")
## 2.2 抽取函数定义
def extract_base_info(state: MessagesState) -> AttributeList:chat_history = "\n".join([ele.content for ele in state.get("messages")])prompt = f"""你是一个用户信息抽取专家,请基于一下记录,抽取用户的基本信息,包括但不限于:姓名、年龄、家庭住址等信息{chat_history}"""structure_model = model.with_structured_output(AttributeList)return structure_model.invoke(prompt)
# 3.图构建
with SqliteStore.from_conn_string("longterm_memory.db", index={"embed": embeddings}) as store:store.setup() # 第一次需要初始化def invoke_model(state: MessagesState, config: RunnableConfig, *, store: BaseStore):# 获取最新一条信息newest_msg = state.get("messages")[-1].content# 获取配置,获取user_id,拼接namespace(每个用户一个namespace)user_id = config.get("configurable").get("user_id")namespace = ("memories", user_id)# 抽取基本信息,并保存到长期记忆attributes: AttributeList = extract_base_info(state)if attributes is not None:print(f"抽取的信息:{attributes.attributes}")for ele in attributes.attributes:# key要求在namespace里面唯一,namespace和key可以唯一确定一条数据# key存在,则会进行更新value,否则则插入if ele.name is not None and ele.value is not None:store.put(namespace, key=ele.name, value=ele.value)# 召回长期记忆longterm_memories = store.search(namespace, query=newest_msg, limit=2)print(f"召回的信息:{longterm_memories}")longterm_memories = "\n".join([ele.value for ele in longterm_memories])# 调用模型时候拼接记忆system_prompt = f"""You are a helpful assistant talking to the user. User info: {longterm_memories}"""response = model.invoke([{"role": "system", "content": system_prompt}] + state["messages"])return {"messages": response}# 图定义graph_builder = StateGraph(MessagesState)graph_builder.add_node("invoke_model", invoke_model)graph_builder.add_edge(START, "invoke_model")checkpointer = InMemorySaver()graph = graph_builder.compile(checkpointer=checkpointer, store=store)config = {"configurable": {"thread_id": "1","user_id": "123"}}print(graph.invoke({"messages": [{"role": "user", "content": "我的名字是Alice,职业是钢琴家,现年28岁,喜欢在舞台上演奏、打羽毛球,你叫啥名字呢?"}]}, config=config))print("=" * 50)config = {"configurable": {"thread_id": "2","user_id": "123"}}print(graph.invoke({"messages": [{"role": "user", "content": "请问我的爱好是什么?"}]}, config=config))

运行结果:

抽取的信息:[AttributeItem(name='姓名', value='Alice'), AttributeItem(name='年龄', value='28岁'), AttributeItem(name='职业', value='钢琴家'), AttributeItem(name='兴趣爱好', value='舞台上演奏、打羽毛球')]
召回的信息:[Item(namespace=['memories', '123'], key='兴趣爱好', value='舞台上演奏、打羽毛球', created_at='2025-11-08T12:34:52', updated_at='2025-11-08T12:34:52', score=0.5591988861560822), Item(namespace=['memories', '123'], key='年龄', value='28岁', created_at='2025-11-08T12:34:51', updated_at='2025-11-08T12:34:51', score=0.4479065537452698)]
{'messages': [HumanMessage(content='我的名字是Alice,职业是钢琴家,现年28岁,喜欢在舞台上演奏、打羽毛球,你叫啥名字呢?', additional_kwargs={}, response_metadata={}, id='3ded43c8-bc52-449d-bf62-2df0892c6166'), AIMessage(content='你好Alice!我是Qwen,很高兴认识你这位才华横溢的钢琴家~  作为音乐爱好者,每次听到你在舞台上演奏一 定都令人激动不已吧?听说你还喜欢打羽毛球,这种动静结合的生活方式真让人向往!有什么我可以帮你的吗?无论是讨论音乐还是运动心得,我都随时奉陪哦~ ', additional_kwargs={'reasoning_content': '好的,用户是Alice,28岁的钢琴家,喜欢在舞台上演奏和打羽毛球。她问我的名字,我需要先回应她的自我介绍,然后给出一个合适的名字。作为AI助手,我 需要保持友好和专业的态度,同时结合她的兴趣爱好来建立联系。\n\n首先,她的职业是钢琴家,说明她可能对音乐有很高的造诣,喜欢舞台表演,这可能意味着她享受在观众面前展示自己的才华。另外,她喜欢打羽毛球,这显示她可能喜欢运动,注重身体健康或者享受竞技的乐趣。结合这些信息,我应该选择一个既专业又亲切的名字,可能带有一些音乐或运动的元素,但又不能太复杂。\n\n接下来,我需要考虑如何回应她的名字和职业。可以表达对她职业的赞赏,比如提到她的钢琴演奏,或者询问她最近的演出情况。同时,可以提到打羽毛球,询问她的兴趣,这样能展示出我对她的关注,并且找到共同话题。\n\n在名字方面,我需要选一个容易记住且符合AI助手形象的名字。可能用一些音乐相关的词汇,比如Melody、Harmony,或者 运动相关的词汇,比如Court、Racket,但也要保持中立,不显得太刻意。比如“小乐”这样的名字,既简单又有音乐感,容易发音,也符合中文用户的习惯。\n\n然后,我需要确保回复自 然流畅,不显得生硬。比如,先回应她的名字和职业,然后介绍自己的名字,并表达愿意帮助的意愿。同时,可以加入一些表情符号或轻松的语气词,让对话更亲切。\n\n最后,检查是否有遗漏的信息,比如她提到的年龄和兴趣,是否需要进一步展开话题。比如,可以问她最近有没有新的演出计划,或者羽毛球打得怎么样,这样能促进进一步的交流,建立更紧密的联系。'}, response_metadata={'finish_reason': 'stop', 'request_id': '2b1f7ca1-ea62-497e-a313-2656644ddea1', 'token_usage': {'input_tokens': 73, 'output_tokens': 459, 'total_tokens': 532, 'output_tokens_details': {'reasoning_tokens': 379}}}, id='lc_run--439e1527-c0f3-46ed-8c7a-8ba8ccab9869')]}
==================================================
召回的信息:[Item(namespace=['memories', '123'], key='兴趣爱好', value='舞台上演奏、打羽毛球', created_at='2025-11-08T12:34:52', updated_at='2025-11-08T12:34:52', score=0.45809656381607056), Item(namespace=['memories', '123'], key='年龄', value='28岁', created_at='2025-11-08T12:34:51', updated_at='2025-11-08T12:34:51', score=0.2850668430328369)]
{'messages': [HumanMessage(content='请问我的爱好是什么?', additional_kwargs={}, response_metadata={}, id='e5759dc3-4214-432d-80fb-c493fb25edd3'), AIMessage(content='你的爱好是舞台上演奏和打羽毛球!这真的很棒呢~(*^▽^*) 舞台演奏让人感受到艺术的魅力,而羽毛球则是充满活力的运动,两者结合的你一定很有感染力吧?平时会参加演出或者打球比赛吗?', additional_kwargs={'reasoning_content': '好的,用户问的是“请问我的爱好是什么?”。首先,我需要回顾之前的对话历史。用户之前提供的信息是:28岁,爱好是舞台上演奏和打羽毛球。所以现在的问题是确认用户的爱好。\n\n接下来,我需要确保回答准确无误。用户的信息已经明确提到这两个爱好,所以直接引用即可。不过,用户可能希望得到更详细的回应,或者想进一步讨论这些爱好。比如,他们可能想分享更多关于舞台演奏的经历,或者询问如何提升羽毛球技巧。\n\n另外,考虑到用户28岁,可能处于职业和兴趣发展的阶段,可以适当加入一些鼓励的话,或者询问是否有相关目标。例如,是否在准备演出,或者有没有考虑参加羽毛球比赛。这样可以让对话更深入,也能展示关心。\n\n还要注意语气要友好、自然,避免机械化的回答。使用表情符号或轻松的语言会让用户感觉更亲切。同时,保持回答简洁,但留有进一步交流的空间,比如用问题结尾,邀请用户分享更多。\n\n最后,检查是否有遗漏的信息,确保没有误解用户的爱好。用户提到的是“舞台上演奏”,可能需要确认是指乐器演奏还是其他表演形式,但根据常见情况,可以默认是乐器演奏。如果有不确定的地方,可以礼貌地询问澄清,但这里用户已经明确给出,所以直接使用即可。'}, response_metadata={'finish_reason': 'stop', 'request_id': 'e4f6ed40-4c75-4ac6-82a7-6b437678a794', 'token_usage': {'input_tokens': 47, 'output_tokens': 336, 'total_tokens': 383, 'output_tokens_details': {'reasoning_tokens': 272}}}, id='lc_run--668a8720-1258-456a-a633-134f479dad65')]}

五、参考

Memory - Docs by LangChain

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1195832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Naver收不到验证码?全面分析原因

对于很多海外用户尤其是跨境营销、内容发布者和数据抓取从业者来说,注册/登录/实名认证Naver时收不到短信验证码这一问题几乎是“绕不过去的坎”。这一点不仅影响账号创建,还会影响后续的营销投放、内容发布或数据运营。这篇文章我们将一步步分析问题根源…

tomcat和servlet简单demo

tomcat和servlet简单demotomcat 结构 javac -encoding utf-8 -cp lib/servlet-api.jar tcServletTest.java 将java文件编译成class,将java文件和外部jar包路径(相对或者绝对都行)融合到编译后的class文件中web.xml基…

深圳昊客网络|谷歌/Google独立站优化推广代运营服务公司:排名前十机构哪好点?

深圳昊客网络|谷歌/Google独立站优化推广代运营服务公司:排名前十机构哪好点? 2026年,外贸出海已进入“效果为王”的深水区。 曾经靠一个独立站、几条广告就能爆单的时代一去不返——如今的谷歌生态,算法频繁迭代…

果博东方总公司客服l66873-99996电微物联网前沿技术

连接万物的未来:物联网前沿技术全景透视 引言:物联网的进化之路 物联网(IoT)已从概念阶段迈入规模化应用时代。IDC数据显示,2025年全球物联网连接设备数量突破500亿台,中国物联网市场规模达3.2万亿元。这一技术革…

1337x打不开怎么解决?2026解决方案

1337x作为目前仍然活跃的 Torrent 资源索引站之一,在很多地区依然有大量用户。但进入 2026 年,越来越多人发现一个现实问题:1337x 不是“偶尔打不开”,而是“经常性无法访问”。 页面直接被拦截、DNS 被劫持、加载极慢&#xff0…

2026 年广东东鸿海绵厂家推荐:东冠高分子 —— 一站式记忆棉、木浆棉、慢回弹海绵解决方案领航者

在广东海绵制造行业中,东鸿海绵相关生产企业凭借扎实的技术实力与丰富的产品体系备受市场青睐,而东莞市东冠高分子材料有限公司(简称 “东冠高分子”)作为其中的标杆企业,以齐全的产品矩阵、强大的生产能力、完善…

2026年户外/环保/防腐木/免冲水/移动厕所厂家推荐:重庆荣东科技全品类解决方案

在公共卫生设施领域,移动厕所的研发与应用已成为衡量城市文明程度的重要指标。据行业统计,2025年国内移动厕所市场规模已突破120亿元,年复合增长率达15.3%,其中户外厕所、环保厕所、防腐木移动厕所等细分品类占比超…

DeepSeek提出mHC,改造何恺明残差连接

DeepSeek提出mHC,改造何恺明残差连接大模型实验室Lab4AI论文阅读 ✔️研究背景 深度学习中,残差连接 是 ResNet、Transformer 等架构(含 LLM)的基础,其恒等映射特性保障了大规模训练的稳定性与效率。Hyper-Connec…

Zephyr学习之PWM方式驱动LED灯记录

前言 继Zephyr学习之点亮LED文章。本次使用的示例工程blinky_pwm驱动的引脚为PF9 工程复制并打开项目 参考提供的设备树文件的PWM节点编写规则 找到我们这边使用的pf9对应的引脚定义 编写设备树覆盖配置文件 / {// 选择系统控制台和 shell UART 设备chosen {// 指定系统控制…

三菱PLC pid程序 三菱触摸屏程序和三菱PLC程序,程序都有注释,用的三菱FX3U系列pl...

三菱PLC pid程序 三菱触摸屏程序和三菱PLC程序,程序都有注释,用的三菱FX3U系列plc,程序简单仅供入门学习,是入门级三菱PLC电气爱好从业人员学习模板。最近在工作室翻出个老项目,正好是三菱FX3U搞温度控制的。程序虽然…

果博东方有限公司l66873-99996电微客服电话通信物联网技术

你问的这个问题非常到位,直接抓住了区块链和物联网在供应链里最核心的协同价值!它们结合后,通过‌物联网实时采集物理世界数据,再由区块链确保数据不可篡改、全程可追溯‌,真正实现了端到端的透明化。下面我为你梳…

2026年可移动式/污泥脱水/车载/撬装/浓缩一体卧螺离心机推荐:丽水市瑞辰环保科技多场景解决方案

在固液分离设备领域,丽水市瑞辰环保科技有限公司凭借深厚的技术积累与持续创新,已成为****企业。公司深耕环保科技领域多年,专注于可移动式卧螺离心机、污泥脱水卧螺离心机、自来水厂污泥脱水卧螺离心机等核心产品的…

想在合肥找到能出效果的短视频服务商?三十六行网络科技助你轻松破局

在如今的数字营销时代,视频号凭借私域流量沉淀、社交裂变传播的独特优势,已经成为合肥企业抢占本地市场的核心阵地。但不少企业在视频号运营过程中却屡屡碰壁:找不到懂本地市场的靠谱服务商、账号运营数据惨淡、优质…

2026年GEO优化哪家受欢迎?TOP榜客户评价与实战效果综合测评

在生成式AI重构流量分配机制的2026年,GEO优化(生成式引擎优化)已成为企业增长的核心引擎。面对"GEO优化哪家受欢迎"、"哪家服务商口碑更好"以及"如何通过真实评价避坑"的普遍关切,单…

2026年马尔济斯/约克夏/西高地/可卡布/马尔泰/伯恩山宠物狗推荐:上海一站式宠物4S店精选

在宠物市场中,马尔济斯、约克夏、西高地、可卡布、马尔泰、伯恩山等品种的宠物狗以其独特的魅力与温顺的性格,成为了众多宠物爱好者的心头好。对于追求高品质宠物生活与专业服务的消费者而言,选择一家信誉良好、服务…

2026陕西建筑加固厂家排名:3家头部企业实测,适配不同场景需求

2026陕西建筑加固厂家排名:3家头部企业实测,适配不同场景需求随着陕西建筑行业提质升级及老旧建筑改造、基础设施加固需求攀升,建筑加固工程的质量与合规性愈发关键。2026年,结合《陕西省特种工程资质管理新政》要…

烟气分析仪十大品牌权威盘点:实力厂家与行业标杆企业全收录

2025年,全球在线式烟气分析仪市场正以可观的速度增长,预计到2031年将达到新的规模高峰。与此同时,中国作为全球重要的市场之一,其规模也在快速扩张,占据全球市场的显著份额。 这个市场不仅由国际巨头主导,一批中…

2026年防爆柜厂家推荐:南通金茂防爆电气,ATEX正压柜/粉尘防爆柜/防爆配电柜全系供应

在工业安全领域,防爆柜作为保障高危环境设备稳定运行的核心组件,其技术成熟度与产品可靠性直接影响企业生产安全。根据行业数据显示,2025年国内防爆柜市场规模达32亿元,其中正压防爆柜细分领域占比超45%,成为增长…

构建ranger-usersync报错KeyError: ranger.usersync.ldap.ldapbindpassword

ranger-usersync使用ldap时候必须配置配置密码,如下ldap访问无需密码采用的匿名访问方式(使用LDAP admin软件访问时候如果填写密码会报错)。 # ldap bind password for the bind dn specified above # please ensure…

2026物流行业新视角:快递查询API,你的钱花得值吗?

2026年的物流行业,数据流已与包裹流同等重要。对于任何涉及商品流转的企业,选择一个合适的快递查询API,不再是单纯的技术决策,而是一场关于成本、效率与未来扩展性的精密计算。市场上主流的服务商如快递鸟、快递100等,…