跳到正文
缓存
返回

装插件容易,卸插件难:一篇把动态组合讲明白的论文

装插件容易,卸插件难。

这话不是段子。VS Code 把所有扩展塞进一个”扩展宿主”进程,想卸掉某个扩展的代码,就得把整个宿主重启,所有扩展一起跟着遭殃。装的时候点一下就好,卸的时候要拖上所有邻居陪葬。插件系统是这个样子,现在越来越火的 AI agent harness(给 agent 挂工具、挂记忆、挂子任务的那层壳)更是这个样子——agent 每给自己改一次组件,就得重启整个运行时,攒下的状态全没了。

这种”运行时往里加组件、撤组件、换配置”的需求,叫动态组合。它天天都在发生,但理论根基一直很弱。最近读到一篇论文就是来补这块的:《A Programming Paradigm for Spatiotemporal Composability》,北大和 DeepSeek 的研究者写的(Shi Yifan、Wei Zhang、Tianyi Cui),稿子日期是 2026 年 8 月 13 日,PDF 一共 88 页,写得相当扎实。

论文的核心观点可以压成一句话:动态组合有两个互相正交的维度,正好对应编程语言理论里两个老概念——effects(效果)和 coeffects(共效果)。把它们从静态类型系统搬到运行时,就有了形式化基础,还顺手实现了一个叫 Cordis 的框架。

两个维度

时间组合性(temporal composability):组件撤掉时,它往共享环境里改过的东西得全部还原。装一个组件,等于往环境里写一堆资源、事件、状态;卸一个组件,等于把这堆东西干干净净收回来,还不能碰别人写的。

空间组合性(spatial composability):组件之间要能声明”我依赖什么”,运行时负责解析、提供、收回这些依赖。A 提供数据库,B 需要数据库,那么 B 就该等 A 在场才激活,A 撤了 B 自动停用,而不是等 B 真的去读一个不存在的依赖时才炸。

静态世界里这两件事早有解法:时间上靠词法作用域(RAII、bracket 模式),空间上靠模块导入。难就难在动态:组件来去不受词法边界约束,依赖的生死也不在编译期可见。所以论文的做法不是给类型系统加更多注解,而是把 effects/coeffects 背后的结构直接实体化,让运行时能亲手操作它们。

可逆效果:每个操作都自带后悔药

一个可逆效果,就是一对变换:正向的效果,加一个显式的逆。运行时把每次操作产生的逆攒成一个累加器。加载组件 = 依次执行它的效果、收集逆;卸载组件 = 把累加器整个跑一遍,环境回到组件来之前的样子。插上时干了什么,拔掉时全还回去,而且不影响别人。

数学上,逆的复合方向相反,天然是后进先出(LIFO),所以单个组件内部的清理顺序是自动的,不用开发者写。更进一步,论文证明了:只要效果之间”独立”(可交换),任意顺序卸载都能回到初始状态。多组件交错运行的系统里,拔掉任何一个,别人该咋样还咋样——这是定理保证的,不是靠各插件作者自觉。

响应式共效果:声明你要什么,剩下的交给运行时

coeffects 描述的是”计算对环境的要求”,静态版本就是一堆类型注解。论文把它做成了响应式机制:组件声明一个依赖规格(我需要哪些 key),运行时盯着共享的依赖表,每次变更都对照规格分类:激活、停用、无关。依赖齐了组件自动激活,依赖被撤组件自动走卸载流程。

它还加了两个很实用的机制。isolate(隔离):同一个依赖 key 在不同上下文里可以解析到不同的值,相当于运行时的 ad-hoc 多态,多租户、测试环境、组件沙箱都用得上。intercept(拦截):给依赖访问挂横切元数据,比如”这个社区组件只能用只读数据库”,不用改组件本身一行代码。

一个类型装下所有

效果上下文和共效果上下文被统一成一个递归类型,一肩挑三件事:当前状态、恢复用的累加器、依赖表。组件和环境的一切交互都走这一个类型。论文把这种做法叫”上下文范式”:介于纯函数式(状态显式穿线,安全但繁琐)和命令式(隐式全局状态,方便但看不见)之间——每个操作的归属清清楚楚,写起来又不累。

从演算到生产

第四部分给动态组合建立了一个完整演算:组件 = 依赖声明 + 提供声明 + 效果函数,实例叫 fiber,带生命周期状态机。元理论证了五件事:保真、时间组合性、空间组合性、进展、合流。比如卸载某个组件时,依赖它的组件会先收到通知、先完成自己的 teardown,然后才轮到它退场——这个顺序是结构保证,不是靠开发者自觉。

实现叫 Cordis,一个 TypeScript 元框架:不做具体业务,只提供动态组合语义。分三层:核心库、声明式组件加载器、上层应用框架。加载器支持配置树、增量协调和热模块替换(HMR)。HMR 有个亮点:因为 fiber 已经圈定了组件全部的效果和依赖,热替换不需要开发者标注”接受边界”(对比 Webpack/Vite 那套),而且替换是事务性的——新模块导入失败就回滚缓存、恢复全部旧组件,系统永远不会卡在半替换状态。

案例是 Koishi,一个建在 Cordis 上的开源机器人框架,四年攒了 4000 多个社区插件,服务端和 Web 控制台是两个独立的 Cordis 应用。生产里”禁用某个插件、效果原地回收""开发时改插件保存即热更、连接不中断”都是日常。插件作者甚至不用写卸载逻辑——凡是通过 context 做的效果,清理顺序自动生成。论文也提到 Koishi 现在跑的是 Cordis v3,论文介绍的是 v4,核心组合模型一致。作者承认这是单一生态的观察性证据,不是对照实验,量化收益留作未来工作。

一点看法

整篇读下来,最让我感兴趣的是论文自己指出的未来方向:自演化 agent harness。现在 agent 已经能给自己换工具、换组件了,而 harness 恰恰是”高频动态组合”最典型的地方——每次自我修改都要重启、丢状态,agent 就永远长不大。这套可逆效果 + 响应式共效果的组合,要是真能在 agent 运行时里落地,自我演化就有了恢复性和协调性的底层保证。

一篇 88 页的论文,形式化(五个元定理)和工程验证(一个 4000 插件的生产框架)是完整闭环的,这在系统论文里不算常见。我对这类”理论长在真系统上”的工作一直有好感。


分享这篇文章:
编辑文章
上一篇
DeepSeek 把自家 agent 框架开源了:145k star 的 DeepSeek Harness 拆开看看
下一篇
蓝牙耳机能听不能说,问题居然出在 BIOS