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

[Deep Agents:LangChain的Agent Harness-08]利用SummarizationMiddleware对长程对话瘦身

_DeepAgentsSummarizationMiddleware是专为长程对话设计的上下文管理组件。它会自动压缩日益增长的对话历史,防止超出模型的Token限制。它本质上是一个对话压缩机。当消息列表的长度或Token数量超过设定的阈值时,它会调用一个指定的LLM(摘要模型)来对较旧的消息进行总结,并将原消息替换为一条包含摘要内容的消息。这样既保留了对话的核心信息,又大幅减少了上下文的Token数量,腾出了更多的上下文窗口存放后续的对话消息。

之所以被命名为_DeepAgentsSummarizationMiddleware,是为了与langchain.agents.middleware.SummarizationMiddleware区分开来,后者是一个更通用的摘要中间件,而前者则是专门针对Deep Agents设计的。但是Deep Agents为_DeepAgentsSummarizationMiddleware的定义的别名也叫SummarizationMiddleware,下面的演示实例使用的就是这个类型别名。

1. 体验SummarizationMiddleware的消息摘要功能

在正式介绍SummarizationMiddleware之前,我们先通过一个实例来体验一下提供的消息摘要功能。我们创建了一个Agent,并且注册了get_windget_temperatureget_sky_conditionsget_humidityget_atmospheric_pressure这五个工具来获取天气信息。注册的SummarizationMiddleware采用ChatOpenAI作为摘要模型,触发规则的设置促使消息列表中的消息数量超过5条时会进行一次摘要,并在完成摘要后保留最新的一条消息。为了查看摘要后的对话历史,我们紧随其后注册了一个通过check_summary函数定义的中间件,它会在每次模型调用时打印当前的消息列表。

fromtypingimportCallable,Anyfromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportwrap_model_callfromdeepagents.middleware.summarizationimportSummarizationMiddlewarefromdeepagents.backendsimportFilesystemBackendfromlangchain_openaiimportChatOpenAIfromlangchain_core.messagesimportHumanMessage,AIMessage,ToolMessage,AnyMessagefromdotenvimportload_dotenvimportasyncio,uuid load_dotenv()defget_wind(city:str)->str:"""Get the current wind for a given city."""returnf"The current wind in{city}is 10 km/h from the north."defget_temperature(city:str):"""Get the current temperature."""returnf"The current temperature in{city}is 25°C."defget_sky_conditions(city:str):"""Get the current sky conditions."""returnf"The current sky conditions in{city}are clear."defget_humidity(city:str):"""Get the current humidity."""returnf"The current humidity in{city}is 60%."defget_atmospheric_pressure(city:str)->str:"""Get the current atmospheric pressure for a given city."""returnf"The current atmospheric pressure in{city}is 1013 hPa."@wrap_model_call# type: ignoreasyncdefcheck_summary(request,handler):formessageinrequest.messages:message.pretty_print()returnawaithandler(request)# type: ignorellm=ChatOpenAI(model="gpt-5.2-chat")tools=[get_wind,get_temperature,get_sky_conditions,get_humidity,get_atmospheric_pressure]summarization_middleware=SummarizationMiddleware(model=llm,backend=FilesystemBackend(virtual_mode=True),trigger=("messages",5),keep=("messages",1))agent=create_agent(model=llm,tools=tools,middleware=[summarization_middleware,check_summary])# type: ignoretool_calls:list[tuple[Callable[...,Any],str]]=[(tool,f"{tool.__name__}_{uuid.uuid4()}")fortoolintools]ai_message=AIMessage(content="")ai_message.tool_calls.extend([{"id":id,"name":tool.__name__,"args":{"city":"Suzhou"}}fortool,idintool_calls])tool_messages=[ToolMessage(content=tool("Suzhou"),tool_call_id=id)fortool,idintool_calls]messages:list[AnyMessage]=[HumanMessage(content="What's the weather like in Suzhou today?"),ai_message,*tool_messages,AIMessage(content="I have gathered all the weather data for Suzhou. Here is the summary:")]asyncdefmain():result=awaitagent.ainvoke(input={"messages":messages})# type: ignoreprint(result["messages"][-1].content)asyncio.run(main())

我们在调用Agent的时候构造了七条消息来模拟一段相对较长的对话历史,这明显达到了我们为SummarizationMiddleware设置的触发条件。按照定义的摘要规则,摘要完成会保留一条消息,所以摘要后对话历史中只有两条消息,如下的输出证实了这一点。由于摘要的存在,对话历史承载的信息并未丢失,所以Agent最终依然能够生成我们希望的结果。

================================ Human Message ================================= You are in the middle of a conversation that has been summarized. The full conversation history has been saved to /conversation_history/session_51599319.md should you need to refer back to it for details. A condensed summary follows: <summary> ## SESSION INTENT User wanted to know the current weather conditions in Suzhou. ## SUMMARY The user asked for today's weather in Suzhou. The assistant retrieved real-time weather data via tools. Results: - Temperature: 25°C - Wind: 10 km/h from the north - Sky conditions: Clear - Humidity: 60% - Atmospheric pressure: 1013 hPa No further questions or follow-up actions were taken. ## ARTIFACTS None ## NEXT STEPS None </summary> ================================== Ai Message ================================== I have gathered all the weather data for Suzhou. Here is the summary: Here is the weather summary for **Suzhou today**: - 🌡 **Temperature:** 25 °C - 🌬 **Wind:** 10 km/h from the north - ☀️ **Sky conditions:** Clear - 💧 **Humidity:** 60% - 🌍 **Atmospheric pressure:** 1013 hPa If you’d like a forecast for later today, the coming days, or another city, just let me know.

2.利用SummarizationEvent记录摘要事件

我们先来看看_DeepAgentsSummarizationMiddleware对应状态类型SummarizationState的定义。当它完成摘要后,会将描述此次摘要事件的信息封装成SummarizationEvent对象,并将其保存在Agent状态的_summarization_event字段中。SummarizationEvent利用定义的字段,记录这次瘦身行动的具体细节。

class_DeepAgentsSummarizationMiddleware(AgentMiddleware):state_schema=SummarizationStateclassSummarizationState(AgentState):_summarization_event:Annotated[NotRequired[SummarizationEvent|None],PrivateStateAttr]classSummarizationEvent(TypedDict):cutoff_index:intsummary_message:HumanMessage file_path:str|None

SummarizationEvent包含了三个字段:

  • cutoff_index:一个整数,代表摘要消息在消息列表中的位置。当下一次摘要发生时,这个字段可以用来确定从哪里开始进行摘要。否则就会出现摘要消息再次被摘要的情况,必然导致信息丢失;
  • summary_message:一个HumanMessage对象,代表摘要后的消息内容。这个消息会被插入到消息列表中,来替代被摘要掉的消息。这个消息的内容通常会包含一个摘要文本,以及一些关于摘要事件的元数据信息,例如摘要的时间、摘要的范围等;
  • file_path:一个字符串或None,代表存放完整对话历史的文件路径。如果_DeepAgentsSummarizationMiddleware配置了一个后端来保存完整的对话历史,那么这个字段就会包含保存的文件路径;如果没有配置后端或者保存失败,那么这个字段就会是None。由于调用LLM才会考虑压缩上下文文窗口的问题,所以摘要消息仅仅是提供给LLM使用,其他本地执行的工具或者节点依然使用原始的消息,这就是原始消息还得保留的原因。

3. 配置摘要使用的模型和规则

如下所示的是_DeepAgentsSummarizationMiddleware__init__方法的定义。它的参数决定了摘要使用的模型、触发的条件、保留的消息数量、以及一些其他的配置选项。

class_DeepAgentsSummarizationMiddleware(AgentMiddleware):def__init__(self,model:str|BaseChatModel,*,backend:BACKEND_TYPES,trigger:ContextSize|list[ContextSize]|None=None,keep:ContextSize=("messages",_DEFAULT_MESSAGES_TO_KEEP),token_counter:TokenCounter=count_tokens_approximately,summary_prompt:str=DEFAULT_SUMMARY_PROMPT,trim_tokens_to_summarize:int|None=_DEFAULT_TRIM_TOKEN_LIMIT,history_path_prefix:str="/conversation_history",truncate_args_settings:TruncateArgsSettings|None=None,**deprecated_kwargs:Any,)->NoneTokenCounter=Callable[[Iterable[MessageLikeRepresentation]],int]_DEFAULT_MESSAGES_TO_KEEP=20

各参数说明如下:

  • model:字符串或BaseChatModel对象,代表用于生成摘要的语言模型。可以是一个预定义的模型名称(例如"gpt-5.2-chat")或者一个已经实例化的模型对象;
  • backend:后端对象或配置,用于保存原始的对话历史。当消息列表被摘要时,原始的对话历史会被保存到这里,以便后续查询和参考;
  • trigger:ContextSize对象或针对不同类型的多个ContextSize列表,代表触发摘要的条件;
  • keep:ContextSize对象,代表在摘要后保留的消息数量,默认为20条;
  • token_counter:用于计算消息列表的Token数量的函数。这个函数会在摘要触发条件中被调用,以判断消息列表是否超过了Token数量的限制。默认值是count_tokens_approximately,这是一个近似计算Token数量的函数;
  • summary_prompt:代表用于生成摘要的提示词模板。这个模板会被用来指导模型如何生成摘要文本。默认值是DEFAULT_SUMMARY_PROMPT,这是一个预定义的摘要提示词模板;
  • trim_tokens_to_summarize:一个整数或None,代表在生成摘要时要保留的Token数量。如果消息列表的Token数量超过了这个整数,那么在生成摘要之前会先对消息列表进行修剪,以确保输入模型的内容不会过多。默认值为20;
  • history_path_prefix:保存完整对话历史的文件路径前缀,默认为/conversation_history
  • truncate_args_settings:TruncateArgsSettings对象或None,代表在修剪消息参数设置,如果工具参数很多或者每个参数的内容较长,可以通过这个设置控制针对参数的修剪策略;

对于我们演示的这个实例,由于我们为_DeepAgentsSummarizationMiddleware指定的backend是一个以当前工作目录为根目录的FilesystemBackend对象,所以我们会在子目录conversation_history中看到保存的完整对话历史文件,文件名通常包含一个唯一的标识符来区分不同的对话会话,例如session_51599319.md

ContextSize定义了DeepAgents衡量上下文窗口大小的三种度量衡。它允许我们在配置triggerkeep时,使用不同的单位。这是一个典型的联合类型,通过元组的第一个元素(字面量标签)来区分具体的类型。

ContextSize=ContextFraction|ContextTokens|ContextMessages ContextFraction=tuple[Literal["fraction"],float]ContextTokens=tuple[Literal["tokens"],int]ContextMessages=tuple[Literal["messages"],int]

三种类型体现了不同的度量方式:

  • ContextFraction:使用一个0到1之间的浮点数来表示消息列表中被摘要掉的消息占总消息数量的比例。例如(“fraction”, 0.5)代表当消息列表中的消息数量超过了总消息数量的一半时,就会触发摘要;
  • ContextTokens:使用一个整数来表示消息列表中被摘要掉的消息的Token数量。例如(“tokens”, 1000)代表当消息列表中的Token数量超过了1000时,就会触发摘要;
  • ContextMessages:使用一个整数来表示消息列表中被摘要掉的消息的数量。例如(“messages”, 5)代表当消息列表中的消息数量超过了5条时,就会触发摘要;

4. 利用针对模型调用的封装完成消息摘要

_DeepAgentsSummarizationMiddleware利用重写的wrap_model_call/awrap_model_call方法来实现摘要的逻辑。在调用模型和工具之前,它会检查当前的消息列表是否满足触发摘要的条件,如果满足条件就会进行摘要,并且将摘要后的消息列表继续传递给后续的模型调用和工具调用。

class_DeepAgentsSummarizationMiddleware(AgentMiddleware):defwrap_model_call(self,request:ModelRequest,handler:Callable[[ModelRequest],ModelResponse],)->ModelResponse|ExtendedModelResponseasyncdefawrap_model_call(self,request:ModelRequest,handler:Callable[[ModelRequest],Awaitable[ModelResponse]],)->ModelResponse|ExtendedModelResponse
http://www.jsqmd.com/news/793707/

相关文章:

  • 2026年质量好的主体结构工程检测/雷电防护装置检测/市政工程材料检测本地公司推荐 - 行业平台推荐
  • 嵌入式调试系统:DAP与ETB核心组件解析
  • 深入STM32以太网驱动层:DP83848 PHY芯片初始化、中断处理与lwip数据收发的HAL库实现详解
  • 如何5分钟实现微信群消息自动同步:wechat-forwarding完整指南
  • Gazebo物理仿真避坑指南:为什么你的机器人总打滑?手把手教你调ODE摩擦参数
  • LobsterPress v5.0:为AI Agent构建长期记忆系统的架构与实践
  • 从路径匹配到图像识别:深入理解豪斯多夫(Hausdorff)距离
  • SAP CO核心数据表深度解析:从COSP、COSS到COEP、COBK的业务映射与实战查询
  • LLM应用可观测性实战:基于OpenTelemetry与OpenLLMetry的监控方案
  • 深度学习材料生成:从CNN到Transformer的AI材料设计实战
  • 2026年口碑好的大型飞机模型/济南大型飞机模型长期合作厂家推荐 - 品牌宣传支持者
  • 手把手教你排查华为MDC-300F与激光雷达的通信故障:从接口定义到信号测量
  • RSR-core:低比特矩阵向量乘法的高性能优化引擎
  • 2026年知名的济南大型坦克模型/大型坦克模型/济南大型飞机模型/大型可开动装甲车模型多家厂家对比分析 - 行业平台推荐
  • Cursor AI 编码规则启动器:模块化配置与工程化实践指南
  • YOLOv13最新创新改进系列:YYOLOv13主干改进GhostNetV3 ,以极致轻量化之躯,赋能边缘AI实时检测,速度与精度完美融合,重新定义新一代视觉感知!【幽灵疾速,洞察无界】
  • [Deep Agents:LangChain的Agent Harness-09]利用MemoryMiddleware构建能够自我学习和进化的Agent
  • 4J32超因瓦合金厂商联系方式:优质超因瓦合金厂商盘点 - 品牌2026
  • 2026年口碑好的pvc手机防水袋/手机防水袋防水套品牌厂家推荐 - 品牌宣传支持者
  • 神经形态计算系统脉冲通信优化与BrainScaleS架构解析
  • 告别复制粘贴!用jQuery的load()函数5分钟搞定网站公共头部和底部
  • 2026年质量好的水性环氧彩砂涂料横向对比厂家推荐 - 行业平台推荐
  • 2026年靠谱的浙江钥匙链钥匙扣挂件/钥匙扣挂件/立体公仔钥匙扣挂件口碑好的厂家推荐 - 品牌宣传支持者
  • AI助力船舶稳性计算:Gemini3.1Pro设计辅助新思路
  • 1Panel深度解析:现代化Linux服务器运维面板的设计、实践与避坑指南
  • 2026年知名的四川alc隔墙板/四川轻质隔墙alc板实力工厂推荐 - 行业平台推荐
  • 2026年口碑好的江西有轨段滑门/豪华段滑门/有轨段滑门优质厂家推荐榜 - 行业平台推荐
  • PCL 1.7/1.8在Ubuntu 16.04/18.04下编译报错合集:从‘undefined reference’到‘not a member’的保姆级修复指南
  • 怎么通过 Python 脚本实现企业微信机器人定时发送日报
  • SincNet实战:用PyTorch复现说话人识别,并探讨其对抗攻击的脆弱性与防御思路