← 返回目录

第 1 卷

第 1 / 6 卷日期:

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

图 1-1
图 1-1
图 1-2
图 1-2
图 1-3
图 1-3

你好,我是张嘉熙。

前三讲,我们看着一棵树慢慢长成:根由 new Context() 造出(顺带也造出根 fiber),此后每调一次 ctx.plugin() 就长出一个节点,这棵树我们称之为插件树,也就是这一讲的主

角。

第一讲,它怎么长:ctx.plugin() 挂上可拆卸的枝条,ctx.effect() 声明可撤销的副作用。第二讲,树上怎么协作:inject 声明依赖,服务一上线就自动激活。第三讲,节点之

间怎么通信:事件在 ctx 上传播,五种派发方式各有分工。

机制都认识了,可现实中通常大量插件并存:谁挂在哪一层、各自带什么参数、怎么按租户分组——这些装配的活儿全交给代码一行行写死,就成了又长又机械的流水线:加个插件要改代

码,换个参数要改代码,多一个租户还是要改代码。

和谁隔离、谁拦截谁,全都写在这一份 YAML 里。你只需要描述“树应该长成什么样”,框架负责把它变成实物——这就是所谓声明式配置(cordis.yml + Loader),与前三讲那种“一

步步指挥框架干活”的命令式写法正好相对。前三讲我们用纯代码一步步看清底层,这一讲我们将重点转向 Cordis 的声明式 YAML 配置。

那么问题来了:一份 YAML,怎么就能长成一棵运行时插件树?我们先从一份最小的配置开始,把链路完整跑通。

从配置树到插件树

先跑起来:最小配置,最小插件

先不搞复杂。我们把 cordis.yml 改成最小版——只有一个条目:

复制代码 1# 最小版:只有一个条目,先把链路跑通*

2*- id: logger*

3*name:'./plugins/logger.ts'*
4*config:*
5*level: info

跟着我一步步把它扩充,最终版见本讲下半部分。

这里我们先分清这一讲会出现的两棵树:

配置树:写在 YAML 里的配置,它是图纸

插件树:等配置跑起来长成的那棵,它是实物

我们再看看这份配置对应的加载逻辑:建根、挂 Loader、把 include 当配置树第一根枝条挂

3import { fileURLToPath, pathToFileURL } from 'node:url' 4import Loader from '@deepseek-ai/cordis-plugin-loader'

5 6const baseUrl = pathToFileURL(fileURLToPath(new URL('.', import.meta.url))).href

7

8constctx =newContext()//①造出根
9ctx.baseUrl= baseUrl// 告诉它相对路径从哪找
10awaitctx.plugin(Loader, { baseUrl,enableLogs:true})//②挂上Loader:在ctx上

11

12//③把include插件本身当成配置树的第一根枝条挂上去:
13// 它的config指向cordis.yml——include读文件、解析成条目列表
14// 再把这整棵配置树挂载成运行时插件树
15awaitctx.loader.create({
16name:'@deepseek-ai/cordis-plugin-include',
17config: {path:'./cordis.yml'},
18})

19 20await new Promise((resolve) => setTimeout(resolve, 300))// 等所有条目挂完再退出

输出只有一行:

复制代码 1[logger] 启动(level=info)

就这一行,但整条链路已经走完了:YAML 里的一行条目 → 一个 Entry(配置树节点)→ 一次 registry.plugin() 调用 → 一个 Fiber(运行节点)→ 插件 apply() 跑起来 → 打

印。

全站分卷索引 · 6 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷
  5. 5第 5 卷
  6. 6第 6 卷