第三幕 · 可插拔能力 / 07 能力缝:可替换的能力
0%
第三幕 · 可插拔能力 · 第 07 章

能力缝:可替换的能力

接口、实现、使用方三件套。换一个实现,整个产品跟着变 —— 这是 dsh 魔法的真正来源。

🔌开场小故事:插座与插头

你的笔记本电源适配器坏了。你不需要换电脑,只需要买一个「同规格的适配器」。因为接口是标准的:插头、电压、电流都约定好了——这就是为什么你可以借同事的充电器救急,而不是把整台电脑扔进垃圾桶。

dsh 里每一类可替换能力都遵循同样的思路,并且给它起了一个专门的词:seam(能力缝)。seam 是「可替换能力的完整三件套」——少了任何一件,都不叫 seam:就像充电器少一个脚,插不进任何插座。

一句话讲清楚:一个 seam 包含三种角色 —— Service Definition(声明接口)、Service Provider(实现它)、 Consumer(使用它);替换提供方 = 替换整个能力,而所有使用方一行都不用改。
能力缝三角色:接口、实现、使用方
图 07-1 以 bash 执行能力为例:定义接口的是 dsh-shell,实现它的是 bash-local / bash-sandbox,使用它的是 tool-bash 等消费方。

三件套拆开看

① Service Definition —— 接口

拥有自己的 ctx.<key> 和类型词汇的 Cordis 服务(注意:是 Cordis Service, 不是 TypeScript interface)。它回答「这项能力长什么样、能做什么」。 例如 ctx.shell 声明「bash 执行器」应该提供什么。

② Service Provider —— 实现

真正干活的实现,通常不止一个:ctx.shelldsh-bash-local (本机执行)和 dsh-bash-sandbox(沙箱执行), 还有 Windows 的 dsh-pwsh-local(PowerShell)。

③ Consumer —— 使用方

注入并使用该服务的插件,通常是面向模型的工具: dsh-tool-bash 让模型能跑 bash 命令;hooks 桥接(Claude Code / Codex)也消费它。

⚠️官方特别提醒

seam 是完整能力,绝不是其中一个角色。 术语「seam」只保留这个含义;成员应该按角色、类、服务来命名。 一个包也可以同时承担多个角色 —— 比如 dsh-llm 同时是 Service Definition 和 Consumer。

换一个提供方,世界就变了

这就是 dsh 最迷人的地方:文件系统与进程提供方共享同一个执行世界。 把 ctx.fsctx.subprocess 的实现从「本地」换成「E2B 远程沙箱」, 那么基于它们构建的 Bash、PTY 终端、LSP 语言服务器 —— 全部一起搬到了远程 Linux 环境, 不需要任何提供方专用的 fork,消费方一行不改

类似的 seam 遍布全产品:

ctx 键能力提供方示例
ctx.llm模型适配llm-deepseek(DeepSeek)、llm-pi-ai,以及测试用的 llm-replay
ctx.fs文件系统fs-local(本地)、fs-sandbox(受限)、fs-e2b(远程)
ctx.subprocess子进程subprocess-local、subprocess-e2b
ctx.subagents子代理传输进程内 spawn/fork、ACP、Codex、Claude Code、dsh-sdk
ctx.web网页搜索/抓取web-search-exa / perplexity / deepseek、web-fetch-http
ctx.sandbox进程沙箱sandbox-local(按策略包装 argv)
ctx.sessionPersistence会话持久化session-persistence-jsonl、session-persistence-sqlite
ctx.approval审批通道ACP 为自己的 agent 提供桥接监听器
💡为什么「消费方只认 ctx 键」这么重要?

因为依赖被固定在「接口」上而不是「实现」上(第 04 章讲的服务按 key 查找)。 于是:换模型不用改循环;换沙箱不用改工具;换持久化后端不用改任何业务插件。 整个产品的能力边界由这些 seam 划出 —— 读源码时看到 ctx.xxx, 就知道这里有一道「可以换插头」的缝。

费曼自查

1seam 的三个角色分别是什么?为什么缺一不可?
参考:Service Definition(接口)、Service Provider(实现)、Consumer(使用方)。缺了接口无法替换实现,缺了实现没人干活,缺了使用方能力没意义 —— seam 是完整能力,不是单一角色。
2「把执行世界换到远程沙箱,Bash、PTY、LSP 全都跟着搬走」——为什么不用改它们?
参考:因为它们消费的是 ctx.fs / ctx.subprocess 这些接口,而不是某个具体实现。实现换成 E2B 后,所有基于它们的消费方自然运行在远程环境 —— 这就是「换提供方 = 换整个能力」。
3dsh-shell 这个包在 shell 这个 seam 里承担什么角色?tool-bash 呢?
参考:dsh-shell 是 Service Definition(声明接口);dsh-bash-local / dsh-bash-sandbox 是 Provider;dsh-tool-bash 是 Consumer(面向模型的工具)。
4一个包可以同时承担多个角色吗?举例。
参考:可以。dsh-llm 同时是 Service Definition(声明 ctx.llm)和 Consumer(自己也会调用模型能力)。角色需要独立演进时才拆到不同包。