← 返回目录

第 3 卷

第 3 / 4 卷日期:

解耦的手法不止一种(拆函数、拆包、手写装配都行),dsh 的选择是:借 Cordis 现成的 Service/inject 机制,把可替换能力规范化成三个角色,让框架自动搞定切换、暂停、重

启。这套结构叫 seam。注意:seam 指的是这三个角色的整体,不是其中某一个角色。

图 7-1
图 7-1

复制代码

1// src/ch05/seam-demo.ts(节选,完整代码见该文件)
2import{Context,Service}from'@deepseek-ai/cordis'
3import{ defineTool }from'@deepseek-ai/dsh-tools'

4 5// ── 角色一:Definition(契约)──

6abstract class EnvProbe extends Service { 7constructor(ctx: Context) {

8super(ctx, 'envProbe')

9} 10abstract probe(): Promise<{ platform: string; node: string }>

11} 12

13// ── 角色二:Provider(实现,可替换)── 14class LocalEnvProbe extends EnvProbe {

15asyncprobe() {return{platform: process.platform,node: process.version} }
16}
17classFakeEnvProbeextendsEnvProbe{
18asyncprobe() {return{platform:'<fake:platform>',node:'<fake:node>'} }
19}

26ctx.tools.register(defineTool({ 27name: 'env_probe',

28// ... output与parameters写法同上一节,此处略
29asyncexecute() {
30returnctx.envProbe.probe() //只依赖契约,不关心具体实现
31},
32}))
33},
34 }</fake:node></fake:platform>

为什么非要切三个角色?

关键在于中间那层契约:Consumer 的 inject 写的是契约名(envProbe),不是某个具体类,它压根不知道、也不关心背后是 local 还是 fake。于是 Provider 可以放心换成任意实

现,Consumer 一行都不用改。换的过程中,框架先把失去依赖的 Consumer 自动暂停,等新的 Provider 就位再自动重启,绝不会出现“调用到一个不存在的东⻄”的中间态。

跑 npm run start:ch05:seam 看这个魔术(execute 返回 { isError, content, value },下面只展示 value 的变化):

复制代码 1--- 提供方 = local ---

2{ value: { platform:'darwin', node:'v24.4.1'} }
3---卸载Provider后,剩余工具---
4[] ←只卸了Provider,工具一行没动,它自己消失了
5---换上fake后,剩余工具---
6["env_probe"] ←工具自动回来
7{ value: { platform:'<fake:platform>', node:'<fake:node>'} }</fake:node></fake

这套三角色不是玩具,dsh 的 shell seam 真实存在,写进声明式配置(cordis.yml)就是三行,换实现只改 Provider 那一行:

回到 EnvProbe 那几行:三角色其实完全落在基础篇讲过的 Cordis Service 机制上—— EnvProbe 通过 super(ctx, 'envProbe') 注册一个名为 envProbe 的服务;

LocalEnvProbe / FakeEnvProbe 继承它,实例化时就把各自实现挂到这个名字下;

Consumer 用 inject: ['envProbe'] 只按名字取用,压根不认识具体类。也正因如此, Definition 必须是 Service 子类(抽象类),不能是 interface,因为 interface 没有

super,注册不了服务。

最后需要注意:不是所有场景都适合用 seam,我们可以参考下面的表格来判断:

图 9-1
图 9-1

本讲小结

今天这一讲,我们跨进了 dsh,回答了“dsh 里 Agent 动手做事的能力从哪来”这个问题。

我们用真实 dsh 写了一个 greet 工具并跑通全链路,看清了三件事:

  1. 模型只看得见 name/description/parameters 三样;
  2. 返回值要拆成“给程序的值 + 给模型的呈现”;
全站分卷索引 · 4 卷
  1. 1第 1 卷
  2. 2第 2 卷
  3. 3第 3 卷
  4. 4第 4 卷