workflow 架构与隔离
Deno、microVM、host bridge 和 Rust 迁移边界
vyane workflow 不是一个新模型,也不是一个新的 harness。它是 Vyane 里的可编程编排层:把一段 JS 脚本当作流程描述,让脚本决定什么时候并行、什么时候串行、什么时候再派一个 agent。
它在系统里的位置
一次 workflow run 大致是这条链路:
MCP / CLI / HTTP
-> Vyane host(Python 控制面)
-> 组装 JS module(prelude + args + user script)
-> Deno 运行脚本
-> bridge(stdio JSON 行)
-> host hook 处理 agent()/log()/phase()
-> dispatch / routing / budget / journal关键点是:脚本不直接拿 provider key、不直接联网、不直接操作宿主文件。脚本只能通过 bridge 发出意图,例如「我要派一个 agent」。真正的路由、鉴权、预算、账本和模型调用都留在可信的 Vyane host 侧。
谁可信,谁不可信
| 部分 | 信任级别 | 说明 |
|---|---|---|
| Vyane host | 可信控制面 | 读配置、持有凭证、执行 dispatch、写 journal/status |
| workflow JS 脚本 | 不可信输入 | 可能来自模型输出,必须按会绕权限的代码对待 |
| Deno | JS 运行器 | 负责跑脚本,不是最终隔离边界 |
| microVM guest | 隔离房间 | 让脚本所在文件系统和宿主文件系统分开 |
| 下游 agent/harness | 受策略控制 | 由 host 决定是否派发、给什么 workdir/sandbox/budget |
这个模型里最重要的分工是:脚本负责流程,host 负责权力。只要这个边界不破,脚本可以写复杂编排,但不能自己越过 Vyane 去碰宿主秘密。
为什么先用 Deno
Deno 是跑 JavaScript / TypeScript 的运行器。Vyane 选它做 W0 脚本引擎,主要因为:
- JS/TS 是模型最容易稳定产出的脚本语言之一。
await/Promise.all/ top-level await 适合表达并行 agent 编排。- 一个 Deno 进程可以跑完整个 workflow run,
agent()只是通过 stdio 回 host,不会每派一个 agent 就再起一个 JS 引擎。 - Deno 有权限 flag,可以做到零
--allow-*,只保留 stdin/stdout 这条桥。 - 官方 wheel 覆盖 macOS 和 Linux/WSL,适合桌面 Mac、移动 Mac 与 WSL 工作站等环境。
但 Deno 不是保险柜。它的权限模型是第一层门禁,不是不可信代码的最终隔离。安全验证已经证明,模块加载器和文件读权限是两条轴,零 --allow-read 也挡不住某些 import("file://...", { with: { type: "text" }}) 读法。所以 Deno 后面还要套 OS sandbox 或 microVM。
两种运行方式
| 模式 | Deno 在哪跑 | 防什么 | 当前定位 |
|---|---|---|---|
unsafe-local | 宿主机本地进程 | 只靠 Deno 零权限 + host 超时 + macOS sandbox-exec 黑名单挡敏感路径 | 可信 / 受控内部使用,不是终局隔离 |
microvm | 独立 Linux guest 里 | 宿主文件系统根本不进 guest,脚本读不到 .codex / .ssh / 配置目录 | 不可信脚本的硬隔离方向 |
unsafe-local 名字故意保守。它并不是完全没有保护,但它仍和宿主共享同一个操作系统和很多环境事实。只要脚本来源不完全可信,就应该优先使用 microvm。
microVM backend
isolation: "microvm" 目前有两个 backend,由 VYANE_WORKFLOW_MICROVM_BACKEND 选择:
| backend | 运行方式 | 优点 | 代价 |
|---|---|---|---|
lima | macOS 上用 Apple Virtualization.framework 起 Linux guest,桥走 limactl shell | 稳、好调试、适合先当默认 | 依赖预置 guest,冷启动和并发管理更重 |
krunvm | libkrun 每 run 起一个 OCI microVM,用 denoland/deno 镜像 | 更轻、更接近每次 run 一个干净房间 | macOS 需要 case-sensitive APFS 卷,生产 provisioning 还要补 |
两者都遵守同一个原则:backend 不可用时直接报错,不会静默回退到本地 Deno。否则用户以为脚本在 microVM 里,实际跑回宿主机,这是最危险的降级。
已知限制
这些不是「宿主文件读逃逸仍没修」,而是 microVM 主目标之后的工程债:
- bridge frame auth:nonce 不再写进 module 源码,由 host 在脚本 body 运行前通过 stdin init frame 下发;用户脚本作为
AsyncFunctionbody 执行,旧的import.meta.url自读和 wrapper-breakout 静态 import 形态被挡住。它仍是同一 run 内的防伪层,不是替代 microVM 的宿主隔离边界。 - spawn deadline:run 的 wall-clock 超时已覆盖脚本 pump 和 microVM spawn/create/provision 阶段;Lima/krunvm 后端 setup 卡住时会按同一个 deadline 取消。
- krunvm 只读挂载:当前 krunvm
-v不支持:ro,只读挂载要等 backend 能力或改成把 module 打进镜像。 - provisioning:Lima guest 和 krunvm APFS 卷还需要自动健康检查和准备流程。
和 Rust 迁移的关系
现在的 Python 主线已经把几个边界分清楚了:workflow engine 不直接碰 adapter 细节,下游模型调用走 dispatch 接口,journal/status 是独立数据面,transport 有本地 stdio 和 microVM 两类实现。这些边界让后续 Rust 迁移相对顺:可以先迁移稳定内核,再迁移 workflow 调度状态机,最后迁移具体 backend。
Vyane 开源版的 workflow 采用 TOML DAG 这种更稳的声明式 pipeline,没有直接复刻个性化版本的 JS/Deno 脚本引擎。两种形式服务不同阶段:声明式 DAG 更容易审查和复现,脚本式 workflow 表达力更高但需要更强的隔离边界。公开实现的准确状态以其仓库文档为准。
Rust 会让跨端分发、单二进制 CLI、进程管理、并发和错误类型更干净。但它不会自动消除平台差异:macOS 的 sandbox-exec、Linux 的 cgroups/seccomp、Windows/WSL 的路径和服务管理、Lima/krunvm/Firecracker 这些 VM backend,仍然要按平台分别适配。