关于 modal
Modal 是面向 Python 开发者的云平台,可运行 AI/ML 工作负载、批处理任务和 Web 服务。它将无服务器函数、可配置容器、GPU 计算、持久化存储和定时任务整合到以 Python 定义的应用中。
主要功能
- 01运行无服务器 Python 函数
- 02用 Python 配置容器镜像
- 03申请 GPU 计算资源
- 04通过 Volumes 持久化存储
- 05部署 Web 端点
- 06调度并扩展工作负载
适用场景
- 01通过 Web 端点提供 AI/ML 模型服务
- 02执行 GPU 训练或推理
- 03并行处理独立的批量输入
- 04定时运行数据流水线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ k-dense-ai/ scientific-agent-skills --skill modal安装后在 Claude Code 中输入/modal确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api_reference.mdexamples.mdfunctions.mdgetting-started.mdgpu.mdimages.mdresources.mdscaling.mdscheduled-jobs.mdsecrets.mdvolumes.mdweb-endpoints.md详细介绍
Modal
概述
Modal 是一个无服务器运行 Python 代码的云平台,专注于 AI/ML 工作负载。核心能力:
- 按需提供的 GPU 算力(包括 H100、H200、B200 和 B300)
- 无服务器函数,可从零自动扩缩容到数千个容器
- 完全用 Python 代码构建的自定义容器镜像
- 通过 Volumes 实现持久化存储,用于存放模型权重和数据集
- 用于提供模型和 API 服务的 Web 端点
- 通过 cron 或固定间隔执行的定时任务
- 容器复用和预热池以减少冷启动(模型加载仍需时间)
Modal 的 App 和 Image 用 Python 定义;也支持现有的 Dockerfile。
何时使用本技能
在以下情况使用本技能:
- 在云端部署或提供 AI/ML 模型服务
- 运行 GPU 加速的计算(训练、推理、微调)
- 创建无服务器 Web API 或端点
- 并行扩展批处理任务
- 调度周期性任务(数据管道、重新训练、爬取)
- 需要为模型权重或数据集提供持久化云存储
- 希望在自定义容器环境中运行代码
- 构建任务队列或异步任务处理系统
安装与身份验证
安装
uv pip install "modal==1.6.0"
已对照 SDK 1.6.0 和最新的 release notes 进行审查。 本地 SDK 构建和部分处理器已测试;远程构建、部署、 GPU 推理和云配额未执行。使用模型/数据占位符 或第三方工作负载的代码片段仅作示例,需要其声明的依赖项。
身份验证
在创建新凭据之前,优先使用现有凭据。只有下面两个 Modal 专用
变量相关——不要读取、加载或暴露任何其他环境
变量或 .env 文件内容:
- 复用现有的 Modal profile,或检查
MODAL_TOKEN_ID和MODAL_TOKEN_SECRET是否都已设置;仅报告是否存在。 - 如果没有,只在本地
.env文件中查找这两个键(忽略所有其他条目),并在适合工作流时加载它们。 - 只有在没有可用 profile 或 token 对时,才回退到交互式
modal setup。SDK 支持.modal.toml中的 profile;当 profile 通过身份验证时,环境 token 是可选的。
modal setup
这会打开浏览器进行身份验证。对于 CI/CD 或无头环境,使用环境变量:
export MODAL_TOKEN_ID=<your-token-id>
export MODAL_TOKEN_SECRET=<your-token-secret>
如果既没有现有 profile 也没有 token 对,请在 https://modal.com/settings 创建凭据
在确定运行规模之前,查看当前定价和工作区配额。即使还有额度,使用 GPU 也需要付款方式。
参考:详细设置和首个应用演练请参见 references/getting-started.md。
核心概念
App 与函数
Modal 的 App 将相关函数分组。用 @app.function() 装饰的函数在云端远程运行:
import modal
app = modal.App("my-app")
@app.function()
def square(x):
return x ** 2
@app.local_entrypoint()
def main():
# .remote() runs in the cloud
print(square.remote(42))
用 modal run script.py 运行。用 modal deploy script.py 部署。
参考:生命周期钩子、类、.map()、.spawn() 等请参见 references/functions.md。
容器镜像
Modal 从 Python 代码构建容器镜像。推荐的包安装器是 uv:
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("torch==2.12.0", "transformers==5.9.0", "accelerate==1.13.0")
.apt_install("git")
)
@app.function(image=image, gpu="L40S")
def inference(prompt):
from transformers import pipeline
pipe = pipeline("text-generation", model="openai-community/gpt2", device=0)
return pipe(prompt)
关键的镜像方法:
.uv_pip_install()—— 用 uv 安装 Python 包(推荐).pip_install()—— 用 pip 安装(回退方案).apt_install()—— 安装系统包.run_commands()—— 在构建期间运行 shell 命令.run_function()—— 在构建期间运行 Python(例如下载模型权重).add_local_python_source()—— 添加本地模块.env()—— 设置环境变量
参考:Dockerfile、micromamba、缓存、GPU 构建步骤请参见 references/images.md。
GPU 计算
通过 gpu 参数请求 GPU:
@app.function(gpu="H100")
def train_model():
import torch
device = torch.device("cuda")
# GPU training code here
# Multiple GPUs
@app.function(gpu="H100:4")
def distributed_training():
...
# GPU fallback chain
@app.function(gpu=["H100", "A100-80GB", "A100-40GB"])
def flexible_inference():
...
可用的 GPU 字符串包括 T4、L4、A10、L40S、A100、A100-40GB、A100-80GB、RTX-PRO-6000、H100、H200、B200、B200+ 和 B300。
- GPU 始终以字符串形式指定(例如
gpu="H100"、gpu="H100:4")。旧的modal.gpu.*对象自 v0.73.31 起已弃用。 - 每个容器最多 8 个 GPU(A10 除外:最多 4 个)
- 推荐使用 L40S 进行推理(性价比均衡,48 GB 显存)
- H100/A100 可免费自动升级至 H200/A100-80GB;显式指定 A100-40GB 则选择 40 GB
- 使用
gpu="H100!"可阻止自动升级 - B300 和 B200+ 需要 CUDA 13.1+ 兼容性;RTX-PRO-6000 拥有 96 GB 显存
参考:GPU 选型指南及多 GPU 训练请参阅 references/gpu.md。
卷(持久化存储)
卷提供分布式、持久化的文件存储:
vol = modal.Volume.from_name("model-weights", create_if_missing=True)
@app.function(volumes={"/data": vol})
def save_model():
import torch
model = train_model() # Application-defined training code
# Write to the mounted path
with open("/data/model.pt", "wb") as f:
torch.save(model.state_dict(), f)
vol.commit()
@app.function(volumes={"/data": vol})
def load_model():
import torch
vol.reload()
model = MyModel() # Application-defined architecture
model.load_state_dict(torch.load("/data/model.pt", weights_only=True))
- 针对一次写入、多次读取的工作负载(模型权重、数据集)进行了优化
- 命令行访问:
modal volume ls、modal volume put、modal volume get - 每隔几秒在后台自动提交
- 对于生产者/消费者交接,在发出完成信号之前,请关闭输出文件并显式调用
vol.commit();已挂载的消费者必须先关闭其打开的卷句柄并调用vol.reload(),然后才能读取新状态。函数成功返回或后台提交计时器并不构成新鲜度检查。为并发运行分配不同的输出路径。参见提交/重载契约。 - 使用
vol.with_mount_options(read_only=True, sub_path="subset")以只读方式挂载或限制到子目录
参考:v2 卷、并发写入和最佳实践请参阅 references/volumes.md。
密钥
安全地将凭据传递给函数:
@app.function(secrets=[modal.Secret.from_name("my-api-keys")])
def call_api():
import os
api_key = os.environ["API_KEY"]
# Use the key
通过命令行创建密钥:modal secret create my-api-keys API_KEY=sk-xxx
或从 .env 文件创建:modal.Secret.from_dotenv()
参考:仪表盘设置、多个密钥和模板请参阅 references/secrets.md。
Web 端点
将模型和 API 作为 Web 端点提供服务:
web_image = modal.Image.debian_slim().uv_pip_install("fastapi[standard]==0.136.3")
@app.function(image=web_image)
@modal.fastapi_endpoint()
def predict(text: str):
return {"result": model.predict(text)}
modal serve script.py— 开发模式,支持热重载和临时 URLmodal deploy script.py— 生产部署,使用永久 URL- 支持 FastAPI、ASGI(Starlette、FastHTML)、WSGI(Flask、Django)、WebSockets
- 请求体最大 4 GiB,响应大小无限制
参考:ASGI/WSGI 应用、流式传输、认证和 WebSockets 请参阅 references/web-endpoints.md。
定时任务
按计划运行函数:
@app.function(schedule=modal.Cron("0 9 * * *")) # Daily at 9 AM UTC
def daily_pipeline():
# ETL, retraining, scraping, etc.
...
@app.function(schedule=modal.Period(hours=6))
def periodic_check():
...
使用 modal deploy script.py 部署以激活计划。
modal.Cron("...")— 标准 cron 语法,跨部署保持稳定modal.Period(hours=N)— 固定间隔,重新部署时重置- 在 Modal 仪表盘中监控运行情况
参考:cron 语法和管理请参阅 references/scheduled-jobs.md。
扩缩容与并发
Modal 会自动扩缩容器。配置限制:
@app.function(
max_containers=100, # Upper limit
min_containers=2, # Keep warm for low latency
buffer_containers=5, # Reserve capacity
scaledown_window=300, # Idle seconds before shutdown
)
def process(data):
...
使用 .map() 并行处理输入:
results = list(process.map([item1, item2, item3, ...]))
使用 @modal.concurrent 启用每个容器的并发请求处理。将
target_inputs(自动扩缩器的每容器目标值)设置为低于 max_inputs(硬性
上限),以在扩容时保留余量:
@app.function()
@modal.concurrent(max_inputs=10, target_inputs=8)
async def handle_request(req):
...
使用
Function.with_options() / Function.with_concurrency() / Function.with_batching()
(以及 Cls.with_options())在调用时重新配置已部署的函数或 Cls,而无需重新部署:
Model = modal.Cls.from_name("my-app", "Model")
fast = Model.with_options(gpu="H200", max_containers=20)
fast().generate.remote(prompt)
参考:.map()、.starmap()、.spawn() 和限制请参阅 references/scaling.md。
资源配置
@app.function(
cpu=4.0, # Physical cores (not vCPUs)
memory=16384, # MiB
ephemeral_disk=51200, # MiB (up to 3 TiB)
timeout=3600, # Seconds
)
def heavy_computation():
...
默认值:0.125 个 CPU 核心,128 MiB 内存。按 max(request, usage) 计费。
使用 cpu=(request, limit) 和 memory=(request, limit) 设置显式限制。
参考:限制和计费详情请参阅 references/resources.md。
带生命周期钩子的类
对于有状态的工作负载(例如,加载一次模型并服务多个请求):
@app.cls(gpu="L40S", image=image)
class Predictor:
@modal.enter()
def load_model(self):
self.model = load_heavy_model() # Runs once on container start
@modal.method()
def predict(self, text: str):
return self.model(text)
@modal.exit()
def cleanup(self):
... # Runs on container shutdown
调用方式:Predictor().predict.remote("hello")
沙箱
对于运行不受信任或动态生成的代码(例如,AI Agent 输出或代码解释器),请使用 modal.Sandbox —— 一个以编程方式创建和控制的隔离容器,而非装饰过的 Function:
app = modal.App.lookup("sandbox-demo", create_if_missing=True)
# Isolated container; restrict egress for untrusted workloads
sb = modal.Sandbox.create(
app=app,
image=modal.Image.debian_slim(),
block_network=True,
timeout=60,
)
try:
sb.filesystem.write_text("print(2 ** 10)\n", "/tmp/job.py")
proc = sb.exec("python", "/tmp/job.py")
output = proc.stdout.read()
proc.wait()
if proc.returncode != 0:
raise RuntimeError("Sandbox command failed")
print(output)
finally:
sb.terminate(wait=True)
- 使用沙箱的
exec方法在沙箱内运行命令(例如运行python /tmp/job.py),并从返回的进程句柄读取 stdout —— 参见references/api_reference.md - 使用
outbound_cidr_allowlist=[...]/inbound_cidr_allowlist=[...]限制连接 - SDK 1.6 中
Sandbox.create()会阻塞直到调度完成;创建可能抛出ResourceExhaustedError sb.snapshot_filesystem()返回一个默认 TTL 为 30 天的 Image,而非永久存储- 非常适合代码解释器、Agent 工具执行以及按用户隔离
常见工作流模式
GPU 模型推理服务
import modal
app = modal.App("llm-service")
image = (
modal.Image.debian_slim(python_version="3.11")
.uv_pip_install("vllm==0.21.0", "fastapi[standard]==0.136.3")
)
@app.cls(gpu="H100", image=image, max_containers=1)
class LLMService:
@modal.enter()
def load(self):
from vllm import LLM
self.llm = LLM(model="Qwen/Qwen3-8B", max_model_len=4096)
@modal.fastapi_endpoint(method="POST", requires_proxy_auth=True)
def generate(self, body: dict):
from vllm import SamplingParams
params = SamplingParams(max_tokens=256)
outputs = self.llm.generate([body["prompt"]], sampling_params=params)
return {"text": outputs[0].outputs[0].text}
批处理流水线
对独立输入使用 .map(),每次运行/分块使用唯一的输出路径,并在返回前显式提交 Volume。完整的示例批处理模式请参见 references/examples.md。
定时数据流水线
app = modal.App("etl-pipeline")
@app.function(
schedule=modal.Cron("0 */6 * * *"), # Every 6 hours
secrets=[modal.Secret.from_name("db-credentials")],
)
def etl_job():
import os
db_url = os.environ["DATABASE_URL"]
# Extract, transform, load
...
命令行参考
| 命令 | 描述 |
|---|---|
modal setup | 使用 Modal 进行身份验证 |
modal run script.py | 运行脚本的本地入口点 |
modal serve script.py | 带热重载的开发服务器 |
modal deploy script.py | 部署到生产环境 |
modal volume ls <name> | 列出卷中的文件 |
modal volume put <name> <file> | 上传文件到卷 |
modal volume get <name> <file> | 从卷下载文件 |
modal secret create <name> K=V | 创建密钥 |
modal secret list | 列出密钥 |
modal app list | 列出已部署的应用 |
modal app stop <name> | 停止已部署的应用 |
安全注意事项
- 凭据: 复用现有配置文件或两个
MODAL_TOKEN_*值进行 SDK 身份验证。仅在明确需要时读取特定于工作负载的密钥;切勿转储环境变量或将平台令牌转发到容器。 - 子进程 / 自定义服务器: 此处的一些模式(多 GPU 训练启动器、
@modal.web_server应用)在构建期间调用subprocess.run/subprocess.Popen或 shell 命令。保持参数列表固定且硬编码。切勿从未经消毒的用户输入构造子进程或 shell 参数 —— 将不受信任的值作为数据(文件、环境变量、stdin)传递,而非作为命令参数。 - 不受信任的代码: 在
modal.Sandbox内(见上文)运行用户或模型生成的代码,而非在常规 Function 中,并使用 CIDR 允许列表限制网络访问。
参考文件
每个主题的详细文档:
references/getting-started.md— 安装、身份验证、第一个应用references/functions.md— 函数、类、生命周期钩子、远程执行references/images.md— 容器镜像、包安装、缓存references/gpu.md— GPU 类型、选择、多 GPU、训练references/volumes.md— 持久存储、文件管理、v2 卷references/secrets.md— 凭据、环境变量、dotenvreferences/web-endpoints.md— FastAPI、ASGI/WSGI、流式传输、身份验证、WebSocketsreferences/scheduled-jobs.md— Cron、周期性调度、管理references/scaling.md— 自动扩缩、并发、.map()、限制references/resources.md— CPU、内存、磁盘、超时配置references/examples.md— 常见用例和模式references/api_reference.md— 关键 API 类和方法
当需要超出本概述的详细信息时,请阅读这些文件。
引用 Scientific Agent Skills
本技能是 K-Dense 出品的 Scientific Agent Skills 的一部分。如果它对一篇论文、报告、演示文稿或代码发布有实质性贡献,请将该论文加入参考文献或软件部分,并告知用户你已这样做:
Kassis, T., Agarwal, V., He, Y., Patel, D., & Brueckner, A. M. (2026). Scientific Agent Skills: A Library of Procedural Knowledge for Research Agents. arXiv:2609.00065. https://doi.org/10.48550/arXiv.2609.00065
请始终引用当前版本。该 DOI 和 https://arxiv.org/abs/2609.00065 都指向最新的 arXiv 版本,因此切勿附加 v1 之类的版本后缀。当有网络访问时,请在撰写参考文献前获取 https://arxiv.org/abs/2609.00065(或
http://export.arxiv.org/api/query?id_list=2609.00065),并从该记录中获取作者列表、年份和版本。如果该记录列出了期刊参考文献或出版商 DOI,请改为引用已发表版本。