当前位置: 首页 > news >正文

langchain agent的短期记忆

        与langgraph中的图一样,要实现短期记忆依赖于持久化层的支持,从而可以在agent执行过程中支持中断和恢复。

     1.使用短期记忆

        在agent中使用短期记忆,需要在创建agent时传入检查点。

        开发及测试使用基于内存的持久化,具体代码如下:

froDB_URI = "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:#创建检查点m langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver  

agent = create_agent(
    model=llm,
    tools=tools,
    checkpointer=InMemorySaver(),  
)

        生产系统必须使用数据库实现持久化,具体代码如下:

from langchain.agents import create_agent

from langgraph.checkpoint.postgres import PostgresSaver  


DB_URI = "postgresql://postgres:postgres@localhost:5442/postgres?sslmode=disable"
with PostgresSaver.from_conn_string(DB_URI) as checkpointer:
    checkpointer.setup() # auto create tables in PostgresSql
    agent = create_agent(
        "openai:gpt-5",
        [get_user_info],
        checkpointer=checkpointer,  
    )       

      2.定制短期记忆

        用户可以对agent增加自定义的数据,并通过持久化实现短期记忆,具体示例代码如下:

from langchain.agents import AgentState


class CustomState(AgentState):
    user_info: dict

agent = create_agent(
    model=llm,
    tools=tools,
    state_schema=CustomState,
    checkpointer=InMemorySaver()
)
# The agent can now track additional state beyond messages
result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "赵无恤如何击败智伯"}],
        "user_info": {"name": "赵无恤", "title": "大将军"},
    },
    {"configurable": {"thread_id": "1"}}
)

      3.管理短期记忆

        随着对话轮次增多,短期记忆中保存的对话历史数据越来越多,如果超过了大模型的上下文窗口,则会导致上下文丢失或调用错误,即便未超过大模型上下文大小,过多的上下文也会分散大模型的注意力,从而导致表现不佳,为此必须对短期记忆进行管理,使之保持在一个合理的大小,在不超过大模型上下文的前提下,使大模型能够聚焦所处理的问题。

        管理短期记忆方法有四种:截断消息、删除消息、汇总消息和自定义策略。下面分别进行说明

      3.1截断消息

        截断消息时,需要计算当前消息历史的词元数,如果超过了阈值则截断。langchain支持指定阈值及截断策略。

        在一个agent中实施截断策略时,需要使用@before_model注解定义一个中间件方法,该方法在调用大模型之前对消息执行截断操作。

        以下是一个简单的截断示例,每次调用大模型前进行判断,总是保留最近的3条消息。具体代码如下:

from langchain.messages import RemoveMessage
from langgraph.graph.message import REMOVE_ALL_MESSAGES
from langgraph.checkpoint.memory import InMemorySaver
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model
from langgraph.runtime import Runtime
from langchain_core.runnables import RunnableConfig
from typing import Any


@before_model
def trim_messages(state: AgentState, runtime: Runtime) -> dict[str, Any] | None:
    """Keep only the last 3 messages to fit context window."""
    messages = state["messages"]

    if len(messages) <= 3:
        return None  # No changes needed

    recent_messages = messages[-3:] #从原始对话列表中拷贝出最近3条

    return {
        "messages": [
            RemoveMessage(id=REMOVE_ALL_MESSAGES), #删除列表中所有数据
            *recent_messages #保留的最近3条消息
        ]
    }

agent = create_agent(
    model=llm,
    tools=tools,
    middleware=[trim_messages],
    checkpointer=InMemorySaver(),
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

#调用前进入trim_messages方法,此时消息数为0

agent.invoke({"messages": "我最喜欢的篮球运动员是迈克尔乔丹"}, config)

#经过第一次调用,messages中消息数为2,进入trim_messages直接返回

agent.invoke({"messages": "乔丹共获得过几次NBA总冠军?"}, config)

'''

经过第二次斯奥用后,messages中消息数为2,进入trim_messages后会执行阶段操作。

把第一条消息删除了。所以在此调用大模型,不能回答问题

'''

final_response = agent.invoke({"messages": "我最喜欢的篮球运动员是谁?"}, config)

final_response["messages"][-1].pretty_print()

        运行结果如下:

================================== Ai Message ==================================

我不知道你最喜欢的篮球运动员是谁,因为这取决于你的个人喜好。不过,如果你愿意告诉我更多关于你喜欢哪种风格的球员(比如得分型、组织型、防守强悍等),或者你喜欢的球队、时代,我可以帮你分析或推荐可能合你口味的球星! 😊

当然,你也可以直接告诉我答案~

     3.2删除消息

        删除消息时,直接从历史消息列表中删除指定的消息,还可以删除所有的消息。

        在一个agent中实施删除策略时,需要使用@after_model注解定义一个中间件方法,该方法在调用大模型之后对消息执行删除操作。

        以下示例,每次都删除最早的两条消息(当然你可以自己指定条数),具体代码如:

from langchain.messages import RemoveMessage
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import after_model
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.runtime import Runtime
from langchain_core.runnables import RunnableConfig


@after_model
def delete_old_messages(state: AgentState, runtime: Runtime) -> dict | None:
    """Remove old two messages to keep conversation manageable."""
    messages = state["messages"]
    if len(messages) > 2:
        # 删除最早的两条消息
        return {"messages": [RemoveMessage(id=m.id) for m in messages[:2]]}
    return None


agent = create_agent(
    model=llm,
    tools=tools,
    middleware=[delete_old_messages],
    checkpointer=InMemorySaver(),
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}

for event in agent.stream(
    {"messages": [{"role": "user", "content": "我最喜欢的篮球运动员是迈克尔乔丹"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])

for event in agent.stream(
    {"messages": [{"role": "user", "content": "乔丹共获得过几次NBA总冠军??"}]},
    config,
    stream_mode="values",
):
    print([(message.type, message.content) for message in event["messages"]])

        输出如下,可以印证上面的逻辑:

[('human', '我最喜欢的篮球运动员是迈克尔乔丹')]
[('human', '我最喜欢的篮球运动员是迈克尔乔丹'), ('ai', '迈克尔·乔丹(Michael Jordan)是篮球历史上最伟大的球员之一,他的职业生涯充满了传奇色彩。他以出色的得分能力、关键时刻的表现和领导力闻名,带领芝加哥公牛队赢得了6次NBA总冠军(1991-1993, 1996-1998),并6次当选总决赛最有价值球员(FMVP)。乔丹还5次获得常规赛MVP,并14次入选NBA全明星阵容。\n\n除了球场上的成就,乔丹也极大地推动了篮球运动的全球化,并通过与耐克合作推出的“Air Jordan”系列球鞋,对体育文化和潮流时尚产生了深远影响。\n\n如果你喜欢乔丹,你可能也会对他的一些经典时刻感兴趣,比如:\n\n- “The Shot”(1989年季后赛对阵骑士)\n- “The Flu Game”(1997年总决赛第5场)\n- 六次夺冠后的王朝建立\n\n你最喜欢乔丹的哪个赛季或者哪一场比赛呢?')]
[('human', '我最喜欢的篮球运动员是迈克尔乔丹'), ('ai', '迈克尔·乔丹(Michael Jordan)是篮球历史上最伟大的球员之一,他的职业生涯充满了传奇色彩。他以出色的得分能力、关键时刻的表现和领导力闻名,带领芝加哥公牛队赢得了6次NBA总冠军(1991-1993, 1996-1998),并6次当选总决赛最有价值球员(FMVP)。乔丹还5次获得常规赛MVP,并14次入选NBA全明星阵容。\n\n除了球场上的成就,乔丹也极大地推动了篮球运动的全球化,并通过与耐克合作推出的“Air Jordan”系列球鞋,对体育文化和潮流时尚产生了深远影响。\n\n如果你喜欢乔丹,你可能也会对他的一些经典时刻感兴趣,比如:\n\n- “The Shot”(1989年季后赛对阵骑士)\n- “The Flu Game”(1997年总决赛第5场)\n- 六次夺冠后的王朝建立\n\n你最喜欢乔丹的哪个赛季或者哪一场比赛呢?'), ('human', '乔丹共获得过几次NBA总冠军??')]
[('human', '我最喜欢的篮球运动员是迈克尔乔丹'), ('ai', '迈克尔·乔丹(Michael Jordan)是篮球历史上最伟大的球员之一,他的职业生涯充满了传奇色彩。他以出色的得分能力、关键时刻的表现和领导力闻名,带领芝加哥公牛队赢得了6次NBA总冠军(1991-1993, 1996-1998),并6次当选总决赛最有价值球员(FMVP)。乔丹还5次获得常规赛MVP,并14次入选NBA全明星阵容。\n\n除了球场上的成就,乔丹也极大地推动了篮球运动的全球化,并通过与耐克合作推出的“Air Jordan”系列球鞋,对体育文化和潮流时尚产生了深远影响。\n\n如果你喜欢乔丹,你可能也会对他的一些经典时刻感兴趣,比如:\n\n- “The Shot”(1989年季后赛对阵骑士)\n- “The Flu Game”(1997年总决赛第5场)\n- 六次夺冠后的王朝建立\n\n你最喜欢乔丹的哪个赛季或者哪一场比赛呢?'), ('human', '乔丹共获得过几次NBA总冠军??'), ('ai', '迈克尔·乔丹(Michael Jordan)共获得过 **6次NBA总冠军**。\n\n这6次冠军全部是在他效力于**芝加哥公牛队**期间获得的,具体年份如下:\n\n- **1991年**\n- **1992年**\n- **1993年**\n- **1996年**\n- **1997年**\n- **1998年**\n\n这六次夺冠分为两个“三连冠”时期:\n- 第一个三连冠:1991–1993\n- 第二个三连冠:1996–1998\n\n每次夺冠,乔丹都当选为**总决赛最有价值球员(FMVP)**,展现了他在季后赛中的统治力。')]
[('human', '乔丹共获得过几次NBA总冠军??'), ('ai', '迈克尔·乔丹(Michael Jordan)共获得过 **6次NBA总冠军**。\n\n这6次冠军全部是在他效力于**芝加哥公牛队**期间获得的,具体年份如下:\n\n- **1991年**\n- **1992年**\n- **1993年**\n- **1996年**\n- **1997年**\n- **1998年**\n\n这六次夺冠分为两个“三连冠”时期:\n- 第一个三连冠:1991–1993\n- 第二个三连冠:1996–1998\n\n每次夺冠,乔丹都当选为**总决赛最有价值球员(FMVP)**,展现了他在季后赛中的统治力。')]

      3.3汇总消息

        截断消息和删除消息确实能够实现对消息的瘦身,但是一种简单粗暴的方法,从消息队列找那个删除消息可能会导致一些重要信息的丢失,从而导致大模型后继表现不佳。为了应对这个问题,langchain提供了高级消息管理方法,就是使用大模型对于历史消息进行汇总,从而在保证关键信息不丢失的前提下实现了消息瘦身。

        汇总算法如下图所示:

       max_tokens_before_summary指定了进行汇总时的阈值,超过该阈值则执行汇总操作。messages_to_keep指定保留的消息条数。就是说把messages_to_keep之外的消息进行汇总瘦身,最后messages_to_keep的消息原封不动保留,把summary和messages_to_keep对应的消息作为新的消息队列。

        汇总消息实现很简单,直接使用内置的中间件方法SummarizationMiddleware即可。具体实现如下:

from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.runnables import RunnableConfig


checkpointer = InMemorySaver()

agent = create_agent(
    model=llm,
    tools=tools,
    middleware=[
        SummarizationMiddleware(
            model=llm",
            max_tokens_before_summary=1000,  # Trigger summarization at 4000 tokens
            messages_to_keep=4,  # Keep last 20 messages after summary
        )
    ],
    checkpointer=checkpointer,
)

config: RunnableConfig = {"configurable": {"thread_id": "1"}}
agent.invoke({"messages": "我最喜欢的篮球运动员是迈克尔乔丹"}, config)
agent.invoke({"messages": "乔丹共获得过几次NBA总冠军?"}, config)
agent.invoke({"messages": "乔丹的宿敌是谁"}, config)
final_response = agent.invoke({"messages": "我最喜欢的篮球运动员是谁?"}, config)

final_response["messages"][-1].pretty_print()

        输出结果如下,可见虽然仅保留了最后两条,但所以的消息都汇总了,所以可以得到所需的答案。

================================== Ai Message ==================================

你之前提到:“我最喜欢的篮球运动员是迈克尔乔丹”。

所以,你最喜欢的篮球运动员是 **迈克尔·乔丹(Michael Jordan)**! 🏀✨

如果你现在有新的最爱,也欢迎告诉我哦!
 

      4.访问短期记忆

      4.1工具访问短期记忆

        在agent的工具内部可以访问短期记忆,当然可以读、也可以写。

       4.1.1读短期记忆

        在定义工具时增加ToolRuntime参数,通过该参数在工具内部可以访问短期记忆。具体代码如下:

from langchain.agents import create_agent, AgentState
from langchain.tools import tool, ToolRuntime

class CustomState(AgentState):
    user_info: dict #短期记忆中保存用户姓名和头衔

"""

    以下工具根据用户姓名获取其头衔。通过runtime可以访问到短期记忆。

"""

@tool
def get_title(name: str, runtime: ToolRuntime)->str:
     """get title by name."""
     return runtime.state['user_info'][name]
    
agent = create_agent(
    model=llm,
    tools=[get_title],
    state_schema=CustomState,
)

result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "赵无恤的头衔是什么?"}],
        "user_info": {"赵无恤": "大将军"},
    }
)
print(result["messages"][-1].content)

        4.1.2写短期记忆

        写短期记忆时仍需要借助ToolRuntime来实现。

from langchain.agents import create_agent, AgentState
from langchain.tools import tool, ToolRuntime
from langgraph.types import Command
from langchain.messages import ToolMessage
class CustomState(AgentState):
    user_info: dict

@tool
def update_title(name: str, title: str, runtime: ToolRuntime)->str:
     """update title by name."""
     return Command(update={#对用户信息进行更新
        "user_info": {name: title},
        # 把工具调用消息手动插入到对话历史中

        "messages": [
            ToolMessage(
                "Successfully update user's title",
                tool_call_id=runtime.tool_call_id
            )
        ]
    })
    
agent = create_agent(
    model=llm,
    tools=[update_title],
    state_schema=CustomState,
)
# The agent can now track additional state beyond messages
result = agent.invoke(
    {
        "messages": [{"role": "user", "content": "把赵无恤晋升为上卿,居六卿之首"}],
        "user_info": {"赵无恤": "大将军"},
    }
)
for message in result["messages"]:
    message.pretty_print()

        运行结果如下:

================================ Human Message =================================

把赵无恤晋升为上卿,居六卿之首
================================== Ai Message ==================================
Tool Calls:
  update_title (call_6936858c565b426fa006c8)
 Call ID: call_6936858c565b426fa006c8
  Args:
    name: 赵无恤
    title: 上卿
================================= Tool Message =================================
Name: update_title

Successfully update user's title
================================== Ai Message ==================================

已成功将赵无恤晋升为上卿,位居六卿之首。
 

      4.2调用模型前访问

        参见本文3.1截断消息。

      4.3调用模型后访问

        参见本文3.2删除消息。

http://www.dtcms.com/a/560827.html

相关文章:

  • 使用DrissionPage和自动化技术实现得物鞋子信息爬取
  • 做网站的是干嘛的为网站添加统计
  • 网站设计的建设目的做网站推销手表
  • [LitCTF 2023]Vim yyds
  • LNMP环境部署 KodBox私有云盘
  • 解决windows docker开发thinkphp6启动慢的问题
  • Rust编译参数与优化控制
  • springboot后端的接口headers
  • day04(11.2)——leetcode面试经典150
  • mysql常识和jdbc工具类的进化以及连接池思想
  • 七.Docker网络
  • 怎么做免费视频网站吗网站建设哪儿好
  • 推荐优质wordpress外贸网站主题
  • TDengine 数学函数 ASCII 用户手册
  • @Builder注解导致mybatis类型匹配错误 Cannot determine value type from string
  • AI模型开发 | 从零部署Deepseek OCR模型,零门槛开发PDF文档解析工具
  • Linux INPUT 子系统实验
  • 1000套实习报告模板大合集+多行业多专业实习总结实践报告素材
  • 百度网站快速优化国内flask做的网站
  • Spring AI--Prompt、多轮对话实现方案
  • 网页模板免费下载网站广州页面制作公司
  • Java:继承与多态
  • 10. 从0到上线:.NET 8 + ML.NET LTR 智能类目匹配实战--Web API 接口与前端集成:部署与生产运维:稳定性、可观测与成本
  • 网站开发招标网网站免费注册会员怎么做
  • linux系统中网络编程的实现
  • Vue-github 用户搜索案例
  • GD32F407VE天空星开发板的电压电流检测
  • 网站优化文章怎么做蔡甸城乡建设局网站
  • 中小企业网站开发韵茵全屋定制家具品牌排行榜前十名
  • 分库分表MyCat 架构迁移 OceanBase | 百丽核心财务系统迁移经验总结与问题汇总