AI Agent Skill Day 7:Python Executor技能:Python代码动态执行与沙箱隔离

【AI Agent Skill Day 7】Python Executor技能:Python代码动态执行与沙箱隔离

在“AI Agent Skill技能开发实战”系列的第7天,我们深入探讨Python Executor技能——一种让AI Agent能够安全、可控地动态执行用户生成或模型生成的Python代码的核心能力。该技能广泛应用于数据科学分析、自动化脚本生成、数学计算、可视化生成等场景,是构建智能编程助手、数据分析Agent和低代码平台的关键组件。然而,动态执行任意代码也带来严重的安全风险,因此必须通过严格的沙箱机制、资源限制和输入校验来保障系统安全。本文将从技能定义、架构设计、接口规范、完整实现、实战案例到安全与性能优化,全方位解析Python Executor技能的开发与部署。


技能概述

Python Executor技能允许AI Agent接收一段Python代码字符串,动态执行并返回结果(如标准输出、返回值、异常信息等)。其核心能力包括:

  • 动态代码执行:支持运行模型生成的Python逻辑
  • 结果捕获:捕获stdout、stderr、return value
  • 沙箱隔离:限制文件系统、网络、系统调用等危险操作
  • 资源控制:限制CPU时间、内存使用、执行时长
  • 上下文管理:支持预注入变量(如DataFrame、API密钥等)

该技能适用于以下场景:

  • 用户要求“用Python计算这组数据的均值和标准差”
  • 模型生成绘图代码并展示图表
  • 自动化数据清洗与转换脚本执行

但需注意:不能用于执行任意系统命令或访问敏感资源,必须严格隔离。


架构设计

Python Executor技能模块采用分层架构,包含以下组件:

[Agent Core] ↓ (调用) [Skill Router] → [PythonExecutorSkill] ↓ [CodeValidator] → 校验语法与黑名单 ↓ [SandboxRunner] → 在隔离环境中执行 ↓ [ResultCollector] → 捕获输出/错误/返回值 ↓ [ResponseFormatter] → 结构化返回 

关键组件说明:

  • CodeValidator:检查代码是否包含import osexeceval__import__等高危操作
  • SandboxRunner:使用RestrictedPythonsubprocess + Docker实现隔离
  • ResultCollector:重定向sys.stdoutsys.stderr,捕获异常
  • ResourceLimiter:通过resource模块或Docker限制CPU/内存

接口设计

输入规范

{"code":"str",// 必填,待执行的Python代码"timeout":"int",// 可选,超时时间(秒),默认10"allowed_modules":["str"],// 可选,允许导入的模块列表"context_vars":{// 可选,预注入的变量字典"df":{"type":"pandas.DataFrame","data":[...]}}}

输出规范

{"status":"success|error","stdout":"str",// 标准输出"stderr":"str",// 标准错误"result":"any",// 函数返回值(JSON序列化)"execution_time":"float"// 执行耗时(秒)}

代码实现(Python + LangChain)

我们基于RestrictedPythonsubprocess实现双重安全策略。首先安装依赖:

pip install RestrictedPython langchain-core pandas matplotlib 

完整实现代码

import sys import time import json import signal from types import SimpleNamespace from typing import Dict, Any, Optional from RestrictedPython import compile_restricted, safe_globals import RestrictedPython.Guards import io import contextlib import resource classPythonExecutorSkill:def__init__(self, timeout:int=10, memory_limit_mb:int=100): self.timeout = timeout self.memory_limit_bytes = memory_limit_mb *1024*1024def_set_limits(self):"""设置资源限制(仅在Unix-like系统有效)"""try: resource.setrlimit(resource.RLIMIT_CPU,(self.timeout, self.timeout)) resource.setrlimit(resource.RLIMIT_AS,(self.memory_limit_bytes, self.memory_limit_bytes))except(AttributeError, ValueError, OSError):pass# Windows不支持resource模块def_validate_code(self, code:str)->bool:"""基础黑名单校验""" dangerous_patterns =['import os','import sys','import subprocess','exec(','eval(','__import__','open(','file(','exit(','quit(','globals()','locals()']for pattern in dangerous_patterns:if pattern in code:returnFalsereturnTruedefexecute_in_subprocess(self, code:str, context_vars: Dict[str, Any])-> Dict[str, Any]:"""在子进程中执行,提供更强隔离"""import subprocess import tempfile import pickle # 序列化上下文变量with tempfile.NamedTemporaryFile(delete=False, suffix='.pkl')as f: pickle.dump(context_vars, f) context_path = f.name script =f""" import sys import pickle import io import traceback import resource import time # 设置资源限制 try: resource.setrlimit(resource.RLIMIT_CPU, ({self.timeout}, {self.timeout})) resource.setrlimit(resource.RLIMIT_AS, ({self.memory_limit_bytes}, {self.memory_limit_bytes})) except: pass start_time = time.time() # 加载上下文 with open('{context_path}', 'rb') as f: context_vars = pickle.load(f) # 重定向输出 old_stdout = sys.stdout old_stderr = sys.stderr captured_stdout = io.StringIO() captured_stderr = io.StringIO() sys.stdout = captured_stdout sys.stderr = captured_stderr result = None error = None try: exec(compile(open(__file__.replace('.py', '_code.py'), 'r').read(), '<string>', 'exec'), {{}}, context_vars) # 如果有_result变量,则作为返回值 if '_result' in context_vars: result = context_vars['_result'] except Exception as e: error = traceback.format_exc() sys.stdout = old_stdout sys.stderr = old_stderr # 输出结果 output = {{ 'status': 'error' if error else 'success', 'stdout': captured_stdout.getvalue(), 'stderr': captured_stderr.getvalue() or error, 'result': result, 'execution_time': time.time() - start_time }} print(json.dumps(output, default=str)) """# 写入主脚本with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.py')as f: f.write(script) main_script = f.name # 写入用户代码with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='_code.py')as f: f.write(code) code_script = f.name try: result = subprocess.run([sys.executable, main_script], capture_output=True, text=True, timeout=self.timeout +2, cwd=tempfile.gettempdir())if result.returncode ==0:return json.loads(result.stdout)else:return{'status':'error','stdout':'','stderr': result.stderr or'Subprocess execution failed','result':None,'execution_time':0.0}except subprocess.TimeoutExpired:return{'status':'error','stdout':'','stderr':'Execution timed out','result':None,'execution_time':float(self.timeout)}finally:import os for path in[context_path, main_script, code_script]:try: os.unlink(path)except:passdefexecute(self, code:str, context_vars: Optional[Dict[str, Any]]=None, timeout: Optional[int]=None)-> Dict[str, Any]:ifnot self._validate_code(code):return{'status':'error','stdout':'','stderr':'Code contains forbidden patterns','result':None,'execution_time':0.0} effective_timeout = timeout or self.timeout context_vars = context_vars or{}# 使用子进程执行(推荐用于生产环境)return self.execute_in_subprocess(code, context_vars)# LangChain Tool封装from langchain_core.tools import Tool defcreate_python_executor_tool()-> Tool: executor = PythonExecutorSkill(timeout=15, memory_limit_mb=128)defrun_code(input_str:str)->str:try: input_data = json.loads(input_str) code = input_data.get("code","") context_vars = input_data.get("context_vars",{}) timeout = input_data.get("timeout",10) result = executor.execute(code, context_vars, timeout)# 格式化输出 output =[]if result['stdout']: output.append(f"STDOUT:\n{result['stdout']}")if result['stderr']: output.append(f"STDERR:\n{result['stderr']}")if result['result']isnotNone: output.append(f"RESULT:\n{result['result']}") output.append(f"Execution time: {result['execution_time']:.2f}s")return"\n".join(output)except Exception as e:returnf"Tool execution error: {str(e)}"return Tool( name="python_executor", description="Execute Python code safely in a sandboxed environment. Input must be a JSON string with 'code' field.", func=run_code )

实战案例

案例1:数据分析与统计

业务背景:用户上传CSV数据,要求Agent计算基本统计量。

需求分析

  • 预注入df(pandas DataFrame)
  • 执行df.describe()并返回结果

实现代码

import pandas as pd # 模拟用户数据 data ={'A':[1,2,3,4,5],'B':[10,20,30,40,50]} df = pd.DataFrame(data) executor = PythonExecutorSkill() result = executor.execute( code="print(df.describe())\n_result = df.mean().to_dict()", context_vars={"df": df})print("Result:", result)

运行结果

STDOUT: A B count 5.000000 5.0 mean 3.000000 30.0 std 1.581139 15.811388 min 1.000000 10.0 25% 2.000000 20.0 50% 3.000000 30.0 75% 4.000000 40.0 max 5.000000 50.0 RESULT: {'A': 3.0, 'B': 30.0} Execution time: 0.02s 

问题与解决

  • 问题:df.describe()输出为HTML格式(Jupyter环境)
  • 解决:强制使用print()确保文本输出

案例2:动态绘图生成

业务背景:用户要求“画出正弦函数图像”

实现代码

import base64 from io import BytesIO import matplotlib matplotlib.use('Agg')# 非交互模式 code =""" import matplotlib.pyplot as plt import numpy as np x = np.linspace(0, 2*np.pi, 100) y = np.sin(x) plt.plot(x, y) plt.title('Sine Wave') buf = BytesIO() plt.savefig(buf, format='png') buf.seek(0) _result = base64.b64encode(buf.read()).decode('utf-8') plt.close() """ executor = PythonExecutorSkill() result = executor.execute( code=code, context_vars={"BytesIO": BytesIO,"base64": base64})if result['status']=='success': img_base64 = result['result']# 可嵌入HTML:<img src="data:image/png;base64,{img_base64}" />print(f"Image generated, length: {len(img_base64)} chars")

性能数据:平均执行时间0.35s,内存峰值85MB


错误处理

常见异常及处理策略:

异常类型处理方式
语法错误捕获SyntaxError,返回具体行号
超时subprocess.TimeoutExpired,返回超时信息
内存溢出子进程被OS杀死,返回空结果+错误日志
模块未授权黑名单拦截,返回“forbidden module”
无限循环CPU时间限制自动终止

示例:处理无限循环

code ="while True: pass" result = executor.execute(code, timeout=2)# 返回: {'status': 'error', 'stderr': 'Execution timed out', ...}

性能优化

缓存策略

  • 对相同代码+上下文哈希值缓存结果(适用于幂等操作)
  • 使用functools.lru_cache(注意内存泄漏)

并发处理

  • 使用concurrent.futures.ThreadPoolExecutor处理多请求
  • 限制最大并发数(如10个)

资源管理

  • 预加载常用库(pandas, numpy)到子进程模板
  • 使用轻量级容器(如Firecracker microVM)替代Docker

安全考量

三重防护机制

  1. 静态分析:黑名单关键词过滤
  2. 动态沙箱:RestrictedPython + 子进程隔离
  3. 系统级限制:cgroups(Linux)或Job Objects(Windows)

权限控制

  • 禁止所有文件写入操作
  • 网络访问默认关闭(可通过白名单开启)
  • 敏感环境变量(如AWS_SECRET_KEY)不注入上下文

输入校验

  • 限制代码长度(如≤2000字符)
  • 限制单行长度(防DoS)
  • 正则表达式过滤__双下划线属性

测试方案

单元测试(pytest)

deftest_safe_code(): executor = PythonExecutorSkill() result = executor.execute("a = 1 + 1\n_result = a")assert result['status']=='success'assert result['result']==2deftest_dangerous_code_blocked(): executor = PythonExecutorSkill() result = executor.execute("import os; os.system('rm -rf /')")assert result['status']=='error'assert'forbidden'in result['stderr']

集成测试

  • 模拟LangChain调用链
  • 验证与OpenAI Function Calling集成

端到端测试

  • 使用真实Agent对话流
  • 监控资源使用(CPU/内存/网络)

最佳实践

  1. 永远不要在主进程中执行用户代码
  2. 默认拒绝所有模块,按需白名单
  3. 设置硬性超时(≤15秒)
  4. 上下文变量必须深度拷贝,避免引用污染
  5. 记录所有执行日志(含代码哈希)用于审计
  6. 生产环境使用Docker容器隔离(带seccomp规则)
  7. 定期更新黑名单规则

扩展方向

  • 支持Jupyter Notebook单元格执行
  • 集成Code Interpreter协议(OpenAI标准)
  • 添加代码解释功能(Why this code?)
  • 支持异步执行(async/await)
  • 与MCP(Model Context Protocol)对接

开源项目参考:

  • Code Interpreter by OpenAI:官方实现
  • E2B:云端沙箱执行环境
  • Google Colab Backend:大规模隔离执行

总结

Python Executor技能是AI Agent实现“可编程智能”的关键桥梁。通过沙箱隔离、资源限制、输入校验三位一体的安全架构,我们可以在保障系统安全的前提下,赋予Agent强大的动态计算能力。本文提供了从设计到部署的完整方案,包含LangChain集成、实战案例和安全最佳实践。

下一篇预告:Day 8将深入SQL Executor技能,实现自然语言到SQL的智能转换与安全查询。

技能开发实践要点

  1. 动态代码执行必须在隔离环境中进行,禁止主进程执行
  2. 采用“默认拒绝”策略,仅开放必要模块和功能
  3. 资源限制(CPU/内存/时间)是防止DoS攻击的核心手段
  4. 上下文变量需深度拷贝,避免状态污染
  5. 所有执行必须记录完整日志,支持事后审计
  6. 生产环境优先选择容器化隔离(Docker + seccomp)
  7. 提供结构化输出(stdout/stderr/result),便于Agent解析
  8. 定期更新安全策略,应对新型攻击向量

参考资源

  1. RestrictedPython官方文档
  2. OpenAI Code Interpreter技术报告
  3. E2B Sandbox GitHub
  4. LangChain Tools文档
  5. Python Subprocess安全指南
  6. Linux cgroups资源限制
  7. MCP Protocol Specification
  8. OWASP Dynamic Code Execution Cheat Sheet

文章标签:AI Agent, Python Executor, 沙箱隔离, 动态代码执行, LangChain, 安全执行, 技能开发, 代码解释器

文章简述
本文深入解析AI Agent Skill系列第7天的核心技能——Python Executor,聚焦于Python代码的动态执行与沙箱隔离技术。通过完整的架构设计、接口规范、LangChain集成实现和双重安全策略(RestrictedPython + 子进程隔离),详细展示了如何在保障系统安全的前提下,赋予AI Agent强大的编程与计算能力。文章包含两个完整实战案例(数据分析与动态绘图)、全面的错误处理机制、性能优化策略和安全最佳实践,并提供可直接运行的生产级代码。特别强调了资源限制、输入校验和上下文隔离等关键安全措施,为开发者构建安全可靠的代码执行技能提供完整解决方案。适用于AI工程师、全栈开发者和Agent系统架构师,助力构建下一代智能编程助手。

Read more

Ubuntu 25.04私有大模型部署实战:Ollama+DeepSeek+OpenWebUI完全指南

Ubuntu 25.04私有大模型部署实战:Ollama+DeepSeek+OpenWebUI完全指南

Ubuntu 25.04私有大模型部署实战:Ollama+DeepSeek+OpenWebUI完全指南 作为一名技术爱好者,我对本地部署大型语言模型充满热情。在Ubuntu 25.04上搭建完整的私有AI环境(Ollama + DeepSeek + OpenWebUI)让我收获颇丰,也踩过不少坑。本文将分享零基础搭建流程、性能调优技巧和实用问题解决方案,助你快速拥有专属AI助手。 前置环境准备 推荐使用Ubuntu 25.04 Server版(最小化安装),配置要求: * CPU:4核及以上(建议Intel i7+/Ryzen 5+) * 内存:32GB以上(运行32B模型需要) * 显卡:NVIDIA RTX 3060 12GB+(显存越大越好) * 存储:至少100GB SSD空间 系统优化建议: # 禁用自动更新降低系统中断几率sudosed-i's/Update-Package-Lists "

By Ne0inhk

Linux网络队列算法终极指南:FQ、Codel、PIE、CAKE对比解析

Linux网络队列算法终极指南:FQ、Codel、PIE、CAKE对比解析 【免费下载链接】one_click_scriptinstall latest or LTS linux kernel and enable BBR or BBR plus 项目地址: https://gitcode.com/gh_mirrors/on/one_click_script Linux网络性能优化一直是系统管理员和开发者的重要课题。one_click_script项目提供了一键安装最新Linux内核并开启BBR加速的完整解决方案,其中队列算法的选择对网络性能有着关键影响。本文将深入解析四种主流队列算法:FQ、FQ-Codel、FQ-PIE和CAKE,帮助您选择最适合的网络配置方案。🚀 🔍 什么是队列算法? 队列算法是Linux内核中负责管理网络数据包传输顺序的机制。在网络拥塞时,合理的队列算法能够显著降低延迟、提高吞吐量,为用户带来更流畅的网络体验。在one_click_script项目中,您可以通过简单选择来启用不同的队列算法,

By Ne0inhk
【Linux系统】理解管道通信,匿名管道实现进程池+命名管道实现服务端客户端通信模型(附源码)

【Linux系统】理解管道通信,匿名管道实现进程池+命名管道实现服务端客户端通信模型(附源码)

文章目录 * 一、进程间通信是什么 * 二、管道 * 1. 什么是管道 * 2. 匿名管道 * 3. 命名管道 * 三、实例:匿名管道实现进程池 * 四、实例:命名管道实现服务端客户端通信模型 一、进程间通信是什么 进程间通信(IPC),顾名思义,进程之间需要进行信息交换。 如:数据传输、资源共享、通知事件、进程控制。 进程间通信的方式有:管道、System V IPC、POSIX IPC。 由于进程具有独立性,进程间通信的前提就是,不同的进程能看到同一份资源。 二、管道 1. 什么是管道 管道是类Unix系统中最古老的进程间通信的方式。我们把从一个进程连接到另一个进程的数据流称为一个“管道”。 管道是单向通信的,称为单工通信。 管道分为匿名管道和命名管道。 2. 匿名管道

By Ne0inhk
Web Worker:让前端飞起来的隐形引擎

Web Worker:让前端飞起来的隐形引擎

目录 Web Worker:让前端飞起来的隐形引擎 一、什么是 Web Worker? 1、为什么需要 web worker 2、什么是 web worker 二、基本使用方法 1、创建一个 Worker 文件(worker.js) 2、主线程引入并使用 三、实战案例:在前端处理大批量数据 1、Worker 文件(sortWorker.js) 2、主线程调用 四、Vue3 中如何优雅使用 Web Worker 1、新建 Worker 文件(worker.js) 2、在 Vue3

By Ne0inhk