LangChain 1.0-Agent中间件-消息压缩

发布时间:2026-03-02 21:29:53编辑:123阅读(20)

    LangChain 1.0基于中间件的消息压缩

    修剪是最轻量、最直接的压缩方式。它的思想是:只保留最近N条消息或M个token以内的上下文,其余的自动裁剪掉。这种方法通常配合@before_model钩子使用,在每次模型调用前计算历史消息数量或token长度;当接近模型的最大上下文限制(如4k、16k、128k)时,就删除较早的对话,只保留关键的系统提示和最新几轮对话。它的优点是实现简单、执行快速、成本可控,非常适合聊天型Agent或RAG问答系统;缺点是容易丢失远期上下文记忆。

    代码如下:

    from langchain.agents import create_agent, AgentState
    from langchain.agents.middleware import before_model
    from langchain.messages import RemoveMessage
    from langgraph.graph.message import REMOVE_ALL_MESSAGES
    from langgraph.checkpoint.memory import InMemorySaver
    from langgraph.runtime import Runtime
    from typing import Any
    from langchain_ollama import ChatOllama
    import requests
    import time
    from lxml import etree
    from langchain.tools import tool
    
    # 初始化模型
    model = ChatOllama(
        model="qwen3:8b",
        temperature=0.2,
        top_p=0.95,
    )
    
    @tool
    def get_weather(city):
        """
        查询即时天气函数
        :param city: 必要参数,字符串类型。用于表示查询天气的具体城市名称
        :return: 返回即时天气的结果,dict类型
        """
        today_time = time.strftime("%Y-%m-%d",time.localtime())
        url = f'https://www.ks121.com/history/?location={city}&startdate={today_time}&enddate={today_time}'
        headers = {
            "sec-ch-ua": '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
            "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
             Chrome/144.0.0.0 Safari/537.36"
        }
        response = requests.get(url=url, headers=headers, timeout=10)
        html = etree.HTML(response.text)
        res = html.xpath("//div[@class='box']/div[2]/div[1]/div/table/tbody/tr")
        ret = {}
        for i in res:
            one = i.xpath("./td[1]/p[2]/text()")[0]
            two = i.xpath("./td[2]/p[2]/span/text()")[0]
            three = i.xpath("./td[3]/p/text()")[0]
            four = i.xpath("./td[4]/p/text()")[0]
            ret.setdefault('日期', one)
            ret.setdefault('气象', two)
            ret.setdefault('温度', three)
            ret.setdefault('风级', four)
        print(f"工具调用:{today_time}:{city}天气信息:{ret}")
        return ret
    
    @before_model
    def trim_message(state: AgentState, runtime:Runtime) -> dict[str, Any] | None:
        """在模型调用前修剪消息历史,只保留前1+后3条。"""
        messages = state["messages"]
        # 只有超过4条消息才裁剪
        if len(messages) <= 4:
            return None
    
        # 保留首条System提示 + 最近3条
        first_msg = messages[0]
        new_messages = [first_msg] + messages[-3:]
        print(f"修剪消息:从{len(messages)}条 -> {len(new_messages)}条")
        return {
            "messages": [RemoveMessage(id=REMOVE_ALL_MESSAGES),*new_messages]
        }
    
    agent = create_agent(
        model=model,
        tools=[get_weather],
        middleware=[trim_message],
        checkpointer=InMemorySaver(),
    )
    
    config = {
        "configurable":{
            "thread_id":"2"
        }
    }
    
    resp1 = agent.invoke(
        {"messages":"今天上海的天气? 你好,我叫张三。"},
        config=config,
    )
    print(resp1["messages"][-1].content)
    
    
    resp2 = agent.invoke(
        {"messages":"你还记得我叫什么吗?"},
        config=config,
    )
    print(resp2["messages"][-1].content)
    
    resp3 = agent.invoke(
        {"messages":"一天喝多少水比较健康?"},
        config=config,
    )
    print(resp3["messages"][-1].content)
    
    resp4 = agent.invoke(
        {"messages":"介绍下你自己"},
        config=config,
    )
    print(resp4["messages"][-1].content)
    
    resp5 = agent.invoke(
        {"messages":"帮我写一句每日格言"},
        config=config,
    )
    print(resp5["messages"][-1].content)
    
    # 查看保留的历史消息
    for i in agent.get_state(config).values["messages"]:
        print(i.content)

    运行结果:

    工具调用:2026-03-02:上海天气信息:{'日期': '2026-03-02', '气象': '阴', '温度': '6℃\n~ 10℃', '风级': '南风转西北风<3级转3-4

    级'}

    张三你好!今天是2026年3月2日,上海天气阴,气温在6℃到10℃之间,风向由南风转西北风,风级从<3级转为3-4级。建议

    根据温度变化适时增减衣物哦!

    修剪消息:从5条 -> 4条

    记得啊,你叫张三!是不是有什么需要我帮忙的吗? 😊

    修剪消息:从6条 -> 4条

    一般建议成年人每天饮用约2-3升水(约8-12杯),但具体需求因体重、活动量、气候和健康状况而异。例如:

    - 每公斤体重约需30-35ml水

    - 运动后或干燥环境中需增加摄入

    - 摄入含电解质的饮料也可补充水分


    不过每个人的情况不同,如有特殊健康需求,建议咨询专业医生获取个性化建议。

    修剪消息:从6条 -> 4条

    你好!我是一个大型语言模型,可以回答各种问题、提供建议,并帮助你获取信息。我的知识库涵盖了广泛的主题,包括科学

    、文化、技术等。如果遇到复杂问题,我可以通过调用工具获取最新数据。不过我无法处理需要实时操作的任务(如网页点击)

    ,但会尽力用文字描述解决方案。需要什么帮助吗?

    修剪消息:从6条 -> 4条

    “种下习惯,收获命运;点亮微光,终成星河。”

    今天上海的天气? 你好,我叫张三。

    介绍下你自己

    你好!我是一个大型语言模型,可以回答各种问题、提供建议,并帮助你获取信息。我的知识库涵盖了广泛的主题,包括科学

    、文化、技术等。如果遇到复杂问题,我可以通过调用工具获取最新数据。不过我无法处理需要实时操作的任务(如网页点击)

    ,但会尽力用文字描述解决方案。需要什么帮助吗?

    帮我写一句每日格言

    “种下习惯,收获命运;点亮微光,终成星河。”


关键字