为Open WebUI构建安全代码执行沙箱:基于gVisor的本地LLM增强方案
1. 项目概述:为Open WebUI构建安全的代码执行沙箱
如果你正在本地部署大语言模型,比如用Ollama跑Llama 3或者Qwen,并且通过Open WebUI这个漂亮的Web界面来交互,那你可能遇到过这样的场景:你问模型“帮我写个Python脚本来整理桌面文件”,模型很听话地给出了代码,但接下来呢?你只能手动复制这段代码,打开终端,新建一个文件,粘贴,运行。这个过程不仅打断了流畅的对话体验,更关键的是,如果模型生成的代码存在风险(哪怕是无意的),直接在你的宿主机上运行无异于“裸奔”。
这正是EtiennePerot/safe-code-execution这个项目要解决的核心痛点。它不是一个独立的软件,而是一个为Open WebUI量身定制的插件(或称为“功能”与“工具”),其核心价值在于在WebUI对话界面内,安全、一键式地执行模型生成的代码。它的安全基石是Google开源的容器沙箱技术——gVisor。没错,就是那个为Google Cloud和ChatGPT等产品提供安全隔离的底层运行时。这个选择本身就很有说服力:我们不是在用玩具级别的隔离,而是在复用业界顶尖生产环境验证过的方案。
简单来说,这个项目让你能在Open WebUI里,像点击“发送”消息一样,点击“运行代码”。代码会在一个由gVisor构建的、与宿主机高度隔离的沙箱环境中执行,结果直接返回到对话中。这彻底改变了我们与本地LLM的协作模式:从“聊天-复制-手动执行”的割裂流程,变成了“提问-生成-验证-迭代”的闭环交互。无论是验证一段数据处理逻辑、测试一个算法,还是让模型自己写代码查询实时信息(比如日期、网络请求),都变得无比顺畅和安全。
2. 核心概念解析:Function与Tool的双重角色
刚接触这个项目,你可能会对“Function”(函数)和“Tool”(工具)这两个概念感到困惑。它们都提供代码执行能力,但设计哲学和使用场景有本质区别。理解这一点,是高效利用该项目的关键。
2.1 代码执行函数:用户主导的显式执行
你可以把“代码执行函数”想象成一个由你完全控制的“执行按钮”。它的工作流程非常直观:
- 你向模型提问,例如:“用Python计算斐波那契数列的前10项。”
- 模型在回复中生成一个代码块(Markdown格式的
python ...)。 - 在该条模型回复的下方,会出现一个“Run code”按钮。
- 由你点击这个按钮,触发代码在沙箱中执行。
- 执行结果(标准输出、错误信息)会作为一个新的、可见的消息插入到对话中,你和模型都能看到。
它的核心特点是“用户显式控制”。每一次代码执行都需要你主动点击确认。这带来了两个好处:一是安全性感知更强,你清楚地知道什么时候、什么代码被执行了;二是结果对对话双方透明,便于你和模型基于执行结果进行后续的讨论和调试。这非常适合教育、代码评审、分步问题解决等场景,即你希望与模型协作,并清晰地跟踪每一个执行步骤。
2.2 代码执行工具:赋予模型的自主能力
而“代码执行工具”则更进一步,它尝试将执行能力“赋予”模型本身。其工作流程更像是一个自动化的智能体:
- 你在发送消息时,在输入框旁激活“Run code”工具的开关。
- 你提出一个复杂需求,例如:“请帮我分析一下今天GitHub上Trending的Python项目,总结它们的共同特点。”
- 模型在内部“思考”后,可能会自主决定:“要完成这个任务,我需要先获取当前日期,然后发起一个网络请求抓取GitHub页面,最后对内容进行解析和总结。”
- 于是,模型会自动、隐形地调用“Run code”工具。它可能会先执行一段获取日期的代码,再执行一段使用
requests库抓取网页的代码。 - 这些工具调用的过程对你是不可见的,但执行的结果会作为背景信息反馈给模型。
- 最终,模型整合这些信息,给你一个完整的、基于实时数据生成的答案。
它的核心特点是“模型自主决策”。这模拟了ChatGPT的“代码解释器”或“联网搜索”功能。工具的执行过程对用户是黑盒,只有最终答案呈现出来。这极大地扩展了模型的能力边界,使其能够处理需要实时数据、复杂计算或外部交互的任务。适合用于数据分析、信息检索、自动化脚本生成等你希望模型“自主完成”的场景。
选择建议:对于初学者或注重可控性的用户,建议先安装并使用“函数”。它简单直观,风险完全可控。当你熟悉了沙箱的执行效果,并且有需要模型自主完成复杂链式任务的需求时,再考虑启用“工具”。在实际使用中,我通常两者都安装,根据对话的上下文灵活选择使用方式。
3. 环境准备:构建gVisor沙箱基础
安全是这一切的前提。safe-code-execution的强大隔离能力完全依赖于gVisor。因此,第一步不是安装插件本身,而是搭建一个能让Open WebUI安全调用gVisor的环境。这个过程需要一些Linux系统操作知识,但每一步都有明确的意图。
3.1 系统与权限检查
首先,确保你在一个Linux系统上(Windows的WSL2理论上可行,但可能遇到更多路径和服务管理问题,本文以原生Linux为例)。该项目重度依赖Linux的命名空间和cgroup特性。
你需要拥有root权限或能通过sudo执行管理命令。因为我们要创建系统级服务、挂载文件系统、调整内核参数。
打开终端,我们先做一个快速检查:
# 检查当前用户是否有sudo权限 sudo echo “权限检查通过” # 检查uname,确认是Linux内核 uname -s如果第一条命令报错,你需要切换到有sudo权限的用户或联系系统管理员。
3.2 安装与配置gVisor
gVisor的安装有多种方式,这里我们采用其官方推荐的、最适合与容器运行时集成的runsc安装方式。
下载最新稳定版runsc: runsc是gVisor的运行时组件。我们直接从Google的存储库下载预编译二进制文件,这通常比从源码编译更可靠。
( set -e ARCH=$(uname -m) [ “$ARCH” == “x86_64” ] && ARCH=“amd64” [ “$ARCH” == “aarch64” ] && ARCH=“arm64” wget https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}/runsc wget https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}/runsc.sha512 sha512sum -c runsc.sha512 sudo mv runsc /usr/local/bin sudo chmod a+rx /usr/local/bin/runsc )这段脚本做了几件事:自动检测系统架构(amd64或arm64),下载对应的
runsc二进制文件和校验文件,验证文件完整性,然后将其安装到/usr/local/bin目录并赋予执行权限。set -e确保过程中任何一步出错都会停止,避免安装不完整的文件。将runsc注册为Docker/Containerd的运行时: gVisor需要作为容器运行时被调用。我们将其配置为Docker的一个可选运行时。
sudo runsc install --runtime=runsc-gvisor执行成功后,你需要重启Docker服务来使配置生效:
sudo systemctl restart docker验证gVisor安装: 让我们运行一个简单的测试容器,确认gVisor工作正常,并且体验一下它的隔离效果。
# 使用gVisor运行时运行一个Alpine Linux容器 sudo docker run --runtime=runsc-gvisor -it alpine:latest /bin/sh进入容器后,尝试执行一些命令:
# 查看内核版本,会发现不是宿主机的内核,而是gVisor模拟的 uname -a # 尝试访问/proc或/sys下的某些敏感文件,可能会受到限制 # 退出容器 exit如果你看到内核版本显示类似
4.4.0等字样(而非你宿主机的真实内核版本),并且某些系统操作被限制,说明gVisor正在正常工作,提供了有效的隔离。
3.3 为Open WebUI配置沙箱执行服务
这是最关键的一步。Open WebUI本身是一个Web应用,它不能直接以root权限去执行docker run命令。我们需要创建一个安全的中间层——一个简单的本地HTTP API服务,由它来代表Open WebUI与gVisor沙箱交互。项目作者提供了优雅的解决方案。
创建专用系统用户和目录: 为了最小化权限,我们创建一个仅用于运行此服务的系统用户。
sudo useradd -r -s /bin/false openwebui-sandbox sudo mkdir -p /opt/openwebui-sandbox sudo chown openwebui-sandbox:openwebui-sandbox /opt/openwebui-sandbox部署服务脚本: 从项目仓库获取核心的服务端Python脚本。
sudo wget -O /opt/openwebui-sandbox/sandbox_server.py https://raw.githubusercontent.com/EtiennePerot/safe-code-execution/master/sandbox_server.py sudo chown openwebui-sandbox:openwebui-sandbox /opt/openwebui-sandbox/sandbox_server.py sudo chmod +x /opt/openwebui-sandbox/sandbox_server.py这个
sandbox_server.py脚本就是我们的“安全代理”。它启动一个HTTP服务器,监听本地请求。当收到执行代码的请求时,它以openwebui-sandbox用户的身份,使用sudo权限(通过特定的配置)启动一个gVisor容器,在容器内执行代码,然后将结果返回。配置sudo权限(无需密码): 我们需要让
openwebui-sandbox用户能无密码地以root身份执行特定的docker命令,而不能做其他任何事情。这是通过sudoers文件实现的,务必精确操作。sudo visudo -f /etc/sudoers.d/openwebui-sandbox在打开的编辑器中,输入以下内容:
# 允许openwebui-sandbox用户无密码运行特定的docker命令 openwebui-sandbox ALL=(ALL) NOPASSWD: /usr/bin/docker run --rm --runtime=runsc-gvisor * openwebui-sandbox ALL=(ALL) NOPASSWD: /usr/bin/docker kill *重要解释:第一行允许该用户运行
docker run命令,且必须包含--runtime=runsc-gvisor参数,这强制使用gVisor沙箱。*通配符允许接收任意后续参数(如镜像名、命令等)。第二行允许它kill容器,用于超时控制。配置完成后保存退出。visudo命令会进行语法检查,比直接编辑文件更安全。创建并启用Systemd服务: 为了让服务随系统启动并稳定运行,我们将其配置为Systemd服务。
sudo tee /etc/systemd/system/openwebui-sandbox.service << ‘EOF’ [Unit] Description=Open WebUI Safe Code Execution Sandbox Server After=docker.service network.target Requires=docker.service [Service] Type=simple User=openwebui-sandbox Group=openwebui-sandbox WorkingDirectory=/opt/openwebui-sandbox ExecStart=/usr/bin/python3 /opt/openwebui-sandbox/sandbox_server.py Restart=always RestartSec=5 # 安全限制 NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ReadWritePaths=/var/run/docker.sock [Install] WantedBy=multi-user.target EOF这个服务单元文件做了严格的沙箱化:以专用用户运行,限制其权限,只允许访问Docker套接字,并设置自动重启。
启动并测试服务:
sudo systemctl daemon-reload sudo systemctl enable --now openwebui-sandbox.service sudo systemctl status openwebui-sandbox.service查看状态,确认服务是
active (running)。然后我们可以用curl测试一下这个本地API是否工作:curl -X POST http://localhost:8080/run \ -H “Content-Type: application/json” \ -d ‘{“image”: “python:3.11-slim”, “command”: “python -c \“print(‘Hello from gVisor!’)\“”}’如果返回包含
“Hello from gVisor!”的JSON响应,恭喜你,沙箱服务已经就绪。这个localhost:8080就是后续Open WebUI插件需要连接的地址。
4. 在Open WebUI中安装与配置插件
基础环境搭建好后,剩下的就是在Open WebUI界面里的配置工作了,这部分相对直观。
4.1 安装代码执行函数
- 打开你的Open WebUI界面,登录后进入
Workspace(工作区)菜单。 - 选择
Functions(函数)子菜单。这里管理着所有自定义的交互功能。 - 点击页面上的
+按钮,开始创建新函数。 - 在弹出的表单中,填写以下信息:
- Function name:
Run code(这个名字会显示在按钮上) - Function description:
Run arbitrary code safely in a gVisor sandbox.(描述有助于理解功能) - Code section: 这是核心。你需要清空默认的代码,然后将浏览器导航到项目的GitHub仓库,找到
open-webui/functions/run_code.py这个文件,点击“Raw”按钮查看原始内容,并将其全部复制粘贴到这里。
- Function name:
- 点击
Save按钮保存。 - 保存后,你会在函数列表里看到新添加的
Run code。确保其右侧的两个开关(一个可能是“启用”,另一个是“显示在消息中”或类似含义)都处于打开(绿色)状态。
关键代码解析:粘贴的run_code.py文件内容并不长,它的核心逻辑是:当用户在UI点击按钮时,Open WebUI会调用这个函数。函数会提取当前消息中的代码块,然后向我们之前部署的本地服务(http://localhost:8080/run)发起一个POST请求。请求体中包含了要执行的代码、指定的语言(如python)以及超时时间。本地沙箱服务执行完毕后,将结果返回,函数再把这个结果格式化成一条新的消息插入对话。整个过程,Open WebUI只与localhost:8080通信,不直接接触Docker或系统命令。
4.2 安装代码执行工具
工具的安装过程与函数极其相似,但位置不同。
- 在Open WebUI的
Workspace下,这次选择Tools(工具)子菜单。 - 同样点击
+按钮。 - 填写表单:
- Toolkit name:
Run code - Toolkit description:
Run arbitrary code safely in a gVisor sandbox. - Code section: 清空后,从项目的
open-webui/tools/run_code.py文件中复制全部内容并粘贴。
- Toolkit name:
- 点击
Save。
函数与工具代码的差异:虽然两者都叫run_code.py,但tools目录下的版本逻辑略有不同。它被设计成一个“工具调用”的端点,其输入输出格式需要符合Open WebUI工具调用的规范(通常是一个更结构化的JSON)。当模型决定使用工具时,它会向这个端点发送一个符合规范的请求。本质上,它和函数一样,也是将请求转发给本地的沙箱服务。
4.3 为模型启用工具调用能力
安装工具后,它并不会自动对所有模型生效。你需要为支持工具调用(Tool Calling)的模型单独启用它。
- 进入
Workspace->Models。 - 在模型列表中,找到你想要启用代码执行能力的模型(例如
llama3.1:8b,qwen2.5:7b等较新的模型通常支持),点击其旁边的铅笔(编辑)图标。 - 在模型的编辑页面,向下滚动,你应该能看到一个
Tools或Tool Calling的区块。 - 在工具列表中,勾选我们刚刚添加的
Run Code。 - 点击
Save & Update保存模型配置。
重要提示:不是所有模型都支持工具调用。这需要模型在训练时具备相关能力。Ollama官方的llama3.1及以后版本、qwen2.5、deepseek-coder等模型通常支持。如果你勾选后工具不生效,首先应确认模型是否具备此能力。
5. 实战应用与效果演示
配置完成,让我们看看它如何改变工作流。假设我们已安装好“函数”和“工具”,并为一个支持工具调用的模型(如Llama 3.1)启用了工具。
5.1 使用代码执行函数进行交互式编程
场景:我想让模型帮我写一个Python脚本,用来查找并打印当前目录下所有大于1MB的.log文件。
- 提问:在聊天框输入:“写一个Python脚本,找出当前工作目录下所有大小超过1MB的.log文件,并打印它们的完整路径和大小。”
- 模型回复:模型生成了一段包含代码块的回复,类似:
你可以使用以下Python脚本实现该功能: ```python import os def find_large_log_files(directory=‘.’, size_threshold_mb=1): size_threshold = size_threshold_mb * 1024 * 1024 # 转换为字节 for root, dirs, files in os.walk(directory): for file in files: if file.endswith(‘.log’): file_path = os.path.join(root, file) try: file_size = os.path.getsize(file_path) if file_size > size_threshold: print(f“File: {file_path}, Size: {file_size / (1024*1024):.2f} MB”) except OSError as e: print(f“Error accessing {file_path}: {e}”) if __name__ == “__main__”: find_large_log_files() ``` - 执行验证:在这条回复的下方,你会看到一个
Run code按钮。点击它。 - 查看结果:几秒钟后,一条新的系统消息会出现,内容就是这段代码在gVisor沙箱中执行的标准输出。因为沙箱环境是全新的,目录下可能没有.log文件,输出可能是空的,或者提示找不到文件。但这已经完成了验证。
- 迭代优化:你可以基于结果继续对话:“这个脚本在找不到文件时输出不够友好,如果目录下没有.log文件,请提示‘未找到符合条件的文件’。” 模型会生成改进后的代码,你可以再次点击运行验证。
这个过程的价值:你无需离开浏览器,无需打开终端,就在同一个上下文里完成了“提出需求 -> 生成代码 -> 即时验证 -> 反馈优化”的完整循环。对于学习编程、快速原型验证、自动化脚本编写来说,效率提升是巨大的。
5.2 使用代码执行工具进行自动化任务
场景:我想知道当前日期,并获取一个最新新闻标题(假设模型训练数据截止日期较早)。
- 准备:在发送消息前,确保聊天输入框附近的“Run code”工具开关被激活(通常是一个小图标,点击后变亮)。
- 提问:输入:“告诉我今天的日期,并且去BBC新闻首页看看今天的一条头条新闻标题是什么。”
- 模型自主执行:模型收到这个请求后,内部会进行“思考”。它意识到需要两个实时信息:当前日期和网络数据。于是,它可能会自主发起两次工具调用:
- 第一次调用:执行
python -c “from datetime import datetime; print(datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’))”来获取精确时间。 - 第二次调用:执行一个使用
requests和BeautifulSoup库的Python脚本,去抓取BBC新闻首页,解析出第一个头条新闻的标题。
- 第一次调用:执行
- 返回最终答案:你不会看到工具调用的中间过程。稍等片刻后,模型会直接给出一个整合后的答案,例如:“今天是2023年10月27日。根据抓取的BBC新闻首页信息,当前的一条头条新闻标题是:‘Global Summit Reaches New Climate Agreement’。”
- 验证与追问:你可以追问:“这条新闻的链接是什么?” 模型可能会再次自主调用工具,从刚才抓取的页面中提取出链接并告诉你。
工具模式的核心优势:它将LLM从一个纯文本生成器,变成了一个可以主动使用“手脚”(工具)的智能体。对于需要结合实时信息、计算或外部API的复杂问答,这种能力是革命性的。你只需要提出最终目标,模型会自己规划步骤并执行。
6. 深入原理:gVisor如何保障安全
我们一直在提“安全沙箱”,那么gVisor到底做了什么?理解其原理,能让你更放心地使用这个方案,并理解其能力边界。
6.1 与传统容器(Docker)的隔离差异
普通的Docker容器使用Linux内核的命名空间(namespace)和控制组(cgroup)进行隔离。虽然这提供了不错的资源视图隔离,但所有容器共享宿主机的内核。如果一个容器内的进程通过系统调用(syscall)利用到了内核漏洞,就有可能影响到宿主机或其他容器。这被称为“容器逃逸”。
gVisor采取了截然不同的思路。它不是一个“更严格的容器”,而是一个用户态的内核实现。当你在gVisor中运行一个程序时:
- 拦截系统调用:应用程序发出的每一个系统调用(如打开文件、网络通信),都不会直接到达宿主机内核。
- Sentinel代理:gVisor的核心组件
runsc会拦截这些调用。 - 用户态内核处理:gVisor自己实现了一套完整的、用Go语言编写的Linux内核语义(包括文件系统、网络栈、进程调度等),在用户态处理这个系统调用。
- 安全的宿主调用:只有当必要时(如需要真正的硬件资源、访问受控的外部文件),gVisor才会通过一个极简的、经过严格审查的“主机系统调用接口”与宿主机内核通信,这个接口暴露的攻击面非常小。
简单比喻:传统容器像是给每个租客(容器)一个带锁的独立房间(命名空间),但大家共用一套中央水电系统(内核)。gVisor则是给每个租客一个完全独立的、自包含的迷你屋(用户态内核),迷你屋通过一个极其狭窄、坚固的管道(主机接口)与外界交换水电,这个管道很难被滥用。
6.2 本项目中的安全架构
safe-code-execution项目构建了一个纵深防御体系:
- 第一层:gVisor沙箱:代码最终在gVisor容器内运行,与宿主机内核隔离。即使代码恶意尝试执行
rm -rf /或进行网络扫描,其破坏力也被限制在沙箱内。 - 第二层:容器限制:通过Docker命令,我们还附加了额外的安全限制:
--read-only:容器根文件系统只读,防止代码篡改系统文件。--network none或 严格限制的网络策略:默认可以切断网络,或仅允许访问特定地址,防止数据外泄或网络攻击。--memory,--cpus:限制资源使用,防止耗尽主机资源。--user nobody:以非root用户身份运行容器内的进程。
- 第三层:权限隔离服务:我们创建的
openwebui-sandbox系统用户和Systemd服务,本身权限就被严格控制,只能执行特定的Docker命令。 - 第四层:Open WebUI上下文:整个流程由Web UI触发,最终用户不直接接触服务器命令行。
这种多层防护使得在Open WebUI中执行任意代码的风险变得极低。当然,没有绝对的安全。gVisor的性能开销比普通容器稍高(因为系统调用需要经过转换),且对某些极其底层的系统调用支持可能有限,但对于执行Python脚本、Shell命令等AI代码生成场景,这完全是可接受的权衡。
7. 高级配置与故障排查
在实际使用中,你可能会需要调整一些配置,或者遇到问题。这里分享一些进阶技巧和常见问题的解决方法。
7.1 自定义沙箱环境
默认情况下,插件使用python:3.11-slim镜像来执行Python代码。但你可能需要其他语言或特定依赖。
修改默认镜像:你需要编辑之前部署的sandbox_server.py脚本。找到其中类似default_image = “python:3.11-slim”的行,将其修改为你需要的镜像,例如:
node:20-alpine用于执行JavaScript/Node.js代码。golang:1.21-alpine用于执行Go代码。ubuntu:22.04用于一个更完整的Linux环境,可以安装多种工具。
修改后,需要重启服务:
sudo systemctl restart openwebui-sandbox.service为工具调用传递语言参数:在工具的Python脚本(open-webui/tools/run_code.py)中,你可以看到它如何构造请求。你可以修改逻辑,让模型在调用工具时指定language(如bash,javascript),服务端根据语言选择不同的基础镜像。这需要一些额外的编程工作。
7.2 网络访问控制
默认配置可能允许沙箱访问外部网络(用于pip install或抓取网页)。如果你希望完全禁止,可以在docker run命令中添加--network none参数。在sandbox_server.py中,找到执行docker run命令的部分(通常在subprocess.run调用中),添加此参数。
反之,如果网络访问有问题,请检查:
- gVisor的网络支持:确保宿主机网络正常,且gVisor的
runsc支持你的网络配置(如bridge模式)。 - 防火墙:宿主机防火墙是否阻止了容器出站流量。
- 代理:如果你的环境需要HTTP代理才能访问外网,需要在Docker容器内设置环境变量(如
HTTP_PROXY),这需要在sandbox_server.py中构建命令时添加-e参数。
7.3 常见问题与解决
问题1:点击“Run code”按钮或使用工具后,长时间无响应,最终报超时错误。
- 排查:首先检查沙箱服务状态:
sudo systemctl status openwebui-sandbox.service。查看日志:sudo journalctl -u openwebui-sandbox.service -f。 - 可能原因及解决:
- 服务未运行:按照第3.3节步骤重启服务。
- Docker权限问题:确认
/var/run/docker.sock的权限,确保openwebui-sandbox用户所在组有读写权限。通常docker组有权限,可以将用户加入该组:sudo usermod -aG docker openwebui-sandbox,然后重启服务。 - gVisor运行时未注册:运行
sudo docker info | grep -i runtime,检查输出中是否包含runsc-gvisor。如果没有,重新执行sudo runsc install --runtime=runsc-gvisor并重启Docker。 - 镜像拉取慢:首次运行会拉取Docker镜像(如
python:3.11-slim),如果网络慢会导致超时。可以手动提前拉取:sudo docker pull python:3.11-slim。
问题2:代码执行成功,但返回结果乱码或格式错乱。
- 排查:这通常是编码问题。沙箱内是纯净环境,默认编码可能是
POSIX或C.UTF-8。 - 解决:在生成的代码中,显式指定编码。对于Python,可以在脚本开头添加:
或者,在import sys import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding=‘utf-8’) sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding=‘utf-8’)sandbox_server.py中,为docker run命令设置环境变量-e PYTHONIOENCODING=utf-8。
问题3:工具调用不生效,模型无视我的请求。
- 排查:
- 确认模型是否支持工具调用。尝试问模型:“你支持工具调用(Tool Calling)吗?” 一些旧版模型可能不支持。
- 在Open WebUI的模型设置页面,确认
Run Code工具已被勾选并保存。 - 发送消息时,确认输入框旁的“Run code”工具图标是激活状态(高亮)。
- 查看Open WebUI的后台日志或浏览器开发者工具的网络请求,看是否有向
/api/tools/run_code发起的请求。
- 解决:如果模型不支持,需要更换模型。如果支持但未触发,尝试更明确的指令,如“请使用代码执行工具来获取当前日期”。
问题4:执行需要安装额外包的Python代码失败(ModuleNotFoundError)。
- 原因:gVisor沙箱每次都是全新的、纯净的环境。
- 解决:在代码中内联安装。例如:
注意,这会增加代码执行时间。对于常用且稳定的依赖,更好的方式是构建一个自定义的Docker镜像,预装好这些包,然后修改# 在执行主逻辑前,先安装依赖 import subprocess import sys subprocess.check_call([sys.executable, “-m”, “pip”, “install”, “-q”, “pandas”, “requests”]) # 然后导入并使用 import pandas as pd ...sandbox_server.py使用这个自定义镜像作为默认镜像。
8. 性能优化与生产考量
在个人开发环境中,默认配置通常足够。但如果使用频繁,或考虑在团队中部署,以下几点优化建议值得参考:
- 镜像层缓存:虽然gVisor容器本身是临时的,但Docker镜像层会被缓存。确保使用像
python:3.11-slim这样体积较小的官方镜像。可以进一步构建一个包含常用数据科学库(如numpy, pandas, scikit-learn)的专属镜像,避免每次临时安装。 - 资源限制:在
sandbox_server.py的docker run命令中,明确设置内存(-m 512m)和CPU(--cpus=1)限制,防止单个失控的代码耗尽主机资源。 - 超时控制:插件本身和
sandbox_server.py都有超时设置(如30秒)。对于复杂计算,可能需要适当调高。但务必设置一个上限,避免无限循环代码挂起服务。 - 日志与审计:生产环境应考虑记录代码执行日志。可以修改
sandbox_server.py,将收到的请求(去除敏感信息后)和执行结果摘要写入一个日志文件或发送到监控系统,用于安全审计和故障排查。 - 多语言支持增强:目前的实现主要针对Python。你可以扩展
sandbox_server.py,使其能根据请求中的language字段,智能选择不同的基础镜像和执行命令(如Node.js用node -e,Bash用/bin/bash -c)。
将安全的代码执行能力集成到Open WebUI中,通过gVisor沙箱提供坚实隔离,这不仅仅是添加了一个功能,而是从根本上扩展了本地大语言模型的实用性和安全性边界。它模糊了“对话”与“执行”的界限,让LLM从一位博学的顾问,变成了一个能动手操作的伙伴。从简单的计算验证到复杂的、需要实时数据的自动化任务,这个组合为你打开了一扇新的大门。
