← 返回目录

第 3 卷

第 3 / 6 卷日期:

图纸与实物:两棵树对照。现在树长高了,值得把两张图完整对照起来:

图 7-1
图 7-1

isolate 与 intercept

现在来到这一讲最具魔法的部分。我们来设想一个真实需求:两个租户共用一棵树,但各自需要一个私有的 shell,租户 A 用 mock,租户 B 用 b-provider,互不干扰;租户 B 还要求把

shell 的 model 强制覆盖成 intercept-model。

要让每个租户“自成世界”、各配一个私有 shell,得靠 isolate;要让租户 B“篡改”shell 的配置,得靠 intercept。两者都写在配置里。这是这一讲 cordis.yml 的最终完整版:

复制代码 1# 最终版:完整配置树

2- id: logger 3name: './plugins/logger.ts'

10config:

11provider: deepseek 12model: deepseek-chat

13 14- id: tenant-a# 租户 A 组

15name:'@deepseek-ai/cordis-plugin-group'
16group:true
17isolate:
18shell: tenant-a #给shell服务加前缀tenant-a ——本组自成世界
19config:
20-id: a-shell #组内私有的shell
21name:'./plugins/shell.ts'
22config:
23provider: mock
24model: mock-chat
25-id: a-chat #消费shell的插件
26name:'./plugins/chat.ts'

27 28- id: tenant-b# 租户 B 组

29name:'@deepseek-ai/cordis-plugin-group'
30group:true
31isolate:
32shell: tenant-b #前缀与租户A不同——两个世界互不相通
33intercept:
34shell:
35model: intercept-model #只改本组"看到"的shell配置,b-shell实例本身不变
36config:
37-id: b-shell
38name:'./plugins/shell.ts'
39config:
40provider: b-provider
41model: b-model
42-id: b-chat
43name:'./plugins/chat.ts'

44 45- id: spare

46name: './plugins/chat.ts' 47disabled: true# 保留在配置里,但不挂载

跑一遍 npx tsx src/ch04/main.ts,输出:

4[shell]实例就绪:provider=mock, model=mock-chat
5[shell]实例就绪:provider=b-provider, model=b-model
6[chat]收到:[b-provider/b-model]回复:你好(我看到shell配置:{"model":"intercept-m
7[chat]收到:[mock/mock-chat]回复:你好

两个 chat 的先后顺序因异步竞争每次可能不同,不影响结论。

同一份 shell.ts 代码,跑出了三个实例;两个 chat 各自注入自己组里的 shell;而且只有租户 B 的 chat 看到了被覆盖的 model。整棵插件树长这样:

复制代码 1root(new Context())

2├── logger→ logger.ts(level=info)
3├── shell→ shell.ts(provider=deepseek)
4├── tenant-a(组)→isolate:shell: tenant-a
5│ ├── a-shell→ shell.ts(provider=mock)
6│ └── a-chat→ chat.ts(注入shell)
7├── tenant-b(组)→isolate:shell: tenant-b +intercept: shell.model
8│ ├── b-shell→ shell.ts(provider=b-provider)
9│ └── b-chat→ chat.ts(注入shell)
10└── spare→ chat.ts(disabled,保留但不挂载)

isolate:同名服务各住一间(底层换 symbol 键)。为什么需要它?服务名在 Cordis 里全局唯一,同名只能注册一次,直接往组里放第二个 shell 会当场报错(service "shell"

has been registered)。

解法就一句:给服务名换一个 symbol 键。配置写作 isolate: { shell: tenant-a },这个字符串叫 label,框架会把它换成不同的 symbol。标同一 label 的共享同一份实例,

于是 a-chat 拿到组内 a-shell(mock),b-chat 拿到组内 b-shell(bprovider),谁也看不到根级那个 deepseek。反过来也一样:比如根级的 spare,一旦

启用,没有 isolate 加持,看到的自然就是根级这个 deepseek,隔离是双向的。图纸还是那

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