作用
HarnessAgent 把 agent 对工作区的访问从”一定是本机磁盘”抽象成统一接口。所有文件工具(read_file / write_file / edit_file / grep_files / glob_files / list_files)和可选的 execute(shell)都从这个抽象走。
这样做让你能在三种部署模式之间切换,而不改 agent 代码:
- 本机 + shell —— 单进程、本地、信任环境;
- 共享存储 —— 多副本 / 多 pod 共享同一份长期记忆;
- 沙箱 —— 文件与命令都在隔离容器里执行,跨调用恢复同一份工作区。
三种声明式模式
在HarnessAgent.Builder 上用 filesystem(...) 三选一(不调就是默认模式 3):
filesystem(...)与abstractFilesystem(...)互斥;后者是给完全自管文件系统的逃生口,正常用法不需要。
模式 1:共享存储(RemoteFilesystemSpec)
适合”多副本,但用户的长期记忆要一致”。把一个 BaseStore 实现(Redis / JDBC / 内存)传进去,框架自动按路径前缀把工作区文件路由到这个 KV 存储:
所有配置项
内置路由规则
框架自动把以下路径路由到共享 KV,每个路径段各自独立命名空间,不会互相污染:
其余不在上表的路径落到本地
LocalFilesystem(无 shell)。
示例场景:多副本客服 agent
三个 pod 各跑一个HarnessAgent,用同一个 Redis 做 BaseStore:
- 三个 pod 上本地磁盘的
AGENTS.md/knowledge//skills/作为只读模板(git 同步); - 运行时产物(
MEMORY.md、memory/、对话日志)自动存到 Redis,任意 pod 都能读到最新状态; - 用户 alice 的记忆在 KV 键
agents/customer-service/users/alice/memory/...下。
BaseStore 可用实现
模式 2:沙箱(SandboxFilesystemSpec 系列)
适合”代码会执行不可信操作、或要隔离生产环境”。所有文件操作和 shell 命令都发到沙箱里执行,宿主完全不受影响。
Docker 沙箱
DockerFilesystemSpec 所有配置项:
Kubernetes 沙箱(agent-sandbox)
Kubernetes 后端完全基于 agent-sandbox:沙箱 pod 由集群里的 agent-sandbox 控制器管理,镜像、资源、PVC 都声明在集群侧的SandboxTemplate / SandboxWarmPool 里(不在 Java 侧配置),Java 侧通过 SandboxClaim 从预热池领取实例。使用前需要先安装 agent-sandbox 控制器并创建好模板和预热池。
KubernetesFilesystemSpec 主要配置项:
apiUrl / gateway* 都不配时,默认用 kubectl port-forward 方式建立本地隧道(适合开发环境)。运行时镜像必须满足运行时镜像约束;工作区持久化依赖模板里的 PVC 配置,详见沙箱 - Kubernetes 后端的状态保存。
E2B 沙箱
Daytona 沙箱
AgentRun 沙箱(阿里云)
所有沙箱后端的公共配置(继承自 SandboxFilesystemSpec)
快照策略
沙箱可以做快照,使下一次call() 恢复之前的环境状态(安装的依赖、生成的文件等):
示例场景:编程助手(Docker + 本地快照)
工作区投影(Workspace Projection)
沙箱启动时,框架自动把宿主工作区里的”静态资产”打成 tar,注入(hydrate)到沙箱的/workspace。这些静态资产包括:
AGENTS.md(人格文件)skills/(技能目录)subagents/(子 agent 声明)knowledge/(知识库).skills-cache/(技能缓存)
workspaceProjectionRoots(List) 自定义包含哪些路径,或用 workspaceProjectionEnabled(false) 完全关闭。
模式 3:本机 + shell(默认)
什么都不写就是这个:工作区落到${cwd}/.agentscope/workspace/,shell 在宿主上跑:
所有配置项
路径解析策略(LocalFsMode)
Overlay 文件系统
本机模式实际产出的是一个OverlayFilesystem:
- 上层(读写):
LocalFilesystemWithShell,根在workspace,提供 shell; - 下层(只读):
LocalFilesystem,根在project。
pwd 是 project 目录,所以 agent 执行 ls 看到的是项目文件。
项目可写模式(projectWritable)
默认情况下,所有写入都落到 workspace——这对阅读/分析类场景足够,但如果 agent 的核心任务是生成代码(如写一个微服务),你会发现文件全写到了 .agentscope/workspace/ 而不是项目目录。
开启 projectWritable(true) 后,框架会根据路径自动路由写入目标:
示例场景:本地开发助手
/Users/alice/my-project 和 /Users/alice/.config 下的文件,在 /Users/alice/my-project 下执行 shell 命令,但无法访问其他宿主目录。
IsolationScope —— 多用户与多副本怎么分桶
模式 1(共享存储)和模式 2(沙箱)都用同一个IsolationScope 概念,决定谁和谁共享同一份状态:
各 Scope 的降级规则
USERscope 下,如果RuntimeContext.userId为空,降级为SESSION(按 sessionId 隔离)。SESSIONscope 下,如果RuntimeContext.sessionId为空,跳过状态查找,创建全新环境。AGENTscope 的命名空间键由 agent name(build 时固定)决定,不会因缺少上下文字段而降级。
沙箱模式下的并发行为
IsolationScope 在沙箱模式下是顺序复用的共享,不是实时的实例共享。同一 scope key 的并发调用各自启动独立容器;每次调用结束时,最后写入的快照胜出。对 AGENT / GLOBAL 这种多用户共享 scope,如果需要串行化,使用 executionGuard(SandboxExecutionGuard) 做并发守卫。
示例:用 Scope 组合实现不同业务需求
场景 1:每个用户独立的编程沙箱,跨会话保留安装的依赖多用户隔离怎么实现
RuntimeContext.userId 是切多用户的钥匙:
userId 不传的情况下走单租户默认,所有人共享一个根。
运行时数据 vs 静态资产
运行时数据(对话日志、tasks、memory)跟着IsolationScope / userId 走,自动隔离。
静态资产(AGENTS.md、tools.json、knowledge/)对所有用户共享,不按 userId 自动分区。差异化只能通过「用户覆盖目录」实现:
技能和工具在各模式下的行为
技能(Skills)
DynamicSkillMiddleware 在每轮推理前从技能仓库列表合并技能,渲染到 system prompt 里。技能文件的加载走 AbstractFilesystem 接口,所以在三种模式下透明工作:
四层优先级不变(低 → 高):
projectGlobalSkillsDir → skillRepository → workspace/skills/ → <userId>/skills/。
文件工具(read_file / write_file / edit_file / …)
所有文件工具都通过AbstractFilesystem 接口调用,每次操作传入当前 RuntimeContext,由文件系统后端决定实际读写位置。agent 代码完全感知不到模式差异。
Shell 执行(execute)
tools.json / MCP 服务器
tools.json 在 build() 时一次性从工作区读取(走 WorkspaceManager,支持两层读),注册 MCP server 和 allow/deny 过滤。三种模式下行为一致——都是在 build 时读取配置,不受运行时 filesystem 模式影响。
在共享存储模式下,tools.json 也走”远端为上层、本地模板为下层”的 overlay:通过管理台修改 tools.json 后,需要重新 build agent 才能生效(MCP server 注册是一次性的)。
工作区里的两层读取
AGENTS.md、MEMORY.md、KNOWLEDGE.md 等关键文件在读取时有”两层兜底”:先看你配的文件系统后端,没有再退回本地磁盘。这对模式 1(共享存储)下的”模板文件” 很有用:第一个副本启动时本地有 AGENTS.md 模板,立刻可用;后续副本会从共享存储读出最新版本。
写入永远走配置的文件系统后端。