Cordis:时空可组合性元框架
一句话定位
A Meta-Framework of Spatiotemporal Composability——一个让插件可以”随时插拔、按依赖自动组装”的 TypeScript 插件元框架。它是 DeepSeek Harness(dsh)的底座:dsh 里的一切(LLM 适配器、工具注册、会话、agent loop 本身)都是 Cordis 插件,“组合方式”本身是数据(cordis.yml)。
值得快速了解的理由:
- 论文级形式化:配套 92 页论文《A Programming Paradigm for Spatiotemporal Composability》(DeepSeek × 北大,arXiv:2608.25512)
- 生产验证:同一设计已在 Koishi 聊天机器人框架运行 4 年,4000+ 社区插件在生产环境验证过
- 是理解 DeepSeek Harness “一切皆插件、无特权核心、自进化”架构的钥匙
背景与来历
| 维度 | 事实 |
|---|---|
| 作者 | Yifan Shi(北大 / DeepSeek-AI,即 Koishi 作者 Shigma)、Wei Zhang(北大)、Tianyi Cui(DeepSeek-AI) |
| 版本 | cordis 4.0.0-rc.8(API 未稳定,可能随时变化),MIT |
| 体量 | 核心 packages/core 仅约 1,850 行 TypeScript |
| 前身 | Koishi 的插件内核(跑了 4 年)抽出来独立成库 |
| 现状 | 被 DeepSeek Harness 以 vendor 方式内置(vendor/cordis),官方文档托管在 dsh 文档站 |
| 数据 | 7,847 stars / 477 forks(2026-08-31) |
论文要解决的痛点:绝大多数插件系统(包括 VSCode)插件一旦加载就无法运行时单独卸载,卸载必须重启整个宿主进程——论文统计 VSCode Marketplace Top 100 扩展中 87 个含可执行代码、启用后无法运行时卸载。对 Agent harness 而言这不可接受:agent 需要自进化——运行时增删工具、替换适配器、修改自身,而不能每次都重启。
核心思想:时空可组合性
论文把”动态组合”拆成两个正交维度:
- 时间可组合性(Temporal)——可逆 effect(revertible effects):每个上下文变换都携带逆变换,由运行时追踪;组件被移除时,其全部副作用被完全回滚。工程落地为一条朴素纪律:任何注册都返回一个 disposer,卸载即逆序出栈。
- 空间可组合性(Spatial)——响应式 coeffect(reactive coeffects):组件声明自己依赖什么(inject),上下文的每次变化按依赖规格通知组件——依赖就绪则加载,依赖消失则卸载,依赖恢复则复活。
数学根基是类型论中经典的 effect / coeffect(代数效应与处理器)概念提升为运行时机制,二者统一为一个 context 类型,构成一套编程范式;再组合出 component 概念与动态组合演算(calculus),把单组件的可组合性推广到整个交错系统。
传统框架是”你在我的框架里写业务”;Cordis 是”框架本身就是一堆可以互相替换的插件”——连 logger、timer、事件系统都和业务插件地位平等。
五个核心概念(速查)
- 插件是实现 Service 的对象。三种形态:函数(最常见,导出
apply(ctx))、对象(带apply方法)、类(Service子类)。可选导出name(诊断标识)、inject(依赖)、Config(配置 schema)。 - 上下文(Context)是服务的容器。一个服务占据稳定的
ctx.<key>(如ctx.tools、ctx.llm);消费方按 key 查找而非 import 实现,因此配置层可以换提供方而不改消费方。 inject声明服务依赖。插件保持 PENDING 直到所列服务全部就绪;加载顺序由依赖表达,而非 YAML 里的书写顺序。依赖跟踪是持续的:运行中服务消失,依赖它的插件跟着卸载;服务恢复,再自动加载。- 类型化事件用于通信。通过 TS 声明合并(
interface Events)注册事件名与签名,五种分发模式见下表。 - 注册是可逆的副作用。
ctx.on()、ctx.plugin()、服务注册、harness 注册表(如ctx.tools.register)本身已是 effect,卸载自动撤销;框架不管理的资源(定时器/连接/watcher)要自己包进ctx.effect()并返回 disposer。
事件分发模式
| 模式 | 调用 | 语义 |
|---|---|---|
| emit | ctx.emit(name, ...args) | 同步广播,不等待、不收集返回值 |
| parallel | await ctx.parallel(...) | 所有监听器并发运行,一同等待 |
| serial | await ctx.serial(...) | 按序执行;第一个非 null/false/undefined 返回值胜出并短路 |
| bail | ctx.bail(...) | serial 的同步版 |
| waterfall | ctx.waterfall(name, ...args, next) | 环绕中间件:监听器收到 (...args, next),可包装 next() 的返回值,也可不调 next() 直接返回(否决/短路) |
waterfall 纪律:只观察/标注的监听器必须调用 next();不调就是有意短路。harness 用它做 agent/request(替换模型调用配置)、approval/request(策略代替用户作答)等决策事件。
最小可运行示例
// hello.ts —— 插件就是一个函数
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello'
export function apply(ctx: Context) {
console.log('hello from my first plugin')
}# cordis.yml —— 应用 = 一份插件组合数据
- name: './hello.ts'带依赖的例子:
// consumer.ts
export const name = 'consumer'
export const inject = ['greeter'] // 等 greeter 服务就绪才启动
export function apply(ctx: Context) {
console.log(ctx.greeter.greet('world'))
}提供方用 Service 子类(运行时 super(ctx, 'greeter') 注册 + 编译时 declare module 声明合并给 ctx.greeter 上类型)。去掉提供方后消费方安静地停在 PENDING——不崩溃、不半运行。
Fiber 状态机
每个已加载插件实例有一个 fiber(运行时句柄):
PENDING → LOADING → ACTIVE → UNLOADING → DISPOSED
↘ FAILED
- PENDING:已声明,等待所需服务(合法状态,不是错误)
- FAILED:apply 或配置校验抛异常
fiber.dispose()等待所有清理(含异步 disposer)完成,并递归卸载子插件- disposer 按注册逆序启动;多个异步 disposer 并发运行——有顺序要求的拆除放进同一个 disposer 内依次 await
- “插件没输出”的第一排查项:遍历
ctx.registry看 fiber 是否卡在 PENDING(缺服务)
核心实现在 packages/core/src/fiber.ts(486 行):ctx.effect(execute, label) 的执行结果归一为四种形态——函数/可舍弃值直接收集、Promise 用 then 收集、同步迭代器逐项收集、异步迭代器逐项 await(每轮检查 epoch 是否失效)——回收时统一转成一串 disposer。
配置与 HMR
- 配置校验:插件导出
Config(Standard Schema 验证器,dsh 用 Schemastery),apply(ctx, config)前校验,坏配置直接 FAILED 并给出精确报错(如$.targets expected array but got not-an-array),插件绝不会带着不完整配置启动。 - 条目元数据:
id(稳定标识,让 loader 区分”修改”与”删了再加”)、disabled: true(保留条目但卸载插件)、config、组(嵌套子列表整体加载/卸载)、isolate(给组内某服务名提供独立实例,两组互不影响)。 - HMR:卸载释放 effect + 依赖驱动加载,所以热替换 = 先卸载再加载。
@cordisjs/plugin-hmr监视文件,保存即完成旧实例回卷 → 新代码加载。编辑 cordis.yml 也触发:按iddiff,只动变化的条目(没有id的条目每次读取都获得新 id,会被视为删了重挂)。
仓库结构与源码地图
packages/
├── core/ # 全部内核,约 1,850 行 TS
│ └── src/
│ ├── fiber.ts (486 行) ★ effect 追踪 + fiber 状态机,必读
│ ├── reflect.ts (281 行) ctx 属性访问代理(服务解析/隔离)
│ ├── utils.ts (278 行) DisposableList 等
│ ├── logger.ts (246 行) logger 服务
│ ├── registry.ts (214 行) 插件注册表(Plugin.Runtime)
│ ├── events.ts (178 行) 五种分发模式
│ ├── service.ts (80 行) Service 基类
│ └── context.ts (78 行) Context:extend/isolate/intercept
├── loader/ # 声明式加载器:读 cordis.yml、配置协调
├── include/ # YAML !!js 表达式节点
├── hmr/ # 热模块替换插件
├── group/ # 组(嵌套条目单元)
├── timer/ logger-console/ # 基础设施插件(本身也是插件,体现无特权核心)
├── create/ # 脚手架(create-cordis)
└── utils/
源码阅读顺序建议:context.ts(容器模型)→ service.ts + registry.ts(服务注册与依赖)→ events.ts(分发模式)→ fiber.ts(effect 归一化与拆除,最核心)。
论文要点
《A Programming Paradigm for Spatiotemporal Composability》(cordiverse/paper,2026-08-26 提交 arXiv:2608.25512,92 页):
- 识别动态组合的两个正交维度:时间(移除时完全回滚副作用)与空间(声明式依赖的响应式管理)
- 把经典 effect/coeffect 提升为运行时机制:revertible effects(每个上下文变换携带运行时追踪的逆变换)+ reactive coeffects(上下文变化按 coeffect 规格通知组件)
- effect context 与 coeffect context 统一为单一 context type,构成编程范式
- 组合出 component 概念与动态组合演算(9 规则 + fiber 状态机),元理论五条定理把时空可组合性从单组件推广到交错组件系统:Preservation / Recovery exactness(撤销精确)/ Ordering+Coherence(依赖有序)/ Progress(无环必终止)/ Confluence(热改收敛到干净安装态)
- 实现即 Cordis:核心库(effect tracking + coeffect resolution)+ 声明式组件加载器(配置协调 + HMR)
深度分析见 2608.25512_spatiotemporal-composability。
学习路径(推荐顺序)
- 本页 → 建立概念地图(30 分钟),配合 使用教程 边学边练
- Cordis 教程 7 讲(deepseek-harness 仓库
docs/cordis-tutorial/,有中文版,每章是可运行示例): 第一个插件 → 生命周期与 effect → 服务 → 事件 → 配置 → 组合与 HMR → 进入 harness(注册真实工具) - Cordis 入门(
docs/cordis-primer.md/.zh.md):一页概念参考,含 waterfall 语义与 loader 配置细节 - 源码:按上文地图读
packages/core(总量不到 2,000 行,半天可读完) - 论文:先读 2608.25512_spatiotemporal-composability 深度解析(五条元定理/演算规则/理论↔实现对照),再啃 92 页原文;CSDN 上有社区实现剖析(搜 “Cordis 算法与实现剖析”)可对照
教程环境:git clone deepseek-ai/deepseek-harness && pnpm install,在 tmp/cordis-tutorial/ 下用 node --import tsx ../../vendor/cordis/bin.js 运行每章示例(启动器创建根 Context、挂 Loader、读当前目录 cordis.yml)。
资源
- 快速入门项目:
~/tmp/proj/cordis——7 课可运行示例(插件/effect/服务/事件/配置/诊断/HMR),npm start聚合演示,npm run lesson:01..07逐课运行(注意 lesson:07 需--expose-internals) - 仓库:https://github.com/cordiverse/cordis(clone 存档:
~/6ai/opensources/cordis) - 论文:https://github.com/cordiverse/paper(clone 存档:
~/6ai/opensources/cordis-paper/paper.pdf) - 入门文档:https://deepseek-harness.github.io/deepseek-harness/reference/cordis-primer
- 教程(中文):deepseek-harness 仓库
docs/cordis-tutorial/*.zh.md(本地:~/6ai/opensources/deepseek-harness/docs/cordis-tutorial/) - Koishi(前身生态):https://github.com/koishijs/koishi
- Schemastery(配置 schema):https://github.com/shigma/schemastery