这篇论文的核心是在回答一个很实际的问题:
一个正在运行的软件系统,能不能安全地动态加载、卸载、替换组件,并且自动处理组件之间不断变化的依赖关系?
论文提出了一套统一的理论和框架,称为“时空可组合性”(spatiotemporal composability),并实现为 TypeScript 元框架 Cordis。论文全文 88 页,作者来自北京大学和 DeepSeek-AI。paper.pdf
1. 它要解决什么问题?
传统软件组合主要是静态的:
- 编译时导入模块;
- 启动时加载插件;
- 运行期间基本不改变结构。
但现代系统越来越需要动态组合:
- 插件实时启停;
- 热模块替换;
- 在线切换数据库或服务实现;
- AI Agent 自己修改工具和执行环境;
- 多组件系统在不中断服务的情况下升级。
难点有两个方向。
时间维度:卸载时能否彻底撤销?
组件加载后可能做了很多事情:
- 注册事件监听器;
- 添加路由;
- 修改配置;
- 创建定时器;
- 分配资源;
- 注册服务;
- 启动子组件。
如果只是调用一个 unload() 或 deactivate(),开发者很容易漏掉某些清理逻辑。
论文把这个问题称为“时间可组合性”:
组件被移除后,它对共享环境造成的影响应当被完整撤销。
空间维度:依赖变化时能否自动响应?
例如:
- A 提供数据库;
- B 依赖数据库;
- C 依赖 B;
- 数据库实现被替换或暂时消失。
系统需要自动决定:
- 谁可以激活;
- 谁必须停用;
- 依赖恢复后谁重新加载;
- 如何避免读取不存在的服务;
- 如何处理依赖顺序和循环依赖。
论文把这个问题称为“空间可组合性”:
组件能够声明依赖,运行时能够发现、解析,并对依赖变化做出反应。
2. 最重要的两个概念
论文把经典编程语言理论中的 effects 和 coeffects,从“静态类型分析”提升成了运行时机制。
可以用一句简单的话理解:
| 概念 | 它描述什么 |
|---|---|
| Effect | 组件对环境做了什么 |
| Coeffect | 组件需要环境提供什么 |
2.1 可逆 effect:每个副作用都带一个逆操作
论文把普通副作用抽象成:
也就是说,一个操作不仅修改上下文,还要返回一个可以撤销它的函数。
例如:
运行时会把所有逆操作累积起来。组件卸载时,系统自动按正确顺序执行这些逆操作。
关键点在于:清理逻辑不再是一个与初始化分离的、容易遗漏的 deactivate() 函数,而是和每个副作用绑定在一起。
论文还讨论了多个 effect 交错执行时的独立性。如果两个组件的操作满足一定的交换条件,那么即使它们的加载、卸载顺序不同,最终恢复结果仍然一致。
2.2 响应式 coeffect:依赖满足状态变化会触发生命周期
组件可以声明依赖集合:
运行时根据上下文判断:
- 依赖从不满足变成满足:
activating - 依赖从满足变成不满足:
deactivating - 依赖状态没有改变:
neutral
于是组件生命周期可以自动驱动:
这比普通依赖注入更动态。传统 DI 通常只在初始化时注入一次;这里依赖绑定本身是持续反应式的。
3. 两者如何统一?
论文提出一个统一的 Context:
所有组件与环境的交互都通过这个 Context 完成。
这样做有几个好处:
- 组件的副作用都能被追踪;
- 依赖注册本身也是一种可逆 effect;
- 依赖变化可以触发生命周期变化;
- 组件可以嵌套,形成树状控制结构;
- 加载、卸载、替换都能在统一模型中描述。
论文还引入了“观察等价”(observational equivalence):
不要求物理内存或内部表示完全恢复,只要求外部可观察行为恢复。
这很重要。例如:
malloc后free不会让堆布局逐字节回到原样;- 删除一个生成的名字后,下一次生成的名字可能不同;
- 但从组件可以观察到的行为来看,它们可以被认为等价。
4. 组件模型和形式化证明
论文将组件建模为:
组件的实例称为 fiber,每个 fiber 有生命周期状态,例如:
运行时维护:
- 组件的父子关系;
- 当前提供的服务;
- 组件激活时看到的依赖版本;
- 已积累的逆操作;
- 组件是否正在退出或重新加载。
论文形式化证明了几个性质:
Preservation
运行时步骤不会破坏系统状态的类型和结构约束。
Temporal composability
组件卸载后,它的贡献可以被撤销;在满足相应条件时,其他组件的状态不会被破坏。
Spatial composability
依赖只有在满足时才会激活;提供者撤销后,依赖者会被正确停用或重新解析。
Progress
在无循环依赖、操作能够终止等条件下,系统最终可以到达稳定状态。
Confluence
在组件 effect 相互独立、组件完整安装其声明服务且没有失败时,不同的生命周期调度顺序可以收敛到等价的最终状态。
这相当于说:
最终系统状态主要由目标配置和依赖关系决定,而不应依赖调度器恰好采用了哪一种顺序。
5. Cordis 实现了什么?
论文不是纯理论工作,还实现了 Cordis。
主要功能包括:
Core Library
提供:
- effect tracking;
- effect inverse;
ctx.set()/ctx.get();- 组件注册;
- 组件生命周期管理;
- Proxy 形式的上下文访问。
Declarative Component Loader
可以用声明式配置描述系统:
配置变化后,loader 会做增量协调,而不是重启整个系统。
例如:
- 修改组件 ID 或 URL:重建;
- 修改 isolate:重新分配作用域;
- 修改 intercept:原地更新;
- 修改 config:交给组件做增量更新;
- 修改 disabled:卸载或重新加载。
Hot Module Replacement
HMR 的流程大致是:
- 找出受影响的模块;
- 判断哪些模块可以热替换;
- 失效相关模块缓存;
- 撤销旧组件的 effect;
- 加载新组件;
- 如果加载失败,恢复旧模块和旧组件。
这比很多传统 HMR 机制更“整体化”,因为组件的所有副作用都由生命周期统一管理。
Koishi 案例
论文以 Koishi 作为生产案例:
- 超过 4000 个社区插件;
- 插件来自不同作者;
- 涵盖聊天平台适配器、数据库、管理后台等;
- 支持插件动态停用、替换和依赖重新解析。
论文声称,Koishi 证明了这套模型至少能够支撑一个真实的大型插件生态。
6. 这篇论文真正有价值的地方
我认为它最有价值的不是某个具体 API,而是提出了一种清晰的统一视角:
动态组件系统的核心,不只是“加载代码”,而是管理组件对环境的全部贡献,以及它对环境的全部需求。
传统插件系统往往把这些问题拆散处理:
- 初始化函数负责加载;
- 卸载函数负责清理;
- DI 容器负责注入;
- 配置系统负责重载;
- HMR 系统负责替换;
- 容器编排系统负责服务依赖。
这篇论文试图把它们统一为:
这个抽象对于以下场景尤其有启发:
- 插件化 IDE;
- Agent 工具运行时;
- 多租户服务;
- 在线升级;
- 规则引擎;
- 游戏 Mod 系统;
- 浏览器扩展;
- 微内核式应用;
- 自演化软件。
7. 它的局限和需要谨慎看待的地方
7.1 “可撤销”不是任何副作用都能实现
论文明确承认系统边界问题。
对于以下操作,通常不能真正恢复原状:
- 向外部网络发送消息;
- 给用户发邮件;
- 写入其他进程共享的文件;
- 已经完成的支付;
- 已被外部观察到的输出;
- 不受本系统独占控制的共享状态。
这类操作只能:
- 延迟提交;
- 记录并补偿;
- 采用业务层面的反向操作。
所以它保证的不是“宇宙级回滚”,而是:
在系统能够独占控制、并且能够提供逆操作的边界内,实现可靠撤销。
7.2 逆操作仍然需要开发者提供
论文将多个原子 effect 的逆自动组合起来,但每个原子操作仍然需要提供逆。
例如:
它减少了遗漏和顺序错误,却没有消除“逆操作如何定义”的根本困难。
7.3 形式化保证依赖较强条件
全局 confluence 等性质需要假设:
- effect 之间满足独立性;
- 依赖图无环;
- 组件完整安装其声明的服务;
- 没有失败或失败处理满足特定条件;
- 生命周期操作可以最终完成。
现实系统中,这些条件并不总是成立。
7.4 依赖类型和版本问题还没有彻底解决
当前模型主要依赖 key:
这会产生:
- key 冲突;
- 接口漂移;
- 不同版本提供相同 key;
- 编译时类型与运行时对象不一致。
论文讨论了命名空间、peer dependency、结构兼容性等方案,但明确把统一解决方案留作开放问题。
7.5 实验验证还不够量化
Koishi 案例说明它“能用”,但还没有充分回答:
- 运行时开销是多少?
- effect tracking 增加多少内存?
- 依赖重新解析的复杂度如何增长?
- 与 OSGi、普通 DI、现有 HMR 的性能差多少?
- 是否真的提高了开发者生产力?
- 在大量并发、频繁热替换下是否稳定?
论文自己也承认,案例是单一生态、单一语言、观察性证据,不是严格的对照实验。
7.6 安全性不能只靠 Context
依赖声明和拦截机制可以实现一定程度的能力控制,但如果组件代码在同一个 JavaScript 进程里运行,它仍可能直接访问宿主对象。
真正运行不可信代码,仍然需要:
- WebAssembly;
- 独立进程;
- 沙箱;
- 软件故障隔离;
- 容器或虚拟机。
8. 对 AI Agent 的意义
论文特别提到“自演化 Agent harness”。
这套模型可以用于:
它解决的是传统 Agent 自修改中的两个危险:
- 修改失败只能整体重启;
- 修改了一个模块,却不知道哪些依赖者已经失效。
不过,真正的 Agent 自修改还需要额外解决:
- 非确定性;
- 长期状态迁移;
- 外部副作用;
- 权限升级;
- 恶意或错误生成代码;
- 并发中的安全更新;
- 数据库 schema 迁移。
因此,这篇论文提供的是一个很有潜力的底层组合机制,而不是完整的“安全自演化 Agent”方案。
总体评价
我的判断是:
这是一篇偏编程语言理论和运行时架构的论文,核心贡献是把“副作用撤销”和“动态依赖响应”统一成一个可形式化、可实现的组件模型。
它最强的地方:
- 问题定义清楚;
- effects 和 coeffects 的结合有理论动机;
- 对卸载顺序、依赖顺序、异步、失败和收敛进行了较完整的形式化;
- 有 Cordis 和 Koishi 作为实际实现支撑。
它最弱的地方:
- 很多保证依赖理想化假设;
- 对不可逆外部副作用的处理仍需业务补偿;
- 缺少系统化性能和生产力评估;
- 依赖版本、兼容性和安全边界仍未完全解决。
如果用一句话概括:
论文把“插件加载/卸载”从工程惯例提升成了一个有状态、可撤销、依赖响应式的运行时组合理论;它很适合作为动态插件系统和 Agent 运行时的设计基础,但距离普适、低成本、完全安全的动态软件更新平台,还有一段工程距离。