跳到主要内容

运行时、沙箱与错误

.flow 不是被解释执行的——它被编译成受限 JavaScript,在沙箱里跑。 本页讲执行模型、agent 调用的宿主管线、事件与错误码。

执行模型

一次 run 的完整旅程:

  1. 输入校验:workflow 输入按 input 类型的 JSON Schema 校验 (外部类型 Schema 为 {},接受任意);失败 → WorkflowInputValidationError
  2. 沙箱装载:编译产物在子进程 → isolated-vm → 可信 bootstrap ($runtime ABI)中执行;唯一出网通道是 $host.invoke(op, payload), 跨边界值只能是 JSON
  3. 确定性执行:pipeline/stage/if/parallel/parallelMap/verify/require/ 投影/内建函数在 bootstrap 内确定执行;agent 调用与事件转发到宿主侧
  4. 输出校验:最终返回值按输出类型 Schema 再校验一次;失败 → OutputValidationError

第 2、4 步意味着:跨界数据永远是可信的、可序列化的

agent 调用的宿主管线

每一次 agent() 都走同一条管线:

检查取消/期限
→ agent_runs 预算(超限 = LimitExceededError)
→ 并发闸(semaphore)
→ target 解析
team.main / team.member → 成员资格校验(失败 = CapabilityViolationError)
→ tools 收窄(请求 ∩ host policy)
→ write 策略描述符
→ OutputContract{schema, repairAttempts}
→ [尝试循环]
限时调用:min(timeout, 剩余期限) 的 AbortSignal + Promise 竞速
(迟到结果永不生效)
→ 结构化 → Ajv 校验
→ 失败且预算未尽 → 携带 {previousOutput, errors} 修复重调
→ 失败且错误码 ∈ retry.on 且次数未尽 → 重试
→ 事件 agent.started / agent.completed / agent.failed

事件

FlowEventSink.emit 转发全部事件,类型包括:

workflow.started / workflow.completed / workflow.failed
pipeline.started / pipeline.completed / pipeline.failed
stage.started / stage.completed / stage.failed
agent.started / agent.completed / agent.failed
verify.evaluated / require.failed / progress

emit progress {...} 只产生 progress 事件——Runtime 不关心消费方, CLI / WebSocket / OpenTelemetry 由宿主接入。事件流是 workflow 可观测性的一等入口。

运行时错误码

错误类产生时机
workflow-input-validationWorkflowInputValidationError输入未通过 Schema
output-validationOutputValidationErroragent 输出(修复耗尽)或 workflow 输出未通过 Schema
agent-invocationAgentInvocationErroragent 调用失败/超时
capability-violationCapabilityViolationError非团队成员解析、target 非法等
limit-exceededLimitExceededErroragent_runs 超限
workflow-timeoutWorkflowTimeoutErrorduration 超限
assertionWorkflowAssertionErrorrequire 失败
cancelledFlowCancelledError外部取消
isolateFlowIsolateErrorisolate 内部错误
worker-crashedFlowWorkerCrashedError沙箱子进程崩溃(所有 pending promise 随之 reject)

错误跨进程传递用 DTO(__flowError: true + code/message/stage/span/details), 宿主侧重构为类型化错误。retry.on 匹配的是这里的错误码,不是 message。

序列化边界

跨 进程 / isolate / host ABI 的值仅限 JSON: null / bool / number / string / array / plain object。 这就是为什么语言里没有函数值、没有类实例、没有循环引用—— 所有数据结构天生可序列化,是编译目标而不是运行时约定。

编译期 vs 运行期

错误分两层,先编译期后运行期:

  • 编译期(flow check):语法、语义、类型,50 余种诊断码, 见语言参考。 有 error 级诊断时不产出可执行代码
  • 运行期:上表的错误码,多数对应声明式结构(limits/timeout/ require/expect),即:把失败模式写进声明,让运行时替你看着

下一步