你花了一下午,把一套工作流调成了一个 Agent:用哪几个基因、按什么顺序、出错了怎么兜底,都试过了。接下来常见的两件事是:同事也想用;你自己要换台电脑。
以前,这两件事都得照着记忆再搭一遍。v0.9.2 给了 Agent 一个能带走的形态:
rotifer genome export:把这个 Agent 用了哪些基因、怎么组合,写进一个.rgenome文件;rotifer genome import:在另一台机器上读这个文件,从 Registry 把 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 文件,顶层有这几个字段:
blueprintFormat:1。格式已固定,以后要改就换新的编号。protocolVersion:写出这个文件的协议版本(仅供参考)。snapshot:按内容算出的 v2 ID 列出每个基因,以及组合方式(Single、Seq、Par、Cond或Try,都是agent run能执行的写法)。capabilities:每个角色要做什么事——领域、输入输出格式,以及最低适应度(--min-fitness,默认 0.3)。导入方靠它去找能做同一件事的别的基因。resolutionHints:这个基因在 Registry 上的作者、名字和精确版本。metadata:名字、创建时间,以及可选的描述(-d)。
这个基因组的身份号 genomeId 是 bp1: 加一段 SHA-256,只覆盖影响执行的部分;它出现在 --json 输出里,人读的输出里不显示。
导出时,先替你查一遍密钥
工作流里最容易出事的,是把 API key 一起分享出去。所以导出在写文件之前,会把每个键和值过三层检查:常见的凭证字段名、已知的密钥格式(各家 API key、JWT、私钥)、以及看起来像随机串的高熵文字。命中任何一层就中止导出,告诉你是文件里哪个位置、哪条规则,但不会把命中的值打印出来。
导入时,同样的检查会对收到的文件再跑一遍。
导入时,它怎么决定装哪个基因
每个角色有两种解析方式:
- 按钉住的版本装(
--pinned):严格按文件里写的作者、名字、版本去 Registry 装,装完重新计算基因 ID,和文件里的不一致就拒绝。 - 按能力挑(默认):文件里记着每个角色「要做什么事」(领域、输入输出格式、最低适应度),导入时会去找能做同一件事的基因,但只有全部条件都满足才会换用:在 Arena(基因竞技场)里达到
verified档且没被作废;适应度高于文件要求;安全分 V(g) 高于作者建议、--min-vg和 0.7 三者里最严的那个;评估证据不超过 90 天;是 Native 或 Hybrid 且有 WASM 产物;输入、输出格式与原基因完全一致(描述、标题、示例、默认值除外)。换成别的作者的基因,还要你确认一次(脚本里用--yes)。
一个合格的都没有,就退回钉住的原版,并把每个被拒的候选和原因列出来。所有下载和校验都先在临时目录里完成,通过了才动你的 genes/ 目录。
今天的实际情况:Registry 里还没有任何基因达到 verified 档。所以按能力挑这条路今天一次都不会换基因,每次导入装的都是作者钉住的原版——也就是上面那句 fallback。规则已经立好,等有基因攒够证据、进了这一档,它才会开始真正挑选。在那之前,导入给你的是:把原来那个 Agent 原样重建出来,每个基因都和文件对过。
哪些还带不走
- Wrapped 基因带不走。 用了 Wrapped 基因(调用外部服务的包装型基因)的 Agent 可以导出,会给出提示;但导入时这个角色会被拒绝:Wrapped 基因的源码不经 Registry 分发,也没有合格的替代可选。
- 没发布过的基因带不走。 只存在你本机的基因,Registry 里没有可以重建的来源。
- 还原不精确的文件一律拒收。 缺
composition、缺必填字段、格式定义里用了$comment以外的$关键字、有重复键、有1e400这类非有限数或 JSON 表示不准的整数、或者用了agent run执行不了的条件写法,导入都会拒绝。导出也按同一套文件检查自查,不会写出导入时格式检查通不过的文件。
升级前请看:四项可能影响已有用法的变化(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 拒绝。原来设置过这些请求头的基因要去掉它们。
本版其他改动
- 条件判断按数值比较数字:
EQ、NEQ把1和1.0视为相等。 - 沙箱的 HTTP 客户端升级到了 ureq 3。
install会核对下载到的内容:装上的基因与 Registry 记录的内容哈希比对,不一致只告警、不中断。- 并行、条件、兜底三种组合能在编译过的基因上运行,失败时也不再显示「完成」。
- 安全扫描不再把字符串和注释里的
eval当成问题。 - 同一个内容哈希归先发布的人;Arena 排行榜翻页不再重复或漏行。
- 依赖安全告警清零:wasmtime、rustls-webpki、webpki-roots。
完整列表见更新日志。
走到了哪一步
让 Agent 靠换上更好的基因不断变好,是 Rotifer 的目标。这个版本走出的是能检查的第一步:Agent 的基因组成了一个可以搬走、可以打开看、可以重建的文件,每个基因按内容认身份,每次替换都要过写明的规则。替换本身还没有发生,因为还没有基因攒够这些规则要求的证据。
如果你导出了一个 Agent,在别处却重建不出来,请把导入时的输出贴到 GitHub issue。这是我们最想知道的情况。
安装 / 升级
npm install -g @rotifer/playground@0.29.0如果你写过 TryCatch 兜底的 handler、存过 genomeId、蓝图里用了条件判断,或者基因会自设请求头,请先看上面「升级前请看」一节。
