Skip to main content

LangChat 代码执行引擎 (Sandbox) 设计文档

一、模块定位

Sandbox 模块为 LangChat 平台提供安全的代码执行能力,支持在工作流节点中运行用户编写的 JavaScript、Python 和 Shell 代码。模块位于 langchat-corecn.langchat.core.sandbox 包中,作为核心基础能力供上层业务(如工作流引擎)调用。

限制

GraalPy 本质上是一个嵌入在 JVM 中的 Python 解释器,它只包含 Python 标准库(json、math、re、datetime、collections 等)。它没有 pip,也无法自动下载安装第三方包。 更关键的限制是:即使手动放入 numpy,它也跑不起来,因为 numpy 依赖 C/Fortran 编译的原生扩展(BLAS/LAPACK),而 GraalPy 运行在 JVM 之上,无法加载 .so/.dylib 原生库。 总结一下各类 Python 库的支持情况:
分类示例GraalPy 支持原因
标准库json, math, re, datetime, collections支持纯 Python 实现,内置
纯 Python 第三方库pyyaml, jinja2理论可行需手动放入,无 C 扩展依赖
C 扩展第三方库numpy, pandas, scipy, pillow不支持依赖原生 C 编译,JVM 无法加载
网络请求库requests, httpx不支持沙箱限制 + 依赖链复杂
如果业务场景需要 numpy/pandas 这类库,只能通过 RemoteCodeExecutor 调用独立部署的 Sandbox 服务(真实的 Python 环境 + pip 安装好的依赖)。这也是我们预留 Remote Provider 的核心原因

二、架构设计

2.1 Provider 模式

采用 Provider(供应商)模式,将代码执行的调度层执行层解耦:
调用方(WorkflowNode、API等)


CodeExecutorFactory          ← 统一入口,根据语言路由到对应 Provider

    ├── GraalVmCodeExecutor  ← Provider 1: GraalVM 沙箱 (JS / Python)
    ├── LocalCodeExecutor    ← Provider 2: 本地命令行 (Shell)
    └── RemoteCodeExecutor   ← Provider 3: 远程 Sandbox API (预留扩展)

2.2 目录结构

sandbox/
├── CodeLanguage.java           # 语言类型枚举 (JAVASCRIPT / PYTHON / SHELL)
├── CodeExecutionException.java # 结构化异常 (含 ErrorType 枚举)
├── CodeExecutionResult.java    # 执行结果 (成功/失败/耗时)
├── CodeExecutorFactory.java    # 工厂门面,统一调度
├── provider/
│   ├── CodeExecutor.java       # 执行器接口
│   ├── GraalVmCodeExecutor.java    # GraalVM Polyglot 实现
│   ├── LocalCodeExecutor.java      # 本地命令行实现
│   └── RemoteCodeExecutor.java     # 远程 API 实现 (预留)
├── JavaScriptExecutorTest.java # JS 测试用例 (19项:基础运算/网络限制验证/高级特性/错误处理)
├── PythonExecutorTest.java     # Python 测试用例 (20项:基础运算/标准库/网络限制验证/数据聚合/错误处理)
└── ShellExecutorTest.java      # Shell 测试用例 (32项:基础命令/网络请求/磁盘目录/IP获取/系统信息/文本处理)

2.3 核心接口

public interface CodeExecutor {
    // 执行代码,返回结构化结果
    CodeExecutionResult execute(String code, Map<String, Object> params,
                                CodeLanguage language, int timeoutSeconds);
    // 支持的语言集合
    Set<CodeLanguage> supportedLanguages();
    // 执行器名称
    String getName();
}

2.4 使用示例

// 方式一:快捷调用(抛异常)
String result = CodeExecutorFactory.execute(code, params, "javascript");

// 方式二:获取结构化结果(不抛异常)
CodeExecutionResult result = CodeExecutorFactory.executeWithResult(
    code, params, CodeLanguage.PYTHON, 60);
if (result.isSuccess()) {
    System.out.println(result.getOutput());
} else {
    System.out.println(result.getErrorMessage());
}

// 方式三:注册自定义执行器(如远程 Sandbox)
CodeExecutorFactory.register(new RemoteCodeExecutor("http://sandbox:8080"));

三、执行器详解

3.1 GraalVM Polyglot 执行器

属性说明
支持语言JavaScript (GraalJS)、Python (GraalPy)
隔离级别GraalVM 沙箱隔离(无法访问宿主文件系统和网络)
代码规范必须包含 main(params) 函数作为入口
超时控制默认 30 秒,可自定义
代码限制最大 100,000 字符
执行流程:
用户代码 → 格式校验(main函数) → 参数JSON序列化 → 拼接调用代码 → GraalVM执行 → 返回结果
JavaScript 代码模板:
// 用户代码
function main(params) {
    return { result: params.x + params.y };
}

// === LangChat Sandbox ===(自动拼接)
var __lc_params = {"x":1,"y":2};
var __lc_result = main(__lc_params);
JSON.stringify(__lc_result);
Python 代码模板:
# 用户代码
def main(params):
    return {"result": params["x"] + params["y"]}

# === LangChat Sandbox ===(自动拼接)
import json
__lc_params = json.loads('{"x":1,"y":2}')
__lc_result = main(__lc_params)
print(json.dumps(__lc_result))

3.2 本地命令行执行器

属性说明
支持语言Shell (bash/sh)
隔离级别无隔离,直接在宿主操作系统上执行
代码规范标准 Shell 命令/脚本,无 main 函数要求
代码限制最大 50,000 字符
安全警告: 此执行器没有沙箱隔离,调用方必须做好权限控制。生产环境建议使用 Docker 容器或远程 Sandbox 代替。

3.3 远程 Sandbox 执行器(预留)

属性说明
支持语言由远程服务决定(理论上支持所有语言)
隔离级别Docker 容器级隔离
扩展能力支持安装第三方库(numpy、pandas 等)
部署方式独立 Sandbox 服务,通过 HTTP API 调用

四、核心依赖

4.1 GraalVM Polyglot 引擎

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-code-execution-engine-graalvm-polyglot</artifactId>
</dependency>
基于 GraalVM Polyglot API,通过 LangChain4j 封装。内部使用 org.graalvm.polyglot.Context 创建沙箱化的执行环境。 核心类:
  • GraalVmJavaScriptExecutionEngine — 创建 GraalJS 上下文执行 JavaScript
  • GraalVmPythonExecutionEngine — 创建 GraalPy 上下文执行 Python

4.2 本地命令行引擎

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-community-code-execution-engine-local</artifactId>
</dependency>
核心类:
  • CommandLineExecutionEngine — 通过 ProcessBuilder 在本地执行 Shell 命令

五、Python 类库支持与限制

5.1 GraalPy 支持的标准库

模块说明示例
jsonJSON 序列化/反序列化json.loads(), json.dumps()
math数学函数math.sqrt(), math.pi, math.factorial()
re正则表达式re.findall(), re.sub()
datetime日期时间处理datetime.now(), timedelta()
collections集合工具Counter, defaultdict, OrderedDict
itertools迭代器工具chain(), combinations()
functools函数工具reduce(), partial()
string字符串常量string.ascii_letters, Template
random随机数random.randint(), random.choice()
hashlib哈希算法hashlib.md5(), hashlib.sha256()

5.2 不支持的第三方库

GraalPy 运行在 JVM 之上,不支持需要 C 扩展编译的第三方库
原因
numpy依赖 C/Fortran 扩展 (BLAS/LAPACK)
pandas依赖 numpy + C 扩展
requests依赖 urllib3 + C SSL 绑定
scipy依赖 numpy + C/Fortran 扩展
matplotlib依赖 C 扩展渲染引擎
pillow (PIL)依赖 C 图像处理库
解决方案: 如果工作流需要使用这些第三方库,应通过 RemoteCodeExecutor 调用独立部署的 Sandbox 服务,在真实的 Python 环境中执行。

六、错误处理

6.1 结构化异常

CodeExecutionException 包含 ErrorType 枚举,分类所有可能的错误:
ErrorType触发场景
EMPTY_CODE代码为空
INVALID_FORMAT代码缺少 main 函数 / 超出长度限制
TIMEOUT执行超时(死循环、大量计算)
RUNTIME_ERROR语法错误、类型错误、引用错误、除零错误等
ENGINE_INIT_FAILEDGraalVM 引擎初始化失败
UNKNOWN未分类的错误

6.2 错误信息提取

GraalVM 的原始异常信息通常很长且包含堆栈跟踪。GraalVmCodeExecutor 内部会解析常见错误类型(SyntaxError、TypeError、NameError、ImportError 等),提取用户友好的错误摘要。

七、已知限制与弊端

7.1 GraalVM 引擎限制

限制影响规避方式
不支持 C 扩展 Python 库无法使用 numpy/pandas 等数据科学库使用远程 Sandbox
首次执行冷启动慢第一次创建 Context 需要 1-3 秒引擎预热 / 复用 Context
内存占用较大每个 GraalVM Context 占用 50-100MB控制并发数
Python 兼容性非 100%部分 Python 3 语法/特性可能不支持提前测试验证

7.2 本地命令行限制

限制影响规避方式
无沙箱隔离可执行任意系统命令(rm -rf 等)调用方做命令白名单过滤
平台依赖Shell 语法在 Windows/Linux/macOS 上可能不同明确目标运行平台
无超时控制CommandLineExecutionEngine 本身不支持超时上层做超时控制

7.3 架构层面

限制说明
每次创建新引擎实例当前每次执行都 new 一个 Engine,无法复用,影响性能
无资源计量无法限制 CPU/内存使用量,恶意代码可能耗尽资源
无多租户隔离所有用户共享同一个执行环境

八、网络请求能力说明

8.1 各执行器的网络能力对比

执行器网络请求能力原因替代方案
GraalVM JS不支持沙箱中无 fetch/XMLHttpRequest/WebSocket API使用 Shell curl
GraalVM Python不支持requests/urllib/socket 等模块不可用(C 扩展 + 沙箱限制)使用 Shell curl
Local Shell完全支持直接在宿主机执行 curl/wget 等命令
Remote Sandbox取决于远端远端容器可安装任意依赖

8.2 Shell 网络请求示例

# GET 请求
curl -s --connect-timeout 5 --max-time 10 'https://httpbin.org/get?name=LangChat'

# POST JSON
curl -s -X POST -H 'Content-Type: application/json' \
    -d '{"key":"value"}' 'https://httpbin.org/post'

# 获取 HTTP 状态码
curl -s -o /dev/null -w '%{http_code}' 'https://httpbin.org/status/200'

# 带认证的请求
curl -s -H 'Authorization: Bearer token' 'https://api.example.com/data'

8.3 JS/Python 中处理网络数据的推荐模式

由于 JS/Python 沙箱无法直接发起网络请求,推荐采用两阶段模式
  1. 阶段一:由外部(Shell curl 或 Java HttpClient)完成 HTTP 调用,获取响应 JSON
  2. 阶段二:将响应 JSON 作为参数传入 JS/Python 沙箱,由沙箱负责数据解析和转换
Shell curl → JSON 响应 → 作为 params 传入 → JS/Python 沙箱解析处理

九、Shell 系统信息获取能力

Shell 执行器可以获取宿主机的各类系统信息,常用命令如下:

9.1 磁盘与目录

功能命令说明
磁盘使用概览df -h显示所有挂载点的磁盘使用情况
根目录列表ls -la /列出根目录下所有文件和目录
用户主目录ls -la $HOME列出当前用户主目录内容
当前工作目录pwd输出当前工作路径
目录大小du -sh /* | sort -rh | head -10按大小排序显示各目录
查找大文件find . -type f -size +1M | head -10查找超过 1MB 的文件

9.2 网络与 IP

功能命令兼容性
本机 IP (macOS)ifconfig | grep 'inet ' | grep -v '127.0.0.1' | awk '{print $2}'macOS
本机 IP (Linux)ip -4 addr show | grep -oP 'inet \K[\d.]+' | grep -v '127.0.0.1'Linux
公网 IPcurl -s https://httpbin.org/ip需要网络
主机名hostname通用
网络接口列表ifconfig -l (macOS) 或 ip link show (Linux)平台相关
端口监听lsof -iTCP -sTCP:LISTEN -P -n (macOS) 或 ss -tlnp (Linux)平台相关
DNS 查询nslookup baidu.comdig +short baidu.com需安装工具

9.3 主机与运行环境

功能命令
系统信息uname -a
当前用户whoami
用户详情id
环境变量env
CPU 信息 (macOS)sysctl -n machdep.cpu.brand_string
CPU 核数 (macOS)sysctl -n hw.ncpu
内存总量 (macOS)sysctl -n hw.memsize
CPU 信息 (Linux)cat /proc/cpuinfo
内存信息 (Linux)free -hcat /proc/meminfo
系统运行时间uptime
进程列表ps aux | sort -k3 -rn | head -10

十、扩展路线

  1. 引擎复用池:复用 GraalVM Context,减少冷启动时间
  2. 远程 Sandbox 实现:对接 Docker 容器化的代码执行服务,支持完整的 Python 生态
  3. 资源限制:通过 GraalVM ResourceLimits API 限制 CPU 和内存
  4. 执行审计:记录每次代码执行的输入、输出、耗时,便于安全审计
  5. 命令白名单:为 Shell 执行器增加命令过滤机制