前面我们介绍了 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 首先是一个逻辑路径,不一定对应真实磁盘文件。
例如,同样执行:
write_file(
file_path="/workspace/report.md",
content="Research report"
)
底层可能有三种结果:
- 文件内容保存在 LangGraph 的 State 中。
- 文件内容保存在 LangGraph Store 中。
- 文件实际写入服务器的某个目录。
Agent 不需要知道这些区别。
Deep Agents 通过 BackendProtocol 定义统一的文件操作接口,再由不同 Backend 完成具体实现。
其调用关系可以概括为:
Agent
│
▼
Filesystem Tools
│
▼
FilesystemMiddleware
│
▼
BackendProtocol
│
├── StateBackend
│
├── StoreBackend
│
├── FilesystemBackend
│
└── CompositeBackend
这里需区分两个概念:Filesystem 是 Agent 使用文件的能力,Backend 是支撑这种能力的存储实现。
前者解决 Agent 如何读写文件,后者解决文件保存在哪里、由谁管理、何时失效。
二、StateBackend 状态存储
StateBackend 是 Deep Agents 默认使用的 Backend。
不指定 backend 参数时:
from deepagents import create_deep_agent
agent = create_deep_agent(
model="openai:gpt-5.5"
)
实际使用的就是:
from deepagents.backends import StateBackend
agent = create_deep_agent(
model="openai:gpt-5.5",
backend=StateBackend()
)
它不会把文件写入操作系统目录,而是将文件内容保存在 LangGraph 的 Agent State 中。
例如,Agent 创建:
/workspace/plan.md
/workspace/research.md
/workspace/report.md
这些文件只是逻辑上存在,并不会在磁盘上实际存在。
State 与 Checkpoint
StateBackend 的一个重要特点,是与 LangGraph 的状态管理机制结合。
Agent 执行过程中,文件的变更会更新 State。如果配置了 Checkpointer,这些状态还可以随执行过程保存。
例如:
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 仍然可以读取这份文件。
用法如下:
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 的文件。
例如:
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。
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 看到的虚拟路径会映射到指定目录下。
例如:
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。
例如:
from deepagents.backends import (
CompositeBackend,
StateBackend,
StoreBackend,
)
backend = CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(
namespace=lambda rt: (
"users",
rt.context["user_id"],
)
),
}
)
在这个配置中:
/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 中。
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:
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:
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 修改真实项目文件,可以增加一条路由:
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。
例如:
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。
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 | 存储位置 | 典型场景 |
|---|---|---|
| StateBackend | LangGraph State | 临时工作文件 |
| StoreBackend | LangGraph 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 机制。这也是下一篇将继续讨论的内容。
社区讨论
参与讨论
有问题或想法?欢迎继续讨论。