Cordis:用可逆效应与响应式余效应,把运行时换组件变成有定理兜底的乐高
Paper Info
- Title: A Programming Paradigm for Spatiotemporal Composability
- Authors: 北京大学,DeepSeek-AI(Yifan Shi, Wei Zhang, Tianyi Cui)
- arXiv: https://arxiv.org/abs/2608.25512
- 视频讲解: [Harness自动优化] 06期 | Bilibili
Overview
-
论文身份:DeepSeek Harness 的理论地基论文,作者来自北京大学与 DeepSeek,理论落在开源元框架 Cordis 上;DeepSeek 开源的 DSH 在仓库文档中声明由 Cordis 驱动。
-
核心问题:运行时动态组合(加载、卸载、替换组件)长期缺少形式化基础,论文把它拆成时间可组合性(卸载后副作用完整回收)与空间可组合性(依赖声明与响应式管理)两个正交维度。
-
方法一句话:把程序语言理论里的效应与余效应从编译期静态分析改造成运行时机制——每次环境修改自带逆变换(可逆效应),每份依赖写成声明式规格并随上下文变化分类响应(响应式余效应)。
-
关键机制一:逆变换由调用方在执行点当场构造并满足见证约束,运行时把各步逆变换按相反顺序复合成后进先出的累加器,组件卸载即调用累加器把环境还原到加载前。
-
关键机制二:提供者卸载分两阶段,先让服务对外不可见、等所有消费者完成 清理,再执行累加器释放资源;加载按效应迭代分步推进,目标变化在迭代边界分流卸载,在途异步操作落地后即转卸载,失败回滚并记录在组件实例上。
-
形式化保证:保持性、恢复精确性、保序性、进度、合流性五条元定理,分别依赖良构注册表、操作两两独立、依赖无环且迭代有界、无失败且静止等明确前提;合流性明确不覆盖失败情形。
-
观测等价:恢复是"外部观察者无法区分"意义的等价,不是字节级还原,由此内存分配布局、生成性名称等实现差异被吸收,多组件交错修改下的效应独立性才成立。
-
实现与案例:Cordis 用单原语
ctx.effect跟踪全部上下文修改,插件作者无需另写卸载路径;事务式热模块替换导入失败可整体回滚;Koishi 生态四年积累四千多个社区插件,属单一 TypeScript 生态观察性案例而非受控对比。 -
系列定位:Meta-Harness、AHE、Continual Harness 回答 harness 能否被自动搜索与演化,Autogenesis 给出协议层直觉,本篇回答每一次热替换的正确性由什么保证,可视为 harness 自动优化系列的形式化地基篇。
-
边界判断:发射型操作(写共享文件、发网络数据)不可恢复,只能靠延迟提交或应用层补偿;键的可交换性是提供者接口义务;暂无抽象开销的定量数据;不可信代码仍需外部沙箱。
核心内容解读
为什么重启已经不够用了
插件系统与自进化智能体 harness 都要在运行时加载、卸载、替换组件,但正确性长期靠工程经验兜底。论文给出两个扎眼数字:VS Code 装机量前 100 的扩展中 87 个含可执行代码却无法单独卸载,而声明了扩展间依赖的只有 7 个,跨扩展拿到的接口还是无类型的 any。换句话说,主流插件生态的"动态组合"在工程层面其实是"半动态组合"——能装不能拆、或者拆得干净与否全凭作者自觉。
把代价拆开看:时间上重启把进程里积累的缓存、长连接、算到一半的任务全部丢弃,重建成本从几秒到几分钟不等,这段时间要么服务中断、要么靠冗余副本硬顶;空间上容器编排只能管到服务粒度,同一进程里两个模块的依赖它表达不了,本来一次函数调用被迫走进程间通信。论文进一步指出,自进化 harness 把这个问题推到了临界点——如果 harness 在服务请求的同时还在生成并替换自己的组件,每次自修改都是一次动态组合,重启代价会放大到不可用;更糟的是,一次错误的自修改若把负责恢复的进程也搞挂了,系统连自救的机会都没有。
程序语言理论的旧工具为何被改造
效应(effect)描述计算如何修改环境,余效应(coeffect)描述计算依赖环境的什 么。这两个概念早已存在于程序语言理论中,但都只服务于编译期静态分析:作用域在写代码时就已经固定,运行时才加载的插件没有词法作用域可套,运行时才冒出来的依赖编译期也算不到。论文的取舍很直接——把这些概念从编译期分析对象实体化成运行时能直接操作的对象,把"分析"换成"机制"。
时间维度上的核心机制叫可逆效应:系统里每一次对环境的修改(注册路由、加事件监听、注入服务)都不能只交出新状态,必须同时交出逆操作说明这一步怎么撤。运行时把这些逆操作按相反顺序复合成一条后进先出的撤销链,论文里叫累加器。组件卸载即调用累加器,环境回到加载前的状态。逆操作必须由调用方在执行点当场构造并满足见证约束——怎么撤取决于执行时的现场(分配的句柄、注册时占用的键),框架统一生成的逆操作覆盖不了这种执行上下文。
观测等价与多组件交错的独立性
单个组件的撤销链好理解,但多个组件交错改环境时,A 的逆操作执行时状态已经被 B 动过了,还测得准吗?这引出效应独立性:如果两个组件的操作两两可交换——你先注册我再注册和反过来结果一样——任意顺序撤销都能回到初始状态。论文用观测等价放宽了恢复标准:两个状态只要对外提供的服务行为一致就算等价,不要求内存逐字节相同。比如 free 之后堆的布局不会回到 malloc 之前,但只要没有任何操作能看出区别 ,就算恢复成功。借助这层放宽,内存分配器换块地址这类实现差异才不会破坏可交换性。
可交换性既是判别条件也是接口义务:网络路由、事件监听列表里加条目顺序无关则可交换;中间件链有先后则不可交换;好在组件之间注册依赖这件事本身天然可交换,因为不同组件提供的键被要求互不重叠,谁先注册谁后注册都不打架。
空间维度:声明式依赖与响应式余效应
时间维度讲清楚后,空间维度转向依赖管理。组件把依赖写成声明式规格:"我需要哪些键"。系统维护一张全局依赖表记录谁在提供什么服务,每次表有变化就用每个组件的规格去比对,把变化分成三类:让组件从缺依赖变齐了的激活它、让它失火的反向变化、其余中性不动的中性变化。组件只在依赖全部就绪时才激活,依赖消失时被带着走,卸载流程不会等到运行时才撞上缺失的依赖。
同一个键在不同子上下文里可以解析成不同实例(隔离),访问键时还能被外层附加一层元数据做约束(拦截),比如只给社区插件只读的数据库权限。