前面几篇介绍了 Deep Agents 的整体架构、Planning 和 Tools。Agent 已经能够拆解任务、调用工具。
然而,当 Agent 执行复杂任务时,多步骤会产生中间结果,此时,需考虑大量中间结果应该放在哪里。
Deep Agents 引入 Filesystem,为 Agent 提供一个可读写的工作空间。Agent 可以把资料、笔记和中间结果保存为文件,需要时再读取。
本文重点介绍 Filesystem 的作用、核心 API、运行机制,以及如何在实际项目中使用。
一、为什么 Agent 需要文件系统
普通 Agent 通常通过 Messages 保存交互过程。
例如:
User: 分析三个技术方案
Agent: 调用工具获取方案 A
Tool: 返回 5000 字
Agent: 调用工具获取方案 B
Tool: 返回 8000 字
Agent: 调用工具获取方案 C
Tool: 返回 6000 字
Agent: 开始比较分析
模型需要在上下文中处理这些材料。随着任务推进,后续产生的分析、修改和工具结果又会继续增加上下文长度。
这会带来几个问题:
- 上下文膨胀:大量历史材料持续占用 Token。
- 信息难以定位:需要重新查找某项结果时,只能依赖历史消息。
- 中间结果难以复用:同一份分析需多次重复生成。
- 任务恢复困难:对话被压缩后,原始细节可能不会再保留。
为了解决上述问题,可引入文件系统(Filesystem)。
引入 Filesystem 后,Agent 可以采用另一种方式:
/workspace/
├── sources/
│ ├── solution-a.md
│ ├── solution-b.md
│ └── solution-c.md
├── analysis/
│ ├── comparison.md
│ └── risks.md
└── report.md
搜索得到的资料放在 sources/,分析过程放在 analysis/,最终结果写入 report.md。
模型不必在每次推理时都重新读取全部资料。它可以先查看目录,再选择需要的文件或片段。
这里有一个重要区别:Messages 记录 Agent 的交互过程,Filesystem 保存 Agent 的工作内容。
文件系统不会自动减少所有 Token 消耗。只有当 Agent 将信息写入文件,并在后续按需读取时,才能真正减少不必要的上下文占用。
二、Deep Agents 的 Filesystem
Deep Agents 提供的是一套虚拟文件系统接口。
它允许 Agent 使用类似操作系统的方式管理文件,但这些文件不一定存储在本地磁盘上。
整体结构如下:
Deep Agent
|
v
FilesystemMiddleware
|
+---------+---------+
| | |
read_file write_file grep ...
| | |
+---------+---------+
|
v
Backend
|
+--------+--------+
| | |
State Store Local
这里涉及两个关键组件。
1. FilesystemMiddleware:提供操作能力
FilesystemMiddleware 是 Deep Agents 的内置中间件。
它负责向模型提供文件操作工具,处理工具调用,并将实际读写操作交给 Backend。
使用 create_deep_agent() 时,框架默认集成这一能力。
2. Backend:负责文件读写与存储
Backend 决定文件实际保存在哪里。
例如:
StateBackend:保存到 LangGraph State。FilesystemBackend:操作真实文件系统。StoreBackend:使用 LangGraph Store。CompositeBackend:根据路径将操作交给不同 Backend。
默认使用 StateBackend()。因此,Agent 写入 /workspace/report.md,并不意味着磁盘上会出现同名文件。
文件路径是 Agent 使用的逻辑地址,实际存储方式由 Backend 决定。
这也是为什么需要把 Filesystem 和 Backend 分开。前者解决如何操作文件,后者解决文件保存在哪里。
三、Filesystem 的核心 API
Deep Agents 提供了一组内置文件工具。
| 工具 | 主要作用 |
|---|---|
ls | 列出目录中的文件 |
read_file | 读取文件内容 |
write_file | 创建或覆盖文件 |
edit_file | 修改文件中的指定内容 |
glob | 根据文件名模式查找文件 |
grep | 搜索文件内容 |
delete | 删除文件或目录 |
execute | 执行 Shell 命令,需要支持执行的 Backend |
其中,delete 需要 Deep Agents 0.7 或更新版本,并且取决于 Backend 是否支持。execute 也不是普通 Filesystem 默认具备的能力。
这些工具由 Agent 调用,不需要手工注册。
下面的参数示例表示模型发起的工具调用,并非文件对象的方法。
1. ls:了解工作空间
{
"path": "/workspace"
}
调用 ls 后,Agent 可以获得目录下的文件信息,例如名称、大小和修改时间。
实际工作中,Agent 应先了解已有文件,再决定需要读取什么,避免重复生成资料。
2. read_file:按需读取内容
{
"file_path": "/workspace/sources/solution-a.md",
"offset": 0,
"limit": 80
}
read_file 支持按行分页读取。
file_path:文件路径。offset:从哪一行开始读取。limit:本次读取的行数。
对于几千行的日志或长文档,Agent 可以先读取前面几十行,再根据需要继续读取其他部分。
返回内容包含行号,便于定位资料和修改位置。
3. write_file:创建工作成果
{
"file_path": "/workspace/analysis/summary.md",
"content": "# 分析结果\n\n方案 A 的实现成本较低。"
}
write_file 用于创建新文件,也可以完全替换已有文件。
需要注意:write_file 不是追加操作。
当目标文件已经存在时,新内容会覆盖原来的内容。因此,对于已经生成的报告,不宜每次修改都重新写入全文。
4. edit_file:局部修改
{
"file_path": "/workspace/analysis/summary.md",
"old_string": "方案 A 的实现成本较低。",
"new_string": "方案 A 的初期实现成本较低,但维护成本仍需评估。"
}
edit_file 通过精确字符串匹配完成替换。
默认要求 old_string 在文件中唯一。如果需要替换所有匹配项,可以设置 replace_all=True。
实际使用时,应先通过 read_file 确认原文,再进行修改。否则容易因为空格、换行或内容变化导致匹配失败。
5. glob 与 grep:定位目标文件
当工作空间包含大量文件时,逐个读取效率很低。
glob 根据文件路径模式进行匹配:
/workspace//*.md
它可以帮助 Agent 找出工作空间中的 Markdown 文件。
grep 则用于搜索文件内容,例如查找包含“风险”或“数据库连接”的文件。
两者的区别很简单:glob 找文件,grep 找内容。
综合起来,上述工具处理顺序是:
ls / glob
↓
grep
↓
read_file
↓
edit_file
先缩小范围,再读取和修改。这样既减少无效工具调用,也能降低误操作的概率。
四、实战:让 Agent 管理自己的工作文件
我们构建一个简单的需求分析 Agent。
任务是读取两份材料,分别保存原文,提取关键问题,最后生成需求分析报告。
1. 安装依赖
pip install -U deepagents langchain-openai
同时配置 API KEY。
2. 创建 Agent
from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from langgraph.checkpoint.memory import MemorySaver
def get_material(name: str) -> str:
"""读取指定名称的演示材料。"""
materials = {
"requirements": (
"用户希望支持批量导入任务、"
"任务状态跟踪和失败重试。"
),
"operations": (
"系统需要保留操作日志,"
"限制并发任务数量,"
"并提供失败告警。"
),
}
return materials.get(name, "未找到材料")
agent = create_deep_agent(
model="openai:gpt-5.5",
tools=[get_material],
backend=StateBackend(),
checkpointer=MemorySaver(),
system_prompt="""
你是一名需求分析助手。
工作规范:
1. 使用 get_material 获取原始材料。
2. 将材料分别保存到 /workspace/sources/。
3. 将分析结果保存到 /workspace/analysis.md。
4. 根据分析生成 /workspace/report.md。
5. 修改已有文件前先读取文件。
6. 不要编造原始材料中不存在的事实。
7. 完成后给出报告路径和主要结论。
""",
)
这里有三个重要配置。
tools 注册业务工具,负责获取材料。
backend=StateBackend() 提供虚拟文件存储。
checkpointer=MemorySaver() 在当前进程中保存线程检查点,让相同线程的后续调用能够接着使用之前的状态。
3. 执行任务
config = {
"configurable": {
"thread_id": "analysis-001"
}
}
result = agent.invoke(
{
"messages": [{
"role": "user",
"content": (
"分析 requirements 和 operations "
"两份材料,并形成需求报告。"
)
}]
},
config=config,
)
print(result["messages"][-1].content)
按照系统提示,Agent 应依次获取资料、写入文件、分析并生成报告。
预期的工作空间结构如下:
/workspace/
├── sources/
│ ├── requirements.md
│ └── operations.md
├── analysis.md
└── report.md
目录中的文件由 Agent 在运行时生成,实际调用顺序取决于模型决策。
4. 在后续对话中复用文件
result = agent.invoke(
{
"messages": [{
"role": "user",
"content": (
"读取 /workspace/report.md,"
"补充一个实施优先级建议。"
)
}]
},
config=config,
)
print(result["messages"][-1].content)
因为使用相同的 thread_id 和 Checkpointer,Agent 可以继续访问该线程先前保存的文件状态。
这里的 MemorySaver 仅适合开发与演示。进程结束后,这些数据不会自动成为持久化文件。
另外,Agent 是否真的调用了文件工具、是否正确生成报告,需要通过工具调用记录和实际文件状态验证,不能只检查模型的最终回答。
五、工程实践:怎样设计工作空间
有了文件操作能力,不代表 Agent 自己就能有效管理工作内容。
真正影响执行质量的,是文件如何组织、什么时候写入、什么时候读取。
1. 按内容用途划分目录
对于一个 Research Agent,可以采用如下结构:
/workspace/
├── sources/ # 原始材料
├── notes/ # 阅读笔记
├── analysis/ # 分析结果
├── drafts/ # 报告草稿
└── final/ # 最终成果
这种结构比将所有内容写入一个 research.md 更容易维护。
原始材料通常不应随意修改;分析文件可以反复更新;最终报告则应明确版本。
通过目录划分,可以让 Agent 知道不同文件分别承担什么作用。
2. 区分原始证据和分析结论
例如,研究某个框架性能时,不能只保存:
框架 A 性能更好。
应该同时保留测试条件:
测试环境:Python 3.13
并发数量:100
请求数量:10000
框架 A:平均延迟 120ms
框架 B:平均延迟 145ms
以上数值仅为示例。
如果没有原始数据,后续 Agent 无法判断结论是否可靠,也无法在测试条件变化后重新分析。
工程中建议遵循一个原则:原始资料保留证据,分析文件记录判断,最终报告面向交付。
3. 用小文件管理阶段成果
一个复杂任务通常包含多个步骤:
收集资料
↓
整理事实
↓
比较方案
↓
识别风险
↓
形成结论
每个阶段都可以形成独立文件。
例如:
analysis/
├── facts.md
├── comparison.md
└── risks.md
当某一步出现问题时,只需要重新处理相关文件,而不必重复生成全部内容。
这种设计还方便 Subagent 协作。不同 Subagent 可以负责不同研究方向,将成果写入共享工作空间,主 Agent 再汇总。
不过,共享文件也意味着可能发生写入冲突。并行任务最好各自使用独立路径,由主 Agent 统一合并结果。
六、文件系统如何改善 Context 管理
Filesystem 的另一个重要作用,是与 Context Engineering 配合。
假设某次搜索返回了几万字资料。
使用文件系统后,可采用以下流程:
工具返回大量内容
↓
保存原始结果
↓
提取关键内容
↓
后续按需读取
Deep Agents 的 FilesystemMiddleware 还支持自动转存过大的工具结果,并用文件路径提示替代完整内容,使 Agent 后续可以通过 read_file 分段读取。
但这里需要区分两件事。
文件存储负责保留信息,Context 管理负责选择当前需要哪些信息。
如果 Agent 每次都把所有文件重新读入上下文,即使使用 Filesystem,也无法解决上下文膨胀问题。
因此,实际工程中应让 Agent 优先查看目录、搜索相关内容,再读取需要部分的内容。
七、易忽略的问题
1. 文件不等于长期记忆
Agent 将内容写入文件,不代表它下次启动时一定能够读取。
默认 StateBackend 的文件保存在当前线程状态中。跨调用恢复依赖 Checkpointer,跨线程共享则需要其他存储方案。
即使文件可以持久化,Agent 也需要知道文件存在,并在合适的时候读取。
因此,Filesystem、Persistence 和 Memory 虽然有关联,但解决的问题不同。
2. 虚拟文件不等于真实磁盘文件
默认 StateBackend 中的 /workspace/report.md 只是虚拟路径。
如果需要让 Agent 处理真实项目文件,可以配置 FilesystemBackend:
from deepagents.backends import (
CompositeBackend,
FilesystemBackend,
StateBackend,
)
backend = CompositeBackend(
default=StateBackend(),
routes={
"/workspace/": FilesystemBackend(
root_dir="./agent-workspace",
virtual_mode=True,
)
},
)
再将这个 backend 传给 create_deep_agent()。
这样,/workspace/ 下的文件操作会映射到指定的本地目录,而框架内部产生的临时文件仍由默认 StateBackend 管理。
这里也要注意:本地文件操作与 Shell 执行是两种不同能力。使用 FilesystemBackend 不会自动赋予 Agent 执行任意命令的权限。
3. 控制修改和删除权限
write_file 能覆盖文件,delete 可以删除文件甚至递归删除目录。
因此,不能只依赖提示词要求 Agent 小心操作。
对于只需读取资料的任务,可以限制 FilesystemMiddleware 暴露的工具:
from deepagents.middleware import FilesystemMiddleware
filesystem = FilesystemMiddleware(
backend=backend,
tools=[
"read_file",
"ls",
"glob",
"grep",
],
)
将该实例传入 create_deep_agent(middleware=[filesystem]),就可以隐藏写入、编辑、删除和执行工具。
这项工具白名单能力需要 Deep Agents 0.7 或更新版本。
对于需要修改真实文件的任务,还应结合路径权限、人工审批以及独立运行环境控制风险。
4. 不要把工作空间当作无限存储
文件虽然不必全部进入模型上下文,但依然存在存储成本。
特别是大量搜索结果、日志和自动转存的工具输出,可能持续积累。
实际系统应关注文件大小、数量、生命周期和清理策略。
临时资料与需要长期保留的成果应采用不同的保存策略,避免工作空间随着任务次数增加而失去可维护性。
总结
Filesystem 是 Deep Agents 处理复杂任务的重要基础能力。
它让 Agent 能够把资料、笔记、分析和最终成果保存为文件,并通过 ls、read_file、write_file、edit_file、glob 和 grep 等工具管理工作内容。
这套机制的价值,在于让任务执行过程中的信息具有明确的位置、结构和复用方式。配合合理的目录设计与按需读取策略,可以减少重复处理,并为长任务和多 Agent 协作提供支持。
不过,文件系统只定义操作方式,不决定数据必须保存在哪里。实际项目还需要选择合适的 Backend,处理持久化、隔离和安全边界。
社区讨论
参与讨论
有问题或想法?欢迎继续讨论。