跳到主要内容
Cowers://KNOWLEDGE
全部文章
Agent 架构

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 就能直接跑的:

  1. Python 版本deepagents 要求 Python 3.11+(它依赖的 LangGraph 生态基线在这里)。3.10 及以下装不上或运行时报错。

  2. 模型 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() 时抛导入错误,而不是在安装阶段报错。

  3. 凭据:对应 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 sf

Agent 可以判断需要调用:

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() = 执行 Agent

6. 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. HarnessProfileregister_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_toolspermissions 的区别

这两个要重点区分。

方式 控制层级 作用
excluded_tools 工具级别 直接让模型看不到某些工具
permissions 路径级别 工具还在,但限制能操作哪些路径

记忆:

excluded_tools 管“有没有这只手”
permissions 管“这只手能碰哪里”

例如:

不让 Agent 看到 read_file
→ 用 excluded_tools
 
允许 Agent 使用 read_file,但只能读 docs/
→ 用 permissions

13. 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 一下全都有了

也就是说,「读」被 grepexecute 绕过,「写」被 edit_filedelete 绕过。而日志上看不出异常——模型只是在用它被允许使用的工具。

正确做法: 内置文件工具的完整清单是 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 的默认行为约束到你想要的范围」,这是行为约束,不是安全边界。三条硬限制:

  1. permissions 只覆盖内置文件工具ls / read_file / glob / grep / write_file / edit_file / delete)。官方文档写得很明确:自定义工具和 MCP 工具即使访问文件系统,也不受权限约束。你自己写的 def read_config(path) 想读哪读哪。
  2. permissions 完全不适用于 sandbox backend。sandbox 的 execute 支持任意命令执行,权限规则对它不生效——有了 execute,前面所有路径规则等同于零。
  3. excluded_tools 只是「不给模型看见」。它把工具从模型可见的清单里拿掉,工具本身和它背后的能力都还在。任何绕过模型工具选择的路径(你自己的代码直接调、子 Agent 用了不同配置、MCP 服务端自己的行为)都不受它约束。

真正的安全边界只能在外层建:容器 / VM 隔离、文件系统挂载权限(只读挂载、chroot)、独立的低权限运行账户、网络出口限制、以及在自定义工具内部自己做路径校验和鉴权。

记忆口诀:excluded_toolspermissions 防的是「模型好心办坏事」,不是「有人蓄意突破」。 处理不可信输入或敏感数据时,必须再加真正的隔离层。

记忆表:

需求 用什么
创建 Agent create_deep_agent()
执行 Agent agent.invoke()
给 Agent 添加自定义能力 tools=[...]
设置 Agent 行为 system_prompt
隐藏默认工具 excluded_tools
限制文件路径访问 permissions
固定不可变工具集合 frozenset(...)