OpenClaw工具拆解之sandboxed_write+sandboxed_edit

一、sandboxed_write 工具

1.1 工具概述

功能 :沙盒中写入文件
核心特性

  • 通过 bridge 写入隔离文件系统
  • 自动创建父目录
  • 支持沙盒根目录限制
  • 参数标准化包装

1.2 创建函数

位置:第 115006 行

javascript 复制代码
function createSandboxedWriteTool(params) {
    return wrapToolParamNormalization(createWriteTool(params.root, { 
        operations: createSandboxWriteOperations(params) 
    }), CLAUDE_PARAM_GROUPS.write);
}

1.3 沙盒写入操作

位置:第 115068 行

javascript 复制代码
function createSandboxWriteOperations(params) {
    return {
        // 1. 创建目录
        mkdir: async (dir) => {
            await params.bridge.mkdirp({
                filePath: dir,
                cwd: params.root
            });
        },
        
        // 2. 写入文件
        writeFile: async (absolutePath, content) => {
            await params.bridge.writeFile({
                filePath: absolutePath,
                cwd: params.root,
                data: content
            });
        }
    };
}

1.4 包装链

复制代码
createWriteTool (外部库)
    ↓ (使用沙盒操作)
wrapToolParamNormalization (参数标准化)
    ↓ (CLAUDE_PARAM_GROUPS.write)
最终工具

1.5 参数标准化

javascript 复制代码
const CLAUDE_PARAM_GROUPS = {
    write: [
        {
            keys: ["path", "file_path", "filePath", "file"],
            label: "path alias"
        },
        {
            keys: ["content"],
            label: "content"
        }
    ]
};

const CLAUDE_PARAM_ALIASES = [
    { original: "path", alias: "file_path" },
    { original: "path", alias: "filePath" },
    { original: "path", alias: "file" }
];

1.6 执行流程图

复制代码
sandboxed_write 工具调用
    ↓
1. 参数标准化
   ├─ path/file_path/filePath/file → path
   └─ content → content
    ↓
2. 验证必填参数
    ↓
3. 沙盒桥接写入
    ├─ bridge.mkdirp(创建父目录)
    └─ bridge.writeFile(写入文件)
    ↓
4. 返回结果

1.7 返回结果格式

成功

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully wrote 123 bytes to sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "bytesWritten": 123,
    "sandbox": true
  }
}

二、sandboxed_edit 工具

2.1 工具概述

功能 :沙盒中编辑文件
核心特性

  • 通过 bridge 访问隔离文件系统
  • 支持编辑失败恢复
  • 支持参数别名
  • 精确文本替换

2.2 创建函数

位置:第 115009 行

javascript 复制代码
function createSandboxedEditTool(params) {
    return wrapToolParamNormalization(
        wrapEditToolWithRecovery(createEditTool(params.root, { 
            operations: createSandboxEditOperations(params) 
        }), {
            root: params.root,
            readFile: async (absolutePath) => (await params.bridge.readFile({
                filePath: absolutePath,
                cwd: params.root
            })).toString("utf8")
        }), 
        CLAUDE_PARAM_GROUPS.edit
    );
}

2.3 沙盒编辑操作

位置:第 115077 行

javascript 复制代码
function createSandboxEditOperations(params) {
    return {
        // 1. 读取文件
        readFile: (absolutePath) => params.bridge.readFile({
            filePath: absolutePath,
            cwd: params.root
        }),
        
        // 2. 写入文件
        writeFile: (absolutePath, content) => params.bridge.writeFile({
            filePath: absolutePath,
            cwd: params.root,
            data: content
        }),
        
        // 3. 访问检查
        access: async (absolutePath) => {
            if (!await params.bridge.stat({
                filePath: absolutePath,
                cwd: params.root
            })) {
                throw createFsAccessError("ENOENT", absolutePath);
            }
        }
    };
}

2.4 编辑恢复包装

位置:第 114462 行

javascript 复制代码
function wrapEditToolWithRecovery(base, options) {
    return {
        ...base,
        execute: async (toolCallId, params, signal, onUpdate) => {
            // 1. 读取参数
            const { pathParam, oldText, newText } = readEditToolParams(params);
            
            // 2. 解析绝对路径
            const absolutePath = typeof pathParam === "string" ? 
                resolveEditPath(options.root, pathParam) : void 0;
            
            // 3. 读取原始内容
            let originalContent;
            if (absolutePath && newText !== void 0) {
                try {
                    originalContent = await options.readFile(absolutePath);
                } catch {}
            }
            
            // 4. 执行编辑
            try {
                return await base.execute(toolCallId, params, signal, onUpdate);
            } catch (err) {
                // 5. 编辑失败处理
                if (!absolutePath) throw err;
                
                let currentContent;
                try {
                    currentContent = await options.readFile(absolutePath);
                } catch {}
                
                // 6. 判断编辑是否已应用
                if (typeof currentContent === "string" && newText !== void 0) {
                    if (didEditLikelyApply({
                        originalContent,
                        currentContent,
                        oldText,
                        newText
                    })) {
                        return buildEditSuccessResult(pathParam ?? absolutePath);
                    }
                }
                
                // 7. 添加不匹配提示
                if (typeof currentContent === "string" && 
                    err instanceof Error && 
                    shouldAddMismatchHint(err)) {
                    throw appendMismatchHint(err, currentContent);
                }
                
                throw err;
            }
        }
    };
}

2.5 参数标准化

javascript 复制代码
const CLAUDE_PARAM_GROUPS = {
    edit: [
        {
            keys: ["path", "file_path", "filePath", "file"],
            label: "path alias"
        },
        {
            keys: ["oldText", "old_string", "old_text", "oldString"],
            label: "oldText alias"
        },
        {
            keys: ["newText", "new_string", "new_text", "newString"],
            label: "newText alias",
            allowEmpty: true
        }
    ]
};

const CLAUDE_PARAM_ALIASES = [
    // path 别名
    { original: "path", alias: "file_path" },
    { original: "path", alias: "filePath" },
    { original: "path", alias: "file" },
    
    // oldText 别名
    { original: "oldText", alias: "old_string" },
    { original: "oldText", alias: "old_text" },
    { original: "oldText", alias: "oldString" },
    
    // newText 别名
    { original: "newText", alias: "new_string" },
    { original: "newText", alias: "new_text" },
    { original: "newText", alias: "newString" }
];

2.6 执行流程图

复制代码
sandboxed_edit 工具调用
    ↓
1. 参数标准化
   ├─ path 别名处理
   ├─ oldText 别名处理
   └─ newText 别名处理
    ↓
2. 验证必填参数
    ↓
3. 读取原始内容
    ↓
4. 执行编辑(精确替换)
    ↓
5. 编辑失败处理
   ├─ 读取当前内容
   ├─ 判断是否部分成功
   └─ 添加不匹配提示
    ↓
6. 返回结果

三、关键机制对比

3.1 功能定位

特性 sandboxed_write sandboxed_edit
用途 创建/覆盖文件 精确修改文件
写操作 覆盖写入 精确替换
恢复机制 不支持 支持

3.2 参数支持

特性 sandboxed_write sandboxed_edit
参数别名 4 个(path) 9 个(path/oldText/newText)
必填参数 path, content path, oldText
可选参数 newText(可为空)

3.3 安全限制

限制类型 sandboxed_write sandboxed_edit
沙盒根目录 root 限制 root 限制
桥接访问 必需 必需
路径边界 bridge 路径 bridge 路径

四、沙盒工具系列总结

4.1 三个沙盒工具

工具 功能 桥接操作
sandboxed_read 读取文件 bridge.readFile, bridge.stat, detectMime
sandboxed_write 写入文件 bridge.mkdirp, bridge.writeFile
sandboxed_edit 编辑文件 bridge.readFile, bridge.writeFile, bridge.stat

4.2 共同特点

  • 通过 bridge 访问隔离文件系统
  • 不能直接访问 host 文件系统
  • 用于容器/Docker/沙盒环境
  • 支持 modelContextWindowTokens 限制
  • 支持图片 sanitization

4.3 使用场景

复制代码
沙盒模式启用时:
├─ 读取文件 → sandboxed_read
├─ 写入文件 → sandboxed_write
└─ 编辑文件 → sandboxed_edit

非沙盒模式时:
├─ 读取文件 → read
├─ 写入文件 → write
└─ 编辑文件 → edit

五、使用示例

5.1 sandboxed_write 工具调用

用户在沙盒中创建文件

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sandboxed_write",
    "arguments": { 
      "path": "src/main.js",
      "content": "console.log(\"Hello\");"
    }
  }
}

执行结果

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully wrote 27 bytes to sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "bytesWritten": 27,
    "sandbox": true
  }
}

5.2 sandboxed_edit 工具调用

用户修改沙盒中的文件

大模型返回

json 复制代码
{
  "tool_call": {
    "name": "sandboxed_edit",
    "arguments": { 
      "path": "src/main.js",
      "oldText": "console.log(\"Hello\");",
      "newText": "console.log(\"Hello, World!\");"
    }
  }
}

执行结果

json 复制代码
{
  "content": [{
    "type": "text",
    "text": "Successfully edited sandbox:src/main.js"
  }],
  "details": {
    "path": "src/main.js",
    "sandbox": true
  }
}
相关推荐
子兮曰1 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰1 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万1 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝1 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋1 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
1点东西1 天前
做了近两年的Agent开发,其实真正要学的就是这五件事
llm·agent·ai编程
七牛云行业应用1 天前
Qwen-Image-2.1 开源部署完整指南:Diffusers、ComfyUI 与推理服务
ai编程
感谢地心引力1 天前
我用 Doubao-Seed-2.1-pro 做了一个深度融入 AI 功能的本地知识库软件
ai·开源·seed·markdown·豆包
卡布鲁1 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
阿昌喜欢吃黄桃1 天前
提示词工程:User Prompt 与 System Prompt
ai·prompt·提示词·提示词工程