- 人工智能
- AI Agent
- 多智能体
- Agent 编排
- 代码智能体
- CLI
【免费下载链接】openrig
Build your own network of agents from Claude Code, Codex and Pi: persistent teams with roles, shared context and owned work.
导读:本文围绕 OpenRig 仓库中packages/daemon/assets/vm-preview-fixtures目录展开,讲解这套"VM 预览夹具"如何在 Tart 预览环境中为两个 OpenRig daemon 提供差异化内容——一个 daemon 拥有可演示的工作流样本,另一个保持空白以对比首次安装体验。读完本文,你将掌握夹具目录的构成、sample-basic-loop.yaml工作流规格的完整字段语义、通过two-daemon-start.sh启动双 daemon 并把夹具装载进<workspace.specs_root>/workflows/的实操步骤,以及编写合规夹具的命名与内容约定。
一、夹具的定位:为"空壳"与"有内容"的对比演示而生
packages/daemon/assets/vm-preview-fixtures目录的职责非常聚焦:为 Tart 预览环境提供样本数据,让一个 OpenRig daemon 拥有具有代表性的内容,同时保持第二个 daemon 为空,用于并排对比。这种"一空一满"的布局服务于一个真实的运营场景:创始人在同一台 VM 里打开两个浏览器标签页,一边看全新安装(blank-slate)的"第一印象" UI,一边看已经被填充(populated)的"活过的" UI。
从 scripts/vm-bootstrap/two-daemon-start.sh 的注释可以确认这套编排的完整意图:
- 在已安装 OpenRig CLI 的 Tart VM 上执行启动脚本;
- 脚本以完全隔离的状态启动两个 daemon:独立的
OPENRIG_HOME目录、独立的端口(默认 7433 / 7434)、独立的 SQLite 路径; - 分别通过
http://<vm-ip>:7433与http://<vm-ip>:7434访问"空白"与"填充"两套 UI。
脚本开头还记录了一个重要的架构教训(forward-fix #1):CLI 的父进程模块级常量(OPENRIG_DIR/STATE_FILE/LOG_FILE,定义于 packages/cli/src/daemon-lifecycle.ts)在导入时就从OPENRIG_HOME解析,因此必须使用OPENRIG_HOME=<dir> rig daemon start这种环境变量方式隔离状态,而不能依赖一个只传给子进程的--openrig-homeflag——后者会让父进程 CLI 的daemon.json、daemon.log仍然写进默认的~/.openrig路径,破坏隔离。
二、目录内容:workflows 工作流样本
夹具目录当前只有一类内容(packages/daemon/assets/vm-preview-fixtures/README.md):
packages/daemon/assets/vm-preview-fixtures/ ├── README.md └── workflows/ └── sample-basic-loop.yaml其中workflows/存放公开的示例工作流规格。basic-loop 样本演示了一条完整的"从 intake 到 close"的交接链路,并且不依赖任何私有 rig 或机器——任何部署环境下都可以直接装载、直接演示。这正是它被选作预览夹具的原因:它只描述产品行为,不绑定具体会话、主机或 rig 实例地址。
三、sample-basic-loop.yaml 全量解析
夹具的核心是 packages/daemon/assets/vm-preview-fixtures/workflows/sample-basic-loop.yaml。它几乎是内建工作流 packages/daemon/src/builtins/workflow-specs/basic-loop.yaml 的镜像,唯一的关键差异是id不同(见第五节)。完整内容如下:
# Example workflow for the populated VM preview. # Its distinct ID avoids colliding with the built-in basic-loop specification # when copied into the populated daemon's configured workflows directory. # The daemon discovers it on the next workflow-library listing. workflow: id: vm-preview-basic-loop version: 1 objective: > Walk one work packet through the conveyor rig slowly enough for a new user to inspect each handoff. target: rig: conveyor entry: role: intake coordination_terminal_turn_rule: hot_potato roles: intake: skill_refs: [openrig-user, backlog-capture] preferred_targets: [intake-lead@conveyor] planner: skill_refs: [openrig-user, verification-before-completion] preferred_targets: [plan-planner@conveyor] builder: skill_refs: [openrig-user] preferred_targets: [build-builder@conveyor] reviewer: skill_refs: [openrig-user, review-team] preferred_targets: [review-reviewer@conveyor] closer: skill_refs: [openrig-user] preferred_targets: [intake-lead@conveyor] steps: - id: intake actor_role: intake objective: Restate the objective and hand off one packet. allowed_exits: [handoff, waiting, failed] next_hop: mode: require suggested_roles: [planner] - id: plan actor_role: planner objective: Produce a short plan for one build turn. allowed_exits: [handoff, waiting, failed] next_hop: mode: require suggested_roles: [builder] - id: build actor_role: builder objective: Build or draft the planned output. allowed_exits: [handoff, waiting, failed] next_hop: mode: require suggested_roles: [reviewer] - id: review actor_role: reviewer objective: Check the output and clear it for close. allowed_exits: [handoff, waiting, failed] next_hop: mode: require suggested_roles: [closer] - id: close actor_role: closer objective: Close the walkthrough packet. allowed_exits: [done, failed] next_hop: mode: forbid invariants: continuation_required: true preserve_lineage: true closure_required: true allowed_exits: [handoff, waiting, done, failed] loop_guards: max_hops: 10 spawn_budget: 0 closure: success: Walkthrough packet closed. degraded: Walkthrough stopped with a named blocker. failed: Walkthrough failed with evidence.3.1 顶层字段与语义
对照 packages/daemon/src/domain/workflow-types.ts 中的WorkflowSpec类型,可以逐字段确认语义:
| 字段 | 值 | 含义 |
|---|---|---|
workflow.id | vm-preview-basic-loop | 工作流唯一标识,入库后作为 library 条目名。故意与内建basic-loop区分,避免装载进 populated daemon 时与内建规格冲突 |
workflow.version | 1 | 规格版本,与name一起构成workflow:name:version形式的稳定 library id |
objective | 见 YAML | 工作流的目标描述("让一个工作包在 conveyor rig 上走得足够慢,便于新用户观察每次交接"),同时作为 library 列表的 summary |
target.rig | conveyor | 目标 rig,是默认值而非硬编码——实例化时targetRig参数可覆盖它,生效绑定记录在实例的boundRig上 |
entry.role | intake | 入口角色,实例创建时从该角色的步骤开始 |
coordination_terminal_turn_rule | hot_potato | 协调终端轮转规则,缺省值就是hot_potato |
3.2 roles:角色的技能与目标席位
每个角色(WorkflowRoleSpec,见 workflow-types.ts)支持两个字段:
skill_refs:该角色实现的技能标识列表。此字段当前是 v2 disposition(仅文档化),所有者解析实际使用preferred_targets,验证器会对此给出 advisory 提示;preferred_targets:操作者提供的该角色优先会话目标,被resolveDefaultOwner/ entry-owner 解析消费。例如intake-lead@conveyor表示"conveyor rig 上的 intake-lead 席位",plan-planner@conveyor表示 planner 角色对应该 rig 上的 plan-planner 席位。
夹具覆盖了 5 个角色:intake(技能 openrig-user + backlog-capture)、planner(openrig-user + verification-before-completion)、builder(openrig-user)、reviewer(openrig-user + review-team)、closer(openrig-user)。它完整勾勒了一条 intake → plan → build → review → close 的生产线。
3.3 steps:五步交接链路
五个步骤构成完整的交接链(WorkflowStepSpec,见 workflow-types.ts):
| 步骤 | 角色 | 目标 | allowed_exits | next_hop |
|---|---|---|---|---|
intake | intake | 重述目标并交出一个包 | handoff, waiting, failed | require → planner |
plan | planner | 为一个 build 轮次产出简短计划 | handoff, waiting, failed | require → builder |
build | builder | 产出或草拟计划的输出 | handoff, waiting, failed | require → reviewer |
review | reviewer | 检查输出并放行收尾 | handoff, waiting, failed | require → closer |
close | closer | 关闭走查包 | done, failed | forbid(终结步骤) |
关键点:
allowed_exits是闭合枚举的一个子集——系统级退出类型只有handoff/waiting/done/failed四种(WORKFLOW_EXIT_KINDS,见 workflow-types.ts),步骤只能从中选取;禁止自由文本、身份或非结构化证据 JSON;next_hop.mode: require表示该步骤必须交接给建议角色;mode: forbid用于终结步骤。注意:mode: prefer已从值空间中移除(与省略 mode 行为完全相同,解析器会给出 what/why/fix 迁移错误);- 本样本没有使用
next_hop.on(按退出类型分支)、harness(固定 agent harness,claude-code/codex)、host固定、gate结构化闸门等进阶字段——它们在本规格中被省略,属于字节恒等省略(byte-identity-by-omission)。
3.4 invariants 与 loop_guards
invariants(WorkflowInvariants,workflow-types.ts):allowed_exits: [handoff, waiting, done, failed]是实际被投影器消费的字段,验证器会子集检查每个步骤的 allowed_exits;continuation_required、preserve_lineage、closure_required目前是 v2 disposition(advisory):谱系始终通过chain_of_record保留,收尾始终由 hot-potato 契约要求,这些 flag 本身不 gate 任何行为;
loop_guards(WorkflowLoopGuards,workflow-types.ts):max_hops: 10是被强制执行的:投影器在比较 hop 数超过有效基线(v1 基线为 0)时,会以 guard 名义诚实失败实例,同时在验证期制裁死循环;spawn_budget: 0当前为 advisory(单前沿模型尚不存在 spawn/fan-out 接缝),但仍建议明确声明以表达"本工作流不派生子实例"的意图。
3.5 closure:收尾消息
closure(WorkflowClosureMessages,workflow-types.ts)提供 success / degraded / failed 三种展示消息。当前没有消费者渲染它们(v2 disposition,advisory),但它们为 UI 未来的收尾展示预留了文案。
四、装载流程:把夹具变成 daemon 可发现的工作流
原文档的 "Use" 部分给出两步装载流程,这里结合脚本与源码展开完整实操:
4.1 启动双 daemon
bash scripts/vm-bootstrap/two-daemon-start.sh脚本核心行为(scripts/vm-bootstrap/two-daemon-start.sh):
- 默认
BLANK_HOME=$HOME/.openrig-blank、POPULATED_HOME=$HOME/.openrig-populated、BLANK_PORT=7433、POPULATED_PORT=7434,均可通过同名环境变量覆盖; FIXTURE_DIR默认指向本仓库的夹具目录;- 对每个 daemon 执行
OPENRIG_HOME="$home" rig daemon start --port "$port" --db "$home/openrig.sqlite",让 daemon 状态、CLI 生命周期簿记(daemon.json、daemon.log)与 SQLite 全部落在各自 HOME 下; - 幂等性由每个 HOME 各自的
daemon.json保证:干净关机后重跑可重建状态;若 daemon 仍在运行,rig daemon start会拒绝重复启动; - 失败时脚本会打印 RESET 指引(
OPENRIG_HOME=... rig daemon stop后再清空目录)。
启动后脚本会提示下一步:OPENRIG_HOME=$POPULATED_HOME OPENRIG_PORT=$POPULATED_PORT rig up product-team实例化一个示例 rig,为 populated daemon 填充"活过的"内容。
4.2 复制工作流 YAML 到 specs_root
cp packages/daemon/assets/vm-preview-fixtures/workflows/*.yaml \ $OPENRIG_WORKSPACE_SPECS_ROOT/workflows/这里的<workspace.specs_root>是符号化配置路径:默认解析为workspaceRoot/specs(见 packages/daemon/src/domain/user-settings/settings-store.ts),环境变量优先级为OPENRIG_WORKSPACE_SPECS_ROOT(见同文件第 225 行)。工作流规格必须落在<workspace.specs_root>/workflows/目录下(这是 bundle 装载器的硬性要求,见 packages/daemon/src/domain/bundle-workflow-specs-router.ts)。
4.3 daemon 自动发现:无需重启
复制完成后的"下一次 workflow-library 列表"即可发现新规格——这是 Slice 11(workflow-spec-folder-discovery)的机制。从源码看,每次 library 列表请求都会机会性地调用scanWorkflowSpecFolder(packages/daemon/src/domain/spec-library-workflow-scanner.ts),其工作细节:
- mtime 检查(OQ-3):文件 mtime 不晚于缓存行
cached_at时直接跳过(按秒比较,规避 HFS+/FAT 文件系统 mtime 取整问题); - readThrough 解析校验:通过 packages/daemon/src/domain/workflow-spec-cache.ts 解析并缓存;解析失败则写入
status='error'的诊断行,Library UI 会以内联错误样式展示,操作者可就地修复文件; - 文件消失清理(OQ-4):源文件被删除的缓存行会被移除,并经由 EventBus 发出
workflow_spec.removed审计事件(reason:file_disappeared)。
因此,把sample-basic-loop.yaml拷入目录、刷新一次 library 列表,populated daemon 的 Spec Library 就会以workflow:vm-preview-basic-loop:1的身份列出它。
五、与内建 basic-loop 的关系:ID 冲突规避
值得特别说明的是:夹具里的sample-basic-loop.yaml在结构与语义上几乎就是内建 starter packages/daemon/src/builtins/workflow-specs/basic-loop.yaml 的复制,但id被改写为vm-preview-basic-loop。注释明确写道:
Its distinct ID avoids colliding with the built-in basic-loop specification when copied into the populated daemon's configured workflows directory.
内建 starter 会在 daemon 启动时通过loadStarterWorkflowSpecs种入 SQLite 缓存(spec-library-workflow-scanner.ts);如果夹具沿用basic-loop这个 id,就会在缓存中产生同名冲突。改名后,populated daemon 可以同时拥有内建basic-loop与夹具vm-preview-basic-loop两个条目,互不干扰——这既是夹具的实用技巧,也是编写自定义工作流时的通用纪律:先查内建 id,避免重名。
coordination_terminal_turn_rule: hot_potato同时与 packages/daemon/src/builtins/workflow-specs/conveyor.yaml 等内建规格保持一致;从 workflow-spec-cache.ts 可以看到该字段的解析默认值就是"hot_potato",即规格缺省时同样生效。
六、夹具编写约定(Conventions)
原文档给出四条编写规范,全部以"可移植、可演示、不泄内幕"为原则,逐一展开:
- 用户相关路径统一用
/Users/example/...:夹具会被拷入不同机器的 workspace,任何含用户名的路径(如机器名、用户名目录)都会破坏可移植性;/Users/example/...是约定俗成的占位符。 - 使用符号化配置路径而非机器特定位置:例如写
<workspace.specs_root>而不是/home/alice/openrig/specs。符号化路径由配置层解析(环境变量OPENRIG_WORKSPACE_SPECS_ROOT或配置键workspace.specs_root),保证夹具与机器无关。 - 使用通用逻辑角色与 ID,而非具体会话、主机或 rig 实例地址:夹具中的
intake、planner、builder、reviewer、closer都是逻辑角色,preferred_targets也只指向intake-lead@conveyor这类逻辑席位;绝不能写入真实 session id、host 地址或 rig 实例名。 - 描述夹具演示的产品行为,省略发布历史、内部归属与实现规划:夹具是面向"新用户能否看懂交接"的演示素材,不是内部文档——发布说明、负责人、实现计划一律不进入夹具。
七、如何基于夹具扩展你自己的演示工作流
如果需要在 populated daemon 中演示更多场景,可遵循以下模式:
- 在
packages/daemon/assets/vm-preview-fixtures/workflows/下新建一个sample-*.yaml,整体骨架复制sample-basic-loop.yaml; - 为
workflow.id取一个不与内建 id(如basic-loop、conveyor、factory-rsi,见 packages/daemon/src/builtins/workflow-specs/)冲突的名字; - 按需补充进阶字段:
next_hop.on做按退出类型分支、harness固定 claude-code/codex、gate声明结构化闸门(目标可以是human@kernel形式的人类席位会话或已声明角色名,人类目标必须携带summary与evidence_ref)、acceptance声明候选/裁决/证据的验收契约; - 通过
rig workflow specs或 Library 列表验证条目出现,并核对拓扑投影(getWorkflowReview会把 roles 映射为节点、把next_hop.suggested_roles映射为 direct 边,把next_hop.on映射为带branchOn的 branch 边)。
相关的测试用例可以进一步印证行为:packages/daemon/test/workflow-spec-cache.test.ts 覆盖缓存 readThrough 与 hot_potato 默认值解析,packages/daemon/test/conveyor-starter.test.ts 覆盖 conveyor 工作流的实例化与启动。
结语
vm-preview-fixtures是 OpenRig 预览演示体系的一小块拼图,但它完整展示了"用可移植的工作流规格填充真实 daemon"的闭环:夹具目录定义样本 → 双 daemon 脚本提供隔离运行环境 →specs_root/workflows/装载 → 扫描器自动发现入库 → Library 与拓扑投影渲染。理解这套机制,你既能熟练搭建双 daemon 对比演示环境,也能掌握为 OpenRig 编写、命名与装载自定义工作流规格的全部纪律。
- 人工智能
- AI Agent
- 多智能体
- Agent 编排
- 代码智能体
- CLI
【免费下载链接】openrig
Build your own network of agents from Claude Code, Codex and Pi: persistent teams with roles, shared context and owned work.
相关推荐
Flutter设备预览工具Device Preview安装与配置指南
Flutter设备预览工具Device Preview安装与配置指南 前言 在Flutter应用开发过程中,开发者经常需要测试应用在不同设备上的显示效果。手动切
UI库/组件移动开发开发工具asdf-vm团队协作工具:统一开发环境的终极解决方案
asdf vm团队协作工具:统一开发环境的终极解决方案 痛点:多版本环境下的团队协作困境 你是否曾经遇到过这样的场景?新同事加入项目,花费数小时配置开发环境;团
CLI开发工具Hive 浏览器架构设计解析:从本地扩展到沙箱 VM 的统一 Agent 浏览器体验
Hive 浏览器架构设计解析:从本地扩展到沙箱 VM 的统一 Agent 浏览器体验 导读 本文基于 Hive 仓库内部的架构设计文档 browser arch
人工智能AI Agent多智能体MCP 服务工具调用浏览器控制
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考