news 2026/9/19 19:02:56

OpenResearch 接入 Modal 无服务器 GPU:用 `orx exp run --backend modal` 按秒计费跑实验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenResearch 接入 Modal 无服务器 GPU:用 `orx exp run --backend modal` 按秒计费跑实验
  • 人工智能
  • AI Agent
  • 深度研究
  • 自主智能体
  • Agent 编排

【免费下载链接】OpenResearch

Turn your coding agents into research agents

项目地址:https://gitcode.com/GitHub_Trending/op/OpenResearch
点击查看免费下载

OpenResearch(orx)把编码 Agent 变成研究 Agent,而实验的算力可以按需落到 Modal 的临时 Sandbox 上:--backend modal会在你的 Modal 账户里拉起一个按秒计费的临时沙箱,跑完即释放、缩到零。本文以 agent-skills/orx-compute/references/modal.md 为骨架,结合仓库中的 Rust 后端实现(src/jobs/modal.rs、src/local/modal.rs),完整讲清楚 flavor 选型、鉴权配置、超时与镜像控制、快照传输,以及提交后由orx supervise守护的完整生命周期。

一、Modal 后端是什么:临时沙箱 + 按秒计费

Modal 后端是orx九种计算后端(hf / modal / k8s / ssh / slurm / ray / openresearch / tinker / local,见 agent-skills/orx-compute/SKILL.md)中的一种。它的本质是:

  • 临时 Sandboxorx在用户的 Modal 账户中创建一次性沙箱执行实验,任务结束即销毁,无需维护常驻节点;
  • 按秒计费:费用记在发起实验的 Modal 账户上,从提交到结束按秒累积;
  • 无需仓库访问权orx把记录的 commit 打成不可变快照传进 Sandbox,Modal 侧不需要访问你的 Git 仓库。

从 src/local/modal.rs 可以看到,所有orx沙箱统一归组在一个名为openresearch的 Modal App 下,提交后可在 Modal 控制台按 App 查看;同时代码会给沙箱打上or_runor_experimentor_project三类标签(src/local/modal.rs),方便事后按实验维度检索。

使用边界:只有当用户明确要求 Modal、或 Modal 已是配置默认后端时才使用它。已连接凭据本身不构成切换到 Modal 的信号。

二、快速上手:四行命令覆盖主流场景

原文档给出的核心用法如下,覆盖单卡 GPU、大显存、多卡与纯 CPU 四种典型场景:

orx exp run <expId> --backend modal --flavor a10g orx exp run <expId> --backend modal --flavor a100-80gb --timeout 8h orx exp run <expId> --backend modal --flavor h100:2 orx exp run <expId> --backend modal --flavor cpu --image python:3.12

对照仓库实现(src/local/modal.rs),--backend modal强制要求--flavor

--backend modal requires --flavor: a Modal GPU (t4, l4, a10g, a100, a100-80gb, l40s, h100, h200, or e.g. h100:2 for a count) — or cpu / cpu-large for CPU-only. Priced per second on your Modal account.

提交成功后,命令行会输出 Modal 沙箱 id(同时是后续重挂载的句柄)、run id 与跟进提示(src/local/modal.rs):

✓ Modal sandbox submitted. sandbox sb-xxxxxxxx (a10g) run <runId>

参数速查表

参数取值/含义说明
--backend modal后端标识固定值,orx exp run的通用启动入口(见 src/compute.rs 中ModalCompute适配器)
--flavorGPU:t4l4a10ga100a100-80gbl40sh100h200;多卡追加:N(如h100:2);CPU:cpucpu-large必填,除非配置默认已含 flavor
--timeout整个 Sandbox 的墙钟上限默认4 小时,覆盖整个 Sandbox 而非单条命令
--image自定义容器镜像覆盖默认的 CUDA PyTorch 或 CPU Python 镜像

三、flavor 到资源的映射:多卡与 CPU 规格的底层实现

--flavor的解析逻辑在 src/jobs/modal.rs 的resolve_flavor中,规则如下:

  • cpu开头(含cpu本身)→不分配 GPU
    • cpu-large映射为8 vCPU + 32 GiB 内存
    • cpu-xlarge映射为16 vCPU + 64 GiB 内存(源码中同样被strip_prefix("cpu-")/cpu_分支覆盖);
    • 其他cpu-*走 Modal 默认资源配置;
  • 其余任意名称一律按Modal GPU 规格透传并转大写,因此a100-80gbh100:2均可直接工作;h100:2中的:N即多卡数量,最终会以gpu参数传给modal.Sandbox.create
// src/jobs/modal.rs — flavor 解析核心 if n.eq_ignore_ascii_case("cpu") { /* 无 GPU */ } if let Some(rest) = n.strip_prefix("cpu-").or_else(|| n.strip_prefix("cpu_")) { match rest.to_ascii_lowercase().as_str() { "large" => (Some(8.0), Some(32768)), // cpu-large: 8 vCPU / 32 GiB "xlarge" => (Some(16.0), Some(65536)), // cpu-xlarge: 16 vCPU / 64 GiB _ => (None, None), } } ModalResources { gpu: Some(n.to_ascii_uppercase()), .. }

默认镜像:GPU 与 CPU 分开

default_image(src/jobs/modal.rs)按是否分配 GPU 选择默认容器:

场景默认镜像
GPUpytorch/pytorch:2.6.0-cuda12.4-cudnn9-runtime
CPUpython:3.12

--image可覆盖默认值;选择镜像时注意与--flavor的 GPU 型号匹配(CUDA 驱动、PyTorch 构建版本)。这个默认策略与 HF、k8s 后端同源(src/commands/exp.rs),保证了多后端行为一致。

超时:--timeout的解析与默认值

原文档明确timeout 默认 4 小时,且覆盖整个 Sandbox。仓库实现(src/local/modal.rs)确认默认值4 * 3600秒;若显式传入--timeout,则复用与 HF 后端一致的parse_timeout(src/jobs/huggingface.rs),支持s/m/h/d后缀及裸秒数:

--timeout 90s # 90 秒 --timeout 30m # 30 分钟 --timeout 4h # 4 小时(默认值) --timeout 1d # 1 天

超时以秒为单位写入ModalJobSpec.timeout_seconds,最终成为modal.Sandbox.create(..., timeout=...)的参数(src/jobs/modal.rs)。为长任务设置合理超时是防止沙箱无限占用的关键护栏——无超时任务在 orx 的 HF/k8s 路径同样被视为隐患。

四、鉴权与运行环境:orx如何免配置使用 Modal

凭据来源(三选一,按优先级)

Modal 鉴权使用MODAL_TOKEN_ID/MODAL_TOKEN_SECRET,或由modal token new生成的凭据。resolve_token(src/jobs/modal.rs)给出完整解析链:

  1. 进程环境变量MODAL_TOKEN_ID/MODAL_TOKEN_SECRET(优先级最高);
  2. 同步环境文件~/.openresearch/env,即orx upSettings → Environment 中配置的变量);
  3. ~/.modal.toml配置文件:读取当前激活 profile 的token_id/token_secret字段;多 profile 且未激活时会要求先执行modal profile activate(对应测试见 src/jobs/modal.rs),也支持MODAL_PROFILE环境变量指定 profile。

modal_auth_env(src/jobs/modal.rs)只在进程环境缺失时才注入同步环境文件中的 token,避免覆盖外部传入的凭据。

托管 Python 环境:首次启动自动就绪

Modal 通过 Python SDK 驱动(没有通用 REST 提交接口),因此orx在首次启动时自动在配置目录的envs/modal下创建 venv 并安装modalSDK(ensure_env,src/jobs/modal.rs),整个过程幂等:

  • 已设置$ORX_MODAL_PYTHON信任用户解释器,不做任何初始化
  • 否则检查托管 venv 是否已存在且能import modal
  • 都没有时,用系统python3建 venv →pip install modal→ 校验导入;
  • 缺失 Python 3 或导入失败时给出可操作报错,并提示删除托管环境强制重建。

因此你不需要手动pip install modal;解释器解析顺序为$ORX_MODAL_PYTHON→ orx 托管 venv → 系统python3(src/jobs/modal.rs)。启动前的preflight(src/jobs/modal.rs)会做三重检查:环境可用性、SDK 可导入、token 已配置,任一不满足即提前失败,避免提交阶段才报错。

沙箱环境变量:密钥走临时 Secret

提交时,orx会把用户在 Settings → Environment 中同步的全部环境变量带入沙箱,并自动补入HF_TOKEN(若本地可解析);但这些密钥不会通过明文的env参数传递,而是封装成modal.Secret.from_dict(env)临时 Secret(src/jobs/modal.rs),避免密钥明文暴露在 Modal 控制台。同时default_python_env(src/jobs/mod.rs)会注入PYTHONUNBUFFERED=1PYTHONIOENCODING=utf-8,保证日志实时流出、编码稳定。

五、快照传输:Modal 永远不需要访问你的仓库

所有后端的通用契约是:orx exp run只运行记录的 commit 的不可变源码快照(agent-skills/orx-compute/SKILL.md)。Modal 路径的具体实现:

  1. 提交前用git archive把实验分支的HEAD打成 tar 包;
  2. 以 SHA-256 摘要内容寻址存放于source-snapshots/目录(SourceSnapshot::create,src/compute.rs);
  3. 提交后由 launcher 通过sb.filesystem.copy_from_local(spec["sourceArchive"], "/tmp/orx-source.tar")拷入沙箱,并用.ready哨兵文件通知脚本解包(src/jobs/modal.rs);
  4. 沙箱内的执行脚本gated_script(src/compute.rs)等待.ready后解包、进入repo/目录执行 run command,stderr 合并进 stdout 一并流回(src/jobs/modal.rs)。

这意味着:未提交的文件不会被运行;任何后端都不依赖 GitHub push。快照的 digest、路径、大小会写入 run 的BackendDescriptor(src/jobs/mod.rs),即使orx supervise重启也能从本地恢复句柄(recover_submission_handle,src/compute.rs),与工作区状态解耦。

六、启动前的三件事:run command、commit 与通用契约

即使使用 Modal 后端,也必须遵守 orx-compute 通用启动契约:

  1. 一律用orx exp run启动:不要直接调用 Modal CLI、训练命令或裸 Python。直接任务不受追踪,可能运行非记录 commit 的代码;
  2. 保持 run command 固定:在基线实验上一次性设置,后续通过子分支改代码/配置;未设置时先执行orx project edit <projectId> --run-command '<cmd>'
  3. 先 commit 再启动:快照只包含已记录 commit 的内容。

orx exp run会把 run 排入队列并立即返回,随后用orx runsorx logsorx exp waitorx exp wake跟进。同一实验已有在飞 run 时会拒绝重复启动,需--force才能刻意并发(src/compute.rs 的reserve_run锁)。

七、提交之后:orx supervise的守护生命周期

提交后请勿杀掉orx supervise进程——它是整个运行期的守护者。orx exp run --backend modal会通过spawn_detached_supervise(src/commands/exp.rs)拉起一个完全脱离终端(独立进程组、无 stdio)的orx supervise <runId>

orx supervise的 Modal 分支(run_modal,src/commands/supervise.rs)依赖 Modal Sandbox 的可重挂载特性:modal.Sandbox.from_id(sandboxId)允许从全新进程重新连回同一沙箱(src/jobs/modal.rs),这与 HF 的 SSE、k8s 的kubectl logs -f是同一套"重放 + 去重"模型:

  • 状态轮询:每 5 秒调用status <sandboxId>,把RUNNING / COMPLETED / ERROR映射到本地 run 状态(src/jobs/modal.rs);
  • 日志流logs <sandboxId>每次连接都从起点重放合并 stdout,supervise按已消费行数skip去重后写入本地日志文件;日志静默 30 秒即重新检查状态(src/commands/supervise.rs);
  • 取消orx exp cancel <expId>先在本地登记取消意图,supervisor 观察到后调用terminate()终止沙箱(src/jobs/modal.rs);
  • 重启幂等:supervisor 带锁运行,重启后从本地 store + 提交句柄恢复,不会重复提交或重复记账(src/commands/supervise.rs)。

等待与休眠:wait 与 wake

  • orx exp wait <expId>:阻塞直到 run 进入终态;--interval默认 5 秒、--timeout默认 1800 秒,超时以非零码退出(表示"尚无变化"而非失败,src/commands/exp.rs)。--project模式是预算循环原语:任一 run 完成即返回,适合饱和调度;
  • orx exp wake <expId>:结束当前轮次、在 run 成功或失败后恢复,仅在本地orx upAgent 会话中可用;wait 与 wake 二选一。

八、Sizing 建议:怎么选 flavor

结合 SKILL.md 的 Sizing 指南 与 Modal 按秒计费的特点:

  1. 先定 GPU 还是 CPU:API 驱动评测、数据准备类任务在cpu/cpu-large上更便宜,完全不需要 GPU;
  2. 选能装下模型 + 最小 batch 的最小规格:从t4/l4/a10g这类入门卡起步,而不是一上来就h100
  3. 遇到真实 OOM 或慢到无法忍受再升级:先看orx logs <runId>确认真实瓶颈,再考虑a100-80gbh100:2多卡;
  4. 只为真正长跑的任务加--timeout:默认 4 小时已覆盖大多数实验,超时越大潜在计费风险越高。

多卡场景注意:h100:2这种:N语法透传的是 Modal GPU 数量规格,适用分布式训练或显存聚合场景;单机多卡与单卡的选择取决于你的训练脚本设计。

九、常见问题速查

现象原因与处理
报错--backend modal requires --flavor未传 flavor 且配置默认不含 flavor;按上表补--flavor
报错The modal Python package isn't importable解释器里没有modalSDK;设置ORX_MODAL_PYTHON指向已装 SDK 的解释器,或删除托管 venv 目录让其重建(launcher 退出码 3 专门标识此场景,src/jobs/modal.rs)
报错No Modal token configured执行modal token new,或在进程环境 / Settings → Environment 设置MODAL_TOKEN_IDMODAL_TOKEN_SECRET
提示存在在飞 run,拒绝启动同实验已有非终态 run;orx exp cancel <expId>或显式--force
orx exp wait超时退出非零退出只表示"这段时间内状态没变",用orx runs <projectId>核对真实状态
沙箱跑完即消失正常现象:Modal Sandbox 用完即释放、缩到零,账单按秒累计
想找历史沙箱在 Modal 控制台按openresearchApp 或or_experiment标签检索

十、源码地图:进一步阅读

  • 后端实现:src/jobs/modal.rs(launcher、flavor 解析、默认镜像、preflight、token 解析)
  • 本地提交入口:src/local/modal.rs(--flavor强校验、4h 默认超时、快照与标签装配)
  • 后端注册与预检:src/compute.rs(ModalCompute适配器:requires-flavor + preflight)
  • 守护进程:src/commands/supervise.rs(Modal 分支run_modal、轮询/日志/取消)
  • 等待与取消:src/commands/exp.rs(exp waitspawn_detached_supervise、cancel 锁)
  • 技能入口与通用契约:agent-skills/orx-compute/SKILL.md

至此,你已经掌握了 Modal 后端从选型、鉴权、提交到守护的完整链路:一条orx exp run --backend modal --flavor <规格>命令,就能把你的研究实验按秒计费地跑在 Modal 临时沙箱上,跑完自动释放,全程由不可变快照保证可复现性。

  • 人工智能
  • AI Agent
  • 深度研究
  • 自主智能体
  • Agent 编排

【免费下载链接】OpenResearch

Turn your coding agents into research agents

项目地址:https://gitcode.com/GitHub_Trending/op/OpenResearch
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 19:02:17

方波信号的傅里叶分解与合成:MATLAB实验与吉布斯现象解析

简介&#xff1a;信号与系统课程中关于信号分解与合成实验的完整报告文档&#xff0c;适合电子信息类本科在读学生、备考或开展信号处理实验的初学者使用。内容依托实验五任务&#xff0c;涵盖方波与三角波信号的分解、各次谐波提取与再合成&#xff0c;并给出示波器观测结果、…

作者头像 李华
网站建设 2026/9/19 19:01:50

Nimmake:面向MCU的跨架构固件构建DSL工具

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

作者头像 李华
网站建设 2026/9/19 19:00:49

初中物理电路教学:从串并联定义到实物连接诊断

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

作者头像 李华
网站建设 2026/9/19 18:58:14

ESP32多芯片适配本质:引脚、时钟与内存三重硬件契约

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

作者头像 李华
网站建设 2026/9/19 18:58:04

BrewUI 实战指南:给 Homebrew 配上图形化界面的完整方案

第一次注意到 BrewUI 这个项目&#xff0c;是在翻开源社区作品列表时无意撞见的。那会儿我刚好被 Homebrew 的命令行参数折腾得有点烦&#xff1a;明明只是想装个软件&#xff0c;却要记住brew search、brew install、brew services start这一连串命令&#xff1b;想看看哪些依…

作者头像 李华