执行模型:静态分析与调度

适用版本:ChengOS v0.1.0+ | 最后核对:2026-08-12 | 来源:crates/cheng-engine/src/lib.rscrates/cheng-engine/src/dagcrates/cheng-engine/src/executor/task_scheduler.rscrates/cheng-core/src/types.rs

引擎建立在一条明确的设计原则之上:静态在左,动态在右;状态兜底,调度闭环。 规划只做一次,离线且无副作用;调度在运行时进行;而持久化的状态机是让整套机制可恢复(而不仅仅是快)的关键。

两个阶段

静态阶段(离线、纯函数)            动态阶段(运行时)
┌──────────────────────┐            ┌──────────────────────┐
│ DAG 规划器            │            │ 任务调度器            │
│ ├─ 分层执行计划       │            │ ├─ spawn 任务         │
│ ├─ 拓扑排序           │            │ ├─ 控制并发           │
│ └─ 阶段选择器         │            │ └─ 回写结果           │
└──────────┬───────────┘            └──────────┬───────────┘
           ▼                                   ▼
┌──────────────────────┐   节点完成      ┌──────────────────────┐
│ 执行队列              │◄───────────────│ 事件                 │
│ (缓冲就绪节点)      │                └──────────────────────┘
└──────────┬───────────┘
           ▼
┌──────────────────────┐
│ 状态机(兜底)        │
└──────────────────────┘

静态那一半是纯函数式且无状态的——它只分析结构并产出执行计划,不产生任何副作用。正因如此,图校验才足够廉价,可以在每次执行前都跑一遍,而且无需数据库就能测试。

静态分析

  1. 构建图。 节点和连线变成邻接表形式的 DAG。
  2. 校验。 拒绝成环、悬空连线和无法满足的必需输入。一张不可能成功的图会在此刻被拒绝,而不是运行到一半才失败。
  3. 拓扑排序。 用 Kahn 算法得出一个合法顺序。
  4. 分层成阶段。 彼此之间没有依赖的节点落入同一阶段,这使它们有资格并发运行。
  5. 选择就绪节点。 阶段选择器识别出依赖已满足的节点。

因为执行计划是推导出来的而非存储的,所以修改工作流绝不会留下过期的计划。

运行时调度

调度器把每个就绪节点作为一个异步 Tokio 任务 spawn 出去,并用信号量限制并发。

设置 默认值
最大并发 CPU 核数 × 3
任务超时 启用,300 秒(5 分钟)

并发数刻意高于核心数,因为多数节点是 I/O 密集的——在等待模型 API、HTTP 调用或数据库。按核心数设置会让机器在任务等待网络时空转。

这个循环是闭合的:某个节点完成会发布完成事件,队列重新评估哪些节点刚变为就绪,调度器再把它们 spawn 出去。这里没有轮询,也没有固定的逐阶段栅栏——一个节点在它自己的依赖被满足的那一刻就运行,而不是等整个阶段都结束。

状态

执行状态是持久化的,而不仅仅保存在内存中,这正是「状态兜底」的含义:一次执行可以被检视、恢复,或在进程重启后被找回。

执行状态——pendingrunningpausedwaiting_for_reviewcompletedfailedcancelledtimeout。后四个是终态。

节点状态——waitingreadyrunningcompletedfailedskipped。节点自身不持有状态;运行进行中时状态记录在执行记录里。

waiting_for_review 值得留意:它在某个工具返回 requires_review: true 时进入,并持久地挂起执行直到决策到来。由于审批总线是一个内存中的广播通道,决策还会同时记录在审批上下文上——否则一个在无人订阅时发布的决策就会永久丢失。

值与流式传输

数据以 NodeValue 形式流动,这是一个包含 filestream 情况的带标签联合类型。正是「流」让模型节点能在 token 抵达时就把它们传给下游,而不必缓冲整段响应;而「文件」让大体积负载以引用方式传递。

节点有三种实现形态:TypedNode(同步、类型安全,用于纯计算)、AsyncTypedNode(异步,用于 I/O 密集或依赖上下文的工作),以及更底层的、接受原始 map 的 AsyncNode

事件

每次生命周期状态转换都会发布一个事件,并通过 WebSocket 抵达客户端:EXECUTION_STARTEXECUTION_PROGRESSNODE_COMPLETENODE_FAILEDEXECUTION_STATE_CHANGED,以及终态的 EXECUTION_COMPLETE / EXECUTION_FAILED / EXECUTION_CANCELLED。流式节点额外发出 NODE_STREAM_*;智能体额外发出迭代、工具和轮次事件。参见 WebSocket API

容器与循环

在普通 DAG 之外,引擎还支持容器节点——子流程、批量子流程和循环——它们包含各自的子图。这些会经过独立的分析阶段,并发出 CONTAINER_STARTED / CONTAINER_COMPLETEDLOOP_ITERATION_* 事件,因此一批 100 个条目是逐次可观测的,而不是一个不透明的节点。

失败、重试与取消

节点失败默认会使整次执行失败。POST /executions/:id/cancel 把活跃运行转为 cancelled;引擎也支持从失败点重试一次失败的执行,这会产生一个新的执行 id,而不是修改旧记录——失败的那次运行会作为证据完整保留。

下一步

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容