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

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

运行 AI 生成代码的生产级应用需要沙箱具备进程级隔离、支持并发会话、提供可编程的生命周期 API、输出可观察的日志和资源指标、强制执行包和网络策略,并与应用后端干净地集成。如果不系统地评估这些维度中的每一个,团队在发布后最容易遇到问题:在预发环境看起来安全的工作负载,在实际流量下失效,在租户之间泄漏状态,或者悄悄执行应用从未打算允许的代码。

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

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

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

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

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

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

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

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

  • 容器镜像是否最小化,只包含工作负载实际需要的运行时和工具?
  • 是否已将能力降至最低必要集合?
  • 容器是否是无根的,或者是否需要 root 以及有哪些相关控制?
  • 是否明确排除了主机 PID 命名空间、主机网络和 Docker 套接字?
  • 挂载的卷是否仅限于明确定义的路径,并且根文件系统尽可能只读?

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

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

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

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

管理并发沙箱会话

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

关键问题包括:

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

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

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

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

会话重用与新鲜环境:某些应用受益于在多个 Agent 轮次中重用长期会话,而其他应用需要为每个请求提供干净的环境。验证两种模式是否都支持,并且会话重用不会携带先前对话中的过时状态。

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

生命周期 API 是你的应用程序与沙箱运行时之间的接口。生产级 API 必须至少暴露:

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

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

流式输出:以流的形式检索 stdout 和 stderr,而不仅仅是执行完成后的最终结果。对于长时间运行的作业、花费数秒的 Agent 步骤以及任何向用户显示增量进度的界面,流式传输至关重要。

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

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

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

针对生产使用需要验证的其他能力:

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

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

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

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

执行日志:平台应记录运行的代码、开始时间、结束时间、退出代码、拥有会话的用户或租户以及使用的模板或快照。这些记录是重建问题发生时情况所需的最小信息。

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

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

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

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

CPU、内存和超时限制

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

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

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

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

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

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

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

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

包安装策略

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

生产级包策略应涵盖:

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

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

离线模式:某些工作负载应完全不允许包安装——环境已预烘焙到镜像或模板中,安装尝试应失败并返回清晰错误。这是评估运行中适当模式,因为可重复性比灵活性更重要。

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

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

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

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

网络和出口控制

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

默认拒绝出口:最强的生产姿态是默认阻止所有出站连接,并明确允许工作负载实际需要的目标。这需要更多配置,但使访问模型可审计。

允许列表目的地:对于编码 Agent,典型的允许目的地可能包括包注册表、Agent 构建要调用的特定公共 API 集,别无其他。对于数据分析 Agent,列表可能包括特定的数据源。验证平台是否支持每会话或每租户的目标允许列表。

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

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

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

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

秘密和凭据注入

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

狭窄范围:每个会话应该只接收它执行特定任务所需的秘密。将包含所有凭据的宽泛环境文件挂载到每个会话中操作上很方便,但意味着任何一个会话中的受损或行为不端代码都可以访问所有这些凭据。

短期凭据:如果后端支持,优先选择 TTL 限制为会话持续时间的短期令牌。这限制了泄露凭据的有效时间窗口。

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

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

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

临时与持久文件存储

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

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

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

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

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

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

后端集成:REST、WebSocket、SDK

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

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

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

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

你的应用需要拥有的集成点:无论传输方式如何,你的应用负责授权(哪些用户可以创建会话以及具有哪些资源限制)、审批门(哪些工具调用或代码执行需要人工审核才能运行)、结果处理(沙箱输出如何由 Agent 向用户展示或采取行动)以及清理(在用户流程完成或 Agent 轮次结束时触发会话拆除)。

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

故障恢复和清理

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

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

会话崩溃恢复:如果沙箱宿主机崩溃或会话 VM 意外退出,平台应检测故障、将会话标记为已终止,并通过 API 暴露该状态,以便应用能够做出反应。会话不应静默消失而不发出 API 信号。

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

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

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

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

Novita Agent 沙箱

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

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

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

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

常见问题

AI 生成代码应选择哪种隔离模型?

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

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

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

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

创建、带流式输出的执行、终止和清理。流式输出是最简实现中最常缺失的能力,也是对于交互式 Agent 界面最重要的能力。清理必须是幂等的,并且必须覆盖整个进程树,而不仅仅是入口点进程。

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

将凭据范围限制在任务所需——不是宽泛的环境文件。优先选择短期令牌。默认不记录完整的 stdout,如果秘密可能出现在其中。验证沙箱在会话拆除时是否清理环境变量和挂载的秘密文件。将编辑视为应用的责任,而不是沙箱的保证。


推荐文章