Deep Agents 通过 ls、read_file、write_file、edit_file、glob 和 grep 等工具向代理暴露文件系统接口。这些工具通过可插拔的后端运行。read_file 工具在所有后端上原生支持图像文件(.png、.jpg、.jpeg、.gif、.webp),将它们作为多模态内容块返回。
沙盒和 LocalShellBackend 还提供 execute 工具。
本页说明如何:
快速开始
以下是一些可与深层代理快速配合使用的预建文件系统后端:
| 内置后端 | 描述 |
|---|
| 默认 | agent = create_deep_agent() 临时存储在状态中。代理的默认文件系统后端存储在 langgraph 状态中。请注意,此文件系统仅在单个线程中持久化。 |
| 本地文件系统持久化 | agent = create_deep_agent(backend=FilesystemBackend(root_dir="/Users/nh/Desktop/")) 这让深层代理访问您本地机器的文件系统。您可以指定代理有权访问的根目录。请注意,任何提供的 root_dir 必须是绝对路径。 |
| 持久化存储(LangGraph store) | agent = create_deep_agent(backend=StoreBackend()) 这让代理访问跨线程持久化的长期存储。这非常适合存储适用于代理在多次执行中的较长期记忆或指令。 |
| 沙盒 | agent = create_deep_agent(backend=sandbox) 在隔离环境中执行代码。沙盒提供文件系统工具加上用于运行 shell 命令的 execute 工具。可从 Modal、Daytona、Deno 或本地 VFS 中选择。 |
| 本地 shell | agent = create_deep_agent(backend=LocalShellBackend(root_dir=".", env={"PATH": "/usr/bin:/bin"})) 直接在主机上进行文件系统和 shell 执行。无隔离——仅在受控开发环境中使用。请参阅下面的安全注意事项。 |
| 复合 | 默认临时,/memories/ 持久化。复合后端非常灵活。您可以指定文件系统中的不同路由指向不同的后端。请参阅下面的复合路由以获取可直接粘贴的示例。 |
内置后端
StateBackend(临时)
# By default we provide a StateBackend
agent = create_deep_agent()
# Under the hood, it looks like
from deepagents.backends import StateBackend
agent = create_deep_agent(
backend=StateBackend()
)
工作原理:
- 通过
StateBackend 将文件存储在当前线程的 LangGraph 代理状态中。
- 通过检查点在同一线程上的多个代理轮次之间持久化。
最适合:
- 代理写入中间结果的草稿板。
- 自动逐出大型工具输出,代理可以逐步读取回来。
请注意,此后端在主管代理和子代理之间共享,子代理编写的任何文件将在该子代理执行完成后仍保留在 LangGraph 代理状态中。这些文件将继续对主管代理和其他子代理可用。
FilesystemBackend(本地磁盘)
FilesystemBackend 在可配置的根目录下读取和写入真实文件。
此后端授予代理直接的文件系统读/写访问权限。
谨慎使用,仅在适当的环境中使用。适当的使用场景:
- 本地开发 CLI(编码助手、开发工具)
- CI/CD 管道(请参阅下面的安全注意事项)
不适当的使用场景:
- Web 服务器或 HTTP API - 改用
StateBackend、StoreBackend 或沙盒后端
安全风险:
- 代理可以读取任何可访问的文件,包括秘密(API 密钥、凭据、
.env 文件)
- 与网络工具结合,秘密可能通过 SSRF 攻击被窃取
- 文件修改是永久的且不可逆的
推荐的保护措施:
- 启用人工介入(HITL)中间件以审查敏感操作。
- 从可访问的文件系统路径中排除秘密(尤其是在 CI/CD 中)。
- 在需要文件系统交互的生产环境中使用沙盒后端。
- 始终将
virtual_mode=True 与 root_dir 一起使用以启用基于路径的访问限制(阻止 ..、~ 和根目录外的绝对路径)。
请注意,默认值(virtual_mode=False)即使设置了 root_dir 也不提供任何安全性。
from deepagents.backends import FilesystemBackend
agent = create_deep_agent(
backend=FilesystemBackend(root_dir=".", virtual_mode=True)
)
工作原理:
- 在可配置的
root_dir 下读取/写入真实文件。
- 您可以选择设置
virtual_mode=True 以在 root_dir 下沙盒化和规范化路径。
- 使用安全路径解析,尽可能防止不安全的符号链接遍历,可以使用 ripgrep 进行快速
grep。
最适合:
LocalShellBackend(本地 shell)
此后端授予代理在您主机上直接的文件系统读/写访问权限并且不受限制的 shell 执行。
极其谨慎地使用,仅在适当的环境中使用。适当的使用场景:
- 本地开发 CLI(编码助手、开发工具)
- 您信任代理代码的个人开发环境
- 具有适当秘密管理的 CI/CD 管道
不适当的使用场景:
- 生产环境(如 Web 服务器、API、多租户系统)
- 处理不受信任的用户输入或执行不受信任的代码
安全风险:
- 代理可以使用您用户的权限执行任意 shell 命令
- 代理可以读取任何可访问的文件,包括秘密(API 密钥、凭据、
.env 文件)
- 秘密可能暴露
- 文件修改和命令执行是永久的且不可逆的
- 命令直接在您的主机系统上运行
- 命令可以消耗无限的 CPU、内存、磁盘
推荐的保护措施:
- 启用人工介入(HITL)中间件以在执行前审查和批准操作。这强烈推荐。
- 仅在专用开发环境中运行。切勿在共享或生产系统上使用。
- 在需要 shell 执行的生产环境中使用沙盒后端。
**注意:**当启用 shell 访问时,virtual_mode=True 不提供安全性,因为命令可以访问系统上的任何路径。
from deepagents.backends import LocalShellBackend
agent = create_deep_agent(
backend=LocalShellBackend(root_dir=".", env={"PATH": "/usr/bin:/bin"})
)
工作原理:
- 扩展
FilesystemBackend,添加 execute 工具以在主机上运行 shell 命令。
- 命令使用
subprocess.run(shell=True) 直接在您的机器上运行,没有沙盒。
- 支持用于环境变量的
timeout(默认 120s)、max_output_bytes(默认 100,000)、env 和 inherit_env。
- Shell 命令使用
root_dir 作为工作目录,但可以访问系统上的任何路径。
最适合:
- 本地编码助手和开发工具
- 当您信任代理时快速迭代开发
StoreBackend(LangGraph 存储)
from langgraph.store.memory import InMemoryStore
from deepagents.backends import StoreBackend
agent = create_deep_agent(
backend=StoreBackend(
namespace=lambda ctx: (ctx.runtime.context.user_id,),
),
store=InMemoryStore() # Good for local dev; omit for LangSmith Deployment
)
When deploying to LangSmith Deployment, omit the store parameter. The platform automatically provisions a store for your agent.
namespace 参数控制数据隔离。对于多用户部署,始终设置命名空间工厂以按用户或租户隔离数据。
工作原理:
最适合:
namespace factory
命名空间工厂控制 StoreBackend 读取和写入数据的位置。它接收 LangGraph Runtime 并返回用作存储命名空间的字符串元组。使用命名空间工厂在用户、租户或助手之间隔离数据。
在构造 StoreBackend 时将命名空间工厂传递给 namespace 参数:
NamespaceFactory = Callable[[Runtime], tuple[str, ...]]
Runtime 提供:
rt.context — 通过 LangGraph 的上下文模式传递的用户提供的上下文(例如,user_id)
rt.server_info — 在 LangGraph Server 上运行时服务器特定的元数据(助手 ID、图形 ID、经过身份验证的用户)
rt.execution_info — 执行身份信息(线程 ID、运行 ID、检查点 ID)
Runtime 参数在 deepagents>=0.5.2 中可用。较早的 0.5.x 版本传递的是 BackendContext——请参阅下面的从 BackendContext 迁移。rt.server_info 和 rt.execution_info 需要 deepagents>=0.5.0。
常见命名空间模式:
from deepagents.backends import StoreBackend
# 按用户:每个用户获得自己隔离的存储
backend = StoreBackend(
namespace=lambda rt: (rt.server_info.user.identity,),
)
# 按助手:同一助手的所有用户共享存储
backend = StoreBackend(
namespace=lambda rt: (
rt.server_info.assistant_id,
),
)
# 按线程:存储范围限定为单个对话
backend = StoreBackend(
namespace=lambda rt: (
rt.execution_info.thread_id,
),
)
您可以组合多个组件以创建更具体的作用域——例如,(user_id, thread_id) 用于按用户按对话隔离,或附加后缀如 "filesystem" 以在相同作用域使用多个存储命名空间时消除歧义。
命名空间组件只能包含字母数字字符、连字符、下划线、点、@、+、冒号和波浪号。拒绝通配符(*、?)以防止 glob 注入。
namespace 参数在 v0.5.0 中将是必需的。始终为新代码明确设置它。
当未提供命名空间工厂时,旧版默认值使用 LangGraph 配置元数据中的 assistant_id。这意味着同一助手的所有用户共享相同的存储。对于多用户投入生产,始终提供命名空间工厂。
CompositeBackend(路由器)
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore
agent = create_deep_agent(
backend=CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(),
}
),
store=InMemoryStore() # Store passed to create_deep_agent, not backend
)
工作原理:
最适合:
- 当您想同时给代理临时存储和跨线程存储时,
CompositeBackend 允许您同时提供 StateBackend 和 StoreBackend
- 当您有多个想要作为单一文件系统提供给代理的信息源时。
- 例如,您有一个存储在
/memories/ 下的长期记忆的 Store,以及一个具有可在 /docs/ 访问的文档的自定义后端。
指定后端
- 将后端实例传递给
create_deep_agent(backend=...)。文件系统中间件将其用于所有工具。
- 后端必须实现
BackendProtocol(例如,StateBackend()、FilesystemBackend(root_dir=".")、StoreBackend())。
- 如果省略,默认是
StateBackend()。
路由到不同后端
将命名空间的一部分路由到不同的后端。通常用于持久化 /memories/* 并将其他所有内容保持为临时。
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, FilesystemBackend
agent = create_deep_agent(
backend=CompositeBackend(
default=StateBackend(),
routes={
"/memories/": FilesystemBackend(root_dir="/deepagents/myagent", virtual_mode=True),
},
)
)
行为:
/workspace/plan.md → StateBackend(临时)
/memories/agent.md → /deepagents/myagent 下的 FilesystemBackend
ls、glob、grep 聚合结果并显示原始路径前缀。
注意:
- 更长的前缀优先(例如,路由
"/memories/projects/" 可以覆盖 "/memories/")。
- 对于 StoreBackend 路由,确保通过
create_deep_agent(store=...) 提供存储或由平台配置。
使用虚拟文件系统
构建自定义后端以将远程或数据库文件系统(例如 S3 或 Postgres)投射到工具命名空间中。
设计指南:
-
路径是绝对的(
/x/y.txt)。决定如何将它们映射到您的存储键/行。
-
高效实现
ls 和 glob(尽可能使用服务器端过滤,否则在本地过滤)。
-
对于外部持久化(S3、Postgres 等),在写入/编辑结果中返回
files_update=None(Python)或省略 filesUpdate(JS)——只有内存状态后端需要返回文件更新字典。
-
使用
ls 和 glob 作为方法名。
-
返回带有
error 字段的结构化结果类型,用于缺少文件或无效模式(不要抛出异常)。
S3 风格大纲:
from deepagents.backends.protocol import (
BackendProtocol, WriteResult, EditResult, LsResult, ReadResult, GrepResult, GlobResult,
)
class S3Backend(BackendProtocol):
def __init__(self, bucket: str, prefix: str = ""):
self.bucket = bucket
self.prefix = prefix.rstrip("/")
def _key(self, path: str) -> str:
return f"{self.prefix}{path}"
def ls(self, path: str) -> LsResult:
# 列出 _key(path) 下的对象;构建 FileInfo 条目(path、size、modified_at)
...
def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult:
# 获取对象;返回 ReadResult(file_data=...) 或 ReadResult(error=...)
...
def grep(self, pattern: str, path: str | None = None, glob: str | None = None) -> GrepResult:
# 可选地在服务器端过滤;否则列出然后扫描内容
...
def glob(self, pattern: str, path: str = "/") -> GlobResult:
# 跨键将 glob 应用于相对于 path 的路径
...
def write(self, file_path: str, content: str) -> WriteResult:
# 强制执行仅创建语义;返回 WriteResult(path=file_path, files_update=None)
...
def edit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult:
# 读取 → 替换(遵守唯一性与 replace_all)→ 写入 → 返回出现次数
...
Postgres 风格大纲:
- 表
files(path text primary key, content text, created_at timestamptz, modified_at timestamptz)
- 将工具操作映射到 SQL:
ls 使用 WHERE path LIKE $1 || '%'
glob 在 SQL 中过滤或在 Python 中获取后应用 glob
grep 可以通过扩展名或最后修改时间获取候选行,然后扫描行
使用权限以声明方式控制代理可以读取或写入的文件和目录。权限在内置文件系统工具上评估,在调用后端之前。
from deepagents import create_deep_agent, FilesystemPermission
agent = create_deep_agent(
backend=CompositeBackend(
default=StateBackend(),
routes={
"/memories/": StoreBackend(
namespace=lambda rt: (rt.server_info.user.identity,),
),
"/policies/": StoreBackend(
namespace=lambda rt: (rt.context.org_id,),
),
},
),
permissions=[
FilesystemPermission(
operations=["write"],
paths=["/policies/**"],
mode="deny",
),
],
)
有关完整的选项集(包括规则顺序、子代理权限和复合后端交互),请参阅权限指南。
添加策略钩子
对于超越基于路径的允许/拒绝规则的自定义验证逻辑(速率限制、审计日志、内容检查),通过子类化或包装后端来强制执行企业规则。
阻止选定前缀下的写入/编辑(子类化):
from deepagents.backends.filesystem import FilesystemBackend
from deepagents.backends.protocol import WriteResult, EditResult
class GuardedBackend(FilesystemBackend):
def __init__(self, *, deny_prefixes: list[str], **kwargs):
super().__init__(**kwargs)
self.deny_prefixes = [p if p.endswith("/") else p + "/" for p in deny_prefixes]
def write(self, file_path: str, content: str) -> WriteResult:
if any(file_path.startswith(p) for p in self.deny_prefixes):
return WriteResult(error=f"不允许在 {file_path} 下写入")
return super().write(file_path, content)
def edit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult:
if any(file_path.startswith(p) for p in self.deny_prefixes):
return EditResult(error=f"不允许在 {file_path} 下编辑")
return super().edit(file_path, old_string, new_string, replace_all)
通用包装器(适用于任何后端):
from deepagents.backends.protocol import (
BackendProtocol, WriteResult, EditResult, LsResult, ReadResult, GrepResult, GlobResult,
)
class PolicyWrapper(BackendProtocol):
def __init__(self, inner: BackendProtocol, deny_prefixes: list[str] | None = None):
self.inner = inner
self.deny_prefixes = [p if p.endswith("/") else p + "/" for p in (deny_prefixes or [])]
def _deny(self, path: str) -> bool:
return any(path.startswith(p) for p in self.deny_prefixes)
def ls(self, path: str) -> LsResult:
return self.inner.ls(path)
def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult:
return self.inner.read(file_path, offset=offset, limit=limit)
def grep(self, pattern: str, path: str | None = None, glob: str | None = None) -> GrepResult:
return self.inner.grep(pattern, path, glob)
def glob(self, pattern: str, path: str = "/") -> GlobResult:
return self.inner.glob(pattern, path)
def write(self, file_path: str, content: str) -> WriteResult:
if self._deny(file_path):
return WriteResult(error=f"不允许在 {file_path} 下写入")
return self.inner.write(file_path, content)
def edit(self, file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult:
if self._deny(file_path):
return EditResult(error=f"不允许在 {file_path} 下编辑")
return self.inner.edit(file_path, old_string, new_string, replace_all)
从后端工厂迁移
后端工厂模式已弃用。直接传递预构造的后端实例,而不是工厂函数。
以前,StateBackend 和 StoreBackend 等后端需要一个接收运行时对象的工厂函数,因为它们需要运行时上下文(状态、存储)才能操作。后端现在通过 LangGraph 的 get_config()、get_store() 和 get_runtime() 助手在内部解析此上下文,因此您可以直接传递实例。
发生了什么变化
| 以前(已弃用) | 以后 |
|---|
backend=lambda rt: StateBackend(rt) | backend=StateBackend() |
backend=lambda rt: StoreBackend(rt) | backend=StoreBackend() |
backend=lambda rt: CompositeBackend(default=StateBackend(rt), ...) | backend=CompositeBackend(default=StateBackend(), ...) |
backend: (config) => new StateBackend(config) | backend: new StateBackend() |
backend: (config) => new StoreBackend(config) | backend: new StoreBackend() |
已弃用的 API
| 已弃用 | 替换 |
|---|
在 create_deep_agent 中传递可调用对象到 backend= | 直接传递后端实例 |
StateBackend(runtime) 上的 runtime 构造函数参数 | StateBackend()(无需参数) |
StoreBackend(runtime) 上的 runtime 构造函数参数 | StoreBackend() 或 StoreBackend(namespace=..., store=...) |
WriteResult 和 EditResult 上的 files_update 字段 | 状态写入现在由后端在内部处理 |
中间件写入/编辑工具中的 Command 包装 | 工具返回纯字符串;不需要 Command(update=...) |
工厂模式在运行时仍然有效,但会发出弃用警告。在下一个主要版本之前更新您的代码以使用直接实例。
迁移示例
# 以前(已弃用)
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
agent = create_deep_agent(
backend=lambda rt: CompositeBackend(
default=StateBackend(rt),
routes={"/memories/": StoreBackend(rt, namespace=lambda rt: (rt.server_info.user.identity,))},
),
)
# 以后
agent = create_deep_agent(
backend=CompositeBackend(
default=StateBackend(),
routes={"/memories/": StoreBackend(namespace=lambda rt: (rt.server_info.user.identity,))},
),
)
从 BackendContext 迁移
在 deepagents>=0.5.2(Python)和 deepagents>=1.9.1(TypeScript)中,命名空间工厂直接接收 LangGraph Runtime,而不是 BackendContext 包装器。旧的 BackendContext 形式仍然通过向后兼容的 .runtime 和 .state 访问器工作,但这些访问器会发出弃用警告,将在 deepagents>=0.7 中移除。
发生了什么变化:
- 工厂参数现在是
Runtime,而不是 BackendContext。
- 删除
.runtime 访问器——例如,ctx.runtime.context.user_id 变为 rt.server_info.user.identity。
- 没有
ctx.state 的直接替换。命名空间信息应该是只读的,并且在运行的生命周期内是稳定的,而状态是可变的并且每步都在变化——从中派生命名空间可能导致数据出现在不一致的键下。如果您有需要读取代理状态的使用场景,请打开一个问题。
# 以前(已弃用,将在 v0.7 中移除)
StoreBackend(
namespace=lambda ctx: (ctx.runtime.context.user_id,),
)
# 以后
StoreBackend(
namespace=lambda rt: (rt.server_info.user.identity,),
)