状态:🗑️ 已废弃(2026-07-11,留档作 rethink 输入)。方案 B(core.async) 曾完整落地为
process/parallel.clj(13 个行为测试全绿,实施偏差见文末 实施笔记),当天与整个 clj-agent-process 模块一并删除 ——用户判定 Process 设计有结构性问题,需重新思考。代码见 git 历史 (V1/Timeline 见 baf1994..764285c,V2 见 63dc926..fa13f2b)。删除动因(rethink 的起点):V2 自由并发没有确定性静止点(快照/Timeline 与并行不可兼得,被迫 V1/V2 两引擎分裂)。对照业界:MS Agent Framework 用 BSP superstep(批内并行 + 屏障处 checkpoint、快照含在途消息,即本文 方案 C 的路线)证明了"既要并行又要快照"的中间点;SK Dapr runtime 则 per-step actor 持久化、彻底放弃全局快照。重新设计时应先回答:编排要不要 全局快照语义——要则 BSP,不要则 actor,而不是两个引擎各占一头。
V1 采用纯函数式同步循环,event-queue 是普通 vector,execute-activated-steps 中 reduce 顺序执行。Fan-out 场景下多个 step 无法并行。
┌──────────────┐
│ event-chan │ ← buffered channel 替代 vector queue
└──────┬───────┘
│ <! 取事件
┌──────▼───────┐
│ router │ ← go-loop: 路由 + 投递
└──────┬───────┘
│ 投递到各 step 的 input-chan
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ step-a │ │ step-b │ │ step-c │ ← 各自 go-loop
│ input-ch │ │ input-ch │ │ input-ch │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└─────────────┼─────────────┘
▼
┌──────────────┐
│ event-chan │ ← >! 产出事件回流
└──────────────┘
┌──────────────────────────────────────────────────────────┐
│ runtime-atom │
│ {:event-queue [...], :steps-state {...}, :context ctx} │
│ │
│ coordinator loop │
│ │ │
│ find-activated │
│ │ │
│ pmap / futures ← 并行执行已激活的 steps │
│ │ │
│ collect results → swap! 合并回 atom │
└──────────────────────────────────────────────────────────┘
| 维度 | 方案 B (Channel) | 方案 C (Atom) |
|---|---|---|
| 并行粒度 | Step 级别持续并行 | 批次级别并行 |
| 通信模型 | 消息传递 | 共享状态 |
| Context 处理 | 各 step 持有副本,最终合并 | 快照读 + CAS 写回 |
| API 变更 | 破坏性(异步返回) | 兼容(同步返回) |
| I/O 密集场景 | 优(go-block 不阻塞) | 差(线程阻塞) |
| 实现复杂度 | 高 | 低 |
| 适合场景 | 长时 I/O step、流式处理 | 短计算 step |
无需向后兼容,完全重写 runtime 层。
{:status (atom :running) ;; :running | :paused | :completed | :failed
:process-spec process-spec
:event-chan (async/chan 256) ;; 事件总线
:control-chan (async/chan) ;; 控制信号(pause/resume/stop)
:steps {:step-id step-runtime ...}
:context (atom context) ;; 共享 context(原子引用)
:in-flight (atom 0) ;; 进行中的事件计数
:result-promise (promise) ;; 最终结果
:error (atom nil)}
{:step-spec step-spec
:input-chan (async/chan 64) ;; 接收投递的输入
:state (atom {:collected-inputs {}
:state nil
:activation-count 0})
:worker go-block-handle} ;; step 的 go-loop
start-process 启动 router go-loop + 各 step 的 worker go-loop使用 in-flight 引用计数:
;; 启动 process(返回 result channel)
(start-process process-spec opts)
;; -> 返回 channel,完成时放入 {:status :context :error}
;; 同步便利函数
(run-process process-spec opts)
;; -> 阻塞等待完成,返回 result map
;; 恢复
(resume runtime data)
;; -> 返回 channel
;; 异步启动(返回 ProcessHandle)
(start-process-async process-spec opts)
;; -> ProcessHandle,用于外部交互
;; 发送外部事件
(send-event handle :event-name data)
;; -> true/false
;; 同步发送(带超时)
(send-event! handle :event-name data timeout-ms)
;; -> true/false
;; 状态查询
(get-status handle)
;; -> :running | :paused | :completed | :failed | :stopped
;; 等待完成
(wait-for-completion handle)
(wait-for-completion handle timeout-ms)
;; -> result map
;; 停止
(stop-process handle)
;; -> true
┌──────────────┐
│ event-chan │ ← 内部事件
└──────┬───────┘
│
┌──────▼───────┐
│ router │ ← alts! 多路复用
└──────┬───────┘
│
┌────────────┼────────────┐
│ │ │
event-chan external-chan control-chan
│ │ │
└────────────┼────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
[step-a] [step-b] [step-c]
外部事件通过 external-chan 注入,与内部事件统一路由到目标 step。
落地 im.ttalk.agent.process.parallel(同模块新 ns,V1 不动)。与上文设计稿的偏差与决策:
start-process 直接返回 ProcessHandle(不再区分
「返回 result channel 的 start-process」与 start-process-async 两个入口);
终态结果放 handle 内的 promise-chan,wait-for-completion 可重复取。
send-event!(带超时的阻塞版)未做——send-event 走 put! + in-flight 预记账已足够。:running 且无暂停 step → :completed。:auto-complete?(缺省 true):设计稿没回答「等外部事件的常驻
process 会在事件流干时被误判完成」——置 false 后只能由 :terminate /
stop-process / 全局超时结束。pause-info 查全量,
resume 三参形式指定 step-id)。同一 :paused 结果只能 resume 一次
(活运行时,区别于 V1 的纯数据快照可反复 resume)。:error {:reason :step-timeout} 处理进入
error-handler/失败流程;on-activate 的线程后台跑完但返回值被丢弃——step 只
拿快照执行、所有共享状态写入都在 worker 侧 apply,超时后天然写不进任何状态。run-process 同步入口与 V1 同构(阻塞到终态或暂停),保险丝 :max-events 保留。:resume)与终态关闭退出;
pause 信号不走 control-chan(worker 直接置 status atom,router 每轮循环检查)。Can you improve this documentation? These fine people already did:
David Gao & Daivd GaoEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |