1. 概述

沙箱容器提供强隔离环境,所有 Shell 命令、文件 IO 操作都在容器内部执行,保证宿主环境不受污染。Skill 的脚本文件(*.sh*.py 等)存放在宿主文件系统,但 Agent 需要在隔离容器内执行这些脚本。

HarnessAgent 架采用 物化 → 工作区投影 → 容器内执行 三阶段透明处理流程,对上层 Agent 屏蔽文件分发细节。


2. Skill 分类与路径映射

Skill 分为两大来源,四大层级:

  • 工作区 skill

    • workspace/skills/
    • <userId>/skills/
  • 市场 skill

    • 项目全局
    • Git / MySQL / Nacos / classpath

两大来源的 skill 在进入沙箱前存放位置不同,最终在容器内拥有固定路径:

  • 工作区:/workspace/skills/<name>
  • 技能市场:/workspace/.skills-cache/

3. 核心三步执行流程

3.1 第一步:市场 Skill 物化落地

市场 Skill 从配置中心拉取后仅为内存字节数组,无法直接被 Shell 调用。在每一轮推理调用开始前,MarketplaceStager 将资源持久化写入宿主目录:./.skills-cache/

落地策略

  1. 文件 SHA-256 增量写入
    对每个文件计算内容哈希,仅写入发生变更的文件,未变更文件直接复用本地缓存,减少磁盘 IO
  2. 孤儿目录自动清理
    同步清理已下架、已移除的 Skill 残留目录,防止缓存目录无限膨胀。
  3. 自动恢复脚本可执行权限
    资源入库序列化时会丢失 POSIX 文件权限,框架使用启发式规则自动补充 +x 权限:
    • 规则1:文件头部包含 shebang #!
    • 规则2:后缀为脚本类型:.sh/.bash/.py/.rb/.pl/.js/.mjs
    • 权限策略:仅对已有读权限的文件追加执行权限;
    • 静态文件(.json/.md/.txt)保持默认权限 644

仅针对市场 Skill ,工作区 Skill 已存在于磁盘,直接跳过此步骤。


3.2 第二步:工作区投影进入沙箱

沙箱实例调用 start() 启动时,Harness 将宿主工作区静态资源打包为 tar 压缩包,解压后注入容器内的 /workspace 目录。

配置项 workspaceProjectionRoots 默认包含下面目录:

AGENTS.md
skills/
subagents/
knowledge/
.skills-cache/

自动包含两类 Skill 目录,两块内容会一同被打包,批量注入沙箱:

  • 工作区 skills/
  • 上一步生成的缓存目录 .skills-cache/

缓存优化机制

  • 框架对所有待投影文件整体计算 SHA-256 指纹:
    • 本次哈希 = 上一次哈希:跳过文件注入,复用容器内已有文件;
    • 文件内容发生改动:重新打包并同步文件到沙箱。
  • 多次调用 call() 执行技能,不会反复拷贝不变的文件,提升沙箱启动速度。

两个配置开关配置位置(DockerFilesystemSpec / KubernetesFilesystemSpec
):

配置项 作用
workspaceProjectionRoots(List) 自定义需要打包投影的根目录,默认只包含 skills、.skills-cache 等
workspaceProjectionEnabled(false) 关闭整个工作区投影;关闭后沙箱内不存在技能文件,脚本无法运行

3.3 第三步:自动渲染容器路径,执行脚本

框架自动给技能文件拼接容器内的绝对路径,Agent 只负责执行命令,完全不用关心文件来自本地工作区还是技能缓存。

3.3.1 自动替换文件根路径

沙箱运行时,框架会统一把路径前缀改成容器内部目录:

技能来源 容器内最终路径前缀
本地工作区自定义技能 /workspace/skills/<技能名>
从技能市场下载的缓存技能 /workspace/.skills-cache/<来源>/<技能名>

3.3.2 自动生成执行命令

框架拼接出完整绝对路径,最终执行 Shell 命令类似:

python3 /workspace/skills/code-reviewer/scripts/run-checks.sh <目标路径>

命令直接在沙箱容器内运行,读取的就是上一步投影进来的文件。


3.3.3 路径自适应能力

如果沙箱运行时工作区挂载根目录发生变更(例如运行根目录改为 /home/agentscope/workspace),框架会自动替换路径前缀,保证命令始终指向正确文件,Agent 无需修改代码。


4. 跨调用保留执行副作用

多次连续调用 call() 执行同一个技能脚本时,会产生两类运行数据:

  1. 静态文件skills 源码、配置文件(由工作区投影每次注入)
  2. 运行副作用数据
    • pip installnpm install 安装的第三方依赖包
    • 代码编译产出、临时文件、生成的业务产物

副作用数据只存在于容器运行时,默认沙箱销毁后全部丢失,下一次调用又要重新安装依赖,速度很慢。

开启快照配置 snapshotSpec(...) 后,框架会把容器内整个 /workspace 目录打包保存为镜像快照,并绑定一个唯一的 scope key

完整执行顺序(同一 scopeKey):

  1. 本次调用开始:先拉取并恢复快照,把上一次执行留下的依赖、编译产物全部还原到容器里。
  2. 再执行第二步:叠加执行工作区文件投影(hydrate),只覆盖本地有变更的技能源码,不会覆盖已经装好的依赖。
  3. 运行脚本,产生新的副作用。
  4. 本次调用结束:更新快照,持久化新的产物与依赖。

最终效果:

  • 第一次运行:全新容器,执行安装依赖。
  • 后续同 scopeKey 的调用:直接恢复快照,依赖已存在,不需要重复执行 pip/npm install
  • 技能代码文件依旧会按投影机制实时更新,代码改了会覆盖,已安装的包保留。

快照用来持久化脚本运行产生的安装包、编译产物这类运行副作用。只要调用使用相同 scopeKey,沙箱先还原上一次的运行环境,再覆盖最新的技能代码,做到依赖只装一次,反复调用零重复安装。


5. 两种 Skill 加载链路完全隔离

5.1 读取 Skill 元信息(不需要沙箱)

调用 load_skill_through_path 加载 SKILL.md、引用文档、入参定义时,直接读取宿主文件或内存数据,不经过容器、不依赖目录投影。

即便关闭 workspaceProjectionEnabledSkill 描述依然可以正常加载。

5.2 执行 Shell 脚本(必须走沙箱+投影流程)

只有调用 execute_shell_command 运行脚本文件时,才需要完成「物化→投影→容器执行」整套流程。关闭投影会直接导致脚本文件在容器内找不到,执行报错。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐