AI 生成代码沙箱的生产级应用要求

AI 生成代码沙箱的生产级应用要求

运行 AI 生成代码的生产级应用需要一个沙箱,它能实施进程级隔离、支持并发会话、暴露可编程的生命周期 API、提供可观测的日志和资源指标、实施包和网络策略,并与应用后端干净地集成。如果没有系统地评估这些维度中的每一个就选择沙箱,是团队在发布后遇到问题的最常见方式:一个在预发布环境中看起来安全的工作负载,在生产流量下会失败,在租户之间泄漏状态,或者静默执行应用从未打算允许的代码。

本指南是一份需求清单。它涵盖了在每个隔离级别需要验证的内容、生产级生命周期 API 必须暴露什么、可观测性和资源控制应该是什么样的,以及后端集成模式在哪些地方会决定设计的成败。无论你是在评估托管沙箱还是构建自己的沙箱,这些都是在交付之前值得回答的问题。

沙箱隔离:进程、容器和 MicroVM

隔离是一个光谱,每个级别在性能、可移植性以及你对生成代码的信任程度方面都有不同的权衡。

进程级隔离 使用操作系统原语——命名空间、cgroups、seccomp 和 AppArmor 或 SELinux 配置文件——来限制进程可以访问的内容。它速度快,不需要单独的 VM 内核,但所有进程共享宿主机内核。一个内核漏洞或一个通过 seccomp 过滤器的特权系统调用可能会影响同一宿主机上的其他工作负载。对于低风险、短生命周期、受信任的代码路径,进程隔离是一个合理的起点,但对于不受信任的 AI 生成代码(可能尝试系统调用、子进程生成或包安装)来说,它是一个薄弱的边界。

在此级别需要验证的内容:

  • 哪些系统调用被阻止,当尝试未知系统调用时的默认策略是什么?
  • 命名空间是按任务、按租户划分,还是在作业之间共享?
  • cgroup 限制是在任务级别还是在宿主机级别强制执行?
  • 沙箱在退出时是否清理所有进程、临时文件、套接字和共享内存?

容器级隔离 增加了文件系统和网络命名空间边界,并使镜像管理可重复。容器比完整 VM 启动更快,更容易组合,并且被编排层广泛支持。权衡是容器仍然共享宿主机内核,并且容器边界仅与底层运行时配置一样强大。特权容器、广泛的能力集、挂载的宿主机套接字和宿主机网络模式都会将有效边界降低到几乎为零。

在此级别需要验证的内容:

  • 容器镜像是否最小化,只包含工作负载实际需要的运行时和工具?
  • 能力是否被丢弃到所需的最小集合?
  • 容器是否是无根的,或者是否需要 root 以及相关的控制措施?
  • 是否明确排除了宿主机 PID 命名空间、宿主机网络和 Docker 套接字?
  • 挂载的卷是否仅限于明确定义的路径,并且根文件系统在可能的情况下是否为只读?

MicroVM 隔离 将每个工作负载放入一个轻量级虚拟机内——拥有自己的客户机内核、虚拟设备和 KVM 支持的客户机与宿主机之间的边界。像 Firecracker 这样的技术使用最小化的设备模型来减少攻击面,同时保持足够快的启动速度以用于交互式使用。MicroVM 边界意味着客户机中的内核漏洞不会自动影响宿主机或其他客户机。

在此级别需要验证的内容:

  • 每个代理运行、每个租户或每个并发会话是否获得一个单独的 MicroVM?
  • 从 API 调用到可执行状态的启动延迟是多少,并且这个延迟是从热池、快照还是冷启动测量的?
  • 客户机镜像是否进行版本控制、审计其包含的运行时和工具,并按计划定期更新?
  • 如果客户机内核崩溃或无响应,宿主机级别会发生什么?

实际的决策取决于你的威胁模型。对于不受信任的 AI 生成代码,MicroVM 隔离是最强大的普遍可用边界,但它不能替代文件系统策略、出站控制、包治理或秘密处理。这些控制必须放在你选择的任何隔离层之上。

管理并发沙箱会话

一个为多个用户同时生成代码的生产级应用需要一个将并发性作为首要关注点(而非事后考虑)的沙箱。

关键问题是:

每会话隔离:当 50 个会话同时运行时,每个会话是否有自己独立的文件系统、进程树、网络命名空间和凭证范围?会话之间的状态泄漏是多租户沙箱应用中最具破坏性的故障模式之一,并且在会话顺序运行的测试中通常不可见。

会话限制和背压:沙箱是否将并发限制作为清晰的 API 契约公开?如果 500 个请求到达而平台支持 100 个并发会话,API 是返回结构化错误、将请求排队,还是静默降级?生产级应用需要这个信号来实现背压、队列管理和面向用户的反馈。

负载下的资源公平性:当一个会话消耗异常高的 CPU 或内存时,其他会话是否受到每会话资源限制的保护,还是一个嘈杂的工作负载会降低整个池的性能?

热池和会话启动延迟:交互式编码功能需要亚秒级的会话启动时间。这通常需要一个预初始化环境的池,可以立即被认领,而不是按需启动。验证平台是否记录了热池的可用性,以及在不同并发级别下预期的启动延迟。

会话重用 vs. 全新环境:一些应用受益于跨多个代理轮次重用长期存在的会话,而另一些则每次请求都需要一个干净的环境。验证这两种模式是否都受支持,并且会话重用不会携带来自先前对话的陈旧状态。

生命周期 API:创建、执行、终止

生命周期 API 是你的应用程序和沙箱运行时之间的接口。一个生产级的 API 必须至少暴露以下内容:

创建:初始化一个新的沙箱会话,可选地从模板或快照开始,具有指定的资源限制、环境变量和挂载的卷。响应应包含一个会话 ID 和一个就绪信号,而不仅仅是一个确认。

执行:提交代码或命令以供执行。这应该是一个返回执行 ID 的异步调用。API 必须支持指定工作目录、调用的环境覆盖以及超时。

流式输出:以流的形式检索 stdout 和 stderr,而不仅仅是在执行完成后作为最终结果。流式传输对于长时间运行的作业、需要很多秒的代理步骤以及任何向用户显示增量进度的 UX 都很重要。

终止:在完成之前结束正在运行的执行。沙箱应保证进程树被清理,而不仅仅是父进程。

清理:销毁会话并释放所有相关资源——文件系统、内存、进程槽位、网络状态以及任何持有的凭证。此调用应该是幂等的,以便在网络错误后重试不会导致错误。

上传和下载文件:在执行前将输入文件传输到沙箱,并在执行后检索输出工件。文件传输应受大小限制,并且对于哪些路径可写应有策略控制。

值得验证的其他生产级能力:

  • 暂停和恢复:能否在不丢失状态的情况下暂停和稍后恢复长期运行的会话?这对于速率限制、成本控制以及代理轮次之间的会话移交很有用。
  • 快照:当前会话状态能否被捕获并用作未来会话的起点?这是热池和可重用环境的关键机制。
  • 超时执行:如果执行的代码超过挂钟超时,平台是否干净地终止它并报告正确的退出状态?

可观测性:日志、指标和追踪

你无法调试或审计你看不到的东西。生产级沙箱需要内置的可观测性,而不是事后添加。

Stdout 和 stderr 捕获:每次执行都应产生一个与会话 ID 和执行 ID 关联的捕获输出记录。这应该在执行完成后通过 API 访问,而不仅仅是作为实时流可用。

执行日志:平台应记录哪些代码运行了、何时开始、何时结束、退出代码是什么、哪个用户或租户拥有该会话,以及使用了哪个模板或快照。这些记录是重构出问题时发生情况所需的最低限度。

资源指标:生产级应用需要每个会话的 CPU 使用率、内存峰值、挂钟时间和文件系统写入指标。这允许进行容量规划、异常检测和每会话成本归因。

错误追踪:当沙箱无法启动、执行或清理时,错误表面应该是结构化的:错误代码、消息、会话 ID 以及足够的上下文,以区分用户错误(错误代码、缺少包)和平台错误(配额超限、内部故障)。

审计追踪:对于多租户应用,审计追踪应使代理行为可重构:会话 ID、租户、执行顺序、包安装、联系的外部域名、写入的文件和清理结果。原始客户代码和完整命令输出默认可能不属于审计日志——为你的保留和访问策略实际能支持的内容进行设计。

需要避免的是:一个只显示“执行失败”、没有结构化错误、没有会话级别日志、并且无法区分超时、OOM 和进程逃逸尝试的沙箱。这迫使你在应用层检测所有内容,这重复了工作并且错过了沙箱可以直接观察到的事件。

CPU、内存和超时限制

无限制的资源消耗是沙箱化工作负载在生产中引起问题的最简单方式之一——要么通过降低其他会话的性能,要么通过产生意外的基础设施成本。

一个生产级沙箱必须在会话级别(而不仅仅是宿主机级别)强制执行限制:

CPU:限制单个会话可以消耗的 CPU 时间。一个产生无限循环的会话不应降低同一宿主机上其他会话的性能。验证限制是硬限制(进程被限制或杀死)还是软限制(它与其他进程竞争可用 CPU)。

内存:设置一个内存上限,触发清理或终止,而不是允许会话耗尽宿主机内存。验证达到限制时会发生什么:OOM 杀死、结构化错误响应,还是静默挂起。

挂钟超时:每次执行调用都应有一个最大持续时间。超时应在平台级别可执行,而不仅仅在客户端级别——如果客户端断开连接,沙箱仍应在配置的限制下终止执行。

磁盘使用:生成的代码可能会写入大型输出文件、安装大型包或填满工作目录。会话工作目录上的磁盘配额可防止失控写入。

进程数:AI 生成的代码可能会生成子进程、后台工作线程或 shell 命令,这些命令自身又会生成更多进程。会话命名空间中进程总数的限制可防止 fork 炸弹和失控的进程树。

评估沙箱平台时,检查这些限制是否可配置(针对每个会话,以便不同的用户层级或任务类型可以有不同的限制),它们是否在沙箱级别强制执行,以及达到限制是否会产生结构化的 API 错误还是静默失败。

包安装策略

AI 生成的代码经常请求包安装——pip installnpm installapt-get、Git 克隆、直接 URL 获取。这些操作中的每一个都会在运行时将外部代码拉入沙箱,这是沙箱需要治理的最高风险操作之一。

一个生产级包策略应涵盖:

注册表允许列表:哪些包注册表是允许的?PyPI 和 npm 是默认的,但许多团队希望选择限制为内部镜像、精选注册表或明确批准的来源。

安装缓存:当许多会话安装相同的流行包时,层缓存或拉取代理可避免冗余下载,减少启动延迟,并为你提供一个检查点来检查正在获取的内容。

离线模式:一些工作负载应该在没有包安装的情况下运行——环境已预置到镜像或模板中,并且安装尝试应失败并显示清晰的错误。这是评估运行(可重复性比灵活性更重要)的适当模式。

哈希验证和锁定文件:当允许包时,固定版本和哈希验证可降低注册表泄露改变沙箱内运行代码的风险。

大小限制:包及其传递依赖可能很大。对每个会话的总下载大小设置上限可防止意外或故意的存储耗尽。

包日志记录:每次安装尝试都应记录在执行审计日志中:包名称、请求的版本、注册表来源以及成功或失败。这是你在事件发生后重构进入沙箱内容所需的数据。

要问沙箱供应商的问题不是“用户可以安装包吗?”,而是“每次安装如何审计,默认允许哪些注册表,我能否为敏感工作负载配置更严格的策略?”

网络和出站控制

网络访问是沙箱到达意外目标的第二个主要向量。默认开放的出站在开发中很方便,但对于运行 AI 生成代码的生产级应用来说,这是一个糟糕的默认设置。

默认拒绝出站:最强大的生产姿态是默认阻止所有出站连接,并明确允许会话合法需要的目标。这需要更多配置,但使访问模型可审计。

允许列表目标:对于编码代理,典型的允许目标可能包括包注册表、代理被构建为调用的特定公共 API 集合,以及没有其他。对于数据分析代理,列表可能包括特定数据源。验证平台是否支持每会话或每租户的目标允许列表。

DNS 策略:DNS 应与出站策略一致处理。一个无法访问任意 HTTP 目标的会话,也应该无法解析任意 DNS 名称并利用它来推断网络拓扑或通过基于 DNS 的通道绕过控制。

内部服务访问:AI 生成的代码不应能够访问云元数据端点(例如,AWS 实例元数据服务)、内部 API、私有数据库或管理面板,除非明确配置。验证沙箱的默认网络策略是否阻止了众所周知的内部地址范围。

包下载出站:包安装是网络操作。如果出站受到限制,请确保包注册表允许列表与出站策略一致,或者使用受信任网络内部的拉取代理。

出站连接日志记录:即使允许出站,记录会话联系的域和 IP 对于事件调查也很有用。并非所有沙箱平台都原生提供此功能;验证你将获得什么。

秘密和凭证注入

AI 代理经常需要凭证——API 密钥、数据库连接、OAuth 令牌、短期云凭证。沙箱如何处理秘密对于安全性和操作可靠性都很重要。

窄范围:每个会话应仅接收其执行特定任务所需的秘密。将所有凭证的广泛环境文件挂载到每个会话中,操作上很方便,但意味着任何会话中被入侵或行为异常的代码都可以访问所有这些凭证。

短期凭证:在后端支持的情况下,优先选择具有与会话持续时间绑定的 TTL 的短期令牌。这限制了泄露凭证有用的时间窗口。

注入机制:验证秘密是作为环境变量、挂载的文件还是通过秘密 API 注入的。环境变量默认情况下可被会话中的所有进程访问;挂载的文件可以限定在特定路径和权限集。对于最敏感的凭证,考虑使用秘密 API,该 API 仅向明确授权的进程提供值。

编辑:沙箱不应通过 stdout、stderr、执行日志、错误消息或模型可见的工具响应回显秘密。编辑是应用层的责任,但支持可配置日志擦除的沙箱可减少意外暴露的影响范围。

清理:会话结束后,验证环境变量、挂载的秘密文件以及任何缓存的凭证数据是否作为会话拆除的一部分被清理,而不是留给下一个会话继承。

临时 vs. 持久文件存储

不同的工作负载有不同的持久化需求,一个生产级沙箱应清晰地支持两种模式。

临时会话:短生命周期代码执行的默认设置是一个会话,它创建一个干净的工作目录,运行代码,产生输出,然后被销毁。临时会话易于推理:每次运行都从已知的基线开始,没有状态积累,清理也很直接。对于评估作业、一次性代码补全以及任何可重复性比连续性更重要的任务,它们是正确的选择。

持久化工作区:长时间运行的编码代理、迭代开发工作流和多轮代理会话通常需要一个在多次执行调用中存活的工作区。在一个轮次中安装的文件、缓存的依赖项、编写的代码和积累的历史应该在下一个轮次中可用。持久化工作区操作更复杂:它们积累状态,它们可能偏离模板,并且它们需要一个明确的生命周期——工作区何时被清理,谁拥有它,以及会话之间有什么访问控制保护它?

快照和模板:模板让你定义一个已知良好的基线环境——运行时、工具、依赖项——并一致地从它启动会话。快照捕获正在运行的会话的当前状态,并将其用作未来会话的起点。两者对于需要可重复环境和低启动延迟的团队都很有用。验证模板是否进行版本控制,创建和更新它们的人是否受控制,以及快照是否按租户隔离。

输出工件导出:执行后,什么可以离开沙箱?一个生产级策略应定义哪些文件路径是可导出的,适用什么大小限制,以及在应用程序接收工件之前是否对它们进行审查或过滤。

跨会话状态:明确你的应用设计是否打算让会话共享状态。意外共享——通过共享的包缓存、共享的卷或错误路由的工作区——是常见的多租户隔离故障。

后端集成:REST、WebSocket、SDK

沙箱只有在干净地集成到应用程序后端时才有用。三种主要的集成模式是 REST、WebSocket 和 SDK。

REST:REST API 是提交离散执行请求并轮询结果的应用程序摩擦最小的集成方式。它适用于短生命周期任务,使用标准 HTTP 工具易于调试,并且自然适合现有的服务架构。权衡是轮询结果与推送通知相比增加了延迟,并且流式传输长时间运行的输出需要 SSE 或轮询日志端点。

WebSocket:WebSocket 连接支持应用程序和沙箱之间的双向、低延迟通信。这对于交互式用例是正确的选择:代码运行时流式传输输出的编码助手,需要实时发送命令和接收响应的浏览器代理,或持续监控执行的评估框架。权衡是操作复杂性:WebSocket 连接需要持久状态、重连处理以及客户端和服务器端更复杂的基础设施。

SDK:语言原生 SDK 隐藏了传输细节,处理身份验证,为会话管理和执行提供类型化接口,并且通常包含用于流式输出、上传文件和管理模板的辅助工具。对于大多数应用开发者来说,SDK 是集成的最快路径。验证 SDK 是否积极维护,是否覆盖完整的 API 表面,并且是否以你的应用程序可以采取行动的结构化方式处理错误。

你的应用程序需要拥有的集成点:无论传输方式如何,你的应用程序负责授权(哪些用户可以创建会话以及使用哪些资源限制)、审批门控(哪些工具调用或代码执行在运行前需要人工审查)、结果处理(沙箱输出如何被代理呈现或处理)和清理(当用户流程完成或代理轮次结束时触发会话拆除)。

一个设计良好的沙箱 API 不会试图拥有你的应用程序的业务逻辑。它暴露原语——创建、执行、流式传输、终止、清理——并让你的应用层在此基础上构建正确的产品行为。

故障恢复和清理

生产系统会失败。一个能优雅处理失败的沙箱可以防止资源泄漏、陈旧状态和难以调试的事件。

执行超时处理:当正在运行的执行超过其超时时间时,平台应干净地终止进程树并返回一个结构化的错误响应——而不是留下一个僵尸会话消耗资源。验证超时后会话会发生什么:是自动清理,还是需要显式的清理调用?

会话崩溃恢复:如果沙箱宿主机崩溃或会话 VM 意外退出,平台应检测到故障,将会话标记为已终止,并通过 API 呈现该状态,以便应用程序做出反应。会话不应无声消失,且不提供任何 API 信号。

清理保证cleanupterminate API 调用应可靠地释放所有资源:CPU 和内存分配、文件系统配额、进程槽位、网络状态和凭证。清理应该是幂等的——对同一会话 ID 多次调用它不应返回错误。这在实践中很重要:在网络错误后重试清理的应用程序代码不应中断。

部分执行失败:当代码在执行过程中失败时——未处理的异常、进程被杀死、缺少包——沙箱应返回一个结构化的结果,区分部分成功(在失败前产生了一些输出)和完全失败。构建在部分结果上的应用程序需要这个来避免向用户呈现不完整或误导性的输出。

失控进程处理:如果生成的代码创建了一个后台进程,该进程在主执行结束后仍然存在,沙箱应作为会话清理的一部分终止它,而不是允许它无限期运行。验证平台的清理是否覆盖完整的进程树,而不仅仅是执行调用的直接子进程。

容量和配额错误:当平台达到会话容量或租户达到其配额时,API 应返回一个特定的错误代码,应用程序可以显式处理——而不是通用的 500 错误或静默挂起。这允许应用程序排队、退避或向用户显示有用的消息。

Novita Agent 沙箱

Novita Agent 沙箱 是一个为代理工作负载构建的托管沙箱平台。它面向编码代理、数据分析代理、浏览器导向的工作流以及更长时间的代理会话,在这些会话中,生成的代码需要在隔离的、可观察的环境中运行,而不会落在应用服务器或共享基础设施上。

对于已经使用 Novita AI 模型 API 的团队,Agent 沙箱可以成为更广泛代理架构的一部分:模型规划和生成代码,沙箱提供具有可编程生命周期的隔离执行,而应用层则拥有授权、审批门控和结果处理。

Novita 已经描述了包括 MicroVM 隔离、并发会话支持、涵盖创建、执行、流式传输、终止和清理的生命周期 API、用于管理会话状态的暂停和自动恢复、用于快速和可重复环境启动的模板和快照,以及与 Novita 模型 API 的集成等功能。在做出架构决策之前,请在 Novita Agent 沙箱文档 和产品页面上验证当前的功能可用性、资源配置选项和定价。关于特定隔离边界、并发限制、启动延迟和网络策略的声明,应针对当前产品文档进行确认。

当根据本指南中的要求评估 Novita Agent 沙箱时,请应用与其他任何供应商相同的清单:每会话隔离边界、生命周期 API 完整性、可观测性表面、可配置的资源限制、包策略选项、出站控制、秘密处理、持久化模型和后端集成支持。

常见问题

我应该为 AI 生成的代码选择哪种隔离模型?

MicroVM 隔离为不受信任的 AI 生成代码提供了最强的边界,但它增加了操作复杂性。当容器正确加固时——无特权模式、最小化能力、尽可能使用只读根文件系统、无宿主机套接字挂载——容器隔离对于低风险工作负载是足够的。仅进程隔离对于可能尝试系统调用、子进程生成或包安装的不受信任代码来说,边界太薄弱了。将隔离级别与你实际的威胁模型相匹配。

如何在生产沙箱中处理包安装?

使用注册表允许列表,而不是默认开放访问。添加一个拉取缓存以减少冗余下载并为你提供一个检查点。记录每次安装尝试,包括包名称、版本、来源和结果。对于可重复性比灵活性更重要的负载——评估运行、自动化管道——考虑离线模式,其中环境是预置的,并且完全禁止安装。

生命周期 API 至少应暴露什么?

创建、执行(带流式输出)、终止和清理。流式输出是最小实现中最常缺失的能力,也是交互式代理 UI 最需要的能力。清理必须是幂等的,并且必须覆盖完整的进程树,而不仅仅是入口点进程。

如何防止秘密通过沙箱泄露?

严格地将凭证范围限定到任务——而不是一个广泛的环境文件。优先选择短期令牌。如果秘密可能出现在 stdout 中,默认情况下不要记录完整的 stdout。验证沙箱在会话拆除时是否清理环境变量和挂载的秘密文件。将编辑视为应用责任,而不是沙箱保证。


推荐文章