尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

smolagents 安全代码执行完全指南:从本地 AST 沙箱到 E2B、Modal、Blaxel 与 Docker 远程隔离

发布时间:2026/9/19 2:13:18

资讯中心
01
ARTICLE

smolagents 安全代码执行完全指南:从本地 AST 沙箱到 E2B、Modal、Blaxel 与 Docker 远程隔离

smolagents 安全代码执行完全指南:从本地 AST 沙箱到 E2B、Modal、Blaxel 与 Docker 远程隔离
smolagents 安全代码执行完全指南从本地 AST 沙箱到 E2B、Modal、Blaxel 与 Docker 远程隔离【免费下载链接】smolagents smolagents: a barebones library for agents that think in code.项目地址: https://gitcode.com/gh_mirrors/smo/smolagentssmolagents 的核心设计是让 LLM 直接以 Python 代码的形式表达行动Code Agent因此由 LLM 生成的代码在哪里执行、如何被执行就成为了整个框架安全性的第一道防线。本文以 secure_code_execution 官方教程 为主体结合仓库源码系统讲解本地LocalPythonExecutor的逐节点 AST 解释防护机制以及 Blaxel、E2B、Modal、Docker 四种远程沙箱的接入方式、多智能体适配与最佳实践。读完本文你将掌握如何评估不同执行环境的威胁模型并为自己的 Agent 应用选择并落地一套安全、可运维的代码执行方案。提示如果你对 Agent 框架还不熟悉建议先阅读 intro to agents智能体导论 和 guided tour of smolagentssmolagents 快速导览再回到本文。为什么让 LLM 写代码比写 JSON 工具调用更安全也更需要安全机制多项研究如 Executable Code Actions Elicit Better LLM Agents 等表明让 LLM 以代码形式编写其行动工具调用效果优于行业通用的把工具调用写成 JSON 形式的工具名 参数的格式。原因在于代码语言本身就是为表达计算机操作而精心设计的可组合性Composability你可以像定义 Python 函数一样嵌套动作、复用一组动作而 JSON 很难做到对象管理Object managementgenerate_image这类动作的输出对象在 JSON 中难以存储与传递通用性Generality代码天然可以表达计算机能做的任何事情训练语料优势Representation in LLM training corpus大量高质量动作代码已经存在于 LLM 的训练语料中直接生成代码可以充分利用这一点。正是基于这个判断smolagents 将重点放在 Code Agent具体来说是 Python Agent上而让 LLM 写代码也意味着必须在 Python 解释器层面投入更高成本去构建安全执行环境——这正是本文要解决的核心问题。本地执行的风险默认行为与四种攻击向量默认情况下CodeAgent会在你自己的环境中运行 LLM 生成的代码在 agents.py 源码 中executor_type的默认值为local。这本身就带有固有风险恶意代码可能通过以下四种途径进入你的系统纯 LLM 失误Plain LLM errorLLM 远非完美可能在试图帮忙时无意生成有害命令。该风险虽低但已有 LLM 尝试执行潜在危险代码的实例被观察到供应链攻击Supply chain attack运行不受信任或被攻陷的 LLM可能让系统暴露在有害代码生成之下。使用知名模型 安全推理基础设施时此风险极低但理论上依然存在提示注入Prompt injectionAgent 浏览网页时可能到达包含恶意指令的网站从而把攻击注入 Agent 的上下文记忆公共 Agent 被滥用Exploitation of publicly accessible agents暴露给公众的 Agent 可能被恶意行为者利用通过构造对抗性输入来滥用其执行能力。一旦恶意代码被执行无论有意还是无意它可能损坏文件系统、滥用本地或云端资源、滥用 API 服务甚至危及网络安全。在 智能体谱系spectrum of agency 中Code Agent 把更高的能动性交给了 LLM这天然与更高的风险相伴。因此安全的思路是用递增的设置成本换取递进的安全等级——但要清醒地认识到没有任何方案能做到 100% 安全。第一层防护LocalPythonExecutor 本地执行器为了加上第一层安全措施smolagents 并没有使用原生的 Python 解释器而是从零重新构建了一个更安全的LocalPythonExecutor。其核心实现位于 src/smolagents/local_python_executor.py整体思路是把代码解析成抽象语法树AST然后逐条操作operation by operation执行并在执行过程中始终强制执行一系列安全规则。防护规则一默认禁止导入白名单机制默认情况下导入import是被禁止的除非用户把模块显式加入授权列表。源码在 utils.py 中定义了默认的内置授权模块集合BASE_BUILTIN_MODULESBASE_BUILTIN_MODULES [ collections, datetime, itertools, math, queue, random, re, stat, statistics, time, unicodedata, ]LocalPythonExecutor.__init__见 local_python_executor.py会把用户传入的additional_authorized_imports与上述默认集合取并集得到最终生效的authorized_imports并且会在初始化时调用_check_authorized_imports_are_installed校验这些模块确实已安装否则直接抛出InterpreterError。子模块的访问默认同样被禁止必须逐项授权。你也可以用通配符形式例如传入numpy.*即可同时放行numpy及其全部子包如numpy.random、numpy.a.b。这一点由check_import_authorized函数实现它先把授权列表构建成一棵导入树build_import_tree再逐级匹配待导入路径——一旦某级节点是*即视为整棵子树放行见 local_python_executor.py。需要特别警惕的是一些看起来无害的包可能暴露危险的子模块。例如random包就能通过random._os触及潜在危险的os模块——这正是子模块必须显式授权这一规则存在的意义。防护规则二危险模块与危险函数黑名单即使在白名单之外解释器还维护了两张黑名单见 local_python_executor.pyDANGEROUS_MODULES [ builtins, io, multiprocessing, os, pathlib, pty, shutil, socket, subprocess, sys, ] DANGEROUS_FUNCTIONS [ builtins.compile, builtins.eval, builtins.exec, builtins.globals, builtins.locals, builtins.__import__, os.popen, os.system, posix.system, ]任何对这类模块/函数的访问都会被check_safer_result拦截并抛出InterpreterError。此外nodunder_getattr会拒绝以__xxx__形式访问 dunder 属性只有__init__、__str__、__repr__三个白名单方法ALLOWED_DUNDER_METHODS例外见 local_python_executor.py 与 local_python_executor.py。防护规则三操作数上限与执行超时解释器对基本操作elementary operations的总数设有上限防止死循环与资源膨胀。从源码可以看到三组关键常量local_python_executor.py常量默认值作用MAX_OPERATIONS10_000_000单次代码执行中 AST 节点求值总数上限evaluate_ast每执行一个节点都会计数并检查见 local_python_executor.pyMAX_WHILE_ITERATIONS1_000_000while循环迭代次数上限超限抛出InterpreterError见 local_python_executor.pyMAX_EXECUTION_TIME_SECONDS30单次代码执行的最大秒数通过timeout装饰器基于ThreadPoolExecutor实现设为None可禁用DEFAULT_MAX_LEN_OUTPUT50_000print输出最大字符数超出部分由truncate_content截断防护规则四未定义操作直接报错任何没有在自定义解释器中显式定义的操作都会抛出错误——因为执行器并非依赖真实 Python 运行时而是递归遍历 AST 节点只对白名单内的节点类型赋值、函数定义、类定义、if/for/while、lambda、推导式等提供求值实现。对于print这类内置工具解释器也做了替换BASE_PYTHON_TOOLS中的print指向custom_print输出被收集进_print_outputs而非直接打到终端。实战验证亲手触发这些防护官方教程给出了可直接运行的验证代码你可以原样复制执行from smolagents.local_python_executor import LocalPythonExecutor # 设置自定义执行器仅授权 numpy 包 custom_executor LocalPythonExecutor([numpy]) # 用于美化打印错误的工具函数 def run_capture_exception(command: str): try: custom_executor(command) except Exception as e: print(ERROR:\n, e) # 未定义命令无法工作 harmful_command !echo Bad command run_capture_exception(harmful_command) # ERROR: invalid syntax (unknown, line 1) # 除非显式加入 additional_authorized_imports否则 os 不会被导入 harmful_command import os; exit_code os.system(echo Bad command) run_capture_exception(harmful_command) # ERROR: Code execution failed at line import os due to: InterpreterError: Import of os is not allowed. Authorized imports are: [statistics, numpy, itertools, time, queue, collections, math, random, re, datetime, stat, unicodedata] # 即使在已授权包里危险子模块也不会被导入 harmful_command import random; random._os.system(echo Bad command) run_capture_exception(harmful_command) # ERROR: Code execution failed at line random._os.system(echo Bad command) due to: InterpreterError: Forbidden access to module: os # 死循环会在 N 次操作后被中断 harmful_command while True: pass run_capture_exception(harmful_command) # ERROR: Code execution failed at line while True: pass due to: InterpreterError: Maximum number of 1000000 iterations in While loop exceeded这些防护机制同样有对应的单元测试覆盖例如 tests/test_local_python_executor.py 中验证了不能覆盖工具名test_assignment_cannot_overwrite_tool、各类语法节点求值、递归函数等行为可作为你深入理解执行器行为的入口。必须清醒认识的边界本地沙箱并非万能教程明确给出了警告没有任何本地 Python 沙箱能做到完全安全。虽然LocalPythonExecutor相比标准解释器提供了显著的安全改进但一个坚定的攻击者或经过微调的恶意 LLM 仍可能找到漏洞并伤害你的环境。例如如果你放行了Pillow等图片处理包LLM 可能生成代码创建成千上万个大型图片文件来填满硬盘更高级的逃逸技术可能利用授权包内部的深层漏洞。因此想获得真正稳健的安全隔离唯一的办法是把 LLM 生成的代码放到远程执行环境如 E2B 或 Docker中运行。使用可信推理供应商的知名 LLM 时恶意攻击风险很低但并非为零对于高安全要求的应用或不太可信的模型应当考虑远程执行沙箱。沙箱化执行的两条路线在 smolagents 中沙箱化代码执行主要有两种方案它们的安全属性与能力边界各不相同在沙箱中仅运行代码片段Approach 1只把 Agent 生成的 Python 代码片段放进沙箱执行Agent 系统其余部分仍留在本地环境。通过executor_typeblaxel、executor_typee2b、executor_typemodal或executor_typedocker即可简单启用但不支持多智能体multi-agents且仍需在本地环境与沙箱之间传递状态数据在沙箱中运行整个 Agent 系统Approach 2把 Agent、模型、工具全部放进沙箱环境运行。隔离性更好但需要更多手工配置并可能要把敏感凭据如 API Key传给沙箱。在源码层面这两条路线分别对应 remote_executors.py 中的四个远程执行器类E2BExecutor、DockerExecutor、ModalExecutor、BlaxelExecutor与 agents.py 中create_python_executor的executor_type分发逻辑def create_python_executor(self) - PythonExecutor: if self.executor_type not in {local, blaxel, e2b, modal, docker}: raise ValueError(fUnsupported executor type: {self.executor_type}) if self.executor_type local: return LocalPythonExecutor(...) ... remote_executors {blaxel: BlaxelExecutor, e2b: E2BExecutor, docker: DockerExecutor, modal: ModalExecutor} return remote_executorsself.executor_type所有远程执行器都继承自RemotePythonExecutor基类。它依赖SafeSerializer在本地与沙箱之间传输变量与最终答案并暴露一个值得注意的参数allow_pickle默认False推荐保持关闭开启后无法安全 JSON 序列化的对象会回退到 pickle 序列化——pickle 反序列化可以执行任意代码只有当你完全信任执行环境时才应开启见 remote_executors.py。下面依次介绍四种远程沙箱的接入方式。使用方式与官方教程保持一致先安装对应 extra 包再在CodeAgent初始化时传executor_type参数。Blaxel 沙箱毫秒级冷启动的托管沙箱安装在 blaxel.ai 注册账号安装依赖pip install smolagents[blaxel]快速开始只需在 Agent 初始化时加上executor_typeblaxelfrom smolagents import InferenceClientModel, CodeAgent with CodeAgent(modelInferenceClientModel(), tools[], executor_typeblaxel) as agent: agent.run(Can you give me the 100th Fibonacci number?)使用with语句把 Agent 作为上下文管理器使用可以确保任务完成后 Blaxel 沙箱被立即清理你也可以手动调用 Agent 的cleanup()方法达到同样效果。工作流程每次agent.run()开始时Agent 状态被发送到 Blaxel 服务端模型仍在本地环境被调用但生成的代码会被送往沙箱执行只有输出结果被返回。Blaxel 提供从休眠状态 25ms 内快速启动的虚拟机并在空闲后缩回零资源同时保留内存状态非常适合需要快速、安全代码执行的 Agent 应用。如果需要更强的隔离可以把整个 Agent 托管到 Blaxel 远程运行实现 Agent、模型、工具三者的完整沙箱化。E2B 沙箱云端代码解释器安装在 e2b.dev 注册账号安装依赖pip install smolagents[e2b]快速开始from smolagents import InferenceClientModel, CodeAgent with CodeAgent(modelInferenceClientModel(), tools[], executor_typee2b) as agent: agent.run(Can you give me the 100th Fibonacci number?)同样建议使用with上下文管理器保证沙箱即时清理或手动调用cleanup()。每次agent.run()开始时 Agent 状态被发送到 E2B 服务端模型调用留在本地代码在沙箱内执行并只返回输出。E2B 下的多智能体需要把 Agent 完全搬进沙箱由于对托管 Agentmanaged agent的调用需要发起模型请求而 smolagents 不会把密钥secrets传给远程沙箱模型调用会缺少凭据——因此 Approach 1 暂不适用于更复杂的多智能体场景。要在 E2B 中运行多智能体需要把 Agent 完全放进 E2B 运行from e2b_code_interpreter import Sandbox import os # 创建沙箱 sandbox Sandbox() # 安装所需包 sandbox.commands.run(pip install smolagents) def run_code_raise_errors(sandbox, code: str, verbose: bool False) - str: execution sandbox.run_code( code, envs{HF_TOKEN: os.getenv(HF_TOKEN)} ) if execution.error: execution_logs \n.join([str(log) for log in execution.logs.stdout]) logs execution_logs logs execution.error.traceback raise ValueError(logs) return \n.join([str(log) for log in execution.logs.stdout]) # 定义你的 Agent 应用 agent_code import os from smolagents import CodeAgent, InferenceClientModel # 初始化子 Agent agent CodeAgent( modelInferenceClientModel(tokenos.getenv(HF_TOKEN), providertogether), tools[], namecoder_agent, descriptionThis agent takes care of your difficult algorithmic problems using code. ) manager_agent CodeAgent( modelInferenceClientModel(tokenos.getenv(HF_TOKEN), providertogether), tools[], managed_agents[agent], ) # 运行 Agent response manager_agent.run(Whats the 20th Fibonacci number?) print(response) # 在沙箱中运行 Agent 代码 execution_logs run_code_raise_errors(sandbox, agent_code) print(execution_logs)这里的要点是把HF_TOKEN通过envs参数注入沙箱让沙箱内的模型调用具备凭据而managed agent机制即把agent作为managed_agents[agent]传入manager_agent正是 多智能体教程 中介绍的用法此处只是把整个体系搬进了 E2B。从源码看E2BExecutorremote_executors.py通过e2b_code_interpreter.Sandbox执行代码并会把final_answer工具替换为抛出FinalAnswerException的形式来判定任务结束、取回最终答案——这是远程执行器与本地执行器在机制上的关键差异。Modal 沙箱按需 Serverless 容器安装在 modal.com/signup 注册账号安装依赖pip install smolagents[modal]快速开始from smolagents import InferenceClientModel, CodeAgent with CodeAgent(modelInferenceClientModel(), tools[], executor_typemodal) as agent: agent.run(What is the 42th Fibonacci number?)with上下文管理器保证 Modal 沙箱在任务完成后被即时清理源码中ModalExecutor.cleanup()调用sandbox.terminate()终止沙箱。运行机制上Agent 状态与InferenceClientModel生成的代码会被发送到 Modal 沙箱中安全执行。从源码看ModalExecutorremote_executors.py会在沙箱内启动jupyter kernelgateway并通过加密端口隧道encrypted_ports建立 WebSocket 连接来执行代码、回传结果。Docker 沙箱自托管容器隔离安装在你的系统上安装 Docker安装依赖pip install smolagents[docker]快速开始与 E2B 类似只需在 Agent 初始化时加上executor_typedockerfrom smolagents import InferenceClientModel, CodeAgent with CodeAgent(modelInferenceClientModel(), tools[], executor_typedocker) as agent: agent.run(Can you give me the 100th Fibonacci number?)with语句保证 Docker 容器在任务完成后立即清理源码中DockerExecutor.cleanup()会依次执行container.stop()与container.remove()。Docker 高级用法自定义沙箱解释器如果要在 Docker 中运行多智能体系统需要在一个沙箱中配置自定义解释器。官方教程给出的方案分为两步。第一步编写 Dockerfile 构建带有限权限的沙箱镜像FROM python:3.10-bullseye # 安装构建依赖 RUN apt-get update \ apt-get install -y --no-install-recommends \ build-essential \ python3-dev \ pip install --no-cache-dir --upgrade pip \ pip install --no-cache-dir smolagents \ apt-get clean \ rm -rf /var/lib/apt/lists/* # 设置工作目录 WORKDIR /app # 以受限权限运行 USER nobody # 默认命令 CMD [python, -c, print(Container ready)]第二步创建一个沙箱管理器来运行代码import docker import os from typing import Optional class DockerSandbox: def __init__(self): self.client docker.from_env() self.container None def create_container(self): try: image, build_logs self.client.images.build( path., tagagent-sandbox, rmTrue, forcermTrue, buildargs{}, # decodeTrue ) except docker.errors.BuildError as e: print(Build error logs:) for log in e.build_log: if stream in log: print(log[stream].strip()) raise # 用安全约束创建容器并配置日志 self.container self.client.containers.run( agent-sandbox, commandtail -f /dev/null, # 保持容器运行 detachTrue, ttyTrue, mem_limit512m, cpu_quota50000, pids_limit100, security_opt[no-new-privileges], cap_drop[ALL], environment{ HF_TOKEN: os.getenv(HF_TOKEN) }, ) def run_code(self, code: str) - Optional[str]: if not self.container: self.create_container() # 在容器内执行代码 exec_result self.container.exec_run( cmd[python, -c, code], usernobody ) # 收集全部输出 return exec_result.output.decode() if exec_result.output else None def cleanup(self): if self.container: try: self.container.stop() except docker.errors.NotFound: # 容器已被移除这是预期情况 pass except Exception as e: print(fError during cleanup: {e}) finally: self.container None # 清空引用 # 使用示例 sandbox DockerSandbox() try: # 定义你的 Agent 代码 agent_code import os from smolagents import CodeAgent, InferenceClientModel # 初始化 Agent agent CodeAgent( modelInferenceClientModel(tokenos.getenv(HF_TOKEN), providertogether), tools[] ) # 运行 Agent response agent.run(Whats the 20th Fibonacci number?) print(response) # 在沙箱中运行代码 output sandbox.run_code(agent_code) print(output) finally: sandbox.cleanup()这段示例体现了 Docker 沙箱的完整安全配置思路资源限制mem_limit512m限制内存、cpu_quota50000限制 CPU、pids_limit100限制进程数权限最小化security_opt[no-new-privileges]禁止提权、cap_drop[ALL]丢弃全部 Linux capabilities、容器内以USER nobody和usernobody运行凭据注入通过environment传入HF_TOKEN避免把密钥写死在代码里资源清理cleanup()负责停止容器避免悬挂容器持续占用资源。需要说明的是仓库自带的DockerExecutorremote_executors.py默认使用python:3.12-bullseyejupyter_kernel_gateway镜像通过 Jupyter Kernel Gateway 与 WebSocket 执行代码并且会在每次启动时生成随机鉴权令牌KG_AUTH_TOKEN——教程中给出的DockerSandbox是另一种完全自建的轻量方案你可以按需二选一。仓库还提供了完整的可运行示例 examples/sandboxed_execution.py 供参考。沙箱通用最佳实践以下实践对 Blaxel、E2B、Modal、Docker 沙箱普遍适用资源管理设置内存与 CPU 上限实现执行超时监控资源使用安全以最小权限运行禁用不必要的网络访问使用环境变量存放密钥环境保持依赖最小化固定包版本如果使用基础镜像请定期更新清理始终确保资源被正确清理尤其是 Docker 容器避免悬挂容器持续消耗资源。两种沙箱路线的安全对比与选型路线一仅在沙箱中运行代码片段优点通过一个简单参数executor_typeblaxel/e2b/docker/modal即可启用无需把 API Key 传给沙箱对本地环境的保护更好配合 Blaxel 的休眠技术可实现快速执行启动 25ms缺点不支持多智能体托管 Agent仍需在本地环境与沙箱之间传输状态仅限于代码执行这一环节。路线二在沙箱中运行整个 Agent 系统优点支持多智能体整个 Agent 系统完全隔离对复杂 Agent 架构更灵活缺点需要更多手工配置可能需要把敏感 API Key 传入沙箱由于操作更复杂延迟可能更高。选型建议对于架构相对简单的多数应用路线一能在安全性与易用性之间取得良好平衡对于需要完全隔离的复杂多智能体系统路线二虽然配置成本更高但能提供更好的安全保证。总结smolagents 的安全代码执行体系是分层递进的本地场景由LocalPythonExecutor通过AST 逐节点解释 导入白名单 危险模块/函数黑名单 操作数与超时上限提供第一层防护适合可信模型的日常使用当风险容忍度较低、或涉及多智能体与高敏感业务时应切换到 Blaxel、E2B、Modal、Docker 等远程沙箱让 LLM 生成的代码在隔离环境中执行。无论选择哪一层都请牢记没有任何执行环境是绝对安全的合理的威胁建模、最小权限原则与严格的资源清理才是 Agent 应用长期安全运行的根本保障。相关阅读多智能体系统 Agent 参考文档 模型接入指南【免费下载链接】smolagents smolagents: a barebones library for agents that think in code.项目地址: https://gitcode.com/gh_mirrors/smo/smolagents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。