[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 自己就会主动执行

backend.download_files(
  ["/memories/AGENTS.md"]
)
源码就是这样写的。
 

>>> 写新记忆 <<<

这却需要模型做判断:

用户刚才这句话值得长期保存吗?

[例如]

用户:“以后解释代码先讲原理,再给代码。”

模型看到你的:memory_policy,然后自己判断:

这是稳定偏好
→ 值得长期保存

这时候它才调用:edit_file(...)

  
(2) “读旧记忆”和“决定产生新记忆”是两个性质不同的动作。
source:

我要找 /memories/AGENTS.md

backend:

/memories/AGENTS.md 在我的世界里到底意味着什么
应该去 State、Store、真实磁盘,还是其他地方找?

 

(3) MemoryMiddleware 是不是专门为“读”而生?

从执行机制上说,基本是的。

它自己主要干两件事:

① 自动读取 memory sources
② 把读到的内容塞进模型的 system prompt
 
 
 

教学版本代码

用户说:
“以后给我解释技术问题时,
先讲直观原理,再给代码。”
        ↓

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>
View Prompt
"""
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>
View Prompt
"""
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 更强在哪里 / 具体是什么意思”给你排一下。

FeatureDeep AgentsHermesHermes 强在哪里
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 扩展层更成熟

 

posted @ 2026-08-20 22:01  郝壹贰叁  阅读(16)  评论(0)    收藏  举报