后端(Backends)
为 Deep Agents 选择和配置文件系统后端,实现虚拟文件系统与策略控制
从"默认存储"到"可插拔后端架构"的关键一步为什么需要后端
你的 Agent 产生的文件应该存在哪里?不同的任务需要不同的答案
只用默认存储的困境
- 所有文件只能存在线程状态里,跨线程不共享
- 想访问本地文件?做不到——没有本地磁盘后端
- 想区分"临时草稿"和"长期记忆"?只有一种存储
- 需要连接 S3、数据库等外部存储?无法扩展
- Agent 乱改敏感文件?没有策略拦截
可插拔后端的解法
- StateBackend:线程作用域,临时草稿本
- FilesystemBackend:访问本地磁盘文件
- StoreBackend:跨线程持久化长期记忆
- CompositeBackend:按路径路由到不同后端
- 自定义后端:S3、Postgres、任何你想要的存储
ls、read_file、write_file、edit_file、glob、grep 等工具暴露文件系统接口。这些工具通过可插拔的后端运行——你决定数据存在哪里、怎么存。
后端全景图
七种内置后端,覆盖从临时草稿到生产部署的全部场景
| 后端 | 存储位置 | 跨线程 | 适用场景 |
|---|---|---|---|
| StateBackend | 线程状态 | 否 | 临时草稿、中间结果 |
| FilesystemBackend | 本地磁盘 | 是 | 本地项目、CI 沙箱 |
| LocalShellBackend | 本地磁盘 + Shell | 是 | 编码助手、开发工具 |
| StoreBackend | LangGraph Store | 是 | 长期记忆、跨会话数据 |
| ContextHubBackend | LangSmith Hub | 是 | Hub 仓库文件系统 |
| Sandbox | 隔离环境 | 否 | 安全执行代码 |
| CompositeBackend | 路由到多个后端 | 视配置 | 混合存储需求 |
ls、read_file 等)统一调用后端接口,后端决定数据实际存在哪里。read_file 在所有后端中原生支持图片、PDF、音频、视频等二进制文件。
架构关系图
graph TB
Tools[文件系统工具] --> Backend[后端]
Backend --> State[State]
Backend --> Disk[文件系统]
Backend --> Store[Store]
Backend --> ContextHub[Context Hub]
Backend --> Sandbox[沙箱]
Backend --> LocalShell[本地 Shell]
Backend --> Composite[组合]
Backend --> Custom[自定义]
Composite --> Router{路由}
Router --> State
Router --> Disk
Router --> Store
Router --> ContextHub
Sandbox --> Execute["+ execute 工具"]
LocalShell --> Execute["+ execute 工具"]
StateBackend
默认后端——线程作用域的临时草稿本
白话:StateBackend 就像你桌面上的"草稿纸"——写在上面的东西,这条对话里随时能翻到,但换一个对话窗口就看不到了。
这是 Deep Agent 的默认文件系统后端。文件存储在当前线程的 LangGraph Agent 状态中,通过检查点在同一线程的多次交互间持久化。文件不会跨线程共享。
import { createDeepAgent, StateBackend } from "deepagents";
// 默认就是 StateBackend,无需显式指定
const agent = createDeepAgent();
// 等价于显式指定
const agent2 = createDeepAgent({
backend: new StateBackend(),
});
适用场景
- Agent 写入中间结果的临时草稿本
- 自动淘汰大型工具输出,Agent 可逐段重新读取
- Supervisor 与子 Agent 之间共享文件
注意事项
- 设计为在图中使用,图外调用方法不会生效
- 子 Agent 写入的文件在状态中持续存在
- 文件对同一线程的 Supervisor 和其他子 Agent 可用
state_backend.upload_files(...))不会生效,直到图执行时才会应用。
FilesystemBackend
让 Agent 访问本地磁盘——真实文件的读写
白话:StateBackend 是草稿纸,FilesystemBackend 就是真实的文件夹。Agent 可以读你电脑上的文件、修改代码、保存结果——就像你亲手在做。
FilesystemBackend 在可配置的 root_dir 下读取和写入真实文件。你可以设置 virtualMode: true 来沙箱化路径,阻止 ..、~ 以及根目录外的绝对路径。
import { createDeepAgent, FilesystemBackend } from "deepagents";
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
backend: new FilesystemBackend({
rootDir: ".", // 根目录,Agent 的文件操作限制在此目录下
virtualMode: true, // 启用路径沙箱化,阻止 .. 和绝对路径逃逸
}),
});
适用场景
- 本地项目开发(编码助手、开发工具)
- CI/CD 流水线
- 挂载的持久卷
安全特性
- 安全的路径解析,防止符号链接遍历
- 可用 ripgrep 进行快速
grep virtualMode: true限制在root_dir内
- Agent 可以读取任何可访问的文件,包括密钥(API 密钥、凭据、
.env文件) - 结合网络工具,密钥可能通过 SSRF 攻击被泄露
- 文件修改是永久性且不可逆的
- 启用 Human-in-the-Loop (HITL) 中间件来审查敏感操作
- 从可访问的文件系统路径中排除密钥(尤其在 CI/CD 中)
- 在需要文件系统交互的生产环境中使用沙箱后端
- 始终在
root_dir下使用virtualMode: true以启用基于路径的访问限制(阻止..、~以及根目录外的绝对路径)。注意,默认设置(virtualMode: false)即使设置了root_dir也不提供任何安全保障。
不适用于 Web 服务器或 HTTP API——请改用 StateBackend、StoreBackend 或沙箱后端。
LocalShellBackend
在 FilesystemBackend 基础上增加 Shell 命令执行能力
LocalShellBackend 在 FilesystemBackend 之上扩展了 execute 工具,允许 Agent 在宿主机上运行 Shell 命令。命令通过 shell 执行(等价 Node.js child_process,无沙箱隔离)。
import { createDeepAgent, LocalShellBackend } from "deepagents";
const backend = new LocalShellBackend({
workingDirectory: ".", // Shell 命令的执行目录
env: { PATH: "/usr/bin:/bin" }, // 限制可用的环境变量,避免泄露宿主环境
});
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
backend, // Agent 获得文件读写 + Shell 命令执行能力
});
配置参数
timeout:命令超时(默认 120 秒)maxOutputBytes:输出限制(默认 100KB)env:自定义环境变量inheritEnv:是否继承宿主环境
适用场景
- 本地编码助手和开发工具
- 信任 Agent 时的快速开发迭代
- 具有适当密钥管理的 CI/CD 流水线
此后端授予 Agent 直接的文件系统读写权限以及宿主机上不受限制的 Shell 执行权限。
- Agent 可以以你的用户权限执行任意 Shell 命令
- Agent 可以读取任何可访问的文件,包括密钥(API 密钥、凭据、
.env文件) - 密钥可能被暴露
- 文件修改和命令执行是永久性且不可逆的
- 命令直接在你的宿主机系统上运行
- 命令可以消耗无限的 CPU、内存、磁盘
- 启用 Human-in-the-Loop (HITL) 中间件在执行前审查和批准操作。强烈推荐。
- 仅在专用开发环境中运行。切勿在共享或生产系统上使用。
- 在需要 Shell 执行的生产环境中使用沙箱后端。
注意:启用 Shell 后,virtualMode 不提供安全保障(命令可访问系统任何路径)。
StoreBackend
跨线程持久化——Agent 的长期记忆存储
白话:如果说 StateBackend 是草稿纸,那 StoreBackend 就是文件柜——今天写进去的东西,明天打开另一个对话窗口还能读到。
StoreBackend 将文件存储在 LangGraph BaseStore 中,实现跨线程的持久化。非常适合存储长期记忆或在多次执行中适用的指令。
import { createDeepAgent, StoreBackend } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";
// 本地开发用 InMemoryStore(内存存储);部署到 LangSmith 时自动提供
const store = new InMemoryStore();
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
backend: new StoreBackend({
// namespace 工厂函数:按用户身份隔离,每个用户有独立存储空间
namespace: (rt) => [rt.serverInfo.user.identity],
}),
store, // 必须传入 store 实例,StoreBackend 依赖它读写数据
});
适用场景
- 使用 LangGraph store 运行时(Redis、Postgres 等)
- 通过 LangSmith Deployment 部署 Agent
- 需要跨线程共享的长期数据
数据隔离
namespace参数控制数据隔离- 多用户部署必须设置命名空间工厂
- 按用户、助手或线程隔离数据
store 参数,平台会自动为 Agent 配置存储。
命名空间工厂
控制 StoreBackend 数据隔离的关键机制
命名空间工厂接收 LangGraph Runtime 对象,返回字符串元组作为 store 命名空间。使用它在用户、租户或助手之间隔离数据。
import { StoreBackend } from "deepagents";
// 按用户隔离:每个用户独立的存储空间,多用户部署必须用这种方式
const byUser = new StoreBackend({
namespace: (rt) => [rt.serverInfo.user.identity], // 用用户 ID 作为命名空间
});
// 按助手隔离:同一助手的所有用户共享数据
const byAssistant = new StoreBackend({
namespace: (rt) => [rt.serverInfo.assistantId], // 用助手 ID 作为命名空间
});
// 按线程隔离:存储限定在单个对话范围,对话结束数据就不共享了
const byThread = new StoreBackend({
namespace: (rt) => [rt.executionInfo.threadId], // 用线程 ID 作为命名空间
});
| Runtime 属性 | 提供的信息 | 典型用途 |
|---|---|---|
rt.context |
用户提供的上下文(如 user_id) |
自定义隔离维度 |
rt.serverInfo |
助手 ID、图 ID、认证用户 | 按用户或助手隔离 |
rt.executionInfo |
线程 ID、运行 ID、检查点 ID | 按会话隔离 |
Runtime 参数在 deepagents>=1.9.1 中可用。早期的 1.9.x 版本传递的是 BackendContext 包装器。rt.serverInfo 和 rt.executionInfo 需要 deepagents>=1.9.0。
namespace 参数在 v1.9.0 将变为必填。新代码请始终显式设置。命名空间组件只能包含字母数字、连字符、下划线、点号等,通配符会被拒绝以防注入。
(user_id, thread_id) 用于按用户按对话隔离,或附加后缀如 "filesystem" 来区分同一作用域使用多个 store 命名空间的情况。
assistant_id。这意味着同一助手的所有用户共享相同的存储。对于多用户的生产部署,请始终提供命名空间工厂。
ContextHubBackend
LangSmith Hub 仓库作为文件系统——无需额外配置 Store
ContextHubBackend 将文件存储在 LangSmith Hub 仓库中。使用 owner/name 或 name 格式的仓库标识符来构造它。无需配置单独的 LangGraph Store。
import { createDeepAgent, ContextHubBackend } from "deepagents";
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
// 传入 Hub 仓库名(owner/name 或 name 格式),无需额外配置 Store
backend: new ContextHubBackend("my-agent"),
});
工作原理
- 首次使用时惰性拉取 Hub 仓库树
- 从内存缓存中提供读取服务
- 写入和编辑作为 Hub 提交持久化
- 使用乐观的父提交写入机制
适用场景
- 无需单独配置 Store 的 LangSmith 原生存储
- 需要文件变更的提交历史
- 受益于版本追踪的工作流
LANGSMITH_API_KEY 环境变量。如果另一个写入者先更新了仓库,你的写入可能失败——冲突时需重新拉取并重试。仅支持 UTF-8 文本文件。
upload_files() 接受 UTF-8 文本,非 UTF-8 文件会被拒绝。
CompositeBackend
路由器——按路径前缀将文件操作分发到不同后端
CompositeBackend 是最灵活的后端。它根据路径前缀将文件操作路由到不同后端,让你同时使用多种存储策略。
import {
createDeepAgent,
CompositeBackend,
StateBackend,
StoreBackend,
} from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";
const store = new InMemoryStore();
const agent = createDeepAgent({
model: "google-genai:gemini-3.1-pro-preview",
backend: new CompositeBackend(
// 第一个参数:默认后端,未匹配路径前缀的文件操作走这里
new StateBackend(), // 线程作用域,临时草稿
// 第二个参数:路由表,键是路径前缀,值是对应的后端
{
"/memories/": new StoreBackend({ // /memories/ 下的文件走跨线程持久化
namespace: () => ["memories"],
}),
},
),
store, // StoreBackend 依赖的 LangGraph 存储
});
/workspace/plan.md 走 StateBackend(线程作用域),/memories/agent.md 走 StoreBackend(跨线程持久化)。ls、glob、grep 会聚合所有后端的结果并显示原始路径前缀。更长的前缀优先匹配。
后端对比总结
一表看清七种后端的核心差异,帮你做出正确选择
| 后端 | 持久化范围 | execute 工具 | 安全隔离 | 推荐用途 |
|---|---|---|---|---|
| StateBackend | 线程内 | 否 | 天然隔离 | 默认选择、临时文件 |
| FilesystemBackend | 本地磁盘 | 否 | 需 virtualMode | 本地开发、CI |
| LocalShellBackend | 本地磁盘 | 是 | 无隔离 | 受控开发环境 |
| StoreBackend | 跨线程 | 否 | 命名空间隔离 | 长期记忆、多用户 |
| ContextHubBackend | Hub 仓库 | 否 | Hub 权限 | LangSmith 原生 |
| Sandbox | 隔离环境 | 是 | 沙箱隔离 | 安全执行代码 |
| CompositeBackend | 混合 | 视路由 | 视配置 | 复杂混合需求 |
StateBackend(默认)开始。需要跨线程数据时加 StoreBackend。本地开发用 FilesystemBackend。混合需求用 CompositeBackend 路由。生产环境优先考虑沙箱后端。
指定后端
一个参数搞定——将后端实例传给 createDeepAgent
将后端实例传递给 createDeepAgent({ backend: ... }) 即可。文件系统中间件将其用于所有工具操作。如果省略,默认为 new StateBackend()。
// 方式 1:省略 backend 参数,默认使用 StateBackend(线程内临时存储)
const agent1 = createDeepAgent();
// 方式 2:指定 FilesystemBackend,访问本地磁盘文件
const agent2 = createDeepAgent({
backend: new FilesystemBackend({ rootDir: ".", virtualMode: true }),
});
// 方式 3:使用 CompositeBackend 按路径路由到不同后端
const agent3 = createDeepAgent({
backend: new CompositeBackend(
new StateBackend(), // 默认后端
{
"/memories/": new StoreBackend({ // /memories/ 走持久化存储
namespace: () => ["memories"],
}),
},
),
store, // StoreBackend 需要 store 实例
});
后端协议版本
- 后端必须实现
BackendProtocolV1或BackendProtocolV2 - V1 后端会自动适配为 V2,无需代码更改
- 框架在运行时通过
adaptBackendProtocol()自动处理
路由示例行为
/workspace/plan.md→ StateBackend/memories/agent.md→ StoreBackendls、glob聚合所有后端结果- 更长的前缀优先匹配
虚拟文件系统
构建自定义后端,将 S3 或 Postgres 映射为文件系统
你可以构建自定义后端,将远程或数据库文件系统映射到 Agent 的工具命名空间。所有路径都是绝对路径(/x/y.txt),你决定如何映射到存储键或数据库行。
import {
type BackendProtocolV2,
type LsResult, type ReadResult, type ReadRawResult, type GrepResult,
type GlobResult, type WriteResult, type EditResult,
} from "deepagents";
class S3Backend implements BackendProtocolV2 {
constructor(private bucket: string, private prefix = "") {}
// 将虚拟路径转为 S3 对象键,加上前缀
private key(path: string): string {
return `${this.prefix}${path}`;
}
async ls(path: string): Promise<LsResult> {
// 列出 key(path) 下的对象;返回 { files: [...] }
}
async read(filePath: string, offset?: number, limit?: number): Promise<ReadResult> {
// 从 S3 获取对象内容;文本返回 string,二进制返回 Uint8Array
}
async readRaw(filePath: string): Promise<ReadRawResult> {
// 返回 { data: FileData },含时间戳等元信息
}
async grep(pattern: string, path?: string | null, glob?: string | null): Promise<GrepResult> {
// 搜索文本文件;跳过二进制文件;返回 { matches: [...] }
}
async glob(pattern: string, path = "/"): Promise<GlobResult> {
// 相对于 path 应用 glob 模式匹配;返回 { files: [...] }
}
async write(filePath: string, content: string): Promise<WriteResult> {
// 写入对象;外部后端返回 { path, filesUpdate: null }
}
async edit(filePath: string, oldString: string, newString: string, replaceAll?: boolean): Promise<EditResult> {
// 先 read → 替换 oldString 为 newString → write 回去
}
}
ls 和 glob(优先用服务端过滤)。外部持久化后端的 write/edit 应返回 filesUpdate: null。所有查询方法必须返回结构化 Result 对象,通过 mimeType 支持二进制文件。
Postgres 风格映射
表结构:files(path text PRIMARY KEY, content text, mime_type text, created_at timestamptz, modified_at timestamptz)
ls使用WHERE path LIKE $1 || '%'→ 返回LsResultglob在 SQL 中过滤或先获取再本地应用 glob → 返回GlobResultgrep按扩展名或最后修改时间获取候选行,扫描行(跳过mime_type为二进制的行)→ 返回GrepResult
设计要点
- 路径是绝对路径(
/x/y.txt),你决定如何映射到存储键或数据库行 - 使用
ls和glob作为方法名 - 外部持久化后端的
write/edit返回filesUpdate: null - 通过返回带有适当
mimeType的Uint8Array内容来支持二进制文件
后端协议详解
BackendProtocolV2 规定的七个必需方法
BackendProtocol(现在的 BackendProtocolV1)。V1 的辅助类型使用不同的字段名:LsResult(error, entries)、ReadResult(error, file_data)、GrepResult(error, matches)、WriteResult(error, path, files_update)、EditResult(error, path, files_update, occurrences)。FileData V1 格式包含 content(str)、encoding("utf-8" 或 "base64")、created_at、modified_at。
| 方法 | 返回类型 | 说明 |
|---|---|---|
ls(path) |
LsResult |
列出目录内容(非递归),按 path 排序 |
read(filePath, offset, limit) |
ReadResult |
读取文件;二进制返回 Uint8Array |
readRaw(filePath) |
ReadRawResult |
读取原始 FileData(含时间戳) |
grep(pattern, path, glob) |
GrepResult |
搜索文本内容,跳过二进制文件 |
glob(pattern, path) |
GlobResult |
按 glob 模式匹配文件 |
write(filePath, content) |
WriteResult |
仅创建语义;冲突时返回 error |
edit(filePath, old, new, replaceAll) |
EditResult |
强制唯一性;支持 replaceAll |
结果类型一览
| 类型 | 成功字段 |
|---|---|
ReadResult | content, mimeType |
LsResult | files (FileInfo[]) |
GrepResult | matches (GrepMatch[]) |
WriteResult | path |
EditResult | path, occurrences |
沙箱扩展
SandboxBackendProtocolV2 额外提供:
execute(command):在沙箱中运行 Shell 命令readonly id:沙箱实例的唯一标识符
可选方法
uploadFiles(files: Array<[string, Uint8Array]>):上传多个文件(用于沙箱后端)downloadFiles(paths: string[]):下载多个文件(用于沙箱后端)
V1 适配工具
adaptBackendProtocol(v1Backend):将 V1 后端适配为 V2adaptSandboxProtocol(v1Sandbox):将 V1 沙箱适配为 V2
import { adaptBackendProtocol, adaptSandboxProtocol } from "deepagents";
// 将 V1 后端适配为 V2,使旧实现可以在新框架中使用
const v2Backend = adaptBackendProtocol(v1Backend);
const v2Sandbox = adaptSandboxProtocol(v1Sandbox);
createDeepAgent() 的 V1 后端。仅当直接调用协议方法时才需要手动适配。
权限控制
声明式控制 Agent 可以读写哪些文件和目录
权限应用于内置的文件系统工具,并在后端被调用之前进行评估。这意味着即使后端支持访问,权限规则也能阻止不安全的操作。
权限的工作位置
- 在文件系统工具层执行,先于后端调用
- 基于路径的允许/拒绝规则
- 支持规则排序和优先级
- 支持子 Agent 权限继承
与后端的配合
- 权限在 CompositeBackend 路由之前评估
- 被拒绝的操作不会到达后端
- 适合控制敏感目录的访问
- 详细配置请参阅权限指南
/secrets/")。如果需要更复杂的逻辑(速率限制、审计日志、内容检查),请使用策略钩子。
策略钩子:子类
通过子类化后端来实现自定义验证逻辑
当权限的"允许/拒绝"不够用时,你可以通过子类化后端来拦截写操作、添加审计日志或执行内容检查。以下示例阻止选定前缀下的写入和编辑:
import { FilesystemBackend, type WriteResult, type EditResult } from "deepagents";
class GuardedBackend extends FilesystemBackend {
private denyPrefixes: string[];
constructor({ denyPrefixes, ...options }) {
super(options);
// 确保前缀以 "/" 结尾,避免匹配歧义
this.denyPrefixes = denyPrefixes.map(
p => p.endsWith("/") ? p : p + "/"
);
}
// 拦截写入操作,禁止在指定前缀路径下创建文件
async write(filePath: string, content: string): Promise<WriteResult> {
if (this.denyPrefixes.some(p => filePath.startsWith(p))) {
return { error: `Writes are not allowed under ${filePath}` };
}
return super.write(filePath, content); // 通过检查,调用父类方法执行写入
}
// 拦截编辑操作,禁止在指定前缀路径下修改文件
async edit(filePath: string, oldString: string, newString: string, replaceAll = false): Promise<EditResult> {
if (this.denyPrefixes.some(p => filePath.startsWith(p))) {
return { error: `Edits are not allowed under ${filePath}` };
}
return super.edit(filePath, oldString, newString, replaceAll); // 通过检查,调用父类
}
}
策略钩子:包装器
通用包装器——适用于任何后端的策略拦截
如果你需要在任意后端上应用策略,包装器模式比子类更灵活。它不依赖具体的后端实现,可以组合使用。
import { type BackendProtocolV2, type WriteResult, type EditResult } from "deepagents";
class PolicyWrapper implements BackendProtocolV2 {
private denyPrefixes: string[];
// inner 是被包装的后端实例,可以是任何实现了 BackendProtocolV2 的后端
constructor(private inner: BackendProtocolV2, denyPrefixes = []) {
this.denyPrefixes = denyPrefixes.map(
p => p.endsWith("/") ? p : p + "/"
);
}
private isDenied(path: string): boolean {
return this.denyPrefixes.some(p => path.startsWith(p));
}
// 读取类方法直接透传给内部后端,不加任何拦截
ls(path) { return this.inner.ls(path); }
read(filePath, offset?, limit?) { return this.inner.read(filePath, offset, limit); }
grep(pattern, path?, glob?) { return this.inner.grep(pattern, path, glob); }
glob(pattern, path?) { return this.inner.glob(pattern, path); }
// 写入类方法加拦截:检查路径是否在禁止列表中
async write(filePath, content): Promise<WriteResult> {
if (this.isDenied(filePath)) return { error: "Writes not allowed" };
return this.inner.write(filePath, content); // 未被禁止,委托给内部后端
}
async edit(filePath, old, newStr, replaceAll?): Promise<EditResult> {
if (this.isDenied(filePath)) return { error: "Edits not allowed" };
return this.inner.edit(filePath, old, newStr, replaceAll);
}
}
子类方式
- 与特定后端耦合
- 覆写方法,调用 super
- 适合 FilesystemBackend 等具体后端
包装器方式
- 与后端解耦,适用于任意后端
- 组合使用,可多层包装
- 适合跨后端的统一策略
多模态文件支持
V2 后端原生支持图片、音频、视频、PDF 等二进制文件
当 read() 遇到二进制文件(通过扩展名判断 MIME 类型)时,返回包含 Uint8Array 内容和相应 mimeType 的 ReadResult。文本文件返回 string 内容。
deepagents>=1.9.0。
const result = await backend.read("/workspace/screenshot.png");
if (result.error) {
console.error(result.error); // 读取失败,输出错误信息
} else if (result.content instanceof Uint8Array) {
// 二进制文件 —— content 是 Uint8Array,mimeType 标识文件类型
console.log(`Binary file: ${result.mimeType}`); // 例如 "image/png"
} else {
// 文本文件 —— content 是 string 类型
console.log(`Text file: ${result.mimeType}`); // 例如 "text/plain"
}
| 类别 | 扩展名 | MIME 类型 |
|---|---|---|
| 图片 | .png, .jpg/.jpeg, .gif, .webp, .svg, .heic, .heif | image/png, image/jpeg, image/gif, image/webp, image/svg+xml, image/heic, image/heif |
| 音频 | .mp3, .wav, .aiff, .aac, .ogg, .flac | audio/mpeg, audio/wav, audio/aiff, audio/aac, audio/ogg, audio/flac |
| 视频 | .mp4, .webm, .mpeg/.mpg, .mov, .avi, .flv, .wmv, .3gpp | video/mp4, video/webm, video/mpeg, video/quicktime, video/x-msvideo, video/x-flv, video/x-ms-wmv, video/3gpp |
| 文档 | .pdf, .ppt, .pptx | application/pdf, application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation |
| 文本 | .txt, .html, .json, .js, .ts, .py 等 | text/plain, text/html, application/json 等 |
{ content: string | Uint8Array, mimeType, created_at, modified_at }。框架透明处理 V1(行数组)和 V2 格式。
type FileData =
// 当前格式 (v2)
| {
content: string | Uint8Array; // string 用于文本,Uint8Array 用于二进制
mimeType: string; // 例如 "text/plain", "image/png"
created_at: string; // ISO 8601 时间戳
modified_at: string; // ISO 8601 时间戳
}
// 旧版格式 (v1)
| {
content: string[]; // 行数组
created_at: string; // ISO 8601 时间戳
modified_at: string; // ISO 8601 时间戳
};
fileFormat: "v1"(例如 new StoreBackend({ fileFormat: "v1" }))。
迁移指南
从后端工厂模式迁移到直接实例模式
从 deepagents 1.9.0 起,后端工厂模式已弃用。现在后端通过内部辅助函数解析运行时上下文,你可以直接传递实例。
之前(已弃用)
// 工厂函数包装
const agent = createDeepAgent({
backend: (config) => new CompositeBackend(
new StateBackend(config),
{
"/memories/": new StoreBackend(config, {
namespace: (rt) => [rt.serverInfo.user.identity],
}),
},
),
});
之后(推荐)
// 直接传递实例
const agent = createDeepAgent({
backend: new CompositeBackend(
new StateBackend(),
{
"/memories/": new StoreBackend({
namespace: (rt) => [rt.serverInfo.user.identity],
}),
},
),
});
BackendContext 迁移
- 工厂参数从
BackendContext变为Runtime ctx.runtime.context.userId→rt.serverInfo.user.identityctx.state没有直接替代方案
V1 到 V2 方法重命名
lsInfo→lsgrepRaw→grepglobInfo→glob- 返回类型从裸数组变为 Result 对象
已弃用的 API
| 已弃用项 | 替代方案 |
|---|---|
BackendFactory 类型 | 直接传递后端实例 |
BackendRuntime 接口 | 后端在内部解析上下文 |
StateBackend(runtime, options?) 构造函数重载 | new StateBackend(options?) |
StoreBackend(stateAndStore, options?) 构造函数重载 | new StoreBackend(options?) |
WriteResult 和 EditResult 上的 filesUpdate 字段 | 状态写入现在由后端在内部处理 |
createDeepAgent() 的 V1 后端。直接调用协议方法时需手动使用 adaptBackendProtocol()。
本章回顾
后端是 Deep Agents 文件系统的灵魂——你控制数据存在哪里
核心知识点
- 七种内置后端覆盖从临时到生产的全部场景
- StateBackend 是默认选择,线程作用域
- StoreBackend 实现跨线程持久化
- CompositeBackend 按路径路由到不同后端
- 自定义后端只需实现
BackendProtocolV2七个方法 - 权限在工具层评估,策略钩子在方法层拦截
- V2 后端原生支持图片、音频、视频等二进制文件
后续章节预告
- 子 Agent:多 Agent 协作与任务分发
- 异步子 Agent:并行执行多个子任务
- 人机交互:HITL 中间件与审批流程
- 权限系统:细粒度的文件访问控制
- 记忆管理:短期 + 长期记忆的最佳实践
- 沙箱:安全隔离的代码执行环境
CompositeBackend 路由 + 权限控制 + 策略钩子的组合,你可以为 Agent 构建一个安全、灵活、可扩展的虚拟文件系统。先用默认后端起步,随着需求增长逐步引入更复杂的配置。
延伸阅读
继续本课程
- 下一章:子 Agent — 多 Agent 协作与任务分发
- 权限系统 — 细粒度文件访问控制的完整指南
- 沙箱 — 安全隔离的代码执行环境
官方文档
- LangGraph.js 官方文档 — 完整 API 参考
- LangSmith — 可视化调试和部署平台
- Deep Agents GitHub — 源码和示例