第 1 卷
张嘉熙 · DeepSeek Harness 前沿工程实践



你好,我是张嘉熙。
上一讲我们解决了“时间”维度的问题——单个插件能否安全地来、安全地走。但现实中的应用,比如 Agent 应用,往往并不只有一个插件。当几十上百个插件同时挤在一个环境里,而
每个插件又有它所依赖的多个服务,一个更尖锐的问题就浮出水面了:多个插件(A、B、C)
都依赖服务 Z 提供的能力,Z 突然被卸载了,这些插件应该怎么办?
Z 可能在运行中途被热替换、被动态卸载、被配置开关关掉。每一次变化,每个依赖 Z 的插件都要重新判断“我还能不能用”。纯靠手动编排,这套逻辑很容易变成一团乱麻,这正是典型
的框架层应该做的事情。
今天,我想带你看 Cordis 给出的答案:你只需要声明“我依赖谁”,框架会自动决定什么时候激活你、什么时候停用你。这就是“空之协同”——这里的“空”指的是空间维度,即组件之间
的依赖与协同。
提供能力的 Service
我们先了解一些基本概念,在 Cordis 里,“能力”是通过 Service(服务)提供的。所谓服务,其实就是一个具名能力——你把它注册到 ctx 上,同时提供一个服务的名字,其他插件
就能通过 ctx.<服务名> 访问它。
复制代码 1// 来自官方 cordis-tutorial 第三章的 greeter.ts
2import { Service, type Context } from '@deepseek-ai/cordis' 3
4declare module '@deepseek-ai/cordis' { 5interface Context {
| 6 | greeter:GreeterService | //声明合并:把greeter挂到Context类型上 |
|---|---|---|
| 7 | } | |
| 8 } |
9
| 10 | exportclassGreeterServiceextendsService{ |
|---|---|
| 11 | constructor(ctx:Context) { |
| 12 | super(ctx,'greeter') //以名字'greeter'注册这个服务 |
| 13 | } |
| 14 | greet(who:string) { |
| 15 | return`Hello,${who}!` |
| 16 | } |
| 17 | } |
18 19export function apply(ctx: Context) {
20ctx.plugin(GreeterService)// Service 子类本身就是一个插件(类形态)
21}
这里我们可以看到:GreeterService 通过 super(ctx, 'greeter') 把自己注册成名为 greeter 的能力,之后任何插件都能通过 ctx.greeter 拿到它。
代码里有两处值得注意:
自动从上下文移除,不用你手动清理。
- declare module '@deepseek-ai/cordis' 是 TypeScript 的声明合并,把
greeter 这个字段“焊”进 Context 接口,让 ctx.greeter 处处能通过类型检查。注意:它不产生任何运行时代码——删掉它,服务在运行时照样工作,但消费方会失去类型安
全,类型化事件体系也会随之退化为脆弱的字符串约定。
声明依赖的 inject
有服务了,还得有人用它。下面我们看看本讲的第二个关键概念:inject(注入)。
复制代码 1// 来自官方 cordis-tutorial 第三章的 consumer.ts
2import type { Context } from '@deepseek-ai/cordis' 3
4export const name = 'consumer' 5export const inject = ['greeter']// 声明依赖:我需要 greeter 服务
6 7export function apply(ctx: Context) {
8console.log(ctx.greeter.greet('world'))// 依赖已就绪,放心使用 9}
看最关键的一行——inject = ['greeter']。这就是《A Programming Paradigm for Spatiotemporal Composability》论文里说的“依赖规格”(specification):它只声明“我需
要哪些服务”,仅此而已,你没有写任何“加载顺序”、“启动检查”、“重试逻辑”。
而 Cordis 保证了一件至关重要的事:当 apply 被调用时,inject 里列出的服务一定已经就绪。如果 greeter 还没上线,consumer 插件就会一直停在 PENDING(挂起)状态,不执
行任何代码;等 greeter 一上线,它才被激活。
inject is not a one-shot boot check——它不是启动时检查一次就完事,而是持续跟踪。