乐于分享
好东西不私藏

OpenClaw 5 个核心工具执行代码详解

OpenClaw 5 个核心工具执行代码详解

核心执行流程概览

大模型返回工具调用       ↓OpenClaw 接收 tool_call       ↓查找工具定义(name → execute 函数)       ↓执行 execute(toolCallId, params, signal, onUpdate)       ↓返回结果(content + details)       ↓发送给大模型

一、exec 工具执行代码

1.1 工具创建函数

位置:第 16558 行

functioncreateExecTool(defaults) {// 1. 解析默认配置const defaultBackgroundMs = clampWithDefault(defaults?.backgroundMs ?? readEnvInt("PI_BASH_YIELD_MS"), 1e41012e4);const allowBackground = defaults?.allowBackground ?? true;const defaultTimeoutSec = typeof defaults?.timeoutSec === "number" && defaults.timeoutSec > 0 ? defaults.timeoutSec : 1800;// 2. 解析安全策略(白名单、黑名单等)const { safeBins, safeBinProfiles, trustedSafeBinDirs } = resolveExecSafeBinRuntimePolicy({...});// 3. 返回工具定义return {name"exec",label"exec",description"Execute shell commands with background continuation...",parameters: execSchema,executeasync (_toolCallId, args, signal, onUpdate) => {// === 执行逻辑开始 ===const params = args;// 1. 参数验证if (!params.commandthrownewError("Provide a command to start.");// 2. 解析后台运行请求const backgroundRequested = params.background === true;const yieldRequested = typeof params.yieldMs === "number";const yieldWindow = allowBackground ?                 (backgroundRequested ? 0 : clampWithDefault(params.yieldMs ?? defaultBackgroundMs, defaultBackgroundMs, 1012e4)) : null;// 3. 解析提权请求const elevatedRequested = elevatedMode !== "off";if (elevatedRequested) {if (!elevatedDefaults?.enabled || !elevatedDefaults.allowed) {thrownewError("elevated is not available right now...");                }            }// 4. 确定执行主机(sandbox/gateway/node)let host = requestedHost ?? configuredHost;if (elevatedRequested) host = "gateway";// 5. 确定安全模式let security = minSecurity(configuredSecurity, normalizeExecSecurity$1(params.security) ?? configuredSecurity);if (elevatedRequested && elevatedMode === "full") security = "full";// 6. 确定审批模式let ask = maxAsk(configuredAsk, normalizeExecAsk$1(params.ask) ?? configuredAsk);const bypassApprovals = elevatedRequested && elevatedMode === "full";if (bypassApprovals) ask = "off";// 7. 解析工作目录const rawWorkdir = params.workdir?.trim() || defaults?.cwd || process.cwd();let workdir = rawWorkdir;if (sandbox) {const resolved = awaitresolveSandboxWorkdir({ workdir: rawWorkdir, sandbox, warnings });                workdir = resolved.hostWorkdir;            }// 8. 解析环境变量(安全检查)const env = sandbox && host === "sandbox" ? buildSandboxEnv({...}) :                 hostEnvResult?.env ?? inheritedBaseEnv;// 9. 处理不同主机的执行逻辑if (host === "node") {returnexecuteNodeHostCommand({...});            }if (host === "gateway" && !bypassApprovals) {// 10. Gateway 白名单检查(可能需要用户审批)const gatewayResult = awaitprocessGatewayAllowlist({...});if (gatewayResult.pendingResult) {// 需要审批,返回待处理状态return gatewayResult.pendingResult;                }                execCommandOverride = gatewayResult.execCommandOverride;            }// 11. 执行命令const run = awaitrunExecProcess({command: params.command,execCommand: execCommandOverride,                workdir,                env,                sandbox,                containerWorkdir,usePty: params.pty === true && !sandbox,                warnings,maxOutputDEFAULT_MAX_OUTPUT,pendingMaxOutputDEFAULT_PENDING_MAX_OUTPUT,                notifyOnExit,                notifyOnExitEmptySuccess,timeoutSec: effectiveTimeout,                onUpdate            });// 12. 处理后台运行let yielded = false;let yieldTimer = null;constonYieldNow = () => {if (yieldTimer) clearTimeout(yieldTimer);if (yielded) return;                yielded = true;markBackgrounded(run.session);resolveRunning();            };if (allowBackground && yieldWindow !== null) {if (yieldWindow === 0onYieldNow();else yieldTimer = setTimeout(onYieldNow, yieldWindow);            }// 13. 等待执行完成returnnewPromise((resolve, reject) => {                run.promise.then((outcome) => {if (yieldTimer) clearTimeout(yieldTimer);if (yielded || run.session.backgroundedreturn;resolve(buildExecForegroundResult({ outcome, cwd: run.session.cwdwarningTextgetWarningText() }));                }).catch((err) => {if (yieldTimer) clearTimeout(yieldTimer);if (yielded || run.session.backgroundedreturn;reject(err);                });            });        }    };}

1.2 执行流程图

exec 工具调用    ↓1. 参数验证(command 必填)    ↓2. 解析后台运行请求(background/yieldMs)    ↓3. 解析提权请求(elevated)    ↓4. 确定主机(sandbox/gateway/node)    ↓5. Gateway 白名单检查    ├─ 需要审批 → 返回 pending 状态,等待用户 /approve    └─ 无需审批 → 继续    ↓6. 启动进程(runExecProcess)    ├─ 创建子进程    ├─ 监听输出    ├─ 记录日志    └─ 返回 session    ↓7. 判断是否后台运行    ├─ 是 → 立即返回"正在运行",用户可用 process 工具管理    └─ 否 → 等待完成,返回完整输出

1.3 返回结果格式

前台运行完成

{"content":[{"type":"text","text":"命令输出内容..."}],"details":{"status":"completed","exitCode":0,"cwd":"/path/to/workdir"}}

后台运行中

{"content":[{"type":"text","text":"Command still running (session abc123, pid 12345). Use process (list/poll/log/write/kill/clear/remove) for follow-up."}],"details":{"status":"running","sessionId":"abc123","pid":12345,"startedAt":1711716000000,"cwd":"/path/to/workdir","tail":"部分输出..."}}

需要审批

{"content":[{"type":"text","text":"Approval required (id abc123).\nHost: gateway\nCWD: /workspace\nCommand:\n```sh\nrm -rf /tmp/test\n```\nReply with: /approve abc123 allow-once|allow-always|deny"}],"details":{"status":"pending_approval","approvalId":"abc123"}}

二、process 工具执行代码

2.1 工具创建函数

位置:第 17108 行

functioncreateProcessTool(defaults) {if (defaults?.cleanupMs !== void0setJobTtlMs(defaults.cleanupMs);const scopeKey = defaults?.scopeKey;const supervisor = getProcessSupervisor();constisInScope = (session) => !scopeKey || session?.scopeKey === scopeKey;return {name"process",label"process",description"Manage running exec sessions: list, poll, log, write, send-keys, submit, paste, kill.",parameters: processSchema,executeasync (_toolCallId, args, _signal, _onUpdate) => {const params = args;// === action: list ===if (params.action === "list") {const running = listRunningSessions()                    .filter((s) =>isInScope(s))                    .map((s) => ({sessionId: s.id,status"running",pid: s.pid ?? void0,startedAt: s.startedAt,runtimeMsDate.now() - s.startedAt,cwd: s.cwd,command: s.command,namederiveSessionName(s.command),tail: s.tail,truncated: s.truncated                    }));const finished = listFinishedSessions()                    .filter((s) =>isInScope(s))                    .map((s) => ({sessionId: s.id,status: s.status,startedAt: s.startedAt,endedAt: s.endedAt,runtimeMs: s.endedAt - s.startedAt,cwd: s.cwd,command: s.command,namederiveSessionName(s.command),tail: s.tail,truncated: s.truncated,exitCode: s.exitCode ?? void0,exitSignal: s.exitSignal ?? void0                    }));return {content: [{type"text",text: [...running, ...finished]                            .toSorted((a, b) => b.startedAt - a.startedAt)                            .map((s) => {const label = s.name ? truncateMiddle(s.name80) : truncateMiddle(s.command120);return`${s.sessionId}${pad(s.status, 9)}${formatDurationCompact$1(s.runtimeMs) ?? "n/a"} :: ${label}`;                            })                            .join("\n") || "No running or recent sessions."                    }],details: {status"completed",sessions: [...running, ...finished]                    }                };            }// === 其他 action 需要 sessionId ===if (!params.sessionIdreturn {content: [{ type"text"text"sessionId is required for this action." }],details: { status"failed" }            };const session = getSession(params.sessionId);const finished = getFinishedSession(params.sessionId);const scopedSession = isInScope(session) ? session : void0;const scopedFinished = isInScope(finished) ? finished : void0;// === action: poll ===if (params.action === "poll") {if (!scopedSession) {if (scopedFinished) {// 已完成,返回最终结果return {content: [{type"text",text: (scopedFinished.tail || "(no output recorded)") + `\n\nProcess exited with ${scopedFinished.exitSignal ? `signal ${scopedFinished.exitSignal}` : `code ${scopedFinished.exitCode ?? 0}`}.`                            }],details: {status: scopedFinished.status === "completed" ? "completed" : "failed",sessionId: params.sessionId,exitCode: scopedFinished.exitCode ?? void0                            }                        };                    }return { content: [{ type"text"text`No session found for ${params.sessionId}` }], details: { status"failed" } };                }if (!scopedSession.backgroundedreturn { content: [{ type"text"text`Session ${params.sessionId} is not backgrounded.` }], details: { status"failed" } };// 等待指定时间(timeout)const pollWaitMs = resolvePollWaitMs(params.timeout);if (pollWaitMs > 0 && !scopedSession.exited) {const deadline = Date.now() + pollWaitMs;while (!scopedSession.exited && Date.now() < deadline) {awaitnewPromise((resolve) =>setTimeout(resolve, Math.max(0Math.min(250, deadline - Date.now()))));                    }                }// 获取输出const { stdout, stderr } = drainSession(scopedSession);const exited = scopedSession.exited;if (exited) {markExited(scopedSession, scopedSession.exitCode ?? null, scopedSession.exitSignal ?? null, status);                }const output = [stdout.trimEnd(), stderr.trimEnd()].filter(Boolean).join("\n").trim();return {content: [{type"text",text: (output || "(no new output)") + (exited ? `\n\nProcess exited with code ${scopedSession.exitCode}.` : "\n\nProcess still running.")                    }],details: {status: exited ? "completed" : "running",sessionId: params.sessionId,exitCode: exited ? scopedSession.exitCode : void0                    }                };            }// === action: log ===if (params.action === "log") {if (!scopedSession || !scopedSession.backgrounded) {return { content: [{ type"text"text`Session ${params.sessionId} is not backgrounded.` }], details: { status"failed" } };                }constwindow = resolveLogSliceWindow(params.offset, params.limit);const { slice, totalLines, totalChars } = sliceLogLines(scopedSession.aggregatedwindow.effectiveOffsetwindow.effectiveLimit);return {content: [{ type"text"text: (slice || "(no output yet)") + defaultTailNote(totalLines, window.usingDefaultTail) }],details: {status: scopedSession.exited ? "completed" : "running",sessionId: params.sessionId,total: totalLines,                        totalLines,                        totalChars                    }                };            }// === action: write ===if (params.action === "write") {const { ok, stdin, session } = resolveBackgroundedWritableStdin();if (!ok) return stdin.result// 错误结果awaitwriteToStdin(stdin, params.data);return {content: [{ type"text"text`Wrote ${params.data.length} bytes to stdin.` }],details: {status: session.exited ? "completed" : "running",sessionId: params.sessionId                    }                };            }// === action: send-keys ===if (params.action === "send-keys") {const { ok, stdin, session } = resolveBackgroundedWritableStdin();if (!ok) return stdin.result;// 解析按键const keys = params.keys || [];const hex = params.hex || [];const literal = params.literal;if (literal) {awaitwriteToStdin(stdin, literal);                } elseif (hex.length > 0) {const buffer = Buffer.from(hex.join(""), "hex");awaitnewPromise((resolve, reject) => {                        stdin.write(buffer, (err) => err ? reject(err) : resolve());                    });                } else {// 发送命名按键(如 "enter", "up", "down" 等)for (const key of keys) {const keyData = namedKeyMap.get(key.toLowerCase()) || key;awaitwriteToStdin(stdin, keyData);                    }                }return {content: [{ type"text"text`Sent keys to session.` }],details: { status"running"sessionId: params.sessionId }                };            }// === action: paste ===if (params.action === "paste") {const { ok, stdin, session } = resolveBackgroundedWritableStdin();if (!ok) return stdin.result;// 发送括号粘贴模式(避免输入被解释为命令)if (params.bracketed) {awaitwriteToStdin(stdin, BRACKETED_PASTE_START);                }awaitwriteToStdin(stdin, params.text);if (params.bracketed) {awaitwriteToStdin(stdin, BRACKETED_PASTE_END);                }return {content: [{ type"text"text`Pasted ${params.text.length} characters.` }],details: { status"running"sessionId: params.sessionId }                };            }// === action: submit ===if (params.action === "submit") {const { ok, stdin, session } = resolveBackgroundedWritableStdin();if (!ok) return stdin.result;// 发送 EOF(关闭 stdin)                stdin.end();return {content: [{ type"text"text"Submitted EOF to stdin." }],details: { status"running"sessionId: params.sessionId }                };            }// === action: kill ===if (params.action === "kill") {if (!scopedSession) {return { content: [{ type"text"text`No active session found for ${params.sessionId}` }], details: { status"failed" } };                }// 尝试通过 supervisor 取消const cancelled = cancelManagedSession(params.sessionId);if (!cancelled) {// 回退到直接 kill 进程树terminateSessionFallback(scopedSession);                }return {content: [{ type"text"text`Killed session ${params.sessionId}.` }],details: { status"killed"sessionId: params.sessionId }                };            }return { content: [{ type"text"text`Unknown action: ${params.action}` }], details: { status"failed" } };        }    };}

2.2 支持的 Actions

Action
说明
参数
list
列出所有运行中和最近的会话
poll
轮询后台会话状态
sessionId, timeout
log
读取会话日志
sessionId, offset, limit
write
写入数据到 stdin
sessionId, data
send-keys
发送按键
sessionId, keys/hex/literal
paste
粘贴文本
sessionId, text, bracketed
submit
发送 EOF
sessionId
kill
终止会话
sessionId

三、read 工具执行代码

3.1 工具包装链

createReadTool (外部库)    ↓createOpenClawReadTool (OpenClaw 包装)    ↓wrapToolWorkspaceRootGuard (工作目录限制)    ↓最终工具

3.2 OpenClaw 包装代码

位置:第 115027 行

functioncreateOpenClawReadTool(base, options) {return {        ...patchToolSchemaForClaudeCompatibility(base),executeasync (toolCallId, params, signal) => {// 1. 参数标准化(支持 path/file_path/filePath/file 别名)const normalized = normalizeToolParams(params);const record = normalized ?? (params && typeof params === "object" ? params : void0);// 2. 验证必填参数assertRequiredParams(record, CLAUDE_PARAM_GROUPS.read, base.name);// 3. 执行读取(支持自适应分页)const result = awaitexecuteReadWithAdaptivePaging({                base,                toolCallId,args: normalized ?? params ?? {},                signal,maxBytesresolveAdaptiveReadMaxBytes(options)            });// 4. 处理文件路径const filePath = typeof record?.path === "string" ? String(record.path) : "<unknown>";// 5. 处理图片结果(如果是图片文件)returnsanitizeToolResultImages(awaitnormalizeReadImageResult(stripReadTruncationContentDetails(result),                     filePath                ), `read:${filePath}`                options?.imageSanitization            );        }    };}

3.3 自适应分页读取

位置:第 114782 行

asyncfunctionexecuteReadWithAdaptivePaging(params) {const userLimit = params.args.limit;// 1. 用户指定了 limit,直接返回if (typeof userLimit === "number" && Number.isFinite(userLimit) && userLimit > 0) {returnawait params.base.execute(params.toolCallId, params.args, params.signal);    }// 2. 自适应分页读取const offsetRaw = params.args.offset;let nextOffset = typeof offsetRaw === "number" && Number.isFinite(offsetRaw) && offsetRaw > 0 ? Math.floor(offsetRaw) : 1;let firstResult = null;let aggregatedText = "";let aggregatedBytes = 0;let capped = false;let continuationOffset;for (let page = 0; page < MAX_ADAPTIVE_READ_PAGES; page += 1) {// 读取下一页const pageArgs = { ...params.argsoffset: nextOffset };const pageResult = await params.base.execute(params.toolCallId, pageArgs, params.signal);        firstResult ??= pageResult;const rawText = getToolResultText$1(pageResult);if (typeof rawText !== "string"return pageResult;// 检查是否被截断const truncation = extractReadTruncationDetails(pageResult);const canContinue = Boolean(truncation?.truncated) &&                            !truncation?.firstLineExceedsLimit &&                            (truncation?.outputLines ?? 0) > 0 &&                            page < MAX_ADAPTIVE_READ_PAGES - 1;const pageText = canContinue ? stripReadContinuationNotice(rawText) : rawText;// 聚合文本const delimiter = aggregatedText ? "\n\n" : "";const nextBytes = Buffer.byteLength(`${delimiter}${pageText}`"utf-8");// 检查是否超过最大字节数if (aggregatedText && aggregatedBytes + nextBytes > params.maxBytes) {            capped = true;            continuationOffset = nextOffset;break;        }        aggregatedText += `${delimiter}${pageText}`;        aggregatedBytes += nextBytes;if (!canContinue || !truncation) returnwithToolResultText(pageResult, aggregatedText);        nextOffset += truncation.outputLines;        continuationOffset = nextOffset;if (aggregatedBytes >= params.maxBytes) {            capped = true;break;        }    }// 3. 返回聚合结果if (!firstResult) returnawait params.base.execute(params.toolCallId, params.args, params.signal);let finalText = aggregatedText;if (capped && continuationOffset) {        finalText += `\n\n[Read output capped at ${formatBytes(params.maxBytes)} for this call. Use offset=${continuationOffset} to continue.]`;    }returnwithToolResultText(firstResult, finalText);}

3.4 执行流程

read 工具调用    ↓1. 参数标准化(path/file_path/filePath/file → path)    ↓2. 验证必填参数(path)    ↓3. 判断是否指定 limit    ├─ 是 → 直接读取指定行数    └─ 否 → 自适应分页读取    ↓4. 自适应分页    ├─ 读取第 1 页(默认 2000 行或 50KB)    ├─ 检查是否截断    ├─ 是 → 继续读第 2 页    ├─ 聚合文本    ├─ 重复直到不截断或达到最大页数    └─ 返回聚合结果    ↓5. 处理图片文件(如果是图片)    ├─ 检测 MIME 类型    ├─ 转换为 Base64    └─ 作为附件发送    ↓6. 返回结果

四、write 工具执行代码

4.1 工具包装链

createWriteTool (外部库)    ↓wrapToolParamNormalization (参数标准化)    ↓wrapToolWorkspaceRootGuard (工作目录限制)    ↓最终工具

4.2 Host 写入操作

位置:第 115057 行

asyncfunctionwriteHostFile(absolutePath, content) {const resolved = path.resolve(absolutePath);await fs$1.mkdir(path.dirname(resolved), { recursivetrue });await fs$1.writeFile(resolved, content, "utf-8");}functioncreateHostWriteOperations(root, options) {if (!(options?.workspaceOnly ?? false)) {// 无限制模式return {mkdirasync (dir) => {const resolved = path.resolve(dir);await fs$1.mkdir(resolved, { recursivetrue });            },writeFile: writeHostFile        };    }// workspaceOnly 模式(限制只能访问工作区)return {mkdirasync (dir) => {const relative = toRelativeWorkspacePath(root, dir, { allowRoottrue });const resolved = relative ? path.resolve(root, relative) : path.resolve(root);awaitassertSandboxPath({ filePath: resolved, cwd: root, root });await fs$1.mkdir(resolved, { recursivetrue });        },writeFileasync (absolutePath, content) => {awaitwriteFileWithinRoot({rootDir: root,relativePathtoRelativeWorkspacePath(root, absolutePath),data: content,mkdirtrue            });        }    };}

4.3 执行流程

write 工具调用    ↓1. 参数标准化(path/file_path/filePath/file → path)    ↓2. 验证必填参数(path, content)    ↓3. 解析路径(绝对/相对)    ↓4. 检查 workspaceOnly 限制    ├─ 是 → 验证路径在工作区内    └─ 否 → 允许任意路径    ↓5. 创建父目录(mkdir -p)    ↓6. 写入文件(覆盖模式)    ↓7. 返回成功结果

五、edit 工具执行代码

5.1 工具包装链

createEditTool (外部库)    ↓wrapEditToolWithRecovery (编辑恢复机制)    ↓wrapToolParamNormalization (参数标准化)    ↓wrapToolWorkspaceRootGuard (工作目录限制)    ↓最终工具

5.2 编辑恢复机制

位置:第 114462 行

functionwrapEditToolWithRecovery(base, options) {return {        ...base,executeasync (toolCallId, params, signal, onUpdate) => {// 1. 读取参数const { pathParam, oldText, newText } = readEditToolParams(params);// 2. 解析绝对路径const absolutePath = typeof pathParam === "string" ? resolveEditPath(options.root, pathParam) : void0;// 3. 读取原始内容(用于恢复判断)let originalContent;if (absolutePath && newText !== void0) {try {                    originalContent = await options.readFile(absolutePath);                } catch {}            }// 4. 执行编辑try {returnawait 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 !== void0) {if (didEditLikelyApply({                        originalContent,                        currentContent,                        oldText,                        newText                    })) {// 编辑已应用,返回成功returnbuildEditSuccessResult(pathParam ?? absolutePath);                    }                }// 7. 添加不匹配提示if (typeof currentContent === "string" && err instanceofError && shouldAddMismatchHint(err)) {throwappendMismatchHint(err, currentContent);                }throw err;            }        }    };}

5.3 参数标准化

位置:第 114541 行

constCLAUDE_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",allowEmptytrue        }    ]};constCLAUDE_PARAM_ALIASES = [    { original"path"alias"file_path" },    { original"path"alias"filePath" },    { original"path"alias"file" },    { original"oldText"alias"old_string" },    { original"oldText"alias"old_text" },    { original"oldText"alias"oldString" },    { original"newText"alias"new_string" },    { original"newText"alias"new_text" },    { original"newText"alias"newString" }];

5.4 执行流程

edit 工具调用    ↓1. 参数标准化(9 个别名 → 3 个标准参数)    ├─ path/file_path/filePath/file → path    ├─ oldText/old_string/old_text/oldString → oldText    └─ newText/new_string/new_text/newString → newText    ↓2. 验证必填参数(path, oldText)    ↓3. 读取原始内容(用于恢复判断)    ↓4. 执行编辑(精确替换)    ├─ 查找 oldText(必须完全匹配)    ├─ 替换为 newText    └─ 保存文件    ↓5. 编辑失败处理    ├─ 读取当前内容    ├─ 判断编辑是否已部分应用    ├─ 是 → 返回成功    └─ 否 → 添加不匹配提示(显示当前内容)    ↓6. 返回结果

六、工具调用完整流程

6.1 从大模型到执行

1. 大模型返回 tool_call   {     "id": "call_abc123",     "type": "function",     "function": {       "name": "exec",       "arguments": "{\"command\": \"ls -la\"}"     }   }   ↓2. OpenClaw 解析 tool_call   - 查找工具定义(tools.find(t => t.name === "exec"))   - 解析参数(JSON.parse)   ↓3. 执行工具   tool.execute(toolCallId, params, signal, onUpdate)   ↓4. 捕获结果   - content: [{ type: "text", text: "..." }]   - details: { status: "completed", ... }   ↓5. 发送给大模型   {     "role": "tool",     "tool_call_id": "call_abc123",     "content": "..."   }   ↓6. 大模型生成回复   ↓7. 发送给用户

6.2 工具调用示例

用户列出当前目录的文件

大模型思考:需要执行 ls -la 命令

大模型返回

{"role":"assistant","content":null,"tool_calls":[{"id":"call_abc123","type":"function","function":{"name":"exec","arguments":"{\"command\": \"ls -la\"}"}}]}

OpenClaw 执行

const tool = tools.find(t => t.name === "exec");const params = JSON.parse("{\"command\": \"ls -la\"}");const result = await tool.execute("call_abc123", params, signal, onUpdate);

返回结果

{"role":"tool","tool_call_id":"call_abc123","content":"total 48\ndrwxr-xr-x  5 user user 4096 Mar 29 21:00 .\ndrwxr-xr-x 10 user user 4096 Mar 29 20:00 ..\n-rw-r--r--  1 user user  234 Mar 29 20:00 README.md\n..."}

大模型生成回复

当前目录有以下文件:- README.md (234 字节)- src/ (目录)- docs/ (目录)...

七、关键机制总结

7.1 参数标准化

所有工具都支持参数别名,让大模型更灵活:

// read 工具path = params.path ?? params.file_path ?? params.filePath ?? params.file// edit 工具oldText = params.oldText ?? params.old_string ?? params.old_text ?? params.oldStringnewText = params.newText ?? params.new_string ?? params.new_text ?? params.newString

7.2 安全限制

  • • 工作目录限制workspaceOnly=true 时只能访问工作区
  • • 沙盒隔离:沙盒模式通过 bridge 访问文件系统
  • • 命令白名单:exec 命令需要匹配白名单或通过审批

7.3 错误恢复

  • • edit 工具:编辑失败时判断是否部分成功
  • • read 工具:自适应分页,避免一次性读取大文件
  • • exec 工具:后台运行 + process 工具管理

7.4 结果格式

所有工具返回统一格式:

{content: [{ type"text"text"..." }],  // 文本内容details: { status"completed/running/failed", ... }  // 元数据}