Cordis 是一个面向"时空可组合性"(Spatiotemporal Composability)的 TypeScript 插件元框架,由聊天机器人框架 Koishi 的作者 Shigma(史一凡)创建,以 MIT 协议开源。它本身不提供任何具体业务能力,只负责插件的加载、卸载与依赖管理,通过依赖注入、类型化事件和可逆副作用等原语,让上层应用的所有组件都能像插件一样被自由组装、热重载和安全卸载。DeepSeek Harness 与 Koishi 均构建在其之上,其中 Cordis 的核心逻辑仅约 2000 行 TypeScript,运行时零外部依赖。
"时空可组合性"是 Cordis 全部设计的理论骨架,相关理念由北京大学与 DeepSeek-AI 联合署名的论文《A Programming Paradigm for Spatiotemporal Composability》系统阐述。该论文于 2026 年 8 月 13 日以预印本形式发布,截至本文撰写时仍在持续修订,尚未取得 DOI 或完成同行评审。它将"动态组合"拆解为两个正交维度,分别回答两个不同的问题。
时间维度关注的是:一个组件被卸载后,它产生的所有副作用能否被完整逆转。这里的副作用包括事件监听、定时器、文件句柄、内存分配、注册的命令等。Cordis 要求每次副作用注册时都同时交出一个对应的"清理函数"(disposer),运行时将其存入栈中,在插件卸载时按注册的逆序逐一执行,使系统内部状态恢复到"这个插件从未存在过"的状态。需要说明的是,这一保证主要适用于系统可独占控制、可还原的内部状态;已经发送的网络消息、已被外部系统读取的数据等不可逆操作,不在自动恢复范围内,需借助延迟提交或补偿操作另行处理。这一机制保证了插件可以安全地随时装卸,而不会在长驻进程中留下内存泄漏或僵尸监听器。
空间维度关注的是:组件之间存在依赖关系时,某个依赖出现、消失或改变时,其他组件如何被自动通知。Cordis 采用声明式依赖加反应式响应的方式——插件不"接收注入",而是"声明需求",运行时保证依赖就绪后才启动、依赖消失时先于提供方卸载、提供方改变时只重启真正受影响的插件。两个维度合起来产出一个工程性质:系统的最终状态只取决于"当前启用了哪些插件",与这些插件以什么顺序、经历几轮装卸到达该状态无关,这被称为路径无关性,也是热重载在数学上安全的前提。
可逆副作用是 Cordis 最核心的特性,也是其实现安全热插拔与无残留卸载的基础。
插件的所有副作用都通过上下文对象上的 ctx.effect() 方法注册。调用时立即执行注册逻辑,同时必须返回一个清理函数,运行时把清理函数存入该插件 Fiber 的处置栈中。例如注册一个定时器时,立即执行的逻辑里启动定时器,返回的清理函数里清除该定时器,二者成对交出。
当插件被卸载时,Cordis 会沿着依赖树自动撤销该插件注册的一切:事件监听、定时器、提供的服务、注册的命令等,全部按照注册的逆序(LIFO)逐一调用对应的清理函数。整个过程由运行时完成,插件作者无需手写清理逻辑,也不会因为遗漏某一项清理而导致资源泄漏。
ctx.effect() 常与生成器配合使用,可以做到分步注册、分步回滚:初始化过程中如果中途报错,已经执行过的操作可以被精准回滚,不会留下执行到一半的中间状态。这一能力使得插件的加载与卸载都具备原子性,是"创造模式"下运行时动态装卸插件仍能保持系统干净的关键。
Context 是 Cordis 中最核心的概念,它既是服务容器,也是上下文隔离的边界,同时还是聚合了所有核心能力的门面对象。
每个服务在 Context 上占据一个稳定的键位(如 ctx.tools、ctx.llm、ctx.sessions),提供方通过 ctx.provide() 将对象与服务名绑定,消费方通过 ctx.get() 或直接以 ctx.xxx 的形式读取。其他插件通过键名查找服务,而不是直接导入某个具体实现,从而实现依赖倒置——更换一个服务的实现,所有依赖该服务的插件都无需改动。
Context 通过代理(Proxy)将各个核心服务的方法混入自身,因此 ctx.on() 本质是事件服务的分发、ctx.plugin() 本质是注册表服务的挂载、ctx.effect() 本质是纤程副作用系统的登记。开发者面对的始终是一个统一的 ctx 对象,无需关心底层服务的边界。
每一个插件都在属于它自己的 Context 中被调用,并继承其父上下文的全部功能。Context 因此成为插件隔离的天然边界,父节点卸载时,整棵子上下文构成的子树会跟随一起回收。
inject 声明如何决定插件的加载顺序?Cordis 抛弃了传统依赖注入框架中侵入性强的装饰器,转而采用声明式依赖,插件通过一个 inject 字段声明自身所需的服务。
插件声明 inject: ['llm', 'tools'] 后,Cordis 会等待这两个服务全部就绪才执行插件的启动逻辑,无需手写轮询或启动顺序编排。加载顺序完全由依赖图决定,与代码的书写顺序无关——开发者可以随意调整 ctx.plugin() 的调用次序,插件何时真正启动只取决于它的依赖是否到位。
依赖的监听不是一次性的。运行过程中如果某个被依赖的服务被卸载,依赖它的插件的 Fiber 会自动进入卸载状态并清理资源;当依赖重新出现时(例如热重载),插件又会自动回到挂起状态并重新加载。这保证了依赖始终指向当前正在生效的实现,而不是插件启动瞬间捕获的旧对象引用。
当某个服务的底层实现被替换时,Cordis 只会重载真正声明依赖该服务的插件,依赖未发生变化的插件纹丝不动。在拥有成百上千个独立作者插件的开放生态中,这套声明式规则是它们之间唯一的协调机制,却能让依赖变化被精确地局部化。
直接 import 是静态的、编译期绑定的,而 Cordis 的通信建立在服务发现与类型化事件之上,二者解决的是不同层面的问题。
在 Cordis 中,插件通过键名查找所需服务,而不是在文件顶部 import 某个具体实现。这意味着消费方只依赖契约(接口与服务名),不依赖实现包。更换底层实现时,消费方代码无需任何改动,只需在配置层调整对应键位即可,实现了实现与消费的完全解耦。
服务之间通过 TypeScript 声明合并注册事件名,再按五种模式分发:emit(广播通知,不等待)、parallel(并行扇出)、serial(按序执行直到有结果)、bail(短路,遇返回值即停)、waterfall(瀑布流,监听器排成链,每个都可拦截或修改后传递给下一个)。这使得插件间的通信既有明确类型约束,又能灵活表达从"发后即忘"到"中间件链"的各种交互形态。
由于通信不依赖具体的实现引用,系统中没有任何一个组件是"特权核心"。模型适配器、工具注册表、会话日志乃至主循环本身都是普通插件,开发者可以随时在配置层替换其中任意一项,而无需修改核心代码,这正是"一切皆插件"得以成立的通信基础。
Context 提供三种嵌套操作,分别覆盖局部定制、全局兜底和拦截增强三类玩法,使得同一套服务可以在不同隔离域中对应不同实例。
ctx.extend() 创建一个子上下文,子上下文通过原型链继承父上下文的所有属性,并可附加额外的元数据。它用于在不修改父上下文的前提下做局部扩展,子上下文与父上下文共享同一套服务解析逻辑。
ctx.isolate(name) 创建一个子上下文,专门用于隔离指定的服务。两个隔离域可以各自持有同一服务的不同实现或不同配置,互不影响。在多智能体场景下,这一能力可以让每个智能体拥有独立的 shell、数据库或会话配置,避免彼此干扰。
ctx.intercept(name, config) 为某个服务添加拦截配置,可以在不替换服务实现的前提下,沿原型链为其叠加定制行为。三种操作配合使用,使得"不改实现就改配置""局部定制与全局兜底并存"成为可能,也是会话级服务隔离的物理基础。
热重载(HMR)是指在不重启整个进程的前提下,修改插件源码后让其原地替换、缓存与连接保持不动。Cordis 的可逆副作用与纤程状态机共同支撑了这一能力。
Cordis 自己实现了 Node 环境下的热重载:监听文件变化后,备份模块缓存,清空缓存,重新导入模块,重新注册插件。这一套"备份—清空—重导入"的组合拳跨 Node 22、23、24 三个大版本兼容。开发插件的人保存一下代码,插件即可原地换新,已建立的连接和缓存纹丝不动。
每个插件运行在一个 Fiber 中,运行时为每个 Fiber 维护一个"纪元"(epoch),本质是该插件所有依赖的指纹。依赖发生变化时指纹随之改变,插件被调度重载:先同步清空副作用列表,再逆序调用 dispose,中间用一把 inertia 锁保证重载与卸载不会并发打架。只有依赖真正发生变化的插件会被重启,其余插件保持不动。
这套机制在 Koishi 生态中已被验证多年:管理员从控制台停用一个插件,它对系统的影响会原地撤回,其他插件接着干活;开发者改完插件代码一保存,被修改的插件重新挂载,缓存和连接不受影响。品玩的实测中,DeepSeek Harness 装卸二十余个插件,全程未重启一次。
这一直观效果是时间可组合性与可逆副作用机制共同作用的结果,其核心在于"装卸互为逆操作"。
插件注册时产生的每一处副作用——事件监听、定时器、文件句柄、全局状态修改、注入的服务——都必须通过 ctx.effect() 或 ctx.on() 登记,并返回对应的清理函数。未被登记的裸操作无法进入系统,因此运行时对插件的全部影响都"有迹可循"。
卸载时,运行时按注册的逆序逐一执行清理函数,确保后注册的资源先被释放,避免因依赖顺序错乱而产生悬空引用。若卸载或加载过程中出现同步错误,已执行的部分会被精准回滚,系统不会停留在执行到一半的中间状态。
由于所有副作用都被装进可逆的 effect 中,系统的内部状态不再与操作顺序耦合。先装 A 再装 B,与先装 B 再装 A,最终状态完全一致——历史痕迹被逐步抹平,只剩当下启用了哪些插件。这使得"卸载后系统恢复到插件从未存在过的状态"成为一个可保证的属性,而非依赖插件作者的自觉。
Cordis 通过 isolate 隔离域与会话级配置(preset)的配合,让同一进程内的不同会话可以各自运行互不干扰的服务实例。
同名服务在不同隔离域中会解析到不同的实例。将服务行放入带有 isolate 标记的插件组后,每个组内的插件看到的是该组专属的服务实现,跨组互不可见。这使得多个会话可以各自持有独立的 shell、存储或适配器实例。
进程级的配置树(cordis.yml 及其 patch 层)由整个进程共享,其中的服务是进程级单例;而 preset 是会话级的插件组合,挂在某一个会话上。同一进程内的不同会话可以挂载不同的 preset,彼此不干扰。为保证会话级服务不冲突,preset 中的服务行必须放入带 isolate 的插件组,否则多个会话会碰撞在同一个进程级单例上。
Context 的 extend、isolate、intercept 三板斧,使 Cordis 天然具备多租户能力。在 DeepSeek Harness 中,这一能力被升级为多智能体隔离模型:终端、子智能体、持久化 shell 等资源都可以声明精确的归属作用域,实现进程内多会话、多智能体的安全并存。
Cordis 的配置系统将"组合"本身当作数据来处理,由 Loader 插件读取声明式配置并展开为内存中的插件树。
配置文件是一个 Entry 数组,每个 Entry 通常包含四个字段:id(行标识)、name(插件包名)、config(传给插件的配置)、disabled(是否停用)。配置中仅 config 与 disabled 两个字段支持有限的表达式,其余字段均为字面量,并配有静态校验在提交时强制约束。
真正生效的插件树并非单个文件,而是由多个 patch 层按顺序叠加而成:各 bundle 的 patch → profile 自身的 patch → 机器级全局 patch → 命令行传入的 patch。patch 通过 id 定位目标行,对整条 config 做整体替换(而非深度合并),同一行被多层写入时以后写者为准。这种设计让不同部署形态可以在不改动基础配置的前提下精确覆盖或停用某一行。
配置分为进程级与会话级两层:进程级的 cordis.yml 加 patch 层构成整进程共享的一棵树;会话级的 preset 则作用于单个会话,用于在同一进程内跑出不同形态的实例。配置即架构——新增能力只需在配置中增加一行插件,无需改动框架内核。
Cordis 刻意不依赖 reflect-metadata 等运行时反射设施,而是基于 TypeScript 的声明合并与类型推导来实现依赖管理,这一选择出于工程与生态两方面的考量。
不引入反射意味着框架没有额外的元数据收集与装饰器求值开销,核心代码得以压缩到约 2000 行,运行时除 Node 与 TypeScript 内置能力外没有任何第三方依赖。这使得 Cordis 可以同时在 Browser、Node、Bun 等环境下运行,不被任何特定运行时或绑定所限制。
传统依赖注入框架(如 NestJS、InversifyJS)依赖装饰器与反射,侵入性强且与 TypeScript 的类型系统存在割裂。Cordis 改用声明式依赖声明加模块补充的方式,依赖关系通过 inject 字段与类型拓展表达,既能获得完整的类型推导,又不需要开发者编写繁重的装饰器样板代码。
反射型 IoC 容器通常在应用启动时一次性装配好对象图,之后基本不再变动,这对静态系统足够,却无法应对插件随时来、随时走的动态生态。Cordis 通过声明式依赖加反应式响应,让依赖关系在运行时可被追踪、可被撤销、可被重建,从根本上服务于"可卸"这一目标。
Cordis 是一个元框架,不与任何特定领域绑定,凡是需要长驻运行、高扩展性、无缝热插拔的系统都能从中受益。
这是当前最受关注的场景。DeepSeek Harness 将模型适配器、工具注册表、会话日志乃至 Agent 主循环全部做成 Cordis 插件,使得运行中的系统可以检查自己缺少什么,并现场造出一个插件挂上去接着干活,而所有动态改动都在可逆的 effect 机制下运行,改坏可干净回滚。
Cordis 的直接血统来自 Koishi,后者在国内做 QQ 机器人的圈子里几乎人手一份,社区沉淀了数千个插件。IM 适配器、数据库驱动、功能插件各自声明依赖,切换存储后端或重连消息适配器时,只有真正依赖它的几个插件被重新激活。
类似 VS Code、Obsidian 这类允许用户自由安装、更新、禁用第三方插件的工具,以及需要应对成百上千个功能迥异插件的跨平台桌面端与命令行工具,都适合 Cordis 这套微内核加可插拔能力的思路。
Agent 行为往往是非线性的:一个任务干到一半,中途处理另一个事件再回来。Cordis 原生支持这种交错执行,开发者无需手写复杂的状态机来管理组件的加载、卸载与依赖变化。
传统 IoC 框架(如 NestJS、InversifyJS、Spring 一脉)与 Cordis 都解决依赖管理问题,但二者的设计核心与适用场景有明显差异。
传统 IoC 框架面向静态后端架构,以装饰器(@Injectable)标注类,在应用启动时把对象图装配好、一次成型,之后基本不再变动。Cordis 则面向可扩展的微内核与动态插件系统,采用声明式依赖声明加类型拓展,依赖关系在运行时可被追踪、撤销与重建。
传统框架极难做到真正的热卸载,往往需要自行维护容器的清理逻辑,插件作者仍要手动清理定时器与监听器。Cordis 将干净卸载做成框架在结构上保证的属性:所有副作用都被自动追踪,卸载时沿依赖树自动撤销,无需每个插件作者自觉重写清理逻辑。
传统 IoC 框架通常强绑定 Express、Fastify 等 HTTP 场景,并包含大量元数据反射开销。Cordis 完全无绑定,作为元框架可运行于 Browser、Node、Bun 等多种环境,且极致轻量、零运行时冗余反射。
传统插件系统(如 VS Code 插件、Webpack 插件、Tapable 钩子)与 Cordis 的差异,集中体现在"能卸"这一根本问题上。
大多数传统插件系统解决的是"如何把模块动态塞进系统",却把"如何干净地卸下来"留给了插件作者。插件注册的全局事件监听、定时器、文件句柄、全局状态修改,卸载时若漏掉任何一项,就会造成内存泄漏或僵尸代码继续运行。Cordis 把干净卸载做成运行时强制保证的属性,而非插件作者的自觉纪律。
传统插件系统要么没有依赖声明(如 Pluggy 的 hook 系统),要么依赖关系静态固化。Cordis 让插件声明需求、运行时反应式响应依赖的出现、消失与改变,只有真正受影响的插件会被重启,实现了开放生态中的精确局部化。
传统插件系统往往与某个具体宿主或领域绑定。Cordis 定位为元框架,内核只提供插件、生命周期与副作用、服务、事件、可配置这五样互相咬合的原语,不耦合任何业务领域,上层应用可以在此基础上搭建任意特定领域的微内核系统。
围绕 Cordis 内核,其官方组织 cordiverse 名下构建了一个自上而下铺满参考实现的小生态,DeepSeek Harness 更是将其整体 vendor 进仓库作为框架族使用。
cordis 是框架核心,提供 Context、Service、Fiber、事件与日志门面;cosmokit 是框架与配置校验共用的通用工具库;schemastery 负责每个插件配置背后的结构化 Schema 校验。三者构成内核与配置的基础。
plugin-loader 维护插件树与加载服务,plugin-include 把 YAML/JSON 配置文件解析为加载条目并支持 patch 叠加,plugin-group 管理嵌套插件分组,plugin-timer 提供随插件卸载自动回收的定时器能力。这些本属框架层的设施,在 Cordis 世界里本身也是插件。
plugin-hmr 提供插件与配置的热替换及配置监听,plugin-logger-console 提供控制台日志导出,create-cordis 是交互式项目脚手架。此外还有配套的论文仓库、数据库、WebUI、registry、server 等官方插件包,形成从理论到参考实现的完整闭环。
Cordis 与 Koishi 同源同作者,二者是"内核"与"首个实战验证场"的关系。
Cordis 的作者 Shigma(本名史一凡,北京大学出身、DeepSeek-AI 成员)于 2020 年 1 月发布了聊天机器人框架 Koishi。四年间社区为它积累了 4000 余个插件,而 Cordis 正是 2022 年从 Koishi 中抽出的插件微内核(cordis 包于 2022 年 4 月登上 npm)。Koishi 至今仍是它最大的实战验证案例——论文报告指出,Koishi 当前基于 Cordis v3 运行,是这套模型支撑开放插件生态的观察性证据。Cordis 这个名字来自拉丁语"心"(cor)的所有格,意为"心脏",用作者自己的话说,Koishi 的一切都从 Cordis 开始。
2023 年作者为 Koishi 撰写了《可逆的插件系统》设计文章,基本是后来那篇论文的雏形。2026 年 8 月 13 日,北京大学与 DeepSeek-AI 联合署名的论文《A Programming Paradigm for Spatiotemporal Composability》以预印本形式发布,将这套多年插件生态运营中沉淀的实践提炼为可证明的理论,而 DeepSeek Harness 正是这套内核在 AI Agent 时代的旗舰生产消费者。需要客观指出的是,论文作者自认其验证仍局限于 Koishi 单一生态与 TypeScript 单一语言,且未提供性能基准测试或对照实验,因此它证明的是这套范式的可行性与工程采用,而非通用性或在性能上的优越性。