[sdk] 03 - Deep Agents - Memory and Filesystem
其实就是文件系统中间件。但值得单独拿出来学习。
FilesystemMiddleware │ ▼ File Operations │ ▼ BackendProtocol │ ┌──────────────────┼──────────────────┐ ▼ ▼ ▼ StateBackend StoreBackend FilesystemBackend 当前 thread 跨 thread 真实磁盘 临时文件 长期文件 \ / \ / └───────┬──────┘ ▼ CompositeBackend 按路径选择去哪
FileSystem Middleware
配置完成后,Agent 将自动获得以下四个文件操作工具:
ls:列出当前文件系统中的文件列表。read_file:读取指定文件的全部内容或特定行数。write_file:创建新文件并写入内容。edit_file:对已有文件进行修改(支持追加、替换等操作)。
(1) FileSystemBackend:访问本地磁盘
from dotenv import load_dotenv from langchain.agents import create_agent from langchain_deepseek import ChatDeepSeek from deepagents import FilesystemMiddleware from deepagents.backends import FilesystemBackend from langchain.messages import HumanMessage load_dotenv() model = ChatDeepSeek( model="deepseek-chat", ) agent_local = create_agent( model=model, tools=[], middleware=[ FilesystemMiddleware( backend=FilesystemBackend(root_dir="./test_dir", virtual_mode=True) ) ] ) res = agent_local.invoke( { 'messages': [HumanMessage("调用工具写入一个文件,文件名为:测试.txt, 内容为: '测试'")] # 也体现了 “能解决的问题范围” } ) print(res)
注意: 使用 FileSystemBackend 时需格外注意文件安全性。由于大模型存在幻觉和不稳定性,建议对敏感文件提前进行备份和保护。
(2) StateBackend:线程级短期记忆
它就像一张“草稿纸”,适合在单次任务中临时记录中间信息,任务结束后内容自动释放,不留痕迹。
from dotenv import load_dotenv from langchain.agents import create_agent from langchain_deepseek import ChatDeepSeek from deepagents import FilesystemMiddleware from deepagents.backends import FilesystemBackend, StateBackend from langchain.messages import HumanMessage load_dotenv() model = ChatDeepSeek( model="deepseek-chat", ) agent_local = create_agent( model=model, tools=[], middleware=[ FilesystemMiddleware( # backend=lambda runtime: StateBackend(runtime)
backend=StateBackend() # <-- v0.5起推荐 ) ] ) res = agent_local.invoke( { 'messages': [ HumanMessage("调用工具写入一个文件,文件名为:测试.txt, 内容为: '你好帅'"), HumanMessage('调用工具读取名为测试.txt的文件,告诉我里面的内容') ] }, ) print(res['messages'][-1].content)
invoke 后到底会发生什么?
第一次 Model Call 的时候,模型其实同时看到了两条 HumanMessage。
第一次 Model Call,挂载中间件,告诉模型你有很多工具可以使用:
-
- write_file
- read_file
- ls
- grep
- ...
大模型知道了自己的工具集合,然后给出自己的决定如下:
write_file( file_path="测试.txt", content="你好帅" )
FilesystemMiddleware接住这个“请求” as above.
于是虚拟文件系统里出现了新创建的文件以及写入的内容。
第二次 Model Call 的时候类似,本质也是调用工具。
第三次 Model Call 的时候类似,得到了工具返回的读取结果,之后要返回给用户,也要经过一次模型。(这一次容易被忽略)
(3) StoreBackend:跨线程长期记忆
不同 Agent 之间共享存储。
Shared InMemoryStore ▲ │ StoreBackend ▲ ┌────────────┴────────────┐ │ │ Agent 1 Agent 2 │ │ write_file() read_file() │ │ ▼ ▼ /测试.txt ───────────────→ 读取内容 “你好帅”
from dotenv import load_dotenv from langchain.agents import create_agent from langchain_deepseek import ChatDeepSeek from langchain.messages import HumanMessage from deepagents import FilesystemMiddleware from deepagents.backends import StoreBackend from langgraph.store.memory import InMemoryStore # ============================================================ # 1. 加载环境变量 # ============================================================ load_dotenv() # ============================================================ # 2. 创建模型 # ============================================================ model = ChatDeepSeek( model="deepseek-chat", ) # ============================================================ # 3. 创建一个“共享 Store” # ============================================================ # 这里使用 InMemoryStore 只是为了教学方便。 # # 注意: # InMemoryStore 虽然叫 Store, # 但它仍然只存在于当前 Python 进程内。 # # Python 程序结束后,数据会消失。 # # 正式生产环境通常会换成真正持久化的 Store, # 比如数据库 / Redis / Postgres 等后端。 store = InMemoryStore() # ============================================================ # 4. 创建第一个 Agent # ============================================================ agent_local1 = create_agent( model=model, # 没有业务工具 tools=[], # 关键: # 把同一个 LangGraph Store 提供给 Agent Runtime store=store, middleware=[ FilesystemMiddleware( # StoreBackend 会把“文件” # 保存到上面的 LangGraph Store 中。 # # 新版 Deep Agents 不需要再写: # # backend=lambda runtime: StoreBackend(runtime) # # 直接传实例即可。 backend=StoreBackend() # 使用这个,激活了上面的store参数。 ) ] ) # ============================================================ # 5. Agent 1 写文件 # ============================================================ agent_local1.invoke({ "messages": [ HumanMessage( "调用工具写入一个文件," "文件名为 /测试.txt," "内容为:你好帅" ) ] }) # ============================================================ # 6. 创建第二个完全不同的 Agent 实例 # ============================================================ agent_local2 = create_agent( model=model, tools=[], # 最关键的一点: # # Agent 2 使用的是和 Agent 1 # 完全同一个 store 实例。 # # 因此两个 Agent 背后看到的是同一份存储。 store=store, middleware=[ FilesystemMiddleware( backend=StoreBackend() ) ] ) # ============================================================ # 7. Agent 2 读取 Agent 1 写入的文件 # ============================================================ res = agent_local2.invoke({ "messages": [ HumanMessage( "调用工具读取 /测试.txt," "告诉我里面的内容" ) ] }) # ============================================================ # 8. 打印最终答案 # ============================================================ print(res["messages"][-1].content)
(4) CompositeBackend:复合后端 - 混合存储
from dotenv import load_dotenv from langchain.agents import create_agent from langchain_deepseek import ChatDeepSeek from langchain.messages import HumanMessage from deepagents import FilesystemMiddleware from deepagents.backends import ( StateBackend, StoreBackend, CompositeBackend, ) from langgraph.store.memory import InMemoryStore # ============================================================ # 1. 加载环境变量 # ============================================================ load_dotenv() # ============================================================ # 2. 创建模型 # ============================================================ model = ChatDeepSeek( model="deepseek-chat", ) # ============================================================ # 3. 创建一个共享 Store # ============================================================ # InMemoryStore 只是为了教学方便。 # # 它可以让不同 thread 共享数据, # 但数据只存在当前 Python 进程中。 # # 程序一旦关闭,数据就消失。 # # 所以它适合 demo, # 不代表“真正永久持久化”。 store = InMemoryStore() # ============================================================ # 4. 创建 CompositeBackend # ============================================================ # 这是整个例子的核心。 # # CompositeBackend 本身不真正保存文件。 # # 它像一个“路由器”: # # 普通路径 # ↓ # StateBackend # # /memories/ 开头的路径 # ↓ # StoreBackend # # # 换句话说: # # /draft.txt # /report.md # /temp/data.txt # # → StateBackend # # # /memories/preferences.txt # /memories/profile.md # # → StoreBackend backend = CompositeBackend( # -------------------------------------------------------- # default: # # 如果文件路径没有匹配任何 route, # 默认就交给 StateBackend。 # -------------------------------------------------------- default=StateBackend(), # -------------------------------------------------------- # routes: # # 根据文件路径前缀选择 Backend。 # # 所有 /memories/... 文件 # 都交给 StoreBackend。 # # StoreBackend 使用 Agent Runtime 中传入的 # LangGraph store。 # -------------------------------------------------------- routes={ "/memories/": StoreBackend() }, ) # ============================================================ # 5. 创建 Agent # ============================================================ agent = create_agent( model=model, # 这里没有手动添加业务 tools。 # # FilesystemMiddleware 会自动给 Agent 注入: # # write_file # read_file # ls # grep # glob # edit_file # delete # # 等文件操作工具。 tools=[], # 这个 store 会被 Agent Runtime 使用。 # # StoreBackend 会访问这个 store。 store=store, middleware=[ FilesystemMiddleware( # 新版本直接传 Backend 实例。 # # 不再使用旧写法: # # backend=lambda runtime: CompositeBackend(...) # backend=backend ) ], ) # ============================================================ # 6. Thread 1 # # 用户告诉 Agent: # “我最爱的水果是草莓。” # # 并明确要求保存到: # # /memories/preferences.txt # # 因为路径以 /memories/ 开头, # CompositeBackend 会把这次 write_file # 路由给 StoreBackend。 # ============================================================ config1 = { "configurable": { "thread_id": "1" } } agent.invoke( { "messages": [ HumanMessage( "我最爱的水果是草莓。" "请把这个偏好保存在 /memories/preferences.txt" ) ] }, config=config1, ) # ============================================================ # 7. Thread 2 # # 注意: # # 这是完全不同的 thread。 # # 如果刚才 preferences.txt 是存在 StateBackend, # Thread 2 默认就拿不到。 # # 但是因为它存到了 StoreBackend, # 而两个 thread 又共享同一个 store, # 所以 Thread 2 仍然能够读到。 # ============================================================ config2 = { "configurable": { "thread_id": "2" } } res = agent.invoke( { "messages": [ HumanMessage( "请读取 /memories/preferences.txt," "告诉我最爱的水果是什么。" ) ] }, config=config2, ) # ============================================================ # 8. 打印最终结果 # ============================================================ print(res["messages"][-1].content)
自定义长短记忆
Background Knowldge
需要先了解一些背景知识。
(1) “读旧记忆”和“决定产生新记忆”是两个性质不同的动作。
>>> 读旧记忆 <<<
这是系统确定要做的事情:
每次 Agent 启动,我都希望你看到长期记忆。
所以不需要 LLM 做决定:
Agent 每次开始工作之前,MemoryMiddleware 自己就会主动执行:
>>> 写新记忆 <<<
这却需要模型做判断:
用户刚才这句话值得长期保存吗?
[例如]
模型看到你的:memory_policy,然后自己判断:
这时候它才调用:edit_file(...)
我要找 /memories/AGENTS.md
backend:
/memories/AGENTS.md 在我的世界里到底意味着什么?
应该去 State、Store、真实磁盘,还是其他地方找?
(3) MemoryMiddleware 是不是专门为“读”而生?
从执行机制上说,基本是的。
它自己主要干两件事:
教学版本代码
用户说: “以后给我解释技术问题时, 先讲直观原理,再给代码。” ↓ MemoryMiddleware 先加载旧长期记忆 + Memory Policy ↓ 如果聊天太长: SummarizationMiddleware 调用 DeepSeek #1 → 总结旧聊天 ↓ 主 Agent 调用 DeepSeek #2 同时看到: ① 用户当前问题 ② 旧长期记忆 ③ Memory Policy: “稳定用户偏好值得长期保存” ↓ DeepSeek 判断: “先讲原理、再给代码” 属于稳定偏好, 未来其他 conversation 也有用 ↓ 因此决定调用: edit_file("/memories/AGENTS.md", ...) ↓ FilesystemMiddleware 执行写入 ↓ DeepSeek #3 看到 edit_file 成功 → 再给用户最终回复
以上是下面代码的逻辑。
from dotenv import load_dotenv from langchain_deepseek import ChatDeepSeek from langgraph.checkpoint.memory import InMemorySaver from langgraph.store.memory import InMemoryStore from deepagents import create_deep_agent, MemoryMiddleware from deepagents.backends import ( StateBackend, StoreBackend, CompositeBackend, ) from deepagents.middleware.summarization import SummarizationMiddleware load_dotenv() # ============================================================ # 1. Model # ============================================================ model = ChatDeepSeek( model="deepseek-chat", ) # ============================================================ # 2. 两种 Backend # ============================================================ # A. Deep Agent 自己使用的文件系统 Backend # # 只使用 StateBackend: # read_file / write_file / edit_file 等文件工具 # 操作的文件都存在当前 thread 的 state 中。 agent_state_backend = StateBackend() # B. MemoryMiddleware 使用的 Backend long_term_store = InMemoryStore() # 真正创建的是一个内存里的键值存储, 也可以考虑 PostgresStore. compositeMemory_backend = CompositeBackend(
default = StateBackend(),
routes = { "/memories/": StoreBackend( store=long_term_store, namespace=lambda _runtime: ( "demo-user", "filesystem", ), ) }, ) # ============================================================ # 3. 自定义长期记忆 Prompt # ============================================================ CUSTOM_MEMORY_PROMPT = """
<agent_memory> {agent_memory} </agent_memory> <memory_policy> 只有未来的新 conversation 中仍然有价值的信息, 才应该保存为长期记忆。 应该保存: - 用户明确要求长期记住的信息 - 稳定偏好 - 长期项目约束 - 反复出现的工作方式 - 用户纠正后形成的稳定原则 不要保存: - 临时状态 - 一次性任务 - 很快过期的信息 - 未确认的推测 - 密码、Token 等敏感信息 长期记忆文件使用: /memories/AGENTS.md 如果需要更新长期记忆,可以使用 write_file 或 edit_file。 </memory_policy>
"""
long_term_memory = MemoryMiddleware( # 往往为读而生。 # 这里的 backend 专门给 MemoryMiddleware 使用。 # #sources 中的路径会通过这个 backend 去解释。 # # 因此: # /memories/AGENTS.md # ↓ # memory_backend # ↓ # StoreBackend # ↓ # long_term_store backend=compositeMemory_backend, sources=[ "/memories/AGENTS.md" ], system_prompt=CUSTOM_MEMORY_PROMPT, ) # ============================================================ # 4. 自定义短期记忆压缩 Prompt # ============================================================ CUSTOM_SUMMARY_PROMPT = """
把较早的 conversation 压缩成简洁摘要。 优先保留: - 当前任务目标 - 已经完成的工作 - 尚未完成的工作 - 重要决定及原因 - 用户刚刚纠正过的问题 - 后续继续工作必须知道的上下文 尽量删除: - 寒暄 - 重复讨论 - 已经失去价值的细节 - 与当前任务无关的信息 不要把临时 conversation summary 当成用户的永久长期记忆。 <messages> {messages} </messages>
"""
short_term_summary = SummarizationMiddleware( model=model, # 短期 conversation history 属于当前 thread, # 所以这里使用 StateBackend。 backend=agent_state_backend, summary_prompt=CUSTOM_SUMMARY_PROMPT, trigger=("fraction", 0.8), keep=("messages", 20), ) # ============================================================ # 5. 创建 Deep Agent # ============================================================ agent = create_deep_agent( model=model, # !!! 本例最重要的地方之一 !!! # # Deep Agent 内置 FilesystemMiddleware 使用的是 # agent_state_backend。 # # 因此: # # read_file(...) # write_file(...) # edit_file(...) # # 全部操作当前 thread 的 StateBackend。 backend=agent_state_backend, # 为写,这里会写到不一样的地方。 checkpointer=InMemorySaver(), middleware=[ short_term_summary, # 谁来做summary?自然是同一个模型。毕竟一个模型是可以同时给出多个问题的答案的。 long_term_memory, # 为读 ], ) # ============================================================ # 6. Thread 1 # ============================================================ agent.invoke( { "messages": [ { "role": "user", "content": ( "请记住:以后给我解释技术问题时," "先讲直观原理,再给代码。" ), } ] }, config={ "configurable": { "thread_id": "thread-1" } }, ) # ============================================================ # 7. Thread 2 # ============================================================ result = agent.invoke( { "messages": [ { "role": "user", "content": "我喜欢你怎样给我解释技术问题?", } ] }, config={ "configurable": { "thread_id": "thread-2" } }, ) print(result["messages"][-1].content)
Hermes 的记忆管理优势
Hermes 把“记忆”从一个文件加载功能,做成了一整套持续学习系统。
我按“Deep Agents 已经有 / Hermes 更强在哪里 / 具体是什么意思”给你排一下。
| Feature | Deep Agents | Hermes | Hermes 强在哪里 |
|---|---|---|---|
| 1. 跨 session 长期记忆 | ✅ 有 | ✅ 有 | 两者都有,基础能力差不多 |
| 2. 自动把记忆注入 Prompt | ✅ MemoryMiddleware |
✅ USER.md / MEMORY.md 自动注入 |
基础能力相似 |
| 3. 用户画像与普通记忆分开 | ❌ 默认没有语义分层 | ✅ USER.md + MEMORY.md |
明显更强 |
| 4. 记忆容量控制 | ❌ 默认没有 | ✅ 严格字符上限 | 明显更强 |
| 5. 记忆满了自动整理 | ❌ 没有完整机制 | ✅ Agent 合并、替换、删除旧记忆 | 明显更强 |
| 6. 专用 Memory Tool | ⚠️ 通常靠 write_file/edit_file |
✅ memory add/replace/remove |
更专业、更可控 |
| 7. 后台 Memory Observer | ❌ 默认主要靠主 Agent 判断 | ✅ 有 background review | 很重要的提升 |
| 8. 可用独立便宜模型做 Memory Review | ❌ 默认没有 | ✅ 支持 auxiliary model | 架构更高级 |
| 9. 历史会话搜索 | ⚠️ 有 conversation offload,但没有专门完整检索层 | ✅ session_search + SQLite FTS5 |
明显更强 |
| 10. 事实记忆与程序记忆分开 | ✅ 有 Memory + Skills | ✅ Memory + Skills | 都有,但 Hermes 自动学习更强 |
| 11. 从经验自动生成/修改 Skill | ⚠️ Skills 有,但默认不是完整 self-learning loop | ✅ 自动形成 procedural memory | Hermes 很强的地方 |
| 12. 记忆写入审批 | 可自己用权限/HITL实现 | ✅ write_approval 内建 |
Hermes 产品化更完整 |
| 13. 外接高级 Memory Engine | 要自己接 | ✅ 可接 Mem0、Honcho、Hindsight、OpenViking 等 | Hermes 扩展层更成熟 |

浙公网安备 33010602011771号