我们的 Managed Agents 基准测试显示恢复耗时 540 毫秒,真实答案是 249
在 DigitalOcean Managed Agents 上实测 2,280 个会话:冷启动、恢复与命令延迟的 p50、p95 和 p99。
我们测量了 DigitalOcean Managed Agents 上 Agent 沙箱的启动、暂停、恢复和命令执行速度。2026 年 10 月,在产品公开预览期间,我们历时四天运行了 2,280 个会话。第一次测得的恢复耗时约为 540 毫秒。这个结果是错的,而它出错的方式,适用于任何通过轮询沙箱状态来判断其是否就绪的基准测试。正确测量后,恢复调用在中位数上 249 毫秒返回,且返回那一刻会话即可使用。
本文介绍我们的发现、轮询为何误导了我们,以及如果你正在决定是让 Agent 沙箱持续运行、在轮次之间暂停,还是为每个任务新建一个沙箱,这些数字意味着什么。测试框架已开源(managed_agents_review_benchmark),你可以核验每一个数字。
主要发现
所有数据均由同一区域内的一台 Droplet 客户端实测得出,环境为 mars-2vcpu-4gb 沙箱,使用 opencode Agent 适配器。
| 项目 | p50 | p95 | p99 | n |
|---|---|---|---|---|
| 新会话就绪(create 调用返回) | 658 ms | 970 ms | 2,590 ms | 2,180 |
| 新会话:首条命令执行完成 | 904 ms | 1,300 ms | 2,950 ms | 2,180 |
| resume 调用返回(会话已就绪) | 249 ms | 427 ms | 473 ms | 298 |
| 恢复的会话:首条命令执行完成 | 493 ms | 713 ms | 785 ms | 298 |
| 运行中会话执行命令 | 184 ms | 368 ms | 414 ms | 18,740 |
- resume 是同步的。resume 调用一返回,会话就已就绪。我们测量了 398 次不带轮询的 resume,首条命令全部一次成功。
- 暂停的会话恢复工作所需时间约为新会话的一半:同一次测试中,恢复到首条命令完成耗时 493 ms,新会话为 912 ms。
- 暂停 5 分钟后依然如此。典型 resume 耗时基本不变,只是尾部变长(见下文)。
- 恢复的会话执行命令与新会话一样快:中位数分别为 186 ms 和 189 ms。
- 约 30% 的请求比其他请求慢约 150 ms。因此每项操作的 p95 都约为 p50 的两倍。
为什么轮询测出来是 540 ms
给 resume 计时最直接的做法是:发出 resume 请求,然后轮询会话状态,直到状态变为 READY。我们的测试工具最初就是这么做的。连续三天,它测出的 resume 到就绪中位数为 538–585 ms。
测试框架还记录了每次状态转换前后的时间窗口:最后一次“未就绪”检查发出的时间,以及第一个“就绪”响应到达的时间。对于恢复操作,这个窗口在100% 的流程中都从零开始。恢复调用返回后约 100 ms 发出的第一次状态检查,就已经显示 READY。所以那 540 ms 根本不是恢复耗时,而是:
- 恢复调用本身(约 250 ms),
- 加上轮询间隔(100 ms),
- 再加上状态检查的一次 API 往返(约 190 ms)。
我们等待询问的整个过程中,会话其实一直处于就绪状态。
这是一个普遍限制。轮询客户端能测出的最快状态转换,不会快于一个轮询间隔加一次 API 往返。在这套环境下,这个下限约为 300 ms,比我们要测量的对象还大。用这种方式测出的两个数字,只有在轮询间隔和网络路径都相同时才具有可比性;别人基准测试里的“X ms 就绪”数据,也可能包含同样的误差。
为了准确测量恢复耗时,我们不再轮询。测试框架发出恢复请求后立即执行命令,如果 API 返回“尚未就绪”就每 20 ms 重试一次。结果一次重试都没用上。每条首条命令都成功了,耗时 192 ms,与从未暂停过的会话上执行命令的耗时(189 ms)相同。没有任何环节在等待沙箱唤醒。
新会话约 650 ms 就绪,首条命令约 0.9 s 落地
创建会话同样是同步的。在 100% 的单会话流程中,创建响应本身就报告会话已就绪。
| p50 | p95 | p99 | n | |
|---|---|---|---|---|
| 创建 → 就绪 | 658 ms | 970 ms | 2,590 ms | 2,180 |
| 创建 → 首条命令 | 904 ms | 1,300 ms | 2,950 ms | 2,180 |
中位数每天都很稳定,尾部延迟却不是:
| 日期(2026年) | Create p50 | p95 | p99 | n |
|---|---|---|---|---|
| 10月21日 | 646 ms | 2,007 ms | 2,668 ms | 1,286 |
| 10月23日 | 713 ms | 938 ms | 1,053 ms | 298 |
| 10月26日 | 649 ms | 885 ms | 1,021 ms | 298 |
| 10月27日 | 662 ms | 895 ms | 1,054 ms | 298 |
四天里有三天,95% 的 create 操作在一秒内完成。10月21日这天,p95 约为 2 秒。只做一天基准测试,得到的 p95 会大不相同,具体取决于测的是哪一天。
同时创建五个会话并不会拖慢冷启动:中位数为 909 ms,而逐个创建是 912 ms。
关于延迟预算:从创建会话到执行第一个有效操作,按约一秒来规划。我们见过最慢的一次 create 耗时 6.0 秒,所以 create 超时时间应明显高于 p99。
暂停的会话恢复工作所需时间约为新会话的一半
下面两组数据来自同一次运行、同一个客户端、同一个小时:
| p50 | p95 | p99 | |
|---|---|---|---|
| 创建 → 第一条命令 | 912 ms | 1,198 ms | 1,403 ms |
| 恢复 → 第一条命令 | 493 ms | 713 ms | 785 ms |
恢复在每个百分位上都更快,快约 400–600 ms。而对大多数 Agent 来说,实际差距还要更大。暂停的会话保留着文件、已安装的软件包和工作状态,新会话则必须重新构建这些,而重建时间完全没有算进那 912 ms 里。
暂停也是更省钱的等待方式。空闲会话如果一直运行,就不再为 CPU 付费,因为 CPU 按实际用量计费,但内存仍照常收费。默认的 2 vCPU / 4 GB 沙箱约为每小时 $0.038(4 GB × 每 GB-小时 $0.0095),若每天等待八小时,一个月约 $9。暂停的会话完全不产生计算费用,只需为快照和卷的存储付费,价格为每 GiB-月 $0.05,因此 10 GiB 工作区每月约 $0.50。销毁会话在等待期间不花钱,但下一条消息到来时要承担冷启动和重建状态的代价。以上价格来自 Harness Runtime 定价页,截至 2026 年 10 月。
暂停 5 分钟后,恢复仍是同步的,只是尾部更长
真实 Agent 很少在五秒后就恢复。用户会离开。于是我们让 100 个会话各暂停五分钟,再以每批五个同时恢复。
- 恢复后的第一条命令再次在 100% 的恢复中一次成功。
- 典型恢复耗时几乎没变:249 ms,而未受拖慢的请求为 236 ms。
- 从恢复到第一条命令,p50 为 577 ms,p95 为 923 ms。
- 尾部变长:7% 的恢复调用超过 600 ms,而暂停 5 秒后没有出现这种情况。
慢恢复是成簇出现的。它们来自 20 批中的 4 批,且常常同一批里就有好几个,这说明原因更可能是同时恢复五个会话,而非暂停时间更长。本次测试无法区分两者,因此应把 5 分钟场景下的尾部视为并发恢复的上界,而不是暂停更久的代价。
运行中会话的命令约需 180 ms,约 30% 会多花约 150 ms
| p50 | p95 | p99 | n | |
|---|---|---|---|---|
命令往返(uname) |
184 ms | 368 ms | 414 ms | 18,740 |
这个分布不是一条带长尾的平滑曲线,而是分成两簇:一簇在 170 ms 左右,另一簇在 330 ms 左右。约 30% 的请求落在较慢的那一簇。每个请求变慢的概率与上一个请求无关,而且在我们测试的每一天,这个比例都差不多(28.9%–30.7%)。暂停、恢复和销毁调用也呈现同样的规律。这就是为什么本文中每个操作的 p95 大约是 p50 的两倍,而不是少数离群值拖出的长尾。
作为参照,同一台 Droplet 上发起一次简单的已认证 API 调用耗时 116–171 ms。一条命令的往返时间大部分花在 API 层,沙箱自身的工作量很小。
恢复操作不会让会话变慢:恢复后的命令中位耗时 186 ms,而暂停前是 189 ms。
这对你的 Agent 架构意味着什么
在用户轮次之间暂停,而不是销毁会话。这样能把首个动作的耗时大致减半,同时保留会话状态。
恢复后不要轮询。resume 调用返回时,会话就已经就绪。轮询只会给每次恢复平白增加至少一个轮询间隔和一次往返。下面的最小示例展示了这一模式。
按 p95 而非 p50 来规划延迟预算。一次需要 1 次恢复加 3 条命令的回复,中位耗时约 800 ms(249 + 3 × 184)。把各操作的 p95 相加,可得到约 1.5 s 的保守上限(427 + 3 × 368)。由于约 30% 的请求偏慢,多步回复通常至少会碰上一次。
按观测到的最大值而非中位数设置超时。在这组数据中,最慢的 create 耗时 6.0 s,最慢的命令耗时 4.3 s,最慢的从恢复到首条命令耗时 1.3 s。
以下场景尚未测试:
- 暂停超过 5 分钟
- 多个会话同时恢复(五个并发时尾部已经变长)
- 其他沙箱规格和 Agent 适配器
- 其他区域或 DigitalOcean 之外的客户端
这里测的是沙箱,不是模型。当 Agent 使用 DigitalOcean 托管的模型时,模型调用经由 DigitalOcean 的 Serverless Inference,计费记在同一账户上。Agent 首次模型响应的耗时是下一项要测的内容。
轮次之间暂停且不轮询:一个最小示例
整个模式只需四次API调用:创建会话、执行命令、用户离开时暂停、下一条消息到达时恢复。创建和恢复都是同步操作,无需轮询。示例使用Python 3,依赖requests库和Harness Runtime API:
import os
import requests
API = "https://api.digitalocean.com/v2/agents/sessions"
TOKEN = os.environ["DIGITALOCEAN_TOKEN"]
def call(method, path="", headers=None, **kwargs):
headers = {"Authorization": f"Bearer {TOKEN}", **(headers or {})}
r = requests.request(method, API + path, headers=headers, timeout=30, **kwargs)
r.raise_for_status()
return r.json() if r.content else {}
# Create. Synchronous: the session is ready when this call returns.
spec = "name: demo-agent\nagent: opencode\nsize: mars-2vcpu-4gb\n"
body = call("POST", data=spec, headers={"Content-Type": "application/yaml"})
sid = body["session"]["session_id"]
def run(argv):
"""Run a one-shot command in the sandbox, without a shell.
Returns {"exit_code": ..., "stdout": ..., "duration_ms": ...}."""
return call("POST", f"/{sid}/sandbox/exec", json={"argv": argv})
print(run(["uname", "-a"])["stdout"])
# Pause while the user is away. Compute charges stop; state is kept.
call("POST", f"/{sid}/pause")
# ...later, when the user's next message arrives:
call("POST", f"/{sid}/resume") # synchronous: ready when it returns
print(run(["uname", "-a"])["stdout"]) # no status polling in between
call("DELETE", f"/{sid}") # destroy when the work is finished
你未必需要自己调用 pause:会话在闲置 15 分钟后会自动暂停,向已暂停的会话发送新消息即可恢复。同样的生命周期在 CLI 中也能用,对应命令是 doctl harness-runtime pause 和 doctl harness-runtime resume(参见如何管理会话)。
限制
- 仅一种沙箱规格(
mars-2vcpu-4gb)、一种 Agent 适配器(opencode)、一个客户端区域(RIC1)。 - 所有数字均为客户端观测值,包含 API 往返时间。服务内部耗时更低。
- 暂停时长仅测试了 5 秒和 5 分钟两种。
- Managed Agents 处于公开预览阶段。这些数字反映的是 2026 年 10 月的情况,后续可能变化。
- 尚未与其他沙箱提供商对比。下一步将用同一套测试框架进行对比。
测试方法
规模。四天内(2026 年 10 月 21 日、23 日、26 日和 27 日)共测量 2,298 次完整流程,另有预热试验记录后丢弃。其中 2,280 次流程完成,18 次因 HTTP 403 失败,不计入统计。
单次流程。创建会话 → 运行第一条命令 → 再运行五条 → 暂停(通过状态检查确认)→ 等待 5 秒或 5 分钟 → 恢复 → 运行第一条命令 → 再运行五条 → 销毁。每条命令都是 uname,不经过 shell 执行,因此测量的是平台开销而非工作负载。
客户端。在 RIC1 的一台 Droplet 上运行基于标准库的 Python 测试框架,调用公开的 Harness Runtime API。连接会复用,TLS 握手在所有计时器之外完成,与带连接池的 SDK 做法一致。试验按随机交错的顺序执行。失败会被计数,绝不静默丢弃。测试框架的报告为每个中位数给出百分位自助法 95% 置信区间。
速率限制。所有测试均在 API 默认限制(每小时 5,000 次请求)以内运行。测试框架在会话之间自行控制节奏,会话内部从不等待,因此没有任何计时包含刻意等待。
恢复时间的测量。本文中的恢复数据来自上文所述的非轮询方法。基于轮询的数据仅在解释其为何不准确时才会出现。
已修正。在五路并发的那次运行中,测试工具链的早期版本把连接建立时间计入了两个端到端计时器。本文数据已用工具链记录的逐请求耗时精确扣除这部分开销,公开发布的工具链也已修复该问题。
与已公布数据的一致性。Managed Agents 发布文章中报告命令耗时 189 ms、恢复耗时 305 ms。我们实测命令耗时为 184–189 ms,恢复调用为 249 ms。
复现方法
工具链、测试及完整命令行见 managed_agents_review_benchmark。
本文中的运行使用:
# cold start, commands, polling-based resume (October 21, 23, 26)
python3 ma_bench.py run --trials 300 --warmup 2 --dwell 5 --respect-default-limits
# resume without polling, 5-second pause (October 27)
python3 ma_bench.py run --trials 300 --warmup 2 --dwell 5 --resume-mode exec --respect-default-limits
# resume without polling, 5-minute pause, five sessions at once (October 27)
python3 ma_bench.py run --trials 22 --warmup 2 --dwell 300 --concurrency 5 --resume-mode exec --respect-default-limits