← 返回博客

Rotifer v0.9.2:把调好的 Agent 存成一个文件,换台机器一条命令重建(npm 0.29.0)

rotifer genome export 把 Agent 的基因组写进一个 .rgenome 文件;rotifer genome import 从 Registry 重建它,装了哪个基因、为什么,逐条写给你看。0.29.0 有四项可能影响已有用法的变化。

Rotifer v0.9.2:把调好的 Agent 存成一个文件,换台机器一条命令重建(npm 0.29.0)

你花了一下午,把一套工作流调成了一个 Agent:用哪几个基因、按什么顺序、出错了怎么兜底,都试过了。接下来常见的两件事是:同事也想用;你自己要换台电脑。

以前,这两件事都得照着记忆再搭一遍。v0.9.2 给了 Agent 一个能带走的形态:

v0.9.2 是协议版本号,分两次随 npm 包 @rotifer/playground 发布:0.28.0(导出与导入)和 0.29.0(文件格式固定为格式 1、导入更严格,另有四项可能影响已有用法的变化,见下文「升级前请看」)。请安装 0.29.0。

名词速查:基因是一段可独立运行、可打分的逻辑单元;Agent 由几个基因按某种组合方式(顺序、并行、条件、兜底)拼成;Registry 是存放已发布基因的公共仓库。


三步实操

下面用一个最小的例子,在全新的环境里从头跑一遍(新的 HOME、新装的 0.29.0、两个空工作区)。

第一步:在 A 处搭一个 Agent 并导出。

npm install -g @rotifer/playground@0.29.0

rotifer init demo-a && cd demo-a
rotifer install json-validator
rotifer agent create validator -g json-validator
rotifer genome export validator
  Genome Export
  Agent: validator
  Composition: Single
  Mode: capability + gene IDs
    json-validator: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1  Native
✓ Credential scan passed (key names, value patterns, entropy)
✓ Wrote …/demo-a/validator.rgenome

得到一个 2.7 KB 的 validator.rgenome。这就是要交给同事、或者带去新电脑的那个文件。

第二步:在 B 处导入。 B 从没见过这个 Agent:

cd ..
rotifer init demo-b && cd demo-b
cp ../demo-a/validator.rgenome .
rotifer genome import validator.rgenome --dry-run   # 先看计划,什么都不装
rotifer genome import validator.rgenome
  Genome Import
  Composition: Single
    json-validator: fallback (no-admissible-candidate) → rotifer-protocol/json-validator@0.2.2  8836abe0-…
      original geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
      installed geneId: v2:4226c8748070a2ffd1fe5c787682fa17fe96651ae30a59d600dbd92145d366e1
ℹ     rejected rotifer-protocol/json-validator@0.1.0: Arena tier is 'not_evaluated', not 'verified'; …
✓ Agent 'validator' created

第三步:在 B 处运行。

rotifer agent run validator --input '{"data":{"age":3},"schema":{"type":"object","properties":{"age":{"type":"number"}}}}'
✓ [1/1] json-validator completed via WASM sandbox
ℹ Final output:
{
  "errorCount": 0,
  "errors": [],
  "valid": true
}

重建出来的 Agent 装的是同一个基因:从下载下来的代码重新算出的基因 ID,和文件里写的逐字一致。两台机器上它都在 WASM 沙箱里运行,消耗的燃料一样,都是 907,943。

输出里那句 fallback (no-admissible-candidate)(没有合格替代,退回原版)是这个版本最需要说清楚的地方,下面两节讲它。


文件里有什么

.rgenome 是一个 JSON 文件,顶层有这几个字段:

这个基因组的身份号 genomeId 是 bp1: 加一段 SHA-256,只覆盖影响执行的部分;它出现在 --json 输出里,人读的输出里不显示。

导出时,先替你查一遍密钥

工作流里最容易出事的,是把 API key 一起分享出去。所以导出在写文件之前,会把每个键和值过三层检查:常见的凭证字段名、已知的密钥格式(各家 API key、JWT、私钥)、以及看起来像随机串的高熵文字。命中任何一层就中止导出,告诉你是文件里哪个位置、哪条规则,但不会把命中的值打印出来。

导入时,同样的检查会对收到的文件再跑一遍。

导入时,它怎么决定装哪个基因

每个角色有两种解析方式:

一个合格的都没有,就退回钉住的原版,并把每个被拒的候选和原因列出来。所有下载和校验都先在临时目录里完成,通过了才动你的 genes/ 目录。

今天的实际情况:Registry 里还没有任何基因达到 verified 档。所以按能力挑这条路今天一次都不会换基因,每次导入装的都是作者钉住的原版——也就是上面那句 fallback。规则已经立好,等有基因攒够证据、进了这一档,它才会开始真正挑选。在那之前,导入给你的是:把原来那个 Agent 原样重建出来,每个基因都和文件对过。

哪些还带不走


升级前请看:四项可能影响已有用法的变化(0.29.0)

其中两项是格式变化,两项是更严格的检查。

1. TryCatch 兜底收到的错误换了格式。 在「兜底」组合里,handler 现在收到:

{
  "error": {
    "geneId": "v2:…",
    "errorCode": "FUEL_EXHAUSTED",
    "message": "…",
    "context": { "retryable": …, "details": … }
  },
  "original_input": { "…": "…" }
}

geneId 是实际失败的那个基因的 v2 ID(算不出时为 null);沙箱里的失败保留自己的错误码(FUEL_EXHAUSTED、TIMEOUT、RESOURCE_EXHAUSTED、SANDBOX_VIOLATION、EXECUTION_FAILURE),不再统一成 INTERNAL_ERROR。原来读 error.code 的 handler,要改成读 error.errorCode;retryable 和 details 挪到了 error.context 下面。

2. genomeId 换了格式。 原来是 64 位十六进制,现在是 bp1: 加 64 位,只覆盖实际执行的部分。同一个蓝图在 0.28.0 和 0.29.0 下算出的值不同,不要跨版本比较。

3. 蓝图里的条件判断只收 agent run 能执行的写法。 导入会拒收 MATCHES、对非字符串用 CONTAINS、以及带路径(不是顶层)的字段;导出也不会写出这些写法。

4. 基因的网络请求不能再自设由客户端管理的请求头。 rotifer.net 请求设置 Host、Content-Length、Transfer-Encoding、Expect 或逐跳头时,会在任何流量离开沙箱之前以 -10 拒绝。原来设置过这些请求头的基因要去掉它们。


本版其他改动

完整列表见更新日志。


走到了哪一步

让 Agent 靠换上更好的基因不断变好,是 Rotifer 的目标。这个版本走出的是能检查的第一步:Agent 的基因组成了一个可以搬走、可以打开看、可以重建的文件,每个基因按内容认身份,每次替换都要过写明的规则。替换本身还没有发生,因为还没有基因攒够这些规则要求的证据。

如果你导出了一个 Agent,在别处却重建不出来,请把导入时的输出贴到 GitHub issue。这是我们最想知道的情况。

安装 / 升级

npm install -g @rotifer/playground@0.29.0

如果你写过 TryCatch 兜底的 handler、存过 genomeId、蓝图里用了条件判断,或者基因会自设请求头,请先看上面「升级前请看」一节。