Agent 接了长任务后有两个经典痛点:一是改挂了回不去,只能从头重跑;二是想同时试两条优化路线,只能串行排队等。DigitalOcean 的 Harness Runtime(Managed Agents,公开预览中)把这两个问题做成了命令行能力:checkpoint 把会话状态存档,fork 从存档点分出多个并行变体各跑各的,rollback 一键回到存档点。2026-09-22 发布的 doctl 1.170.0 是包含 harness-runtime(别名 agent)命令(含 fork 与 rollback)的标准版起点。本篇照 DigitalOcean 官方社区教程的 lab 完整走一遍:优化一个慢函数,测试全绿且输出指纹不变才算赢。
Step 1:安装 doctl 并验证 harness-runtime 命令可用
按官方安装指引装好 doctl 后,先用令牌认证,再确认 harness-runtime 命令族存在(别名为 agent):
# 认证(令牌提前导出为环境变量,避免进 shell 历史)
export DIGITALOCEAN_ACCESS_TOKEN="dop_v1_xxxxxxxx"
doctl auth init
# 验证 harness-runtime 命令族与子命令
doctl harness-runtime checkpoint --help
--help 能正常列出 checkpoint 相关子命令(含 fork、rollback)即说明版本达标。如果提示未知命令,检查 doctl 版本:`doctl version` 输出必须不低于 1.170.0。
Step 2:写 spec——把权限边界写进 YAML
Harness Runtime 用一份 YAML spec 声明 Agent 的运行器、规格、环境变量、密钥与权限。官方 lab 的示例(优化一个 Python 报表包)如下,保存为 specs/agents.yaml:
name: anish-fork-lab
agent: claude-code
size: mars-2vcpu-4gb
persistent_workspace: true
env:
ANTHROPIC_BASE_URL: "https://inference.do-ai.run"
ANTHROPIC_MODEL: anthropic-claude-4.6-sonnet
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"
secrets:
ANTHROPIC_API_KEY: "${DIGITALOCEAN_ACCESS_TOKEN}"
permissions:
default: allow
rules:
- tool: bash
match: { command: "rm -rf *" }
action: deny
- tool: bash
match: { command: "git push *" }
action: deny
三处值得注意:模型指向 DigitalOcean 托管推理端点,密钥复用同一个令牌;密钥放在 secrets 而不是 env——env 中的值按原文存储且沙箱内可读,secrets 才是密钥的正规去处;permissions.default 设为 allow 是为了无人值守实验不被审批卡住,官方同时补了两条 deny 规则挡住递归删除和意外推送。对初跑者,官方建议 default 用 ask,先观察这个 Agent 倾向做什么再放开。
Step 3:跑基线——拿到「不许变」的输出指纹
官方 lab 的示例仓库是一个小型 Python 包 orders,其中 build_report() 正确但极慢:每个聚合(按地区收入、按月收入、头部客户、畅销 SKU、复购占比)都对订单全表重复扫描。两万条合成订单跑约 4.3 秒。关键是防作弊设计——仓库里有两样东西把输出钉死:tests/test_report.py 的 15 个测试(含 500 订单报告的黄金文件),以及 bench.py 打印的 16 位全报告 checksum(数值一变 checksum 就变)。启动会话后让 Agent 跑基线:
# 会话内指令:建立基线
python -m pytest tests/ -q # 期望: 15 passed
python bench.py # 记录耗时与 16 位 checksum
# 示例输出: build_report: 4.31s checksum=a3f19c04e2b7d655
把这组数字记下来:后面所有变体必须做到 15 个测试全绿且 checksum 与基线一致。没有这道锁,Agent 完全可能用「改快答案」的方式「提速」。
Step 4:打 checkpoint——存档当前状态
基线确认后立即打 checkpoint,把会话的工作区与状态存档。checkpoint 是 fork 与 rollback 的锚点,养成「每完成一个稳定状态就存档」的节奏:
# 对当前会话创建 checkpoint(会话 ID 从会话列表获取)
doctl harness-runtime checkpoint create --session <SESSION_ID>
# 查看已有 checkpoint
doctl harness-runtime checkpoint list --session <SESSION_ID>
Step 5:fork 并行变体——两条路同时试
官方 lab 先用性能剖析定位热点:repeat_customer_share 一个函数就占了约 76% 的运行时(对每个已支付订单全表重扫一遍,O(n²))。于是 fork 出多个变体并行推进,每个变体领不同的优化任务:
# 从 checkpoint 分出变体 A:优化 repeat_customer_share
doctl harness-runtime checkpoint fork <CHECKPOINT_ID> --name variant-a
# 变体 B:优化按月收入聚合的重复扫描
doctl harness-runtime checkpoint fork <CHECKPOINT_ID> --name variant-b
# 分别向两个变体发指令(用官方 lab 的辅助脚本发 prompt)
python send_prompt.py --session variant-a "把 repeat_customer_share 从 O(n^2) 降到 O(n)。约束:只许加速,15 个测试必须全绿,bench.py 的 checksum 必须与基线一致。"
两个变体在同一台规格的沙箱里互不干扰地跑,这就是 fork 的价值:不用排队等路线 A 失败再试路线 B,也不用为试错牺牲主会话的工作区。
Step 6:验证变体——测试与指纹双闸门
每个变体完成后独立验证,两条闸门缺一不可:测试全绿、checksum 与基线逐位一致。官方 lab 在变体 A 中的结果:repeat_customer_share 改用计数表后整体从 4.31 秒降到约 1 秒出头,15 个测试全绿,checksum 不变;变体 B 的优化对总耗时贡献很小。验证通过才有资格进入合并环节:
# 变体 A 内验证
python -m pytest tests/ -q # 期望: 15 passed
python bench.py # 期望: checksum=a3f19c04e2b7d655(与基线一致)
# 汇总各变体结果后择优
变体A 4.31s -> 1.02s 测试 15/15 checksum 一致 采纳
变体B 4.31s -> 4.12s 测试 15/15 checksum 一致 贡献小, 保留备查
Step 7:合并胜者与回滚演练
把胜出变体的改动应用到主会话后,官方教程建议专门演练一次回滚:故意让主会话改挂一个函数、跑挂测试,然后执行 rollback 回到 checkpoint,再跑一遍测试确认工作区完好如初。回滚演练的价值在于——真出事的那天,你确认过这条路是通的:
# 故意改挂后执行回滚
doctl harness-runtime rollback --session <SESSION_ID> --checkpoint <CHECKPOINT_ID>
# 回滚后复验
python -m pytest tests/ -q # 期望恢复: 15 passed
python bench.py # 期望恢复: 4.31s 基线(如未合并) 或 1.02s(已合并)
常见问题 FAQ
Q1:不用 DigitalOcean 托管推理行不行? spec 里也可以配自有的 Anthropic/OpenAI key(放进 secrets),但官方 lab 默认走 DO 托管端点,一个令牌统一计费最省事。
Q2:checkpoint 能跨会话恢复吗? checkpoint 属于会话,fork 出的变体继承存档点状态;跨项目复用请把代码先推到你自己的远程仓库,别把 checkpoint 当版本管理。
Q3:为什么强调 checksum 而不只是跑测试? 测试覆盖有限,Agent 的「改答案式提速」可能恰好绕过断言;16 位 checksum 把全部输出钉死,是防跑偏的兜底指纹。