插件化 Agent 架构系列 · 4 / 4 · 范式 + 落地
Context Paradigm + Cordis 落地 + Agent 架构连接
前 3 页把 temporal 和 spatial 分开讲;这一页把它们合起来—— 论文的核心贡献是Context Paradigm:effect context 和 coeffect context 统一成一个 context type,成为一套完整的编程范式。 然后落地看 Cordis 的核心 API、Koishi 4000+ 插件的案例,最后连回本仓的 agent adapter 架构。
概览
2
维度合一
5
核心 API
4000+
Koishi 插件
4
本仓 brand adapter
本系列 4 页进度
第一篇 · Context Paradigm 统一
第 4 讲 · 1 范式 + 1 图
图 1 · Context Type 把 effect(时间)和 coeffect(空间)装进同一个数据结构——维度正交但载体合一
Context Paradigm:两维度合一(论文 §3.3)
前 3 页分别讲了 temporal 和 spatial 两维度。论文 §3.3 的核心动作是——把它们塞进同一个 first-class 类型,叫 context type:
// 单一 context type:装下 effect + coeffect 两套机制
context := {
state: Γ, // 当前世界状态
dispose: Γ → Γ, // ← temporal:revertible effects 的 inverse 累积器
inject: Set[K], // ← spatial:组件声明需要的 coeffect 列表
provided: Set[K], // ← spatial:组件提供的 coeffect 列表
store: Map[K, V], // coeffect → value 的实际绑定
isolate: Map[K, R], // realm:决定 key 解析到哪个 binding
intercept: Map[K, M], // metadata:拦截器
parent: Context?, // 父 context(递归结构)
fiber: Fiber?, // 当前 fiber(组件实例)
}
// 一个 ctx.effect 调用 = 修改 state + 累积 dispose
// 一个 ctx.set 调用 = 修改 store + notify 所有 inject 该 key 的 fiber
// 卸载 = 跑 dispose + notify dependents(dependency 消失触发 deactivate)
统一的关系图
flowchart TB
subgraph Context[Context Type · 统一]
State[state: Γ]
Dispose[dispose: Γ → Γ]
Inject[inject: Set of coeffects]
Provided[provided: Set of coeffects]
Store[store: K → V]
end
subgraph Temporal[Temporal · 时间]
State -.ctx.effect.-> Dispose
Dispose -.LIFO 撤销.-> State
end
subgraph Spatial[Spatial · 空间]
Inject -.notify.-> Store
Provided -.ctx.set.-> Store
Store -.activate/deactivate/neutral.-> Inject
end
Context --- Temporal
Context --- Spatial
"Context Paradigm" 这个词意味着什么:论文不是给出一组 API,而是提出一种新的编程范式。类比:OOP 把"对象"作为 first-class 实体;FP 把"函数"作为 first-class;这个范式把"带 effect tracking 和 coeffect resolution 的 context"作为 first-class。论文 §3.3.3 专门讨论了它跟 OOP、FP、COP(context-oriented programming)的对比。
第二篇 · Cordis 核心 API
第 4 讲 · 5 API + 1 流程
Cordis 核心 API:5 个原语走通全流程
范式落到代码就是 5 个原语。这里的 API(Application Programming Interface,应用程序接口)指"框架暴露给组件作者调用的一组函数/方法"。组件作者只用这 5 个 API 就能写出"安全可卸载"代码——runtime 帮你处理剩下的所有事。
5 个核心 API(论文 §5.1 + §5.2 浓缩)
| API | 类别 | 干什么 | 对应理论 |
|---|---|---|---|
| ctx.effect(cb) | temporal | 唯一修改 ctx 的原语;cb 提供 inverse,runtime 自动累积 | revertible effect |
| ctx.use(component, config) | composition | 实例化一个子组件,返回 fiber;卸载时级联到子 | component instantiation |
| ctx.set(key, value) | spatial | 注册一个 coeffect;卸载时自动 unregister,触发依赖者 notify | provision + notification |
| ctx.get(key) | spatial | 读取一个 coeffect 的值(直接读 store,无类型检查) | bare coeffect read |
| ctx[key] (Proxy) | spatial (typed) | 类型安全的 coeffect 访问;走 fiber 链,强制执行 inject 声明 | capability-based access |
一个最小可工作的示例(伪 TypeScript)
// === Provider 组件 ===
class WebSearch implements Component {
inject = []; // 不需要任何依赖
apply(ctx: Context, config: Config) {
ctx.effect(function* () { // effect 迭代器
const api = yield openSearchAPI(config.key);
ctx.set('web_search', api); // 注册 coeffect
yield () => closeSearchAPI(api); // inverse:unregister
});
}
}
// === Consumer 组件 ===
class SummarizerAgent implements Component {
inject = ['web_search']; // 声明依赖
apply(ctx: Context) {
// ctx['web_search'] 是类型安全的访问
// runtime 自动检查 inject 声明
const api = ctx['web_search'];
ctx.set('summarizer', new Summarizer(api));
}
}
// === Orchestrator ===
const root = new Context();
root.use(WebSearch, { key: 'sk-...' }); // Provider 加载 → notify
root.use(SummarizerAgent); // Consumer 加载 → 找到依赖 → ACTIVE
// ...
root.dispose(); // 全部 LIFO 撤销
5 个 API 之外的所有事都是 runtime 帮你做的:LIFO 累积、notify dependents、cascade unload 到 children、HMR 模块替换、inertial 状态机(reload 跑到一半不会被打断)……这就是范式带来的"系统级正确性"。
第三篇 · Koishi 案例 + 跟本仓架构的连接
第 4 讲 · 1 案例 + 3 数据点
案例:Koishi 4 年 + 4000+ 社区插件(论文 §5.3)
Koishi 是一个开源 chatbot 框架,4 年积累 4000+ 社区贡献插件,跑在 Cordis 之上。论文用它证明这个范式能撑住生产级 open ecosystem。
3 个关键数据点
| 数据点 | 含义 |
|---|---|
| 4000+ 社区插件 | open ecosystem 规模——跨作者,每个插件由不同人写,唯一的协调协议就是 coeffect 名 |
| 插件作者不需要写卸载逻辑 | temporal composability 接管——一个新手作者只要走 ctx.effect 路径,自动获得完整清理 |
| 运行时换 IM 适配器 / 换 DB | spatial composability 接管——IM 适配器是 provider,DB 驱动是 provider;切换时只换 provider,consumer 自动 reload |
论文的"存在性证明"立场:作者明确说 (§5.3 末尾)——"the evidence here is drawn from a single ecosystem in a single host language... what the case study establishes is thus an existence-and-adoption result rather than a quantitative one"。他们没声称这个范式在所有场景都更好,只是证明它能撑住 4000+ 插件这个量级,并且跨作者不需要协调。
第 4 讲 · 4 adapter + 1 open problem
连回本仓:4 个 brand adapter 的 Cordis 视角
本仓 experiment_modules/solving/adapters/ 下有 4 个 brand adapter(kimi / qwen / mimo / opencode)+ 1 个 ollama(带 3 个 runner)。用 Cordis 视角审视,能看到清晰的改造方向。
现状 → Cordis 化映射
| 本仓现状 | Cordis 视角 |
|---|---|
| 每个 brand adapter 一个目录,手动 import | 每个 adapter 是一个 component,通过 ctx.use 加载 |
| tool 列表在 adapter 内部硬编码 / 手写注册 | 每个 tool 是 ctx.set(key, impl) 注册的 coeffect |
| adapter 切换 = 改 import 重启 | 切换 = 卸载旧 adapter + 加载新 adapter,runtime 自动 notify所有用 tool 的代码 |
| ollama 三个 runner 各自维护 tool_call 路径(参考 REPORT-ollama-tool-call-substitute-20260902.md) | 三个 runner 是同一个 component 的不同 config,ctx 共享 effect 累积器,无需手写清理 |
论文里的 1 个 open problem(我们也会撞上)
- tool schema 漂移(论文 §6.6):4 个 brand 的 tool schema 是同名但结构不同的——kimi 的 web_search 跟 qwen 的 web_search 字段不完全一致。论文给了 3 个解决方向:key namespacing / peer dependencies / structural compatibility,但都还未彻底解决
- 多版本同 key:npm 之类的包管理器只能选一个版本;但 agent 多 brand 共存时可能需要同一 key 的多个版本——Cordis 目前不直接支持
本系列能给你什么:
- 一套形式化词汇——"这个 adapter 是 component / 这个 tool 是 coeffect / 这个 lifecycle 是 inertial state machine"
- 一个评估现有架构的镜头——哪些痛点用 Cordis 思路可以解决?哪些是 paper 也承认的 open problem?
- 一个未来 roadmap 的方向感——self-evolving harness 不是"再写一个 adapter"能解决的,需要 runtime-level 协调;这就是 Cordis 给的方向