高级 📋 7 个步骤 第 476 / 477 篇

DigitalOcean Harness Runtime 实操:给 Agent 会话做 checkpoint、fork 与回滚

用 doctl 1.170.0 的 harness-runtime(agent)命令管理 Agent 会话生命周期:spec 定义权限边界、跑基准拿 checksum、打 checkpoint、fork 出并行变体各自优化、测试+checksum 双验证后择优保留,最后演练回滚。来自 DigitalOcean 官方 lab 的可复现流程。

2026.09.26· 18 分钟阅读· 约 2092 字· 🔀 doctl / ☁️ Harness Runtime

Agent 接了长任务后有两个经典痛点:一是改挂了回不去,只能从头重跑;二是想同时试两条优化路线,只能串行排队等。DigitalOcean 的 Harness Runtime(Managed Agents,公开预览中)把这两个问题做成了命令行能力:checkpoint 把会话状态存档,fork 从存档点分出多个并行变体各跑各的,rollback 一键回到存档点。2026-09-22 发布的 doctl 1.170.0 是包含 harness-runtime(别名 agent)命令(含 fork 与 rollback)的标准版起点。本篇照 DigitalOcean 官方社区教程的 lab 完整走一遍:优化一个慢函数,测试全绿且输出指纹不变才算赢。

💡 前置准备:DigitalOcean 账号且预付余额为正(Harness Runtime 按用量计费);在控制台 API 页创建一个有完整权限的个人访问令牌;本机安装 doctl 与 Python 3(仅用于向会话发 prompt 的小脚本)。本 lab 用 DigitalOcean 托管推理,同一个令牌同时支付模型费用,不需要额外的 Anthropic 或 OpenAI key。
⚠️ 预览期提示:Managed Agents 处于 public preview,命令形态与计费可能调整,以官方文档为准;doctl 必须是 1.170.0 及以上——更早的标准版没有 fork/rollback,只有 beta 构建才有。

Step 1:安装 doctl 并验证 harness-runtime 命令可用

1 认证并确认命令就位

按官方安装指引装好 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

2 用 spec 文件定义这个 Agent 是谁、能用什么

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 倾向做什么再放开。

⚠️ 权限取舍:default: allow 意味着 Agent 可以不经确认执行任意 shell 命令,仅被显式 deny 规则拦截。只在隔离的实验项目里这样配;跑生产代码库时改用 ask 并按需放行。

Step 3:跑基线——拿到「不许变」的输出指纹

3 测试全绿 + checksum 记录在案

官方 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——存档当前状态

4 在动手改代码之前先存档

基线确认后立即打 checkpoint,把会话的工作区与状态存档。checkpoint 是 fork 与 rollback 的锚点,养成「每完成一个稳定状态就存档」的节奏:

# 对当前会话创建 checkpoint(会话 ID 从会话列表获取)
doctl harness-runtime checkpoint create --session <SESSION_ID>

# 查看已有 checkpoint
doctl harness-runtime checkpoint list --session <SESSION_ID>
💡 checkpoint 的粒度就是你的后悔药粒度。建议每个「测试全绿的稳定点」打一个,命名或备注里写清当时的 checksum,回滚时一眼对得上。

Step 5:fork 并行变体——两条路同时试

5 从存档点分叉,各变体领不同任务

官方 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,也不用为试错牺牲主会话的工作区。

💡 fork 前先做剖析再拆任务,别把两个变体派去做同一件事。让变体 A 专攻热点函数、变体 B 专攻次热点,两条路覆盖的代码不重叠,验证与合并时才不会互相踩脚。

Step 6:验证变体——测试与指纹双闸门

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:合并胜者与回滚演练

7 把结果带回主会话,并验证 rollback 真能兜底

把胜出变体的改动应用到主会话后,官方教程建议专门演练一次回滚:故意让主会话改挂一个函数、跑挂测试,然后执行 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(已合并)
⚠️ 成本与卫生:每个 fork 都是一台按规格计费的沙箱,实验结束及时销毁变体会话;persistent_workspace: true 的会话工作区会保留,长期不清理会产生持续费用。

常见问题 FAQ

Q 三个高频问题

Q1:不用 DigitalOcean 托管推理行不行? spec 里也可以配自有的 Anthropic/OpenAI key(放进 secrets),但官方 lab 默认走 DO 托管端点,一个令牌统一计费最省事。

Q2:checkpoint 能跨会话恢复吗? checkpoint 属于会话,fork 出的变体继承存档点状态;跨项目复用请把代码先推到你自己的远程仓库,别把 checkpoint 当版本管理。

Q3:为什么强调 checksum 而不只是跑测试? 测试覆盖有限,Agent 的「改答案式提速」可能恰好绕过断言;16 位 checksum 把全部输出钉死,是防跑偏的兜底指纹。

← 返回教程中心