前面我们介绍了 Deep Agents 的 Filesystem 机制。通过 ls、read_file、write_file 等工具,Agent 可以把任务计划、中间结果和工作文档保存为文件,再按需读取,减少对模型上下文的依赖。

本文进一步回答一个问题:Agent 写入的文件,究竟保存在哪里?

例如,Agent 执行 write_file("/workspace/report.md", content),这个文件一定会出现在磁盘上吗?任务结束后还能读取吗?另一个 Agent 或新的会话能否访问?

答案取决于 Deep Agents 的 Backend。

Deep Agents 把文件操作和底层存储分离。Agent 使用统一的文件接口,Backend 决定这些文件如何保存、读取,以及能够保留多久。

本文重点介绍 Backend 的工作机制、主要实现、相关 API,以及实际项目中的存储设计。

一、Backend 的职责边界

在普通应用中,文件路径通常对应操作系统中的实际目录。

但在 Deep Agents 中,/workspace/report.md 首先是一个逻辑路径,不一定对应真实磁盘文件。

例如,同样执行:

代码片段Python
write_file(
    file_path="/workspace/report.md",
    content="Research report"
)

底层可能有三种结果:

  • 文件内容保存在 LangGraph 的 State 中。
  • 文件内容保存在 LangGraph Store 中。
  • 文件实际写入服务器的某个目录。

Agent 不需要知道这些区别。

Deep Agents 通过 BackendProtocol 定义统一的文件操作接口,再由不同 Backend 完成具体实现。

其调用关系可以概括为:

代码片段Text
Agent
  │
  ▼
Filesystem Tools
  │
  ▼
FilesystemMiddleware
  │
  ▼
BackendProtocol
  │
  ├── StateBackend
  │
  ├── StoreBackend
  │
  ├── FilesystemBackend
  │
  └── CompositeBackend

这里需区分两个概念:Filesystem 是 Agent 使用文件的能力,Backend 是支撑这种能力的存储实现。

前者解决 Agent 如何读写文件,后者解决文件保存在哪里、由谁管理、何时失效。

二、StateBackend 状态存储

StateBackend 是 Deep Agents 默认使用的 Backend。

不指定 backend 参数时:

代码片段Python
from deepagents import create_deep_agent
 
agent = create_deep_agent(
    model="openai:gpt-5.5"
)

实际使用的就是:

代码片段Python
from deepagents.backends import StateBackend
 
agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=StateBackend()
)

它不会把文件写入操作系统目录,而是将文件内容保存在 LangGraph 的 Agent State 中。

例如,Agent 创建:

代码片段Text
/workspace/plan.md
/workspace/research.md
/workspace/report.md

这些文件只是逻辑上存在,并不会在磁盘上实际存在。

State 与 Checkpoint

StateBackend 的一个重要特点,是与 LangGraph 的状态管理机制结合。

Agent 执行过程中,文件的变更会更新 State。如果配置了 Checkpointer,这些状态还可以随执行过程保存。

例如:

代码片段Python
from langgraph.checkpoint.memory import InMemorySaver
 
agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=StateBackend(),
    checkpointer=InMemorySaver()
)
 
config = {
    "configurable": {
        "thread_id": "task-001"
    }
}
 
agent.invoke(
    {
        "messages": [{
            "role": "user",
            "content": "创建 /plan.md,记录任务计划"
        }]
    },
    config=config
)

当后续调用继续使用 task-001 时,可以通过 Checkpoint 恢复之前的文件状态。

但换成新的 thread_id,就不会自动获得这些文件。

需要注意,InMemorySaver 仅适合本地演示。进程退出后,内存中的 Checkpoint 就会丢失。生产环境需要使用持久化 Checkpointer。

适用场景

StateBackend 适合保存任务执行过程中产生的临时文件,例如:

  • Planning 生成的任务计划。
  • Research 过程中的中间资料。
  • 大型工具结果的临时存放。
  • Subagent 生成的阶段性文档。

尤其适合执行一次任务、生成结果后就不再需要这些文件的场景。

另外,Supervisor 和 Subagent 可以共享当前执行上下文中的 StateBackend 文件。Subagent 结束后,已写入 State 的文件仍可供其他 Agent 使用。

它的主要限制也很明确:不同 Thread 默认不共享文件。如果希望 Agent 下次启动新的会话后,还能读取以前保存的信息,就需要其他 Backend。

三、StoreBackend 持久存储

StoreBackend 用于把文件保存到 LangGraph Store 中。

与 StateBackend 不同,StoreBackend 的核心能力是跨 Thread 访问数据。

例如,用户今天让 Agent 记录一份技术偏好,明天启动新的对话,Agent 仍然可以读取这份文件。

用法如下:

代码片段Python
from deepagents import create_deep_agent
from deepagents.backends import StoreBackend
from langgraph.store.memory import InMemoryStore
 
agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=StoreBackend(
        namespace=lambda rt: ("demo-user",)
    ),
    store=InMemoryStore()
)

这里有两个关键参数。

store 是真正负责保存数据的 LangGraph Store 实例。

namespace 则定义数据的命名空间,用于区分不同用户、租户或 Agent 的文件。

例如:

代码片段Text
user-001
  ├── preferences.md
  └── knowledge.md

user-002
  ├── preferences.md
  └── knowledge.md

即使文件名相同,不同 Namespace 中的数据也应当相互隔离。

Store 不等于数据库

还需要区分 StoreBackend 与底层 Store。

StoreBackend 本身不决定数据一定保存到 PostgreSQL、Redis 或其他数据库。

它依赖 LangGraph 的 BaseStore 实现。

前面使用的 InMemoryStore,仅能在当前进程生命周期内保存数据,不能提供重启后的持久性。

如果希望跨进程、跨部署长期保存,需要配置 PostgreSQL 等持久化 Store,或者使用平台提供的持久化服务。

所以,StoreBackend 能够跨 Thread 访问文件,但能否在重启后恢复,仍取决于底层存储。

四、FilesystemBackend 本地存储

前两种 Backend 都不要求文件实际存在于操作系统的目录中。

如果希望 Agent 直接处理本地项目,例如读取源码、修改配置文件或生成 Markdown 文档,就可以使用 FilesystemBackend。

代码片段Python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
 
agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=FilesystemBackend(
        root_dir="/srv/agent-workspace",
        virtual_mode=True
    )
)

这里的 root_dir 指定实际文件目录。

开启 virtual_mode=True 后,Agent 看到的虚拟路径会映射到指定目录下。

例如:

代码片段Text
Agent 看到的路径:

/reports/summary.md

实际文件路径:

/srv/agent-workspace/reports/summary.md

这种方式特别适合 Coding Agent。

Agent 可以读取项目文件、生成代码、修改文档。修改结果直接保存在工作目录中,可以被 Git、IDE 或其他开发工具使用。

文件路径与运行环境

FilesystemBackend 的持久性取决于实际磁盘或挂载存储。

如果运行在本地开发机上,文件通常可以在程序退出后继续保留。

但如果部署在临时容器或 Serverless 环境中,工作目录可能随着实例销毁而丢失。

因此,使用真实文件系统不意味着数据一定具有持久性。

还有一个更重要的边界:root_dir 并不等于安全沙箱。

virtual_mode=True 可以加强文件操作的路径限制,但它不提供完整的进程隔离、网络隔离或资源限制。

对于处理不可信任务的在线服务,不应把宿主机的敏感目录直接交给 Agent。

五、CompositeBackend 混合存储

实际项目往往同时存在多种文件。

例如,一个 Research Agent 可能需要保存:

  • 当前任务的临时计划。
  • 跨会话使用的研究偏好。
  • 最终生成的报告文件。

这些数据的生命周期不同。如果全部放在 StateBackend 中,长期信息无法直接跨 Thread 共享。

如果全部放在 StoreBackend 中,又可能产生不必要的持久化数据和存储成本。

CompositeBackend 就是用来解决这个问题的。

它根据文件路径,把操作分配给不同的 Backend。

例如:

代码片段Python
from deepagents.backends import (
    CompositeBackend,
    StateBackend,
    StoreBackend,
)
 
backend = CompositeBackend(
    default=StateBackend(),
    routes={
        "/memories/": StoreBackend(
            namespace=lambda rt: (
                "users",
                rt.context["user_id"],
            )
        ),
    }
)

在这个配置中:

代码片段Text
/workspace/plan.md
    └── StateBackend

/workspace/draft.md
    └── StateBackend

/memories/preferences.md
    └── StoreBackend

/memories/research-rules.md
    └── StoreBackend

default 指定默认 Backend。

routes 负责匹配特定路径。如果路径匹配某个前缀,就交给对应 Backend 处理。

当路由存在重叠时,Deep Agents 优先匹配更长的路径前缀。

因此,可以通过目录约定,把不同生命周期的数据分开管理。

一个完整示例

下面构建一个简单的 Research Agent。

要求它把临时研究记录保存在 State 中,而用户研究偏好保存在 Store 中。

代码片段Python
from typing import TypedDict
 
from deepagents import create_deep_agent
from deepagents.backends import (
    StateBackend,
    StoreBackend,
    CompositeBackend,
)
from langgraph.store.memory import InMemoryStore
from langgraph.checkpoint.memory import InMemorySaver
 
 
class AgentContext(TypedDict):
    user_id: str
 
 
backend = CompositeBackend(
    default=StateBackend(),
    routes={
        "/memories/": StoreBackend(
            namespace=lambda rt: (
                "users",
                rt.context["user_id"],
            )
        )
    }
)
 
agent = create_deep_agent(
    model="openai:gpt-5.5",
    backend=backend,
    store=InMemoryStore(),
    checkpointer=InMemorySaver(),
    context_schema=AgentContext,
    system_prompt="""
你是一个技术研究助手。
 
临时研究记录保存在 /workspace/ 目录。
需要跨会话保留的用户偏好保存在 /memories/ 目录。
不要把临时搜索结果写入长期存储。
"""
)

接下来,使用第一个 Thread:

代码片段Python
agent.invoke(
    {
        "messages": [{
            "role": "user",
            "content": (
                "将本次研究主题记录到 "
                "/workspace/topic.md,"
                "并把我偏好技术细节和代码示例的"
                "要求保存到 /memories/preferences.md"
            )
        }]
    },
    config={
        "configurable": {
            "thread_id": "thread-001"
        }
    },
    context={
        "user_id": "user-001"
    }
)

如果 Agent 按要求成功写入两个文件,它们就会进入不同存储。

随后,使用同一个用户身份,但创建新的 Thread:

代码片段Python
agent.invoke(
    {
        "messages": [{
            "role": "user",
            "content": (
                "读取 /memories/preferences.md,"
                "根据其中的要求介绍 LangGraph"
            )
        }]
    },
    config={
        "configurable": {
            "thread_id": "thread-002"
        }
    },
    context={
        "user_id": "user-001"
    }
)

新 Thread 不能自动读取第一个 Thread 中的 /workspace/topic.md。

但只要底层 Store 数据仍然存在,且 Namespace 相同,就可以读取 /memories/preferences.md。

这段代码使用内存版 Store 和 Checkpointer,适合验证机制。生产环境需要替换成持久化实现。

如果还需要让 Agent 修改真实项目文件,可以增加一条路由:

代码片段Python
from deepagents.backends import FilesystemBackend
 
backend = CompositeBackend(
    default=StateBackend(),
    routes={
        "/memories/": StoreBackend(
            namespace=lambda rt: (
                "users",
                rt.context["user_id"],
            )
        ),
        "/project/": FilesystemBackend(
            root_dir="/srv/agent-project",
            virtual_mode=True,
        ),
    },
)

此时,Agent 就拥有三种不同生命周期的存储:

路径Backend主要用途
/workspace/StateBackend当前任务的临时文件
/memories/StoreBackend跨会话信息
/project/FilesystemBackend实际项目文件

这是一种比较实用的存储组织方式。

需要注意,示例中的 /project/ 是单工作区配置。多租户服务不能直接让所有用户共享同一个物理目录,而应当使用独立工作区或隔离的执行环境。

六、Backend 的统一协议

不同 Backend 能够互相替换,关键在于它们实现了统一的 BackendProtocol。

这套协议提供的核心方法包括:

方法作用
ls()列出目录内容
read()读取文件
write()写入文件
edit()修改已有文件
glob()按路径模式查找文件
grep()搜索文件内容
delete()删除文件或目录,可选能力

需要区分 Backend API 与 Agent 使用的 Tool。

例如:

代码片段Text
Agent Tool         Backend API

read_file    ────► read
write_file   ────► write
edit_file    ────► edit
ls           ────► ls
glob         ────► glob
grep         ────► grep

Agent 调用的是工具,Middleware 再通过 Backend 完成操作。

这种设计有一个直接好处:可以接入自定义存储。

例如,企业希望把 Agent 生成的报告保存到对象存储中,就可以实现自己的 Backend,将文件路径映射成对象 Key。

代码片段Python
from deepagents.backends.protocol import BackendProtocol
 
class ObjectStorageBackend(BackendProtocol):
 
    def ls(self, path):
        ...
 
    def read(self, file_path, offset=0, limit=2000):
        ...
 
    def write(self, file_path, content):
        ...
 
    def edit(
        self,
        file_path,
        old_string,
        new_string,
        replace_all=False,
    ):
        ...
 
    def glob(self, pattern, path="/"):
        ...
 
    def grep(self, pattern, path=None, glob=None):
        ...

这里只展示接口结构,实际实现还需要返回协议要求的 LsResult、ReadResult、WriteResult 等结果对象,并正确处理错误。

其中,read() 支持按行分页读取,适合处理较长的文档。grep() 和 glob() 则分别提供内容搜索和路径搜索。

七、生产环境的存储边界

Backend 选型本身并不复杂,真正容易出问题的是数据生命周期和系统边界。

Checkpointer 与 Backend

这两个组件经常被混淆。

Checkpointer 主要保存 Agent 的运行状态,包括消息、执行进度和图状态,用于恢复执行。

Backend 负责 Agent 通过文件接口访问的数据。

当使用 StateBackend 时,两者存在联系:文件内容属于 State,可以进入 Checkpoint。

但使用 StoreBackend 或 FilesystemBackend 时,文件通常由外部存储负责,不会因为配置了 Checkpointer 就自动获得相同的持久化能力。

因此,运行状态恢复与文件持久化需要分别设计。

内部文件与业务文件

Deep Agents 还会使用 Backend 保存部分内部数据,例如较大的工具输出、对话历史归档等。

这些数据通常位于 /large_tool_results/、/conversation_history/ 等内部路径。

如果直接把 FilesystemBackend 设为默认 Backend,内部数据也可能进入项目目录。

这会让项目文件与 Agent 运行产物混在一起。

更合适的方式是使用 CompositeBackend,把业务工作目录单独路由到 FilesystemBackend,其余内部文件保留在 StateBackend 或指定的独立存储中。

对于大量图片、二进制文件,较新的 Deep Agents 还支持通过 FilesystemMiddleware 配置 offload_binary_content=True,将部分媒体内容从消息历史中转存到外部 Backend,减少 Checkpoint 体积。

但如果目标路径仍然指向 StateBackend,就无法真正达到将二进制数据移出 Checkpoint 的目的。

文件并发与版本控制

多个 Agent 同时处理任务时,还需要考虑文件写入冲突。

例如两个 Subagent 同时修改 /workspace/report.md,可能发生内容覆盖、修改失败或结果不一致。

工程上通常需要限制同一文件的并发写入,由 Supervisor 统一合并结果,或者通过版本号、条件写入等方式检测冲突。

对于重要的业务文件,还应当建立独立的版本管理和审计机制,不能把一次 write_file 成功当作完整的事务保证。

存储权限与执行权限

Backend 支持文件读写,不代表 Agent 就具有执行 Shell 命令的能力。

普通 BackendProtocol 主要定义文件操作。

如果需要执行命令,则要使用实现 SandboxBackendProtocol 的 Backend,例如支持执行能力的 Sandbox Backend。

LocalShellBackend 也提供 execute 能力,但它直接在宿主机执行命令,并不提供真正的进程隔离。

因此,文件存储、命令执行和安全沙箱应当分别考虑。不能因为配置了文件路径限制,就认为执行环境已经安全。

这一部分也是下一篇 Shell、Execute 与 Sandbox 的重点。

八、Backend 的选型原则

综合来看,几种主要 Backend 的差别在于数据保存位置和生命周期。

Backend存储位置典型场景
StateBackendLangGraph State临时工作文件
StoreBackendLangGraph Store跨会话文件
FilesystemBackend实际文件系统本地工程文件
CompositeBackend按路径组合多个 Backend混合存储

对于简单 Agent,默认 StateBackend 通常已经足够。

如果需要长期保存用户偏好、知识或记忆,可以增加 StoreBackend。

如果 Agent 需要修改真实项目,就使用 FilesystemBackend,或者具有文件操作能力的隔离 Sandbox。

当一个 Agent 同时需要临时工作空间、长期存储和业务文件时,CompositeBackend 更适合作为统一入口。

但无论选择哪种方式,都要明确文件的访问范围、保存期限以及运行环境的安全边界。

总结

Deep Agents 的 Backend 把 Agent 的文件操作与底层存储解耦。

Agent 使用统一的文件接口,不需要直接处理数据库、磁盘目录或对象存储的实现差异。

StateBackend 适合当前 Thread 的工作文件,StoreBackend 解决跨 Thread 的数据共享,FilesystemBackend 负责真实文件系统的访问,CompositeBackend 则把它们组织在同一个逻辑文件空间中。

在实际工程中,关键是根据数据的生命周期选择存储方式,并明确 Checkpoint、长期存储和执行环境之间的边界。

理解 Backend 以后,就能进一步理解 Deep Agents 的执行环境:文件可以被保存和读取,但 Agent 如果还需要运行代码、操作命令行,就必须引入 Shell、Execute 与 Sandbox 机制。这也是下一篇将继续讨论的内容。