解释 dsh 如何通过 Cordis 将模型适配器、工具、会话与 Agent 循环全部实现为可替换插件。
上一节,你在本地 Web 控制台完成了 DeepSeek API 密钥配置与模型提供方选择,并用一条模型回复验证了整条链路已经生效。那次回复看似来自页面,背后其实是一个正在运行的 dsh 进程。
先摆正一个定位:模型负责推理,Harness 负责“身体”——把模型接到文件、终端、工具、会话与界面上。dsh 正是这样的 Agent 运行时,官方理念只有一句话:Everything is a Plugin(一切皆插件)。
现在问关键问题:刚才那次对话,究竟是由什么“跑”起来的?答案是 dsh 的底层内核 Cordis。模型、工具、会话、存储、沙箱、循环、调度、界面——所有能力都由插件组合而成,可以自由替换与重组。这意味着:你不需要修改任何源码,就能在配置层定制自己的 Agent。
核心概念
Cordis:dsh 的底层插件内核
生活类比:把 dsh 想象成一座城市。供水、供电、通信、交通分别由不同公司负责,市政府不亲自供水,只做两件事:批准谁可以接入,协调各家一起运转。哪家公司被替换,城市其他部分照常工作。
技术定义:Cordis 是 dsh 的底层插件内核,相当于这座城市的“市政府”。它负责挂载(mount)与卸载(unmount)插件、解析插件之间的依赖,并提供插件彼此协作所需的服务与事件。dsh 不是一套写死的程序,而是构建在 Cordis 之上的一组插件集合。Cordis 提供的是组织机制,不是具体能力——模型、工具、界面都来自插件,而不是来自内核。
因为内核只负责组织,任何能力都不享有特殊地位。这也意味着,dsh 的每次启动都只是把一组插件组合起来,而不是启动一个写死的大程序。记住一句话:Cordis 不生产具体能力,它只负责把能力组织起来。
核心概念
插件:能力的标准单元
生活类比:把插件想成电源插座上的电器。插座接口是标准的:插上灯泡是照明,插上电饭煲是做饭。想换功能,拔下来换一个电器即可,插座本身不用改。
技术定义:插件是向共享上下文贡献能力的基本单元,贡献三类东西:服务(其他插件可调用的接口)、类型化事件(可被监听的通知)、可逆效应(加载时产生的注册,卸载时自动回滚)。在 dsh 中,一切能力皆插件:模型适配器、工具注册表、会话日志、沙箱、存储、Agent 循环、界面,无一例外。
你要扩展 dsh,就在其他插件旁边再挂载一个新插件——而不是修改特权核心。这正是“一切皆插件”的含义:能力像积木一样可拆可装。你后续要写的工具、事件监听与 LLM 适配器,也都将是这种插件。
核心概念
共享上下文:插件的公共工作台
生活类比:想象一间共享办公室。每个插件把工具放到公共工作台上并贴上标签;别的插件需要某种能力时,走到台前按标签取用,不必知道工具原来放在谁的抽屉里。
技术定义:共享上下文(shared context)是插件贡献与获取能力的场所,在代码中以 ctx 为入口。dsh 的每个核心子系统都在 ctx 上挂一个键:ctx.llm(模型消息与流式词汇、适配器接缝)、ctx.tools(作用域工具注册表与受守卫的执行流水线)、ctx.sessions(仅追加的会话事件日志)、ctx.agents(Agent 接口与实时注册表)、ctx.agentLoop(实现 Agent 接口的默认驱动)、ctx.systemPrompt(提示词与工具 schema 装配)。
事件同样挂在共享上下文上:session/event、agent/*、fs/*、tools/*、telemetry/* 是现成的扩展点。现在你只需知道事件是插件协作的通知渠道,具体机制在后续课程展开。
这些键不是装饰:你的插件通过它们与其他插件协作。当你读到 ctx.xxx 这样的写法,含义就是“从共享上下文取一个服务”。
核心概念
服务:插件的公共接口
生活类比:把服务想成公共服务台。你要开发票,不需要知道财务部门内部如何运作,只要在“财务服务台”递上单据就能拿到结果。服务把“如何实现”藏在柜台后面,把“如何调用”写在台面上。
技术定义:服务是插件贡献给共享上下文的具名能力,通过 ctx 键对外提供。例如 ctx.sessions 就是 session 子系统对外提供的服务。Cordis 的核心职责之一是解析插件之间的服务依赖:一个插件声明“我需要会话服务”,Cordis 会在挂载它之前把服务准备好。
只要接口不变,服务的实现可以被整体替换——这正是“可替换插件”能落地的机制。比如想换一种持久化方案,就替换提供 ctx.sessions 背后实现的那个插件,调用方不会感知变化。
核心概念
Agent 循环:循环本身也是插件
生活类比:把 Agent 循环想成水泵里的叶轮。它不停做同一件事:吸水、推水,驱动整个系统运转。在 dsh 里,这个不停转动的叶轮不是焊死在泵体上的——它是可以拆下来换掉的零件。
技术定义:Agent 循环是驱动一次 Agent 运行的核心流程:向模型发消息、收到响应、调用工具、把结果交还模型、判断是否继续,直到任务结束。dsh 把“循环”拆成两层插件:core/agent 定义 Agent 接口与实时注册表(ctx.agents),core/agent-loop 提供实现该接口的默认驱动(ctx.agentLoop)。想换循环策略,你替换插件,不碰内核。接口与实现分离,正是“循环可替换”能成立的前提。
模型适配器同理:llm/llm 提供消息与流式词汇,并留出适配器接缝(ctx.llm)。模型、工具、会话、循环——没有哪一样是焊死在程序里的,每一层都可由配置决定。
把这几块拼起来,会得到一个关键结论:dsh 没有需要打补丁的特权核心。要扩展它,就在其他插件旁边挂载一个新插件;注册是“可逆效应”,插件卸载时,它产生的注册会自动回滚,不留残渣。
所以 dsh 更像“用于构建编码 Agent 的模块化运行时”,而不是功能固定的编码助手。换存储、加工具、组装自定义模式,都可以通过配置完成,不必重写运行时。没有特权核心,也意味着没有一处“必须动源码才能改”的死角。
对你的直接收益:你现在见到的“模型对话”,只是默认插件组合的结果,而组合的每一层都可以被替换。
这套架构带来两个实用改变。
第一,问题有明确归属。模型响应不对,去检查模型适配器插件;工具不被调用,去检查工具注册表插件;循环行为异常,去检查 agent-loop 插件。每一处都对应一个插件,而不是在一个大程序里翻找。
第二,替换优先于修改。遇到不满意的默认行为,先问“哪个插件提供了它”,再决定换插件还是加一层插件。下一课你会看到这些替换如何在配置层被组装起来。
另外,dsh 把每次运行写入仅追加的会话日志,可检索、可回放——排查加载与运行故障时,这条记录会反复帮你定位是哪个插件出了问题。
看到 ctx.llm,能力来自 LLM 插件;看到 ctx.sessions,来自 session 子系统;看到 ctx.tools,来自工具注册表;看到 ctx.agents 与 ctx.agentLoop,来自 agent 与 agent-loop。想替换某个行为,就顺着这个键找到提供它的插件,再看它的接口。
官方目前将 dsh 定位为 developer preview(开发者预览版),并明确提示未来可能出现破坏兼容性的变更。要把它用于自动化流水线,请钉住具体包版本(例如 @deepseek-ai/dsh 的确切版本号),升级前核对当前文档。可替换性是双刃剑:组合越自由,插件之间的版本、权限与共享服务就越需要一起测试。
自检:用一段话说明:为什么在 dsh 中,模型适配器、工具、会话与 Agent 循环全都是可替换插件?
因为 dsh 构建在 Cordis 内核之上:Cordis 只负责挂载与卸载插件、解析依赖、提供协作所需的共享上下文,而具体能力全部由插件贡献。模型适配器(ctx.llm)、工具注册表(ctx.tools)、会话日志(ctx.sessions)、Agent 循环(ctx.agentLoop,接口与注册表在 ctx.agents)都是挂在共享上下文上的插件,系统没有特权核心;注册是可逆效应,插件卸载即自动回滚。因此替换实现只需换对应插件,无需修改源码。