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

[Deep Agents:LangChain的Agent Harness-09]利用MemoryMiddleware构建能够自我学习和进化的Agent

Deep Agents将MemoryMiddleware管理的Memory视为Agent的核心资产,并将其作为一等公民。Memory与工具、模型具有同等的重要性,是Agent正常运转的必备组件。Memory建立在由Backend抽象出来的文件系统上,意味着这个所谓的记忆不再存储在不可见的内存或复杂的向量数据库里,而是变成了看得见、摸得着、改得了的磁盘文件。Agent像写日记一样记录经验,而我们可以像管理硬盘一样管理它的灵魂。基于文件系统的Memory设计带来了几个显著的优势:

  • 可解释性:我们直接查看文件内容知道Agent记了什么。如果出现了推理问题,我们查看文件确实是否源于记忆错乱
  • 易于管理和调试:如果它记错了,我们只需要手动删掉或改掉那个文件,它的Memory就立刻变了;

MemoryMiddleware提供了一个持久化的记忆系统,使得Agent能够跨对话会话积累和利用知识。这种记忆系统不仅可以存储事实性的知识,还可以记录经验和学习过程,从而使得Agent具备了学习和进化的能力。借助于MemoryMiddleware,Agent可以在每次对话结束时将新的知识写入到Memory中,并且在下一次对话开始时从Memory中读取之前积累的知识来指导当前的对话。这种机制使得Agent能够不断地学习和适应用户的需求,提供更加个性化和智能化的服务。

1. 让订餐助手记住你的饮食习惯

下面的示例展示了如何利用MemoryMiddleware来构建一个具有学习能力的订餐Agent。这个Agent会根据用户的饮食习惯来处理订单请求,并且在用户提到新的饮食习惯或忌口时,立即将这些信息写入到Memory中,以便在未来的对话中参考和使用。

fromtypingimportLiteralfromlangchain.agentsimportcreate_agentfromlangchain.agents.middlewareimportwrap_model_callfromlangchain.toolsimporttoolfromdeepagents.middlewareimportMemoryMiddleware,FilesystemMiddlewarefromlangchain_openaiimportChatOpenAIfromdeepagents.backendsimportFilesystemBackendfromdotenvimportload_dotenvimportasyncio load_dotenv()@tooldefplace_order(dish_name:Literal["麻婆豆腐","辣椒炒肉","剁椒鱼头","番茄炒蛋","清蒸鲈鱼","清炒菜心"],quantity:int):"""订餐工具,提供菜名和数量,返回订单信息"""returnf"Order placed:{quantity}x{dish_name}"backend=FilesystemBackend(root_dir="./agent_data",virtual_mode=True)file_system_middleware=FilesystemMiddleware(backend=backend)memory_middleware=MemoryMiddleware(backend=backend,sources=["preferences.md"])agent=create_agent(model=ChatOpenAI(model="gpt-5.2-chat"),tools=[place_order],system_prompt=("你是一个私人订餐助手,帮助用户下单。请使用`place_order`工具尽可以根据用户的饮食习惯来处理订单请求""无需用户确认,自己选择菜品和数量来下单。""如果用户提到了新的饮食习惯或忌口,立即写入`preferences.md`中"),middleware=[file_system_middleware,memory_middleware]# type: ignore)asyncdefmain():result=awaitagent.ainvoke(input={"messages":[{"role":"user","content":"帮我点一份外卖,我不能吃辣"}]})print(result["messages"][-1].content)result=awaitagent.ainvoke(input={"messages":[{"role":"user","content":"帮我点一份外卖"}]})print(result["messages"][-1].content)asyncio.run(main())

这个演示实例利用create_agent创建的Agent注册了用来点餐的place_order工具。注册的FilesystemMiddlewareMemoryMiddleware都采用同一个FilesystemBackend,根目录为./agent_data(注册FilesystemMiddleware的目的在于我们需要适用它提供的操作文件的工具)。MemoryMiddleware配置了一个名为preferences.md的Memory源文件,我们可以把它看作是Agent的饮食偏好日记。我们利用系统提示词让Agent尽量按照用户饮食习惯来点餐。

我们两次调用了Agent,第一次调用中用户明确表示了不能吃辣的饮食习惯,Agent在处理这个请求时会将这个信息写入到preferences.md文件中。第二次调用中,用户没有提到饮食习惯的信息,但是由于Agent已经在第一次调用中记住了用户不能吃辣的偏好,所以它会参考这个信息来点餐,从而避免点了辣的菜,如下的输出充分体现了这一点:

好的,已经帮你下单啦 😊 考虑到你**不能吃辣**,我给你搭配了一份清淡又均衡的外卖: - 清蒸鲈鱼 × 1 - 番茄炒蛋 × 1 - 清炒菜心 × 1 都是不辣、口味清爽的菜式,营养也很均衡。 如果你想换成偏素一点、偏肉一点,或者有其他忌口,随时告诉我,我可以下次直接按你的习惯来点。 好的,已经帮你点好外卖啦 ✅
好的,已经帮你点好外卖啦 ✅ 根据你**不能吃辣**的饮食习惯,我给你安排了一份清淡又营养的搭配: - 番茄炒蛋 × 1 - 清蒸鲈鱼 × 1 - 清炒菜心 × 1 如果你想换口味、加菜,或者下次有新的忌口/偏好,随时告诉我就行~

MemoryMiddleware的实现其实非常简单。其state_schema字段表示的状态类型MemoryState中包含了一个memory_contents字段,它返回的字典用来存储记忆内容。它的__init__方法接受一个backend参数和一个sources参数,前者用于指定Memory的存储后端,后者用于指定源文件列表。

classMemoryMiddleware(AgentMiddleware[MemoryState,ContextT,ResponseT]):state_schema=MemoryStatedef__init__(self,*,backend:BACKEND_TYPES,sources:list[str],)->Nonedefbefore_agent(self,state:MemoryState,runtime:Runtime,config:RunnableConfig)->MemoryStateUpdate|Noneasyncdefabefore_agent(self,state:MemoryState,runtime:Runtime,config:RunnableConfig)->MemoryStateUpdate|Nonedefwrap_model_call(self,request:ModelRequest[ContextT],handler:Callable[[ModelRequest[ContextT]],ModelResponse[ResponseT]],)->ModelResponse[ResponseT]asyncdefawrap_model_call(self,request:ModelRequest[ContextT],handler:Callable[[ModelRequest[ContextT]],Awaitable[ModelResponse[ResponseT]]],)->ModelResponse[ResponseT]classMemoryState(AgentState):memory_contents:NotRequired[Annotated[dict[str,str],PrivateStateAttr]]classMemoryStateUpdate(TypedDict):memory_contents:dict[str,str]

2.MemoryMiddleware是如何让Agent拥有跨Thread记忆的?

MemoryMiddleware通过重写before_agent/abefore_agent方法来实现Memory的读取和更新逻辑。这两个方法返回的MemoryStateUpdate对象具有与MemoryState完全一致的结构,加载的Memory内容转换成路径/内容的映射字典后,会存储在memory_contents通道中。before_agent/abefore_agent方法会利用它避免重复加载。在重写的wrap_model_call/awrap_model_call方法中,Memory的内容会转换成LLM请求系统提示词的一部分,进而实现利用记忆指导模型的推理的目的。对于上面演示的这个例子,在附加了由MemoryMiddleware生成的系统提示词后,完整的提示词内容如下(不包含FileSystemMiddleware附加的系统提示词),其中- 忌口:不能吃辣就是写入preferences.md中的内容:

你是一个私人订餐助手,帮助用户下单。请使用`place_order`工具尽可以根据用户的饮食习惯来处理订单请求无需用户确认,自己选择菜品和数量来下单。如果用户提到了新的饮食习惯或忌口,立即写入`preferences.md`中 <agent_memory> preferences.md - 忌口:不能吃辣 </agent_memory> <memory_guidelines> The above <agent_memory> was loaded in from files in your filesystem. As you learn from your interactions with the user, you can save new knowledge by calling the `edit_file` tool. **Learning from feedback:** - One of your MAIN PRIORITIES is to learn from your interactions with the user. These learnings can be implicit or explicit. This means that in the future, you will remember this important information. - When you need to remember something, updating memory must be your FIRST, IMMEDIATE action - before responding to the user, before calling other tools, before doing anything else. Just update memory immediately. - When user says something is better/worse, capture WHY and encode it as a pattern. - Each correction is a chance to improve permanently - don't just fix the immediate issue, update your instructions. - A great opportunity to update your memories is when the user interrupts a tool call and provides feedback. You should update your memories immediately before revising the tool call. - Look for the underlying principle behind corrections, not just the specific mistake. - The user might not explicitly ask you to remember something, but if they provide information that is useful for future use, you should update your memories immediately. **Asking for information:** - If you lack context to perform an action (e.g. send a Slack DM, requires a user ID/email) you should explicitly ask the user for this information. - It is preferred for you to ask for information, don't assume anything that you do not know! - When the user provides information that is useful for future use, you should update your memories immediately. **When to update memories:** - When the user explicitly asks you to remember something (e.g., "remember my email", "save this preference") - When the user describes your role or how you should behave (e.g., "you are a web researcher", "always do X") - When the user gives feedback on your work - capture what was wrong and how to improve - When the user provides information required for tool use (e.g., slack channel ID, email addresses) - When the user provides context useful for future tasks, such as how to use tools, or which actions to take in a particular situation - When you discover new patterns or preferences (coding styles, conventions, workflows) **When to NOT update memories:** - When the information is temporary or transient (e.g., "I'm running late", "I'm on my phone right now") - When the information is a one-time task request (e.g., "Find me a recipe", "What's 25 * 4?") - When the information is a simple question that doesn't reveal lasting preferences (e.g., "What day is it?", "Can you explain X?") - When the information is an acknowledgment or small talk (e.g., "Sounds good!", "Hello", "Thanks for that") - When the information is stale or irrelevant in future conversations - Never store API keys, access tokens, passwords, or any other credentials in any file, memory, or system prompt. - If the user asks where to put API keys or provides an API key, do NOT echo or save it. **Examples:** Example 1 (remembering user information): User: Can you connect to my google account? Agent: Sure, I'll connect to your google account, what's your google account email? User: john@example.com Agent: Let me save this to my memory. Tool Call: edit_file(...) -> remembers that the user's google account email is john@example.com Example 2 (remembering implicit user preferences): User: Can you write me an example for creating a deep agent in LangChain? Agent: Sure, I'll write you an example for creating a deep agent in LangChain <example code in Python> User: Can you do this in JavaScript Agent: Let me save this to my memory. Tool Call: edit_file(...) -> remembers that the user prefers to get LangChain code examples in JavaScript Agent: Sure, here is the JavaScript example<example code in JavaScript> Example 3 (do not remember transient information): User: I'm going to play basketball tonight so I will be offline for a few hours. Agent: Okay I'll add a block to your calendar. Tool Call: create_calendar_event(...) -> just calls a tool, does not commit anything to memory, as it is transient information </memory_guidelines>
http://www.jsqmd.com/news/793690/

相关文章:

  • 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复现说话人识别,并探讨其对抗攻击的脆弱性与防御思路
  • FastbootEnhance终极指南:高效管理Android设备刷机与分区操作
  • 彩铃服务技术解析:从SS7信令到智能网实现
  • 2026年比较好的静电高压模块喷枪/高压模块喷枪品牌厂家推荐 - 品牌宣传支持者
  • ARM架构TLB维护机制与RVALE2/3指令详解
  • 企业微信机器人发送 Markdown 消息样式乱码怎么修复
  • KMS智能激活终极指南:5分钟永久激活Windows和Office全系列
  • ARMv9内存管理单元与TCR2_EL2寄存器详解
  • 2026年比较好的手机防水袋定制/浙江充气手机防水袋/手机袋手机防水袋多家厂家对比分析 - 行业平台推荐
  • VSCode智能体开发框架:构建上下文感知的AI编程助手
  • 2026年4月饰面防火涂料生产商推荐,室内外膨胀型钢结构防火涂料/饰面防火涂料/防火涂料,饰面防火涂料供应厂家哪家权威 - 品牌推荐师
  • 2026年质量好的浙江钥匙链钥匙扣挂件/钥匙扣挂件定制/浙江软胶钥匙扣挂件多家厂家对比分析 - 品牌宣传支持者
  • 基于Next.js与Appwrite构建开源股票分析平台:架构设计与工程实践
  • Flowable实战指南(一、从零搭建审批流)
  • 从AI代码生成行为模式分析到高效人机协作编程实践
  • ATtiny88驱动OLED屏幕实战:如何用U8x8库在8KB Flash里玩转显示(附代码与字体优化技巧)
  • SoC设计中信号完整性优化与MCMM技术解析
  • 双引擎AI代码助手:Claude与Codex集成架构与工程实践