1. 项目概述为Open WebUI构建安全的代码执行沙箱如果你正在使用Open WebUI来管理你的本地大语言模型比如通过Ollama部署的Llama、Qwen或者DeepSeek那么你很可能遇到过这样一个痛点模型可以生成代码但你却无法让它真正“跑起来”。无论是想让它帮你写个Python脚本处理数据还是执行一段Shell命令来检查系统状态最终都只能停留在“纸上谈兵”的阶段。这极大地限制了LLM作为“智能助手”的潜力尤其是在数据分析、自动化脚本编写和系统运维等场景下。今天要聊的这个项目——EtiennePerot/safe-code-execution就是为了解决这个问题而生的。它的核心目标很明确为Open WebUI安全地增加代码执行能力。这里的“安全”是重中之重。直接让LLM在宿主机器上运行任意代码无异于打开潘多拉魔盒一个rm -rf /或者恶意下载脚本就足以造成灾难。因此该项目借鉴了业界成熟的做法使用Google开源的gVisor作为沙箱隔离层。有趣的是根据项目文档的提示这甚至是ChatGPT内部采用的一种安全机制。简单来说gVisor会在用户空间模拟一个内核让代码在一个高度隔离的“容器”中运行既能访问必要的资源如计算、临时文件又无法触及宿主机的核心系统从而在功能性和安全性之间取得了绝佳的平衡。这个项目提供了两种集成模式适应不同的使用习惯一种是代码执行函数Function它会在LLM生成的消息下方显示一个按钮由你手动点击触发执行结果对双方可见另一种是代码执行工具Tool直接授予LLM自主调用执行代码的权限就像给它装上了“Web搜索”一样它可以在思考过程中自行决定何时运行代码来获取信息整个过程对用户可能是透明的。无论你是喜欢保持绝对控制权还是希望赋予AI更大的自主性都能找到合适的方案。接下来我将带你从零开始彻底搞懂这个项目的部署、原理、使用技巧以及那些官方文档可能没细说的“坑”。2. 核心概念解析Function与Tool的双重奏在深入动手之前我们必须先厘清项目提供的两个核心组件代码执行函数Code Execution Function和代码执行工具Code Execution Tool。虽然它们最终都调用相同的底层gVisor沙箱来执行代码但设计哲学和使用体验截然不同。理解二者的区别是选择最适合你工作流的关键。2.1 代码执行函数手动控制的精准打击你可以把代码执行函数想象成一个“代码运行器”插件。它的工作流程非常直观你向LLM提问例如“写一个Python脚本来列出当前目录下所有大于100MB的文件。”LLM生成回复并在消息中附带一个格式正确的代码块通常是Markdown的python ...格式。在该条LLM回复消息的下方会出现一个“Run code”按钮。由你手动点击这个按钮触发代码在gVisor沙箱中执行。执行结果包括标准输出和错误信息会直接显示在聊天界面中作为一条新的消息。同时这个结果也会被反馈给对话上下文LLM可以基于这个结果继续回答你的问题。这种模式的优势在于绝对控制权代码是否执行、何时执行完全由你决定。这给了你充分的机会审查LLM生成的代码避免运行可能存在风险的指令。结果可视化执行过程的结果对你是完全透明的你可以清晰看到代码的输出或报错信息。交互式调试如果代码运行出错你可以将错误信息反馈给LLM让它修正代码然后再次点击运行形成一个交互式的编程调试循环。它最适合的场景是代码审查与学习当你想要LLM教你编程或解释一段代码时可以手动执行来看结果。敏感操作涉及文件操作、系统查询等命令你需要先看一眼代码再决定是否运行。分步任务将一个复杂任务分解成多个代码片段你逐步运行并检查中间结果。2.2 代码执行工具赋予AI自主行动权代码执行工具则代表了另一种思路将执行能力作为一件“工具”赋予LLM本身。这类似于为LLM开启了“函数调用”Function Calling或“工具调用”Tool Calling能力。它的工作流程如下在聊天界面你需要手动激活本次对话的“Run code”工具开关通常在输入框附近。你提出一个任务例如“帮我查一下今天的天气然后计算如果明天气温下降3度会是多少。”LLM在内部推理时可能会认为“要完成这个任务我需要先获取当前日期然后模拟一个网络请求获取天气数据最后进行数学计算。”于是LLM可以自主决定调用“Run code”工具。它可能会生成一段Python代码使用datetime模块获取日期或用requests库如果沙箱内允许网络访问查询一个天气API。工具在后台静默执行这段代码并将执行结果纯文本返回给LLM。LLM接收到结果后整合信息最终生成给你的自然语言回复例如“今天是2023年10月27日。根据模拟数据示例当前气温是22度。明天气温下降3度后预计是19度。”这种模式的优势在于自动化与流畅性LLM可以自主规划并执行多步任务无需你反复点击按钮体验更接近一个真正能“做事”的智能体。信息获取LLM可以主动运行代码来获取实时信息如时间、从特定网页抓取数据突破其训练数据的时间限制。复杂计算对于复杂的数学运算、数据处理LLM可以借助代码工具获得精确结果避免大模型在数学上的“幻觉”。它最适合的场景是需要实时数据的问答例如“现在纽约是几点”、“某某股票最新价是多少”需配合网络访问。多步骤问题解决问题本身需要查询、计算、再推理等多个步骤。当你信任模型时用于执行相对安全、定义明确的任务。注意选择哪种模式对于初学者或处理不确定性任务强烈建议从代码执行函数开始它更安全、可控。当你熟悉了LLM的行为模式并且任务模式固定后可以尝试使用工具模式来提升效率。好消息是这两个组件可以同时安装互不冲突你可以根据对话的上下文灵活选择使用方式。3. 环境准备与核心依赖gVisor沙箱深度解析项目的安全基石完全建立在gVisor之上。因此在安装插件之前我们必须先搭建好gVisor的运行环境。这个过程比安装普通软件包稍复杂但理解其原理能帮你更好地排查后续问题。3.1 gVisor是什么为什么是它gVisor不是一个传统的虚拟机VM也不是一个普通的Docker容器。它是一个用户态内核Userspace Kernel。我们来做个类比宿主系统就像你家的大房子硬件和主操作系统。Docker容器就像在房子里用石膏板隔出的一个房间。它看起来是独立的但与房子共享同一个内核地基和承重墙。如果内核有漏洞攻击者可能打穿石膏板进入其他房间。传统虚拟机就像在房子旁边完全用砖头重建了一个带独立地基的小房子。完全隔离但启动慢、资源占用高。gVisor沙箱就像在你家房子里放了一个特制的、全封闭的“游戏舱”。你可以在舱内跑、跳、操作各种设备系统调用但这些动作都被舱内的机械结构用户态内核翻译和过滤了一遍再以安全的方式影响房子。舱体本身很轻便但隔离性远超石膏板隔间。gVisor通过拦截应用程序发出的所有系统调用如读写文件、创建进程、网络通信在自己实现的安全内核中处理它们从而彻底隔离了沙箱内应用与宿主内核的直接接触。这正是ChatGPT等服务选择它来运行不可信代码的原因。3.2 系统环境检查与依赖安装首先确保你的Open WebUI运行在Linux系统上。gVisor对Linux支持最完善。虽然macOS和Windows通过Linux虚拟机也可能运行但会复杂很多且非官方支持。1. 基础依赖安装在终端中执行以下命令安装必要的工具。以Ubuntu/Debian为例sudo apt-get update sudo apt-get install -y curl git python3-pip2. 安装gVisor的runsc运行时runsc是gVisor的核心组件它是一个符合OCI开放容器标准的运行时可以替代runc来运行容器。# 下载最新稳定版的runsc curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main | sudo tee /etc/apt/sources.list.d/gvisor.list /dev/null sudo apt-get update sudo apt-get install -y runsc安装完成后需要将其配置到Docker如果你的Open WebUI使用Docker运行或Containerd中。3. 配置Docker使用gVisor运行时编辑Docker的守护进程配置文件sudo nano /etc/docker/daemon.json如果文件已存在在其中添加runtimes部分如果不存在创建包含以下内容的文件{ runtimes: { runsc: { path: /usr/bin/runsc } } }保存并退出然后重启Docker服务使配置生效sudo systemctl restart docker验证配置是否成功sudo docker info | grep -i runtime你应该能看到runsc在运行时列表中。4. 验证gVisor安装运行一个简单的测试容器确认gVisor能正常工作sudo docker run --runtimerunsc --rm alpine echo Hello from gVisor sandbox!如果命令成功执行并输出信息说明gVisor环境配置正确。实操心得网络与权限的坑网络问题默认情况下gVisor沙箱内的网络是受限的与宿主机隔离。这意味着如果你希望LLM运行的代码能访问互联网例如用Python的requests库获取网页需要额外配置。项目通常通过映射宿主网络或使用桥接模式来实现。在后续配置Open WebUI时需要注意相关设置。文件系统访问沙箱内默认只有临时文件系统。如果代码需要读取宿主机的特定文件或向宿主机写入持久化结果需要预先在Docker运行参数或Open WebUI配置中定义卷映射Volume Mounts。这是一个关键的安全/功能平衡点映射越多功能越强风险也略微增加。用户与权限确保运行Open WebUI服务的用户如docker组用户有权限调用runsc。有时需要将用户加入docker组并重新登录。4. Open WebUI的沙箱化配置有了gVisor基础环境下一步是让Open WebUI知道如何使用这个沙箱来运行代码。safe-code-execution项目通过一个自定义的“工作器”Worker来实现这一点。我们需要在Open WebUI的配置中指明当有代码执行请求时应该使用我们配置好的gVisor沙箱环境。4.1 获取并配置工作器脚本项目文档中提到的[**set up Open WebUI for sandboxing**](docs/setup.md)是关键一步。我们手动完成这个过程。首先克隆项目仓库或直接下载关键文件git clone https://github.com/EtiennePerot/safe-code-execution.git cd safe-code-execution核心的配置文件是openwebui.compose.yaml。这个文件定义了一个Docker服务它包含了与gVisor交互的代码执行逻辑。我们不需要直接运行这个Compose文件但需要理解其内容并将其整合到我们已有的Open WebUI部署中。关键配置解析查看openwebui.compose.yaml你会看到它定义了一个codebox服务。这个服务镜像是ghcr.io/open-webui/codebox:latest这是Open WebUI官方提供的代码执行后端。配置中的核心部分是runtime: runsc指定使用gVisor运行时。network_mode: host让沙箱共享宿主网络这样沙箱内的代码才能访问互联网。这是实现网络访问的关键。挂载了/tmp卷为代码执行提供临时文件空间。4.2 整合到现有Open WebUI部署假设你的Open WebUI是通过Docker Compose部署的这是最常见的方式。你需要编辑你的docker-compose.yaml文件。添加codebox服务将上述openwebui.compose.yaml中的codebox服务定义复制到你的Compose文件中。修改Open WebUI服务依赖在你的open-webui服务定义中添加对codebox服务的依赖并设置环境变量指向它。services: open-webui: # ... 你原有的配置镜像、端口、卷等 ... depends_on: - codebox # 添加这行 environment: - WEBUI_SECURE_COOKIESfalse # 可能已有 - WEBUI_NAMEMy Open WebUI # 添加以下环境变量告诉Open WebUI代码执行后端的位置 - CODEBOX_API_URLhttp://codebox:8000 - CODEBOX_TIMEOUT300 # ... 其他配置 ... # 新增codebox服务 codebox: image: ghcr.io/open-webui/codebox:latest container_name: codebox runtime: runsc # 使用gVisor运行时 network_mode: host # 共享主机网络使沙箱内代码可访问外网 volumes: - /tmp:/tmp:rw # 提供临时文件空间 restart: unless-stopped重启服务docker-compose down docker-compose up -d4.3 验证沙箱配置服务启动后可以通过以下方式验证进入Open WebUI容器内部执行命令检查docker exec -it open-webui-container-name curl -f http://codebox:8000/health如果返回OK则说明后端服务连通正常。在Open WebUI的“设置”Settings或“系统信息”中有时会显示代码执行后端的连接状态。注意事项网络模式与安全权衡使用network_mode: host是一个重要的安全决策。它让沙箱拥有了接近宿主机的网络能力极大地增强了代码的实用性可以访问API、下载包等。但同时这也意味着恶意代码有可能利用网络进行扫描或攻击外部系统尽管仍受限于沙箱的用户权限。对于纯粹隔离的离线环境你可以移除这个配置这样沙箱将完全无网络访问。请根据你的实际安全需求进行评估。5. 安装代码执行函数Function环境就绪后我们就可以在Open WebUI的界面中安装第一个组件代码执行函数。这个过程主要在Web UI上完成相对直观。5.1 详细安装步骤登录Open WebUI进入管理界面。在左侧导航栏找到并点击Workspace然后选择Functions子菜单。这里管理着所有自定义函数。点击页面上的添加按钮会弹出一个函数编辑窗口。按照以下信息填写表单Function name:Run code(建议保持默认便于识别)Function description:Run arbitrary code safely in a gVisor sandbox.(描述清晰即可)Code section: 这是核心部分。你需要清空默认的代码然后将项目中的函数代码粘贴进去。代码地址是https://raw.githubusercontent.com/EtiennePerot/safe-code-execution/master/open-webui/functions/run_code.py你可以直接在终端用curl获取或者去GitHub页面复制。curl -s https://raw.githubusercontent.com/EtiennePerot/safe-code-execution/master/open-webui/functions/run_code.py将输出的全部内容粘贴到代码框中。点击Save按钮保存函数。保存后你会在函数列表中找到新添加的“Run code”。确保其右侧的两个开关都处于**开启绿色**状态。第一个开关控制函数是否全局可用第二个开关可能控制其在聊天中的显示。5.2 函数代码浅析与自定义虽然直接粘贴就能用但了解代码的大致逻辑有助于调试和自定义。打开run_code.py你会发现它主要包含register_function装饰器这是Open WebUI的函数注册机制。run_code函数这是核心函数。它接收code代码字符串和language编程语言两个参数。内部逻辑函数内部会构造一个HTTP POST请求发送到你之前配置的CODEBOX_API_URL即http://codebox:8000请求体包含了代码、语言和超时设置。结果处理接收后端返回的执行结果标准输出、标准错误、退出码并格式化成易读的消息返回给UI。你可以进行哪些自定义超时时间代码中默认超时是CODEBOX_TIMEOUT环境变量或300秒。如果经常运行长任务可以在Open WebUI的环境变量中调整CODEBOX_TIMEOUT或者在函数代码里硬编码一个更长的值不推荐最好用环境变量。语言支持后端codebox服务支持的语言是固定的通常包括Python, JavaScript, Shell, PHP等。你可以在函数描述中注明支持的语言但无法直接在此处添加新语言需要修改后端镜像。结果格式化如果你希望执行结果的展示样式不同比如高亮错误可以修改函数最后返回消息的Markdown格式。安装完成后你就可以在聊天中测试了。找一个能生成代码的模型如CodeLlama、DeepSeek-Coder等问它一个编程问题看看生成的代码块下方是否出现了“Run code”按钮。6. 安装代码执行工具Tool工具的安装流程与函数类似但它的作用位置和启用方式不同。工具是赋予模型自主权的关键。6.1 工具安装步骤同样在Workspace下这次选择Tools子菜单。点击添加新工具。填写工具信息Toolkit name:Run codeToolkit description:Run arbitrary code safely in a gVisor sandbox.Code section: 清空后粘贴工具专用的代码。地址为https://raw.githubusercontent.com/EtiennePerot/safe-code-execution/master/open-webui/tools/run_code.pycurl -s https://raw.githubusercontent.com/EtiennePerot/safe-code-execution/master/open-webui/tools/run_code.py点击Save保存。工具代码run_code.py的结构与函数版类似但它遵循Open WebUI的工具定义规范通常包含一个register_tool装饰器和更详细的工具描述如输入参数的模式定义以便LLM能更好地理解何时以及如何调用这个工具。6.2 为特定模型启用工具安装工具后它并不会自动对所有模型生效。你需要为每个支持“工具调用”功能的模型单独启用。进入Workspace-Models。在模型列表中找到你想要启用代码执行能力的模型例如llama3.1:8b,qwen2.5:7b等点击其旁边的铅笔✏️编辑图标。在模型的编辑页面向下滚动找到Tools部分。你应该能看到一个Run Code的复选框。勾选它。点击Save Update保存模型配置。重要提示并非所有模型都支持工具调用。这需要模型本身在训练时具备函数调用/工具调用的能力。Ollama的许多较新模型如Llama 3.1及以后版本、Qwen2.5、DeepSeek最新版等都支持。如果在这里看不到工具选项说明你加载的模型可能不支持此特性需要更换模型。6.3 工具的使用方法启用后在聊天界面使用该模型时你会发现输入框上方或附近多了一个工具选择区域。通常有一个“Run code”的开关或图标。手动触发模式在发送消息前确保“Run code”工具开关是打开状态。这样你的这次提问就允许模型在思考过程中自主调用代码工具。对话过程当你提出一个需要计算、查询或执行的任务时观察模型的回复。如果它决定使用工具你可能会在回复中看到短暂的“思考”状态或者在某些UI设计中消息旁会有一个小图标表示工具被调用。工具执行的过程和原始输出对用户可能是不可见的你最终看到的是模型整合了工具结果后的自然语言回答。示例开启工具后问“圆周率的前10位小数是什么” 模型可能会内部调用一个计算math.pi的Python代码然后告诉你结果而你不会直接看到那段代码和它的原始打印输出。7. 实战应用与高级技巧安装配置完毕我们来探讨如何在实际场景中高效、安全地使用这个强大的功能。7.1 场景一数据分析与可视化助手假设你有一个CSV数据文件sales.csv想让AI帮你分析。使用函数模式首先你需要让代码能访问到这个文件。由于沙箱隔离你需要通过卷映射将宿主机的数据目录挂载到沙箱内。这需要在codebox服务的Docker Compose配置中额外添加卷映射例如- /path/to/your/data:/data:ro只读。提问“请编写一个Python脚本读取/data/sales.csv文件计算每个月的总销售额并用柱状图显示。”手动执行与迭代LLM生成代码后你点击“Run code”。如果出错比如缺少pandas库错误信息会显示出来。你可以将错误反馈给LLM“运行出错提示没有pandas模块。请修改脚本考虑在沙箱环境中安装依赖。” LLM可能会生成包含subprocess.run([‘pip’, ‘install’, ‘pandas’])的代码。你再次运行直到成功生成图表图片沙箱可能需要配置输出图片的路径。7.2 场景二自动化系统信息监控你想让AI定期检查服务器状态。使用工具模式为支持工具的模型如llama3.1启用“Run code”工具。提问开启工具开关“检查当前系统的内存使用率和磁盘剩余空间。”自主执行模型会自动生成并执行Shell命令如free -h和df -h解析输出然后用自然语言总结给你“当前内存使用率为65%剩余8.2GB。根目录磁盘使用率为78%剩余45GB。”进阶你可以结合Open WebUI的“提示词预设”或“工作流”功能创建一个定时任务每天自动询问并记录系统状态。7.3 安全使用准则与限制尽管有gVisor沙箱但“安全”是相对的。请务必遵守以下准则最小权限原则卷映射只挂载必要的、非敏感的目录且尽量使用只读:ro模式。资源限制gVisor和Docker本身可以限制CPU、内存使用。考虑在codebox服务配置中添加资源限制防止恶意代码耗尽资源。deploy: resources: limits: cpus: 1.0 memory: 512M网络访问控制如果不需要外部网络移除network_mode: “host”。如果需要但想限制可以考虑使用Docker网络策略或防火墙规则来限制沙箱容器的出站连接。审计日志Open WebUI和Docker的日志会记录代码执行请求。定期检查这些日志了解代码执行情况。不要处理极端敏感信息避免让AI操作含有密码、密钥或核心业务数据的代码。沙箱逃逸虽然极难但并非100%不可能取决于gVisor和内核的漏洞。8. 常见问题排查与调试实录在实际部署和使用中你几乎一定会遇到一些问题。以下是我踩过坑后总结的排查清单。8.1 问题“Run code”按钮不出现可能原因1模型回复中没有正确的代码块。排查检查LLM的回复是否用Markdown的代码语法包裹例如python ...。有些模型在普通对话中可能不会主动生成代码块。在提示词中明确要求“请将代码放在代码块中。”可能原因2函数未正确启用。排查进入Workspace - Functions确认“Run code”函数存在且两个开关都已打开绿色。可能原因3Open WebUI版本或配置问题。排查确保你的Open WebUI版本较新支持自定义函数功能。检查浏览器控制台F12是否有JavaScript错误。8.2 问题点击“Run code”后长时间无响应或报超时错误可能原因1codebox后端服务未启动或连接失败。排查docker ps | grep codebox # 检查容器是否在运行 docker logs codebox # 查看后端日志是否有错误 # 在Open WebUI容器内测试连通性 docker exec open-webui curl -v http://codebox:8000/health解决检查docker-compose.yaml中codebox服务的配置是否正确特别是runtime: runsc和网络设置。确保Open WebUI容器的环境变量CODEBOX_API_URL设置正确。可能原因2gVisor (runsc) 初始化慢或失败。排查查看codebox容器日志的前几行是否有关于runsc的报错。首次启动gVisor沙箱可能需要加载内核模块会稍慢。解决确保系统已安装最新的linux-modules-extra包如果适用。可以尝试增加CODEBOX_TIMEOUT环境变量。可能原因3代码本身陷入死循环或等待。排查尝试运行一段非常简单的代码如print(“Hello”)。如果简单代码可以复杂代码超时可能是代码逻辑问题。8.3 问题代码执行成功但无法访问网络/文件网络问题现象代码中requests.get(“http://example.com”)失败。排查确认codebox服务配置中使用了network_mode: “host”。如果没有添加该配置并重启服务。注意在Docker Compose中network_mode和networks配置是互斥的。文件访问问题现象代码中open(‘/data/file.txt’)报FileNotFoundError。排查确认在codebox服务的volumes中正确挂载了宿主机的路径到容器内的目标路径如/host/path:/data。并确认挂载的权限是可读或可写的。8.4 问题工具模式下模型不调用代码工具可能原因1模型不支持工具调用。排查在Workspace - Models中编辑该模型查看是否有Tools选项。如果没有换一个支持工具调用的模型如llama3.1:8b-instruct-q4_K_M。可能原因2提示词未激发工具使用。排查工具调用需要模型在理解任务后自主决定使用。尝试更明确的任务如“请使用你拥有的工具计算2的100次方是多少”或者在系统提示词中强调模型可以使用代码工具。可能原因3工具开关未打开。排查在聊天界面确认本次消息发送前“Run code”工具的开关是激活状态通常会有高亮或勾选显示。8.5 性能优化与经验之谈冷启动延迟gVisor沙箱的冷启动第一次运行可能有几百毫秒到一秒的延迟。对于需要频繁执行小代码片的场景这可能有点慢。这是为了安全付出的代价。资源复用codebox服务本身是持久运行的它会处理多个执行请求。但每个代码执行请求都会启动一个独立的沙箱容器执行完毕后销毁。因此代码执行之间是隔离的无法共享状态如全局变量。如果需要在多次执行间保留数据必须通过挂载的卷来读写文件。语言运行时缺失默认的codebox镜像可能只预装了Python和Node.js等少数语言。如果你需要运行Go、Rust等代码需要自定义构建codebox镜像添加相应的编译和运行环境。这涉及到Docker镜像构建是更高级的用法。通过以上步骤和问题排查指南你应该能够顺利地在Open WebUI中搭建起一个既强大又安全的代码执行环境。这套组合拳——Ollama提供本地模型、Open WebUI提供交互界面、gVisor提供安全沙箱——真正释放了本地大模型在编程和自动化方面的潜力让它从一个“聊天专家”升级为一个可以动手的“数字助手”。