Deep Agents 学习笔记:Overview 与默认文件系统工具
Deep Agents 在 LangChain Agent 的基础上增加文件系统、任务规划、子 Agent、上下文管理、记忆、权限控制和人工审批等能力。
Deep Agents 学习笔记:Overview 与默认文件系统工具
1. Deep Agents 是什么
Deep Agents 可以理解为:
LangChain Agent + 更完整的执行环境
普通 Agent 通常是:
模型思考 → 调用工具 → 得到结果 → 再生成回答Deep Agents 在这个基础上增加了更适合复杂任务的能力:
文件系统
任务规划
子 Agent
上下文管理
记忆
权限控制
人工审批所以它更适合处理多步骤任务,比如:
- 代码审查
- 项目文件分析
- 自动生成报告
- 多工具协作任务
- 复杂文档处理
2. create_deep_agent() 的作用
create_deep_agent() 用来创建一个 Deep Agent。
跑通之前先备齐三样东西 下面的示例不是
pip install deepagents就能直接跑的:
Python 版本:
deepagents要求 Python 3.11+(它依赖的 LangGraph 生态基线在这里)。3.10 及以下装不上或运行时报错。模型 provider 包要单独装。
deepagents本身不带任何厂商 SDK,model="google_genai:..."这种字符串只是声明,对应的集成包得自己装:pip install deepagents pip install langchain-google-genai # 用 Gemini # pip install langchain-anthropic # 用 Claude # pip install langchain-openai # 用 GPT漏装的表现是
create_deep_agent()或首次invoke()时抛导入错误,而不是在安装阶段报错。凭据:对应 provider 的 API Key 要放进环境变量(
GOOGLE_API_KEY/ANTHROPIC_API_KEY/OPENAI_API_KEY)。不要写进代码里。另外注意示例里的模型名会随时间失效,写的时候查一下当前可用的模型 ID。
示例:
from deepagents import create_deep_agent
def get_weather(city: str) -> str:
"""Get weather for a given city."""
return f"It's always sunny in {city}!"
agent = create_deep_agent(
model="google_genai:gemini-3.5-flash",
tools=[get_weather],
system_prompt="You are a helpful assistant",
)核心参数:
| 参数 | 作用 | 类比 |
|---|---|---|
model |
指定使用哪个大模型 | 大脑 |
tools |
指定 Agent 可以调用哪些工具 | 手脚 |
system_prompt |
指定 Agent 的行为规则 | 说明书 |
记忆:
model = 谁来思考
tools = 能做什么
system_prompt = 应该怎么做3. tools=[get_weather] 的作用
tools=[get_weather]意思是:
把 Python 函数
get_weather交给 Agent,让 Agent 可以在需要时调用它。
模型本身只能“想”和“说”,工具函数让它可以“做事”。
例如用户问:
what is the weather in sfAgent 可以判断需要调用:
get_weather("sf")然后把工具返回结果整理成最终回答。
4. system_prompt 的作用
system_prompt="You are a helpful assistant"它相当于 Agent 的行为说明书。
例如:
system_prompt="You are a code review assistant. Carefully inspect code and explain issues."这个 Agent 就更偏向代码审查助手。
总结:
tools 负责 Agent 能不能做某事
system_prompt 负责 Agent 应该怎么做事5. agent.invoke() 的作用
agent.invoke(
{"messages": [{"role": "user", "content": "what is the weather in sf"}]}
)作用是:
给 Agent 输入一条用户消息,让它真正开始运行。
结构解释:
| 部分 | 作用 |
|---|---|
agent.invoke(...) |
执行 Agent |
messages |
用户和 Agent 的对话输入 |
role: "user" |
表示消息来自用户 |
content |
用户具体说了什么 |
记忆:
create_deep_agent() = 创建 Agent
agent.invoke() = 执行 Agent6. Deep Agents 默认文件系统工具
Deep Agents 默认会给 Agent 一些文件系统工具,让它可以像程序员一样操作文件。
常见工具包括:
ls 查看文件列表
read_file 读取文件
write_file 写入文件
edit_file 修改文件
delete 删除文件或目录
glob 按规则找文件
grep 搜索文件内容
execute 执行命令,取决于 sandbox backend 是否支持这些工具适合做:
读项目代码
搜索函数调用
修改代码
生成报告
执行测试例如代码审查任务中,Agent 可能会:
1. ls 查看项目结构
2. read_file 读取代码
3. grep 搜索函数调用
4. edit_file 修改代码
5. write_file 生成报告⚠️ 默认情况下这些工具碰不到你的真实文件**** 错误认知: 装完就跑上面这段代码审查流程,以为
ls会列出当前项目目录、read_file能读到你磁盘上的源码、write_file会在项目里生成一份报告文件。实际结果:
create_deep_agent()的默认后端是StateBackend——一个存在 LangGraph state 里的虚拟文件系统,完全不接触宿主磁盘。所以:
ls列出来的是空的(或者只有 Agent 自己刚写进去的文件);read_file("src/main.py")读不到你的代码,因为那个虚拟文件系统里根本没有这个文件;write_file写出来的"报告"只存在于 state 里,任务结束后你在磁盘上找不到它。而模型往往不会因此报错卡住——它会顺着空目录继续编,最后给你一份看起来完整、其实基于零份真实代码的审查报告。
原因: 这个默认是有意为之的安全设计:新手跑起来的第一个 Agent 不应该能直接改你的硬盘。要碰真实文件必须显式选择后端。
正确做法: 按需要显式指定 backend:
后端 作用 作用域 StateBackend(默认)虚拟文件系统,存在 LangGraph state 里 单个 thread,跨轮次保留,不跨 thread FilesystemBackend真实本地磁盘,可配置根目录 宿主机 StoreBackend通过 LangGraph BaseStore 做持久化 跨 thread LocalShellBackend宿主机文件 + shell 执行,无隔离 宿主机 Sandbox 后端 隔离环境里的文件 + shell 沙箱内 CompositeBackend按路径把不同目录路由到不同后端 混合 要做真实的代码审查,用
FilesystemBackend并把根目录限定到项目路径。选LocalShellBackend前先想清楚——它在宿主机上跑任意命令,且不受permissions约束(见第 13 节)。
7. Running without the default filesystem tools
文档中的这一节意思是:
Deep Agents 默认有文件系统工具,但可以选择不把这些工具暴露给模型。
也就是说,不是完全删除文件系统能力,而是让模型看不到这些默认工具。
示例:
from deepagents import HarnessProfile, register_harness_profile
register_harness_profile(
"anthropic:claude-sonnet-4-6",
HarnessProfile(
excluded_tools=frozenset(
{"ls", "read_file", "write_file", "edit_file", "glob", "grep"}
),
),
)核心代码:
excluded_tools=frozenset(
{"ls", "read_file", "write_file", "edit_file", "glob", "grep"}
)作用:
把这些工具从 Agent 可见工具列表中排除掉8. excluded_tools 的作用
excluded_tools 用来隐藏 / 排除某些工具。
例如:
excluded_tools=frozenset({"read_file", "write_file"})意思是:
模型不能直接看到 read_file 和 write_file注意:
excluded_tools 不是添加工具
excluded_tools 是隐藏工具9. frozenset 是什么
frozenset({"ls", "read_file"})意思是:
不可修改的集合
普通 set 可以修改:
tools = {"ls", "read_file"}
tools.add("grep")
tools.remove("ls")frozenset 不能修改:
tools = frozenset({"ls", "read_file"})
tools.add("grep") # 会报错这里使用 frozenset 是因为 excluded_tools 是配置项,配置通常希望创建后固定,不要运行过程中被随便改。
10. HarnessProfile 与 register_harness_profile()
示例:
register_harness_profile(
"openai:gpt-4.1",
HarnessProfile(
excluded_tools=frozenset({"read_file", "write_file"})
),
)含义:
给 openai:gpt-4.1 这个模型注册一份配置:
让它在 Deep Agents 中看不到 read_file 和 write_file。三个名字:
| 名字 | 作用 |
|---|---|
HarnessProfile |
一份 Agent 运行配置 |
register_harness_profile() |
把配置注册到某个模型上 |
excluded_tools |
指定哪些工具不让模型看见 |
11. tools、默认文件工具、excluded_tools 的区别
这三个概念容易混:
| 概念 | 含义 |
|---|---|
tools |
你主动给 Agent 的工具 |
| 默认 filesystem tools | Deep Agents 默认带的文件工具 |
excluded_tools |
你不想让模型看到的工具 |
例子:
agent = create_deep_agent(
model="openai:gpt-4.1",
tools=[get_weather],
system_prompt="You are a helpful assistant",
)这里:
get_weather 是你主动给 Agent 的工具
ls/read_file/write_file 是 Deep Agents 默认可能带的工具
excluded_tools 可以隐藏某些默认工具12. excluded_tools 和 permissions 的区别
这两个要重点区分。
| 方式 | 控制层级 | 作用 |
|---|---|---|
excluded_tools |
工具级别 | 直接让模型看不到某些工具 |
permissions |
路径级别 | 工具还在,但限制能操作哪些路径 |
记忆:
excluded_tools 管“有没有这只手”
permissions 管“这只手能碰哪里”例如:
不让 Agent 看到 read_file
→ 用 excluded_tools
允许 Agent 使用 read_file,但只能读 docs/
→ 用 permissions13. permissions 的基本结构
permissions 用来控制文件读写权限。规则用 FilesystemPermission 对象表示(from deepagents import FilesystemPermission),不是裸字典。
每条规则包含三个字段:
| 字段 | 含义 |
|---|---|
operations |
控制操作类型,比如 "read" / "write" |
paths |
控制哪些路径,比如 "/docs/**";支持 ** 递归匹配和 {a,b} 择一 |
mode |
"allow" / "deny" / "interrupt" |
示例:
from deepagents import FilesystemPermission, create_deep_agent
permissions=[
FilesystemPermission(
operations=["read"],
paths=["/docs/**"],
mode="allow",
),
FilesystemPermission(
operations=["read"],
paths=["/**"],
mode="deny",
),
]💡 第三种模式:
interrupt(人工审批) 除了非黑即白的allow/deny,还有mode="interrupt"——命中规则时不放行也不拒绝,而是挂起等人确认,确认后再继续。这正是 人工审批 那套机制在文件系统上的应用。两个前提:需要
deepagents>=0.6.8,并且必须配 checkpointer——没有持久化就没地方存"挂起中"这个状态(permissions本身则要求deepagents>=0.5.2)。适用场景很明确:写操作用
deny太死(Agent 干不了活)、用allow太险(改错了没人知道),interrupt是这两者之间那一档。
含义:
允许读取 /docs/** 下的文件
拒绝读取其他所有文件⚠️ 路径少写前导
/,整条规则静默失效 错误操作: 按写.gitignore的习惯写成"docs/**"、"src/**"。实际结果: 这条规则匹配不到任何路径,而且不报错、不告警——规则就像不存在一样。如果失效的是
deny规则,后果是本该被拦住的操作全部放行;配合下面那条「无匹配即允许」的默认行为,这就是一个彻底的 fail-open:你以为设了防护,实际上一层都没有。原因: 权限里的 glob 是对绝对路径做匹配的,模式必须从根开始写。官方示例全部带前导斜杠:
/workspace/**、/secrets/**、/workspace/.env、/**。正确做法: 所有
paths一律以/开头。写完务必实测一次:让 Agent 去读一个本该被拒的文件,确认它真的被拒——不要靠读配置来确认权限生效。
⚠️ 没有任何规则匹配时,默认是「允许」 这是 Deep Agents 权限模型最需要记住的一条。官方文档原文:"If no rule matches, the call is allowed (permissive default)."
也就是说,权限系统是默认放行、按需拦截,不是默认拒绝。只写几条
allow规则不构成任何限制——没被覆盖到的路径走默认分支,照样能读能写。正确做法: 任何以「限制」为目的的配置,都必须以一条兜底 deny 收尾:
permissions=[ FilesystemPermission(operations=["read"], paths=["/src/**"], mode="allow"), FilesystemPermission(operations=["read"], paths=["/**"], mode="deny"), # ← 缺了这条等于没限制 ]还要注意
operations也得覆盖全:只对read做了兜底 deny,write仍然是默认放行的。
14. 权限规则顺序很重要
权限规则是:
从上到下匹配
第一条命中的规则生效错误例子:
permissions=[
FilesystemPermission(operations=["read"], paths=["/**"], mode="deny"),
FilesystemPermission(operations=["read"], paths=["/docs/**"], mode="allow"),
]如果 Agent 想读:
docs/a.md第一条 ** 已经匹配所有路径,并且是 deny,所以直接拒绝,第二条不会生效。
正确写法:
permissions=[
FilesystemPermission(operations=["read"], paths=["/docs/**"], mode="allow"),
FilesystemPermission(operations=["read"], paths=["/**"], mode="deny"),
]口诀:
具体规则放前面
通用规则放后面例如:
docs/** 比 ** 更具体
src/** 比 ** 更具体
.env 比 ** 更具体15. 常见场景怎么选
场景 1:Agent 可以查天气,但不能读写文件
先看一个看似正确、实际漏得到处都是的写法:
# ❌ 这样写挡不住文件访问
tools=[get_weather]
excluded_tools=frozenset({"read_file", "write_file"})⚠️ 只排掉
read_file/write_file,模型照样能读写文件 实际结果: 内置文件工具不止这两个。上面这么写之后,模型手里还剩:
剩下的工具 它还能干什么 edit_file改文件内容——写操作照做不误 delete删文件 ls列目录结构 glob按模式找文件 grep按内容搜索——只要能 grep,文件内容就能被逐段读出来 execute(用 sandbox backend 时)任意命令, cat一下全都有了也就是说,「读」被
grep和execute绕过,「写」被edit_file和delete绕过。而日志上看不出异常——模型只是在用它被允许使用的工具。正确做法: 内置文件工具的完整清单是
ls/read_file/glob/grep/write_file/edit_file/delete,要排就一次排干净:FILE_TOOLS = frozenset({ "ls", "read_file", "glob", "grep", "write_file", "edit_file", "delete", }) tools=[get_weather] excluded_tools=FILE_TOOLS # 别只写两个并且不要用 sandbox backend——它的
execute能跑任意命令,工具排除和权限规则都管不着它。
场景 2:Agent 可以读 /src/**,但不能读其他文件
思路:
permissions=[
FilesystemPermission(operations=["read"], paths=["/src/**"], mode="allow"),
FilesystemPermission(operations=["read"], paths=["/**"], mode="deny"),
]含义:
先允许 src/**
再拒绝其他所有路径场景 3:Agent 可以读 /src/**,但不能写 /src/**
重点控制:
write因为“不允许写”对应的是写操作。
可以理解为:
read 控制能不能读取
write 控制能不能写入 / 修改16. 本节核心总结
一句话总结:
Deep Agents 默认给模型文件操作工具;如果不希望模型直接碰文件,可以用
excluded_tools隐藏工具,或者用permissions精细控制文件路径权限。
⚠️ 这两个机制都不是安全沙箱**,别拿它们当隔离手段** 上面整节讲的是「怎么把 Agent 的默认行为约束到你想要的范围」,这是行为约束,不是安全边界。三条硬限制:
permissions只覆盖内置文件工具(ls/read_file/glob/grep/write_file/edit_file/delete)。官方文档写得很明确:自定义工具和 MCP 工具即使访问文件系统,也不受权限约束。你自己写的def read_config(path)想读哪读哪。permissions完全不适用于 sandbox backend。sandbox 的execute支持任意命令执行,权限规则对它不生效——有了execute,前面所有路径规则等同于零。excluded_tools只是「不给模型看见」。它把工具从模型可见的清单里拿掉,工具本身和它背后的能力都还在。任何绕过模型工具选择的路径(你自己的代码直接调、子 Agent 用了不同配置、MCP 服务端自己的行为)都不受它约束。真正的安全边界只能在外层建:容器 / VM 隔离、文件系统挂载权限(只读挂载、chroot)、独立的低权限运行账户、网络出口限制、以及在自定义工具内部自己做路径校验和鉴权。
记忆口诀:
excluded_tools和permissions防的是「模型好心办坏事」,不是「有人蓄意突破」。 处理不可信输入或敏感数据时,必须再加真正的隔离层。
记忆表:
| 需求 | 用什么 |
|---|---|
| 创建 Agent | create_deep_agent() |
| 执行 Agent | agent.invoke() |
| 给 Agent 添加自定义能力 | tools=[...] |
| 设置 Agent 行为 | system_prompt |
| 隐藏默认工具 | excluded_tools |
| 限制文件路径访问 | permissions |
| 固定不可变工具集合 | frozenset(...) |