DeepAgents 使用 Docker 沙箱执行 Shell 命令:从 0 到生产部署
DeepAgents 使用 Docker 沙箱执行 Shell 命令从 0 到生产部署一、为什么需要 Docker 沙箱DeepAgents 的execute工具只有在后端实现SandboxBackendProtocol时才会真正执行 Shell 命令。项目里默认使用的FilesystemBackend只负责文件读写不支持执行命令。所以当 Skill 里出现下面这类指令时Agent 无法真正运行脚本python skills/bank-asset-allocation-report/scripts/generate_report.py...直接换成LocalShellBackend虽然简单但它会在宿主机上执行任意命令存在安全风险不适合作为 Web API 的生产方案。更稳妥的做法是使用 Docker 沙箱把命令执行、文件读写都隔离到容器里。二、DeepAgents 沙箱实现原理DeepAgents 已经提供了BaseSandbox它基于两个核心能力实现了大部分文件操作execute()在沙箱里执行 Shell 命令upload_files()把文件传入沙箱download_files()从沙箱取回文件id沙箱唯一标识ls、read、write、edit、grep、glob这些方法都由BaseSandbox自动实现不需要我们重写。三、Docker 镜像准备新建app/docker/DockerfileFROM python:3.12-slim RUN apt-get update \ apt-get install -y --no-install-recommends grep \ rm -rf /var/lib/apt/lists/* WORKDIR /workspace # 如果沙箱里要运行项目脚本就安装项目依赖 RUN pip install --no-cache-dir reportlab python-docx markdown openpyxl pypdf # 容器必须有一个长驻进程否则启动后会立即退出 CMD [python3, -c, import time; time.sleep(1e9)]构建镜像dockerbuild-tdeepagents-sandbox:latest-fapp/docker/Dockerfile.说明python:3.12-slim和项目要求一致。项目pyproject.toml要求3.12,3.13本地.venv也是 Python 3.12。CMD里的time.sleep(1e9)大约 31.7 年对开发环境来说等于常驻而且不占用 CPU。如果容器启动后显示Exited (0)多半是启动命令是裸的python3Python 解释器没有输入就退出了。四、实现 DockerSandbox这里推荐使用 Docker CLI 实现不依赖 Python 的docker包。这样做有两个好处避免项目根目录docker/文件夹和 pip 包docker重名。不需要额外安装 Python SDK生产环境只需要 Docker CLI。新建app/docker/docker_sandbox.pyfrom__future__importannotationsimportioimportsubprocessimporttarfileimporttimeimportuuidfromdeepagents.backends.protocolimport(ExecuteResponse,FileDownloadResponse,FileUploadResponse,)fromdeepagents.backends.sandboximportBaseSandboxclassDockerSandbox(BaseSandbox):def__init__(self,*,image:strpython:3.12-slim,workdir:str/workspace,timeout:int120,volumes:dict|NoneNone,)-None:self._workdirworkdir self._timeouttimeout namefdeepagents-{uuid.uuid4().hex[:8]}cmd[docker,run,-d,--name,name,--workdir,workdir]forhost_path,cfgin(volumesor{}).items():cmd[-v,f{host_path}:{cfg[bind]}:{cfg.get(mode,rw)}]cmd[image,python3,-c,import time; time.sleep(1e9)]procsubprocess.run(cmd,capture_outputTrue,textTrue)ifproc.returncode!0:raiseRuntimeError(fdocker run failed:{proc.stderr})self._container_idproc.stdout.strip()propertydefid(self)-str:returnself._container_id[:12]defexecute(self,command:str,*,timeout:int|NoneNone)-ExecuteResponse:procsubprocess.run([docker,exec,-w,self._workdir,self._container_id,/bin/sh,-c,command,],capture_outputTrue,encodingutf-8,errorsreplace,timeouttimeoutiftimeoutisnotNoneelseself._timeout,)returnExecuteResponse(outputproc.stdoutproc.stderr,exit_codeproc.returncode,)defupload_files(self,files:list[tuple[str,bytes]])-list[FileUploadResponse]:responses[]forpath,contentinfiles:ifnotpath.startswith(/)or..inpath:responses.append(FileUploadResponse(pathpath,errorinvalid_path))continuebufio.BytesIO()withtarfile.open(fileobjbuf,modew)astar:infotarfile.TarInfo(path.lstrip(/))info.sizelen(content)info.mtimeint(time.time())tar.addfile(info,io.BytesIO(content))procsubprocess.run([docker,cp,-,f{self._container_id}:/],inputbuf.getvalue(),capture_outputTrue,)ifproc.returncode0:responses.append(FileUploadResponse(pathpath))else:responses.append(FileUploadResponse(pathpath,errorproc.stderr.decode(errorsreplace).strip()orupload_failed,))returnresponsesdefdownload_files(self,paths:list[str])-list[FileDownloadResponse]:responses[]forpathinpaths:ifnotpath.startswith(/)or..inpath:responses.append(FileDownloadResponse(pathpath,errorinvalid_path))continueprocsubprocess.run([docker,cp,f{self._container_id}:{path},-],capture_outputTrue,)ifproc.returncode!0:errproc.stderr.decode(errorsreplace)errorfile_not_foundifnot foundinerr.lower()orno suchinerr.lower()elseerr.strip()[:200]responses.append(FileDownloadResponse(pathpath,errorerror))continuetry:withtarfile.open(fileobjio.BytesIO(proc.stdout),moder:*)astar:membertar.next()ifmemberisNoneormember.isdir():responses.append(FileDownloadResponse(pathpath,erroris_directory))continuecontenttar.extractfile(member).read()responses.append(FileDownloadResponse(pathpath,contentcontent))exceptExceptionasexc:responses.append(FileDownloadResponse(pathpath,errorstr(exc)[:200]))returnresponses核心实现说明docker run -d创建长驻容器。docker exec在容器里执行命令。docker cp - 容器:/从标准输入接收 tar 包并解压到容器实现文件上传。docker cp 容器:路径 -把容器文件打包成 tar 输出到标准输出实现文件下载。五、接入主 Agent修改app/agent/main_agent.pyfromapp.docker.docker_sandboximportDockerSandbox project_root_pathPath(__file__).parents[1].resolve()(project_root_path/output).mkdir(parentsTrue,exist_okTrue)file_backendDockerSandbox(imagedeepagents-sandbox:latest,workdir/workspace,timeout120,volumes{str(project_root_path):{bind:/workspace,mode:ro},str(project_root_path/output):{bind:/workspace/output,mode:rw},},)这里把app/只读挂载到容器的/workspace把output/单独以可写方式挂载这样Agent 能在容器里读取skills/。生成的文件会写到/workspace/output/session_xxx。宿主机通过app/output/session_xxx直接看到结果。六、验证先验证基础命令dockerrun-dit--namesandbox-test deepagents-sandbox:latest python3-cimport time; time.sleep(1e9)dockerexecsandbox-test python3--version再验证 Python 后端cdD:/AI Learning/financesearch-agents .venv\Scripts\python-B-c from app.docker.docker_sandbox import DockerSandbox s DockerSandbox( imagedeepagents-sandbox:latest, workdir/workspace, volumes{ D:/AI Learning/financesearch-agents/app: {bind: /workspace, mode: ro}, D:/AI Learning/financesearch-agents/app/output: {bind: /workspace/output, mode: rw}, }, ) print(s.execute(python3 --version)) s.write(/workspace/output/test.txt, hello) print(s.read(/workspace/output/test.txt)) 验证完删除测试容器dockerrm-fsandbox-test七、Windows Docker Desktop 常见问题1. 容器启动后立即变成 Exited (0)原因通常是启动命令是裸的python3IMAGE COMMAND STATUS deepagents-sandbox:latest python3 Exited (0)Python 解释器启动后没有 stdin 输入会立即退出。解决Dockerfile 里加CMD [python3, -c, import time; time.sleep(1e9)]。手动运行时使用docker run -dit ... python3 -c import time; time.sleep(1e9)。Docker Desktop 需要处于 Linux 容器模式。2. import docker 指向了项目自己的 docker 目录如果项目根目录有docker/文件夹Python 的import docker会优先解析到这个目录module docker (namespace) from [D:\\AI Learning\\financesearch-agents\\docker]这不是 pip 包docker所以没有from_env()方法。不重命名目录的解决方案就是本文使用的方案不import docker全部改用 Docker CLI。如果一定要用 Python SDK则需要把项目根目录的docker/改名例如infra/否则会一直冲突。3. docker.from_env 需要加载 .env 吗docker.from_env()读取的是进程环境变量不是.env文件。本机 Docker Desktop 默认不需要任何环境变量。远程 Docker 时需要在启动进程前设置DOCKER_HOST、DOCKER_TLS_VERIFY、DOCKER_CERT_PATH等变量。如果这些变量写在.env里需要先调用load_dotenv()再执行docker.from_env()。4. Python 版本不一致有影响吗本地虚拟环境是 Python 3.12.13项目锁定 Python 3.12因此 Docker 镜像使用python:3.12-slim是正确选择。真正需要注意的不是版本而是平台Docker 镜像是 Debian Linux本地是 Windows。脚本里的路径和 Shell 命令要按照 Linux 书写。如果容器里要运行项目脚本必须在 Dockerfile 里安装脚本依赖。八、生产部署建议1. 每个会话使用独立容器当前DockerSandbox每次实例化都会创建新容器。Web 服务里不要让所有用户共用一个容器否则文件会互相污染。建议每个session_id对应一个容器任务结束后dockerstopcontainer_iddockerrmcontainer_id2. 限制挂载权限生产环境不要直接把整个项目目录以可写方式挂载进容器。推荐只挂载必要目录skills/只读挂载。output/可写挂载。数据库配置、密钥、上传文件不要放进容器。3. 远程 Docker部署到远程 Docker 时在服务启动前设置DOCKER_HOSTtcp://192.168.1.10:2375或者DOCKER_HOSTssh://user192.168.1.10使用 Docker CLI 方案时docker run、docker exec、docker cp都会读取同一个环境变量不会出现连接不一致的问题。4. 镜像版本固定生产环境不要使用latest建议固定到具体版本dockerbuild-tregistry.example.com/deepagents-sandbox:2026.08.10.dockerpush registry.example.com/deepagents-sandbox:2026.08.105. 安全控制不要接受不可信用户的任意 Shell 命令。有需要时配合interrupt_on{execute: True}做人工审批。容器内不要安装不必要的网络工具和调试工具。定期清理退出状态的历史容器和未使用的镜像。九、总结这套方案的核心思路是用BaseSandbox继承 DeepAgents 的沙箱接口。只实现execute、upload_files、download_files、id。使用 Docker CLI 替代 Python SDK避免项目目录重名问题。通过只读挂载和独立容器实现生产环境的基本隔离。这样既能让 Agent 真正执行 Skill 里的脚本又比直接在宿主机上执行命令安全得多。

相关新闻

工业通信核心:IEC 104规约原理、报文解析与工程实践指南

工业通信核心:IEC 104规约原理、报文解析与工程实践指南

1. 项目概述:从零开始理解工业通信的“普通话”如果你在电力、轨道交通或者工业自动化领域工作,那么“104规约”这个词你一定不陌生。它就像工业通信世界里的“普通话”,是连接监控主站(调度中心)和远方终端&#xff0…

2026/10/19 7:37:54 阅读更多 →
Unity多人求生游戏开发框架:核心模块、网络同步与性能优化全解析

Unity多人求生游戏开发框架:核心模块、网络同步与性能优化全解析

1. 项目概述:为什么需要一个现成的求生框架?如果你是一个独立开发者或者小型团队,想做一个《森林》、《Rust》或者《英灵神殿》那样的多人求生游戏,那么你肯定知道,从零开始搭建这一切有多让人头疼。资源管理系统、建造…

2026/10/19 15:51:30 阅读更多 →
深度学习进阶:从基础使用到掌握训练循环、数据增强与模型优化

深度学习进阶:从基础使用到掌握训练循环、数据增强与模型优化

1. 从“会用”到“懂用”:深度学习进阶的思维转变很多朋友在入门深度学习时,都走过这样一条路:跟着教程,安装好PyTorch或TensorFlow,跑通一个MNIST手写数字识别的Demo,看着屏幕上跳动的准确率数字&#xff…

2026/10/18 21:14:19 阅读更多 →

最新新闻

SELinux文件系统标签管理与审计日志联调排查实战指南

SELinux文件系统标签管理与审计日志联调排查实战指南

干了这么多年Linux运维,我见过太多人碰到SELinux就是一套组合拳:先setenforce 0,再改/etc/selinux/config,直接把安全机制摁死。这路子确实省事,但要是服务器被日穿了,连个审计线索都捞不着,那时…

2026/10/19 22:06:39 阅读更多 →
Android国密SM2/SM3/SM4轻量级生产级封装工具包

Android国密SM2/SM3/SM4轻量级生产级封装工具包

简介:本资源是一套面向Android开发者的国密算法实战封装库,聚焦SM2非对称加密、SM3密码杂凑与SM4对称分组加密三大核心算法,解决国产密码在移动端集成难、兼容性差、调用接口不统一等实际问题,适用于金融类App、政务平台、物联网终…

2026/10/19 22:06:39 阅读更多 →
diagram-design:从草图到系统,图表设计规范与工程实践

diagram-design:从草图到系统,图表设计规范与工程实践

1. 从一张草图到一套系统:diagram-design 到底在解决什么问题第一次听到“diagram-design”这个词,很多人会下意识觉得它只是“画图”的另一种说法。但真正在项目里被图表折磨过的人都知道,画一张图不难,难的是让这张图在三个月后…

2026/10/19 22:06:39 阅读更多 →
美赛B题O奖论文拆解:概率搜索模型与路径规划实战

美赛B题O奖论文拆解:概率搜索模型与路径规划实战

去年美赛B题一出,圈子里都在讨论那篇O奖论文。英文原题翻译成中文,核心是四个字:失联寻回。那篇O奖论文的标题如果直译,大概就是《从失联到寻回:某片海域的搜寻之旅》。我刻意不提具体海域名,因为做案例分析…

2026/10/19 22:05:38 阅读更多 →
多测站GNSS精密定位:Kriging插值与卡尔曼滤波实现网络RTK

多测站GNSS精密定位:Kriging插值与卡尔曼滤波实现网络RTK

很多人第一次看到“多测站 GNSS 精密定位 Kriging 卡尔曼滤波”这条链路时,第一反应都是:有必要这么复杂吗?单站差分、PPK、PPP不也能定位吗。我之前做某测试场的动态定位精度评估时也这么想,直到某次基准站距离用户站超过30km&…

2026/10/19 22:05:38 阅读更多 →
IM系统用户管理子服务架构设计与踩坑实践:从表结构到缓存策略

IM系统用户管理子服务架构设计与踩坑实践:从表结构到缓存策略

做IM系统,用户管理子服务往往是看起来最简单、坑却最多的那一块。很多人一开始会觉得,无非就是注册、登录、改个昵称、拉个好友列表,有什么好写的?但真正上手做才发现,一个IM系统里最基础的用户服务,牵扯到…

2026/10/19 22:03:35 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介:本资源为电源技术方向的学术研究文档,聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略,面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址: https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集,共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →