当前位置: 首页 > news >正文

Executor执行内核揭秘:QuickJS WASM沙箱如何安全运行LLM生成的代码

Executor执行内核揭秘:QuickJS WASM沙箱如何安全运行LLM生成的代码

【免费下载链接】executorThe missing integration layer for AI agents. Let them call any OpenAPI / MCP / GraphQL / custom js functions in secure environment.项目地址: https://gitcode.com/gh_mirrors/executor14/executor

Executor 是一款面向 AI Agent 的开源集成层("The missing integration layer for AI agents"),让 Claude Code、Cursor、ChatGPT 等任意 MCP 客户端都能安全调用 OpenAPI、MCP、GraphQL 与自定义函数。而它最引人瞩目的技术底座,就是执行内核中基于QuickJS WASM 沙箱的安全代码执行引擎——LLM 生成的 TypeScript 代码被隔离编译成 WASM 后运行,即使代码"失控",也触碰不到宿主机的一丝一毫。本文带你逐层拆解这套沙箱的 5 大安全机制与完整执行流水线 🧩

为什么 AI Agent 需要"安全沙箱"?

现代 AI Agent 的工作模式正在从"逐个调用固定工具"演进为"Code Mode":LLM 直接写一段 TypeScript/JavaScript 代码,在里面自由组合调用几十个已注册的工具。灵活是灵活,但危险也来了:

  • ⚠️代码是 LLM 生成的,天然不可信:可能写死循环、内存爆炸、甚至尝试require("fs")读取密钥;
  • ⚠️直连宿主机执行(比如直接eval)等于把服务器交了出去;
  • ⚠️资源无上限时,一段while(true)就能拖垮整个服务。

Executor 的答案是:把模型生成的代码放进一个由 WASM 构建的独立 JavaScript 解释器里运行——这个解释器就是 QuickJS 的 WASM 移植版。沙箱里跑的每一行代码,都是由 WASM 模块内部的 QuickJS 引擎逐条解释的,与宿主机的 V8 引擎完全无关。

Executor 执行内核:三层可插拔架构

Executor 的执行内核位于packages/kernel/packages/core/execution/,分为清晰的三层:

层级包路径职责
契约层packages/kernel/core/定义CodeExecutor契约、工具代理、TS 类型剥离、代码恢复等共享原语
引擎层packages/core/execution/执行引擎(engine.ts):编排工具桥接、暂停/恢复、审批流
运行时层packages/kernel/runtime-*具体沙箱实现:QuickJS WASM、Deno 子进程、workerd 动态 Worker 等

这种"契约先行"的设计意味着沙箱是可以替换的

运行时路径隔离方式适用宿主
QuickJS WASMpackages/kernel/runtime-quickjs/WASM 解释器内隔离所有宿主(含 Cloudflare Workers)
Deno 子进程packages/kernel/runtime-deno-subprocess/独立 OS 进程本地 CLI / 桌面端
动态 Workerpackages/kernel/runtime-dynamic-worker/workerd Worker 线程Cloudflare
workerd 子进程packages/kernel/runtime-workerd-subprocess/独立进程需要 workerd API 的场景

其中 QuickJS WASM 运行时(README)的定位非常直白:

"Runs untrusted TypeScript/JavaScript in a WASM-backed interpreter with configurable timeout, memory limit, and stack size — safe enough to execute LLM-generated code that calls your registered tools."

(在 WASM 支持的解释器中运行不可信的 TS/JS,可配置超时、内存与栈大小——足以安全执行调用你注册工具的 LLM 生成代码。)

QuickJS WASM 沙箱的 5 大安全机制

核心实现全部集中在 packages/kernel/runtime-quickjs/src/index.ts,值得新手重点关注的有 5 个机制:

1. 全新运行时:一次执行,一次销毁

每次执行都会QuickJS.newRuntime()创建一个全新的 WASM 运行时和上下文,执行完毕立即dispose()销毁。沙箱的初始全局环境里只有 QuickJS 自带的标准对象

  • 没有process,没有require,没有宿主对象;
  • fetch被显式替换为直接抛错的函数(fetch is disabled in QuickJS executor)。

2. 三重资源配额:超时 + 内存 + 栈深度

沙箱给 LLM 代码套上了"三重枷锁",任何一项越界都会立即终止执行:

配额默认值作用
timeoutMs墙钟超时5 分钟整体执行时长上限
memoryLimitBytes内存上限64 MBVM 可分配内存上限
maxStackSizeBytes栈深度1 MB防止无限递归

更妙的是超时的实现方式:宿主通过setInterruptHandler注册了一个协作式抢占钩子,每次 JS 执行到检查点都会被询问"该停了吗?"。这意味着哪怕 LLM 写了一个同步死循环,宿主机也能把它精准掐断——而宿主进程对自己的主线程是无法做到这一点的(见 sealed-bundle.ts 中的注释解释)。

3. 类型剥离 + 代码恢复:LLM 写的"脏代码"也能跑

LLM 最爱输出两类"半成品"代码,Executor 在送入沙箱前分别处理:

  • 类型剥离(strip-types.ts):QuickJS 只认纯 JavaScript,而模型输出的常带: number之类的 TS 注解。Executor 用 Sucrase 做纯语法级类型剥离,as T、泛型、interface 统统去掉,成本低且零语义改动;
  • 代码恢复(code-recovery.ts):模型经常把代码塞在 Markdown 的 ``` 围栏里,或写成export default async () => {...}的形式。恢复器会用 Babel 解析 AST,自动剥掉围栏、解包export default,把任意形态的代码"修复"成一段可直接 await 的执行体。

4. 唯一的合法出口:tools惰性代理

沙箱里没有网络、没有文件系统,那 LLM 代码如何调用外部工具?答案是宿主注入的唯一桥梁——tools代理对象

// LLM 生成的代码可以在沙箱内这样写 const pets = await tools.petstore.findPetsByStatus({ status: "available" });

tools是一个基于Proxy的惰性路径代理:tools.petstore.findPetsByStatus这样的点路径不会真的展开成对象,而是在"调用那一刻"把完整路径petstore.findPetsByStatus和参数序列化后,通过宿主函数__executor_invokeTool回传给 Executor 引擎,走完权限策略校验、凭据注入之后才真正发起 API 请求,结果再以 JSON 字符串的形式送回沙箱。

换句话说:沙箱代码永远只能"点名"调用已注册的工具,任何未注册路径的调用都无从抵达真实世界——这是"最小权限"原则在沙箱边界上的完美落地。

5. 暂停与恢复:人机审批流的"暂停键"

有些工具需要人来把关(OAuth 授权、危险操作审批、表单填写)。当 LLM 代码在沙箱里调用这类工具时,执行引擎(engine.ts 的executeWithPause)会:

  1. 将沙箱执行 fork 为后台 Fiber,生成全局唯一的exec_xxx执行 ID;
  2. 挂起并返回PausedExecution,把审批请求呈现给 UI;
  3. 用户批准后,通过resume接口注入响应,沙箱代码从原处继续跑,直至完成或下一次暂停。

CLI 用户同样能体验这一流程:

executor resume --execution-id exec_123

LLM 代码执行的 6 步流水线

把以上机制串起来,一段 LLM 代码从生成到出结果的完整旅程是:

  1. 代码恢复:剥离 Markdown 围栏、解包export default(code-recovery.ts);
  2. 类型剥离:Sucrase 转成纯 JavaScript(strip-types.ts);
  3. 源码包装:注入tools代理、桥接版console(日志回传宿主)、emit()输出通道,并禁用fetch
  4. 创建沙箱:全新 QuickJS 运行时,套用超时/内存/栈三重配额与中断钩子;
  5. 异步调度:宿主循环执行 WASM 内的 microtask 队列,同时监控"截止线"——工具派发期间暂停计时(DeadlineTracker),避免一次慢 API 调用挤占整体预算;
  6. 结果回收:读回result(返回值)、logs(console 输出)、output(emit 产物),随即销毁运行时。

整条链路对宿主机零侵入,执行结果是一个纯粹的{ result, logs, output }数据结构。

不止 QuickJS:一个契约驱动的运行时生态

因为CodeExecutor只是一个两行接口(execute(code, toolInvoker)),Executor 的"执行内核"实际上是一个运行时生态

  • 本地 CLI / 桌面端可以选择 Deno 子进程这种"操作系统级隔离";
  • Cloudflare 部署则因平台禁止运行时代码生成(ban V8 codegen),必须走 QuickJS WASM——而 QuickJS 恰好是"WASM 里再跑一个 JS 引擎",天然绕开该限制;
  • 甚至还有一个刻意更小的原语sealed-bundle(sealed-bundle.ts):不带工具桥、不走计量计费,专门用来在同一个沙箱里跑系统自带的校验渲染,与用户执行路径物理隔离。

自己动手:3 分钟体验 Executor 沙箱

想亲手验证"LLM 代码在沙箱里作恶会怎样"?本地跑起来只需 Node.js 20+:

npm install -g executor # 安装 Executor CLI executor install # 安装常驻后台服务 executor web # 浏览器打开 Web UI,添加集成并连接 Agent

在 Web UI 的执行面板里粘贴一段含死循环的 TypeScript,你会亲眼看到它在 5 分钟墙钟上限(可自定义至 100ms)被 QuickJS 中断钩子精准打断,宿主服务纹丝不动。如果想深入源码,仓库克隆地址为https://gitcode.com/gh_mirrors/executor14/executor,重点阅读路径:

  • 沙箱核心:packages/kernel/runtime-quickjs/src/index.ts
  • 类型剥离与代码恢复:packages/kernel/core/src/
  • 执行引擎与暂停/恢复:packages/core/execution/src/engine.ts

总结:把"信任边界"画在 WASM 里

Executor 执行内核的设计哲学可以浓缩为一句话:不信任一行模型生成的代码,但给它一张只写着一个出口的名片。WASM 隔离保证了"物理上够不着",三重配额保证了"作不了大事",tools代理保证了"只能走正门",暂停/恢复则把"人类最终审批权"完整地留给了你。

对于正在构建 AI Agent 平台的新手开发者而言,这套 QuickJS WASM 沙箱架构几乎是"LLM 代码执行"这一课题的参考级答案 🚀

【免费下载链接】executorThe missing integration layer for AI agents. Let them call any OpenAPI / MCP / GraphQL / custom js functions in secure environment.项目地址: https://gitcode.com/gh_mirrors/executor14/executor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/4190672.html

相关文章:

  • Tomcat Docker 官方镜像 JDK 与 JRE 变体揭秘:同一 Tomcat 为何体积能省一半?
  • 没有调音台也能开唱:KaraokeEternal推荐的音频与麦克风连接方案
  • 数学建模竞赛获奖名单解读:从能力培养到职业发展的核心价值
  • 如何检测GPT系统提示词泄露:TheBigPromptLibrary实用提取方法全清单
  • 时间序列分析实战:从ARIMA建模到数学建模竞赛应用
  • 如何15分钟搭建微信公众号RSS订阅服务:wewe-rss完整部署指南
  • 层次分析法(AHP)详解:从多准则决策到量化权重的完整指南
  • ModelScope 命令行速查:从下载到发布只需9条命令
  • LKY Office Tools一键安装Office指南
  • LabEvolver:免训练经验进化让AI智能体在湿实验室中安全可靠
  • ncmdump:NCM音乐怎么解密?拖一下就转成MP3
  • DataJoint 2.0:从数据管道到智能工作流,构建能动性科研计算基板
  • AI智能体风险意识与可追溯性:构建可信计算机操作智能体的实践框架
  • 3 个蓝牙代理钉住手机在哪个房间:Bermuda 蓝牙定位实战
  • 6GAgentGym:构建面向6G网络自治的AI智能体训练平台
  • NocoBase 文件管理实战:3 步配好外部存储权限控制
  • EPaxos如何实现1轮网络往返提交?PreAccept快速路径源码级全解析
  • 五分钟写出你的第一份轻量级数据同步配置:Transporter 数据同步工具完全指南
  • Web自动化新范式:从脆弱点击到稳健意图的Typed Actions实践
  • Lexe内置@llrt/test测试框架:为10MB单文件可执行程序编写Jest风格单元测试的完整教程
  • 从精准到氛围:自进化多智能体框架如何重塑临床决策支持系统
  • 5步写出第一个原子化样式:otion安装与快速入门完整教程
  • Linux压缩解压实战指南:tar、gzip、zip 完整操作清单
  • 5 种方式设置 CLS 上下文:nestjs-cls 中间件、Guard、拦截器与 @UseCls 装饰器终极对比
  • Node.js 全栈 API 设计与 GraphQL 实:版本升级最怕忽略什么
  • AgentHazard基准:评估计算机操作型AI智能体安全性的关键挑战与实践
  • 数学建模竞赛实战:从校赛到国赛的降维策略与团队协作
  • 选 v2_1000 还是 clean_3000?minimax-h3-spatial-physics-lora 两大版本对比测评
  • quadtree-js快速上手教程:5分钟安装并跑通你的第一个四叉树
  • STM32以太网实战:从MII/RMII接口到LWIP排错全解析