news 2026/6/16 9:51:19

【AgentScope】6.文件系统(Filesystem)详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【AgentScope】6.文件系统(Filesystem)详解

文件系统(Filesystem)详解

一句话概括

文件系统是 Agent 的"文件柜"——它决定了 Agent 的文件存在哪里:是本机磁盘、远程存储,还是隔离的沙箱环境。

你能学到什么

  • 为什么需要"抽象文件系统"?直接用 Java 的 File API 不行吗?
  • 三种文件系统模式的区别:本机模式、共享存储模式、沙箱模式
  • 什么是"多租户隔离"?为什么要用NamespaceFactory
  • 类层次结构:AbstractFilesystem家族成员之间的关系
  • 如何根据你的场景选择正确的文件系统配置

前置知识

需了解 Java 接口与继承的基本概念,以及 05-memory.md 中 Agent 的双层记忆系统——记忆最终要落到文件系统上。


核心概念

AbstractFilesystem — 文件柜的"统一说明书"

生活类比:想象你去图书馆借书。图书馆有各种藏书的地方:普通书架、珍本室、电子资源库。但作为读者,你只需要知道"我要借这本书",图书馆员会帮你从正确的地方取。AbstractFilesystem就是那个"统一的借书接口"——不管文件实际存在哪里,Agent 都用同样的方式操作。

技术解释AbstractFilesystem是一个接口,定义了所有文件系统都必须支持的基本操作:

publicinterfaceAbstractFilesystem{// 列出目录内容(就像在文件管理器里打开文件夹)List<String>ls(Stringpath);// 读取文件内容(就像用记事本打开文件)Stringread(Stringpath);// 写入文件内容(就像保存记事本内容)voidwrite(Stringpath,Stringcontent);// 编辑文件(就像在 Word 里修改文档)voidedit(Stringpath,StringoldText,StringnewText);// 在文件中搜索(就像 Ctrl+F 查找)List<String>grep(Stringpattern,Stringpath);// 用通配符找文件(就像搜索 *.txt)List<String>glob(Stringpattern);// 上传文件voiduploadFiles(Map<String,String>files);// 下载文件Map<String,String>downloadFiles(List<String>paths);}

为什么需要抽象?如果直接用 Java 的FileAPI,Agent 就只能操作本机文件。但实际场景中,我们可能需要:

  • 把 Agent 的记忆存到云端(多台机器共享)
  • 在沙箱里执行不受信任的代码
  • 根据用户 ID 隔离数据(多租户)

抽象接口让这些需求变成"换个实现类"的事,而不是"重写整个 Agent"。


三种声明式模式 — 选哪种文件柜?

生活类比:假设你要开一家公司,需要一个地方存档案。你有三种选择:

  1. 本机模式:在公司自己的档案室里放铁皮柜——方便、省钱,但只有一个房间,不能多地点共享。
  2. 共享存储模式:租用云端档案服务——多个分公司都能访问同一份档案,但贵一些,而且不能在档案室里"做实验"(不能执行脚本)。
  3. 沙箱模式:租一个独立的实验室——里面有档案柜,还能做化学实验(执行脚本),就算爆炸了也不会影响主楼。

技术对比表

模式配置方法能否执行 Shell适用场景
模式 1:本机 + shellfilesystem(LocalFilesystemSpec)或默认能(宿主机上)单机部署、测试环境、受信任的 Agent
模式 2:复合 + Storefilesystem(RemoteFilesystemSpec)不能多副本共享记忆、生产环境、不需要执行脚本
模式 3:沙箱filesystem(SandboxFilesystemSpec)能(沙箱内)需要隔离执行、处理不受信任的代码

下面我们逐一详解这三种模式。


模式一:本机 + Shell(LocalFilesystemSpec)— 自己的档案室

生活类比:你在自家书房里放了一个文件柜。你想存什么就存什么,想在柜子上贴便签、想用订书机装订文件都可以——这是你自己的地盘。但问题是:如果家里来客人了,他们也能翻你的柜子;如果房子着火了,文件就没了。

技术细节

// 模式 1:本机 + shell(默认就是这种)HarnessAgentagent=HarnessAgent.builder().name("local")// Agent 的名字.model(model)// 使用的大模型.workspace(workspace)// 工作区目录.filesystem(newLocalFilesystemSpec().executeTimeoutSeconds(120))// Shell 命令超时时间.build();

工作原理

  1. LocalFilesystemWithShell是实际创建的类
  2. 根目录就是你的workspace目录
  3. 执行 Shell 命令时,直接在宿主机上跑sh -c 你的命令
  4. 所有文件操作都是普通的 Java 文件 API(FileFiles等)

适用场景

  • 你在开发测试阶段,想快速跑起来
  • 你信任 Agent 执行的代码(不会恶意删除文件)
  • 你只需要单机部署,不需要多机器共享数据

安全提醒:因为能直接在宿主机执行 Shell,如果 Agent 被诱导执行rm -rf /,后果很严重!生产环境要谨慎使用。


模式二:复合 + 共享存储(RemoteFilesystemSpec)— 云端档案服务

生活类比:你的公司在多个城市有分部,每个分部都需要查看同一份客户档案。于是你租了一个专业的档案托管服务——所有档案存在云端,每个分部都能访问。但是,这个托管服务只负责存取档案,不能在里面做实验或开派对。

技术细节

// 模式 2:共享存储(无宿主 shell)HarnessAgentagent=HarnessAgent.builder().name("store").model(model).workspace(workspace).filesystem(newRemoteFilesystemSpec(redisStore)// Redis 作为后端存储.isolationScope(IsolationScope.USER))// 按用户隔离.build();

工作原理

  1. 创建的是CompositeFilesystem——一个"组合型"文件系统
  2. 默认路径(未匹配的路径)→ 本地文件系统(无 Shell)
  3. 共享路径(如MEMORY.mdmemory/sessions/)→ 远程存储(Redis、数据库等)
  4. 通过IsolationScope控制不同用户/会话的数据隔离

共享路径是什么?

默认情况下,以下路径会存到远程存储:

MEMORY.md # Agent 的长期记忆 memory/ # 记忆系统的详细记录 agents/<agentId>/sessions/ # 会话日志

你可以用addSharedPrefix("shared/")添加更多共享路径。

为什么默认没有 Shell?设计目标是跨节点一致的长记忆与日志。如果允许在宿主机执行 Shell,多个节点可能会产生冲突。如果你需要 Shell 能力,请选择模式 1 或模式 3。

适用场景

  • 生产环境,需要多副本共享数据
  • Agent 不需要执行 Shell 命令
  • 需要按用户/会话隔离数据(多租户)

模式三:沙箱(SandboxFilesystemSpec)— 安全实验室

生活类比:你有一间化学实验室,里面放着一个防爆玻璃做的"安全箱"。你可以在里面做危险的化学实验——就算发生爆炸,也只损坏箱子里的东西,主实验室完好无损。而且这个安全箱有专门的通风管道和排污系统,与主楼隔离。

技术细节

// 模式 3:沙箱(以 Docker 为例)HarnessAgentagent=HarnessAgent.builder().name("sandbox").model(model).workspace(workspace).filesystem(dockerFilesystemSpec)// 继承自 SandboxFilesystemSpec.build();

工作原理

  1. 创建的是SandboxBackedFilesystem
  2. 文件操作和 Shell 执行都在沙箱容器内进行
  3. 通过SandboxClient与沙箱通信
  4. 沙箱有独立的生命周期管理:创建、快照、恢复、销毁

沙箱的生命周期

┌─────────────────────────────────────────┐ │ 单次 Agent 调用 │ ├─────────────────────────────────────────┤ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ acquire │ → │ execute │ → │ │ │ (获取) │ │ (执行) │ ... │ │ └─────────┘ └─────────┘ │ │ ↓ ↓ │ │ ┌─────────┐ ┌─────────┐ │ │ │ persist │ ← │ release │ │ │ │ (持久化) │ │ (释放) │ │ │ └─────────┘ └─────────┘ │ │ │ └─────────────────────────────────────────┘

适用场景

  • 需要执行不受信任的代码
  • 需要强隔离的多租户环境
  • 需要可恢复的执行状态(比如暂停后继续)
  • 需要执行快照和回滚

详细配置:参见 11-sandbox.md。


NamespaceFactory — 多租户的"分房间管理"

生活类比:想象一个大型写字楼,里面有很多公司租办公室。每家公司都有自己的"区域"——A 公司在 101 室,B 公司在 102 室。虽然是同一栋楼、同一个物业管理,但 A 公司不能进 B 公司的办公室。NamespaceFactory就是那个"前台接待员"——根据你是哪家公司(用户 ID),把你带到正确的房间。

技术细节

@FunctionalInterfacepublicinterfaceNamespaceFactory{List<String>getNamespace();// 返回路径前缀列表}

每次文件操作时调用,返回当前请求应该使用的路径前缀。例如:

隔离级别路径前缀示例说明
GLOBAL[]全局共享,无前缀
AGENT["agents", "myAgent"]按 Agent 隔离
USER["users", "alice"]按用户隔离
SESSION["sessions", "sess-123"]按会话隔离

实际应用

// 从 RuntimeContext 获取当前用户 ID,用于命名空间AtomicReference<String>currentUserId=newAtomicReference<>();HarnessAgentagent=HarnessAgent.builder().namespaceFactory(()->{StringuserId=currentUserId.get();returnList.of("users",userId);// 每个用户有自己的目录}).build();// 这样,用户 alice 的文件存在 /users/alice/ 下// 用户 bob 的文件存在 /users/bob/ 下// 互不干扰

为什么重要?如果没有命名空间隔离:

  • 用户 A 可能读取用户 B 的私密记忆
  • 不同会话的日志可能混在一起
  • 多个 Agent 实例可能互相覆盖配置

AbstractSandboxFilesystem — 带执行能力的文件系统

生活类比:普通的文件柜只能存取文件。但如果你的文件柜里还有一个"小机器人",能帮你执行指令呢?AbstractSandboxFilesystem就是这样的"带机器人的文件柜"——不仅能存取文件,还能在里面执行命令。

技术细节

publicinterfaceAbstractSandboxFilesystemextendsAbstractFilesystem{// 沙箱的唯一标识Stringid();// 在沙箱内执行命令ExecuteResultexecute(Stringcmd,Durationtimeout);}

继承关系:只有实现了AbstractSandboxFilesystem的文件系统才会注册ShellExecuteTool(Shell 执行工具)。这意味着:

  • LocalFilesystemWithShell— 有 Shell(本地执行)
  • SandboxBackedFilesystem— 有 Shell(沙箱内执行)
  • CompositeFilesystem没有 Shell(只是路由器)
  • RemoteFilesystem没有 Shell(只是存储)

关键代码解读

文件系统配置的选择逻辑

// HarnessAgent.Builder 内部的选择逻辑(简化版)publicHarnessAgentbuild(){AbstractFilesystemfs;if(remoteFilesystemSpec!=null){// 模式 2:复合 + 共享存储fs=remoteFilesystemSpec.toFilesystem();// 注意:CompositeFilesystem 不实现 AbstractSandboxFilesystem// 所以不会注册 ShellExecuteTool}elseif(sandboxFilesystemSpec!=null){// 模式 3:沙箱fs=sandboxFilesystemSpec.toFilesystem();// SandboxBackedFilesystem 实现了 AbstractSandboxFilesystem// 所以会注册 ShellExecuteTool}else{// 模式 1:本机 + shell(默认)fs=newLocalFilesystemWithShell(workspace,executeTimeout);// LocalFilesystemWithShell 实现了 AbstractSandboxFilesystem// 所以会注册 ShellExecuteTool}// 如果实现了 AbstractSandboxFilesystem,注册 ShellExecuteToolif(fsinstanceofAbstractSandboxFilesystem){toolkit.register(newShellExecuteTool((AbstractSandboxFilesystem)fs));}// 注册基础文件工具toolkit.register(newFilesystemTool(fs));}

逐行注释

  • 第 5 行:检查是否配置了RemoteFilesystemSpec(共享存储模式)
  • 第 7 行:创建CompositeFilesystem实例
  • 第 9-10 行:关键点!CompositeFilesystem不继承AbstractSandboxFilesystem,所以不能执行 Shell
  • 第 12-16 行:检查是否配置了沙箱模式,创建沙箱文件系统
  • 第 18-22 行:默认情况,创建本机文件系统,可以执行 Shell
  • 第 25-27 行:判断是否注册 Shell 工具的关键逻辑——只有实现了AbstractSandboxFilesystem才注册
  • 第 30 行:无论哪种模式,都注册基础的文件操作工具

CompositeFilesystem 的路由逻辑

// CompositeFilesystem 的读写路由(简化版)publicclassCompositeFilesystemimplementsAbstractFilesystem{privatefinalAbstractFilesystemdefaultBackend;// 默认后端(本地)privatefinalMap<String,AbstractFilesystem>routes;// 前缀 -> 后端的映射@OverridepublicStringread(Stringpath){// 遍历所有前缀,找最长匹配StringmatchedPrefix=findLongestMatchPrefix(path);if(matchedPrefix!=null){// 找到匹配的前缀,用对应的远程后端AbstractFilesystembackend=routes.get(matchedPrefix);returnbackend.read(stripPrefix(path,matchedPrefix));}else{// 没匹配到,用默认的本地后端returndefaultBackend.read(path);}}privateStringfindLongestMatchPrefix(Stringpath){StringlongestMatch=null;for(Stringprefix:routes.keySet()){if(path.startsWith(prefix)){if(longestMatch==null||prefix.length()>longestMatch.length()){longestMatch=prefix;}}}returnlongestMatch;}}

逐行注释

  • 第 5 行:默认后端,通常是LocalFilesystem(无 Shell)
  • 第 6 行:路由表,存储"前缀 -> 后端"的映射
  • 第 10 行:最长前缀匹配——如果有memory/memory/important/两个前缀,memory/important/file.txt会匹配后者
  • 第 13-15 行:找到匹配后,去掉前缀再传给后端。例如memory/diary.txt匹配memory/前缀后,传给远程后端的路径是diary.txt
  • 第 17-19 行:没匹配到就用默认后端

WorkspaceIndex 索引加速

// RemoteFilesystem 使用本地索引加速查询publicclassRemoteFilesystemimplementsAbstractFilesystem{privatefinalBaseStorestore;// 远程存储后端privatefinalWorkspaceIndexindex;// 本地 SQLite 索引@OverridepublicList<String>ls(Stringpath){// 先查本地索引(快)List<String>fromIndex=index.list(path);if(!fromIndex.isEmpty()){returnfromIndex;}// 索引没命中,回退到远程存储扫描(慢但完整)returnstore.listAll(path);}@OverridepublicList<String>grep(Stringpattern,Stringpath){// 先用索引找候选文件List<String>candidates=index.findCandidates(path);if(candidates.isEmpty()){// 索引返回空,可能是还没有索引到// 回退到全量扫描,确保不遗漏其他节点写入的内容returnstore.grepAll(pattern,path);}// 在候选文件中搜索returnsearchInCandidates(pattern,candidates);}}

逐行注释

  • 第 5-6 行:索引是可选的性能优化,存储在本地 SQLite 中
  • 第 10-12 行:先查本地索引,速度快
  • 第 15-16 行:索引没命中时,回退到远程扫描。这是"尽力而为"的策略——索引可能不包含其他节点新写入的内容,但全量扫描保证不遗漏
  • 第 20-30 行grep同样的策略——先索引,后回退

整体流程图

文件系统类层次结构

┌─────────────────────┐ │ AbstractFilesystem │ ← 文件操作的统一接口 │ ───────────────────│ │ ls/read/write/edit │ │ grep/glob/upload │ │ download │ └─────────┬───────────┘ │ ┌───────────────────────┼───────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ LocalFilesystem │ │ RemoteFilesystem│ │CompositeFilesystem│ │ (纯本地,无Shell)│ │ (KV存储后端) │ │ (路由器,组合多个) │ └────────┬────────┘ └─────────────────┘ └─────────────────┘ │ │ 继承 ▼ ┌─────────────────────────┐ │LocalFilesystemWithShell │ ← 本地 + Shell 执行 │ implements │ │ AbstractSandboxFilesystem│ └─────────────────────────┘ ┌─────────────────────┐ │AbstractSandboxFilesystem│ ← 带执行能力的接口 │ ───────────────────│ │ + id() │ │ + execute() │ └─────────┬───────────┘ │ ┌───────────────────────┼───────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────────┐ ┌─────────────────┐ ┌─────────────────────┐ │LocalFilesystemWithShell│ │BaseSandboxFilesystem│ │SandboxBackedFilesystem│ │ (本地执行) │ │ (远程Unix基类) │ │ (沙箱代理) │ └─────────────────────┘ └─────────────────┘ └─────────────────────┘

简单解读

  1. AbstractFilesystem是最顶层的接口,定义了所有文件系统都能做的事:读、写、列目录、搜索等。

  2. AbstractSandboxFilesystem扩展了上层接口,增加了"执行命令"的能力。只有继承它才能注册ShellExecuteTool

  3. LocalFilesystem是最简单的实现——就是操作本地磁盘,不能执行命令。

  4. LocalFilesystemWithShell在本地文件系统基础上,增加了"执行 Shell 命令"的能力。

  5. RemoteFilesystem把文件存到远程 KV 存储(如 Redis),适合多实例共享。

  6. CompositeFilesystem是个"路由器"——根据文件路径前缀,把请求转发到不同的后端。

  7. SandboxBackedFilesystem把所有操作都转发到沙箱容器内执行。

三种模式的工作流程

┌─────────────────────────────────────────────────────────────────┐ │ Agent 调用文件操作 │ └─────────────────────────────────┬───────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ 选择哪种文件系统模式? │ └─────────────┬───────────────┘ │ ┌─────────────────────────┼─────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ 模式 1:本机 │ │ 模式 2:复合+Store │ │ 模式 3:沙箱 │ │ │ │ │ │ │ │ 本地磁盘 │ │ 路由到不同后端 │ │ Docker/容器 │ │ sh -c 执行 │ │ 共享路径→Redis │ │ 隔离执行 │ │ │ │ 本地路径→本地 │ │ │ └───────┬───────┘ └─────────┬─────────┘ └─────────┬─────────┘ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────────┐ ┌───────────────────┐ │ workspace/ │ │ MEMORY.md → Redis │ │ 沙箱容器内 │ │ ├── MEMORY.md │ │ memory/ → Redis │ │ 独立的文件系统 │ │ ├── memory/ │ │ agents/ → Redis │ │ 独立的进程空间 │ │ └── sessions/ │ │ 其他 → 本地 │ │ 可快照/恢复 │ └───────────────┘ └───────────────────┘ └───────────────────┘

与其他模块的关系


⬅️ 上一篇:05-memory | 📖 回到目录 | ➡️ 下一篇:07-tool


学习要点

必须记住的三个概念

  1. 抽象接口的价值AbstractFilesystem让 Agent 代码与存储实现解耦。今天存本地,明天存 Redis,后天存 S3——Agent 代码不需要改。

  2. 三种模式的本质区别

    • 本机模式 = 文件在本地 + Shell 在本地执行
    • 复合模式 = 文件在远程 + 无 Shell
    • 沙箱模式 = 文件在沙箱 + Shell 在沙箱执行
  3. 命名空间隔离:多租户场景下,必须用NamespaceFactory隔离不同用户的数据。

常见错误

错误现象解决方案
在复合模式下调用 ShellShellExecuteTool不存在改用本机模式或沙箱模式
忘记配置命名空间用户 A 能看到用户 B 的数据配置NamespaceFactory
在本机模式执行危险命令误删宿主机文件使用沙箱模式隔离
沙箱忘记持久化容器重启后数据丢失配置SandboxStateStore

选择指南

┌─────────────────────┐ │ 需要执行 Shell 吗? │ └─────────┬───────────┘ │ ┌───────────────┴───────────────┐ │ 否 │ 是 ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 需要多实例共享? │ │ 信任执行的代码? │ └────────┬────────┘ └────────┬────────┘ │ │ ┌───────┴───────┐ ┌───────┴───────┐ │ 否 │ 是 │ 是 │ 否 ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────────┐ ┌─────────┐ ┌─────────┐ │本机模式 │ │复合+Store模式│ │本机模式 │ │沙箱模式 │ │(默认) │ │ │ │ │ │ │ └─────────┘ └─────────────┘ └─────────┘ └─────────┘

进一步阅读

  • 沙箱详解:11-sandbox.md——深入了解沙箱的生命周期管理
  • 工具详解:07-tool.md——了解FilesystemToolShellExecuteTool的使用
  • 工作区详解:03-workspace.md——了解WorkspaceManager如何使用文件系统
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 9:50:56

遗传算法工程实践:选择、交叉、变异的参数真相与动态调优

1. 项目概述&#xff1a;为什么第二部分比第一部分更值得细读“遗传算法入门——第二部分”这个标题乍看平平无奇&#xff0c;像是某门在线课程的普通章节名&#xff0c;但如果你已经翻过第一部分&#xff0c;就会明白&#xff1a;那只是铺开一张白纸&#xff0c;而这一部分&am…

作者头像 李华
网站建设 2026/6/11 4:18:01

Python 类型检查器众多,库维护者该如何抉择?

所有文章2026 年&#xff1a;现在真的要运行五个类型检查器吗&#xff1f;Pyrefly v1.0 发布&#xff01;类型正确&#xff0c;代码错误&#xff1a;类型检查器捕获的惊人错误将 Pyrefly 类型检查添加到你的智能循环中Python 类型检查器比较&#xff1a;速度和内存使用情况如何…

作者头像 李华
网站建设 2026/6/12 2:20:58

绝区零智能自动化助手:解放双手,让AI为你打理游戏日常

绝区零智能自动化助手&#xff1a;解放双手&#xff0c;让AI为你打理游戏日常 【免费下载链接】ZenlessZoneZero-OneDragon 绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄 项目地址: https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon …

作者头像 李华
网站建设 2026/6/11 18:19:41

2026年永康别墅大门,选这几家才靠谱

永康别墅大门产业带作为全国门窗制造的核心枢纽&#xff0c;经过三十余年发展&#xff0c;已形成从原材料加工到终端售后的完整链条。然而&#xff0c;随着住宅形态向大宅化、定制化演变&#xff0c;行业正面临技术突围的关键窗口期。2026年&#xff0c;别墅大门领域的竞争焦点…

作者头像 李华