Skip to content

Reactive Coeffects:空间维度的依赖响应

论文版本948a07b (main)

论文用 Reactive Coeffects 解决 spatial composability——组件依赖出现、消失或更换后,系统能否自动调整生命周期和依赖连接。核心思想:组件声明自己需要什么(coeffect specification),运行时在上下文变化时按规格通知组件。

Coeffect Context 与 set 即 effect

【论文原文结论 §3.2.1】Coeffect context Σ ≔ (k : K) ⇀ 𝒱_k 是依赖键到类型化值的有限偏函数。getset 是两个核心操作(Def 23),且 set(k, v) 本身就是一个 effect——coeffect 操作就是 effect,coeffect provision 自动继承可跟踪性和可恢复性。这是 Revertible Effects 与 Reactive Coeffects 的协同点:声明一个依赖(provide)的同时就在做一个可撤销的 effect。

Coeffect Specification 与三态变化

【论文原文结论 §3.2.2】Coeffect specification d ⊆ K 是组件声明的依赖键集合;satisfaction predicate σ ⊧ d ≔ ∀k ∈ d. k ∈ dom(σ)。任何把 σ 变为 σ' 的 effect 可按 d 分类为 activating / deactivating / neutral(Def 26):

  • activating:变化前 σ 不满足 d,变化后 σ' 满足——组件应被激活;
  • deactivating:变化前满足,变化后不满足——组件应被卸载;
  • neutral:变化前后都满足或都不满足——组件无需切换状态(但依赖实例可能已换)。

数据库 A + RAG B 完整案例

具体案例(贯穿全文的标准例子):

插件 A(数据库插件):set('database', dbService)  提供依赖
插件 B(RAG 插件):声明 d_B = {'database'}       需要依赖
  • A 出现:A 通过 ctx.set('database', dbService) 提供数据库服务;σ 中 'database' 键出现。B 声明的 d_B = {'database'} 由不满足变为满足——activating,B 被通知并自动激活,从 ctx.get('database') 拿到 dbService。
  • A 消失或被替换:A 的 fiber 进入 Unloading,'database' 键从 σ 移除。B 的 d_B 由满足变为不满足——deactivating,B 被通知并自动卸载(其自身的 inverse accumulator 被应用,清理 B 占用的资源)。
  • 新 A 就绪:新版数据库插件 A' 通过 ctx.set('database', newDbService) 提供。B 的 d_B 再次由不满足变为满足——activating,B 使用新依赖重新激活,拿到 newDbService。

整个过程中,B 的代码不需要写"如果 A 不在了就……"的主动检测——运行时按 coeffect 规格自动通知。

Committed View vs Target View

【论文原文结论 §4.1, §4.2】Cordis 实现中,有两份关键的依赖解析视图:

  • Committed View(ω):当前已提交的依赖解析,记录 fiber 名到 provider 的映射。组件在整个 episode 中读到的是这份视图,保证一个组件的本次生命周期内依赖不变(参见 Ordering 定理)。
  • Target View:期望 fiber 应处于的依赖解析状态,由 refresh 在配置变化时重算。当 target 与 committed 不一致时,触发生命周期转换(激活、卸载或重载)。

【论文原文结论 §4.2】关键细节:target view 记录的是 provider identity(fiber uid),而不是 value 是否相等。论文明确指出:"makes the comparison usable, since a different fiber providing an equal value would otherwise compare equal"——如果只比 value,提供相同 value 的不同 fiber 会被误判为"依赖没变",从而不触发重新激活,导致 consumer 用着旧 fiber 的快照而不知 provider 已换。fiber.target 在 Cordis 实现中存的是依赖解析结果的 uid 摘要(§5.1.3)。

Isolation 与 Interception

【论文原文结论 §3.2.3】两个正交的派生机制:

  • Isolation:通过 isolation realm table ρ : K ⇀ R,同一 key k 在不同 context 解析到不同 realm r,从而绑定到不同值。ctx.isolate(k, r) 派生一个新 context,仅覆盖 ρ 在 k 处的映射(derived realization,不修改父表)。
  • Interception:在 coeffect 上挂元数据 ι : K → ℙ_k,provider 函数接收 metadata 并据此调整行为。ctx.intercept(k, ν) 派生 context 合并 ν 到 k 处的元数据。

两者区别:Isolation 替换"key 解析到什么 binding"(换实例),Interception 修改"binding 被使用的方式"(加约束,不换实例)。Isolation 是 derived realization(不需要 inverse,丢弃 child context 即可),而 set 是 in-place realization(需要 inverse 来撤销)。

场景对照

场景用 Isolation用 Interception
多租户 / 不同 Workspace同一 key 'db' 各租户解析为各自实例
子 Agent 独立环境同一 'filesystem' 各子 Agent 独立实例
测试替身用 mock realm 覆盖
文件系统只读权限挂 readonly metadata
不同插件用不同模型'model' key 各自隔离
社区插件严格访问策略挂 capability 限制 metadata

Reactive Coeffects 解决了"依赖变化的感知",但组件实例自己的加载/卸载/重载过程仍需一个状态机来管。见 生命周期与卸载顺序

非官方社区学习站,解读 cordiverse/paper 论文,关联 DeepSeek Harness 架构。论文作者 Yifan Shi、Wei Zhang(北大)与 Tianyi Cui(DeepSeek-AI)。 · 隐私政策 · 服务条款 · 关于