看到 DeepSeek Harness 的"一切皆插件",我最关心的是:到底能替换到哪一层?
增加一个工具,接入一个模型,还是连 Agent 的执行方式都能改?
顺着 dsh 的公开文档往下看,会发现模型适配器、工具注册表、会话日志,甚至 Agent loop 本身,都被放进了同一套插件组合机制。
开发者能够调整的范围,因此从"Agent 有什么能力",延伸到了"Agent 怎样运行"。这是理解 dsh 的起点,也是它最值得展开讲的地方。
一、执行循环也能替换
一个 Agent 的基本工作过程,可以画得很简单:接收输入,准备上下文,请求模型;模型提出工具调用,程序执行并记录结果,再把结果交给模型继续处理。
一次用户输入,可能在内部触发多次模型请求。Agent loop 负责组织这些步骤。

单看这个循环,并没有特别神秘的地方。dsh 值得关注的是:组织循环的组件,本身也是插件。
模型适配器、工具注册表、会话日志和默认执行循环,都可以通过配置组合与替换。它们共同依赖的插件底座是 Cordis。官方架构文档
这让扩展有了不同深度。增加工具,可以扩充能力;接入模型请求事件,可以调整请求行为;替换压缩组件,可以改变上下文处理方式。默认循环确实无法满足需要时,还可以实现自己的 Agent 驱动。
当然,能替换不代表可以忽略接口约定。新实现仍然需要正确处理输入、状态、取消和资源释放等问题。这里的价值在于,框架给出了可替换的位置,开发者不必把所有变化都塞进同一个核心实现。
连默认运行方式都保留扩展空间,是 dsh 插件架构最有辨识度的一点。
二、插件怎样正确退出
插件加载成功,只完成了一半。
它可能注册了工具,添加了监听器,又往提示词里贡献了一段内容。卸载时,这些注册也需要清理。否则,重新加载几次以后,旧监听器和重复注册就可能留在运行环境里。
Cordis 为此提供了依赖注入和生命周期机制。插件通过稳定的服务名使用能力,例如 ctx.llm、ctx.tools 和 ctx.sessions,通过依赖声明等待服务就绪。
工具、监听器和提示词片段等注册,则跟随所属插件的生命周期清理。Cordis 入门
这样,组件替换就有了明确的归属:谁注册,谁拥有,退出时由谁撤销。使用服务的插件,也不需要到处绑定某个具体实现。
这里的"可逆"有准确范围。撤销一个工具注册,不会自动撤销它已经修改的文件;释放运行资源,也不等于回滚所有外部操作。
这部分底层能力来自 Cordis。dsh 的设计价值,是把它贯穿到 Agent 的主要运行组件中。讨论它的创新,应该把这层来源讲清楚。
三、共享能力,分层装配
组件能替换以后,还要回答一个问题:这次替换影响谁?
dsh 使用 Profile、Agent preset 和作用域表达不同范围。把这些名字放回各自的位置,就容易理解了。
| 概念 | 主要职责 |
|---|---|
| Profile | 定义启动哪一套应用组合 |
| Agent preset | 定义创建 Agent 时使用的插件组合 |
| Agent | 实际工作的运行实例 |
| Session | 保存会话事件,提供模型历史的来源 |
Profile 是具名的启动组合。web、headless、sdk 等入口,通过同一个启动器加载不同组合,并应用配置覆盖。它不能直接等同于开发、测试、生产环境。应用启动文档
preset 更接近装配模板。同一套模板可以创建多个 Agent,也可以声明多套模板,供不同会话选择。加载了模板,不代表候选 Agent 已经全部启动。
已有 Agent 的组合保持稳定,修改后的模板供后续创建的 Agent 使用。这个约定能避免一次模板编辑,让正在执行的实例突然换掉整套能力。Agent preset 文档
作用域进一步控制注册的可见范围:可以提供宿主级公共能力,也可以只向特定 Agent 注册工具和监听器。不过,作用域本身不构成操作系统沙箱。作用域文档
因此,"共享"需要说具体。多个 Agent 可以复用插件代码和公共服务,同时保留各自的配置与会话历史。复用了同一份代码,不等于运行状态和模型上下文也合成了一份。
四、日志与上下文分开管理
这一点不如插件口号显眼,却直接影响系统能不能解释清楚自己的行为。
dsh 的 Session 使用仅追加的事件日志,再从日志中派生模型可见的消息历史。
发生过什么,与下一次让模型看到什么,可以分别管理。
一次工具返回的原始文本,可以保留在日志中;后续请求使用经过修剪的版本。较早的一段对话,也可以在模型可见的历史里被摘要替代,而原始事件仍然存在。

Session 文档将"模型可见即已记录"作为约束:模型请求中的内容,应当能够从日志重建。Session 文档
模型漏掉某个信息时,就有了更具体的检查方向:信息有没有进入会话?进入了原始日志,还是也进入了当前消息视图?中间是否发生过修剪或压缩?
屏幕上看得到一段内容,并不能充分说明模型当时也看到了它。
重建输入,也不保证重新调用模型会生成完全相同的输出。跨进程保留记录,则需要配置持久化后端;Session 组件本身使用内存存储。
这套设计给上下文处理留出了空间:既能控制当前输入长度,又能保留检查内容来源的依据。
五、判断与执行各司其职
"一切皆插件"不意味着所有插件都由模型挑选,也不存在每轮必须执行一遍的固定插件清单。
读取文件这样的工具,可以由模型选择。上下文压力检查、会话记录和执行策略,则有自己的程序触发点。
以原生工具调用为例,模型看到当前可见工具的名称、描述和参数结构。模型提出调用后,框架校验参数、执行允许/拒绝/询问策略,再分发到具体实现。工具执行文档
dsh 还区分了会话事件、Agent 事件和能力事件,供插件接入不同阶段的行为。扩展者需要选择合适的事件接口,遵守其分发与调用约定。
这条边界很重要。模型可以承担语义判断和内容生成,已经明确的执行约束则需要程序落实。不能只在提示词里写一句"请遵守规则",就认为框架已经完成了控制。
六、用压缩检验组合设计
架构最终要落到具体功能上。上下文压缩就是一个很好的观察点。
当会话越来越长,基础压缩实现会根据窗口容量、输出预算和预留空间判断压力。组合了工具结果修剪器以后,处理过程大致如下:

修剪器按规则缩短超大的工具文本结果,保留头尾并加入标记。这一步不调用模型。如果修剪后空间已经足够,就可以跳过摘要。工具结果修剪器
仍然过大时,才需要额外调用模型提炼较早历史,同时保留近期内容。压缩组件还要负责范围选择、结果验证和状态提交。基础压缩文档
这里能看出组合的价值:压力测量、规则修剪、模型摘要,各自承担明确职责。改变摘要方式时,可以复用已有的状态管理和校验机制,把定制集中在摘要实现上。
摘要毕竟会丢失细节。保留日志,提供了追查来源的条件,却不能保证模型压缩后仍然掌握全部信息。压缩也不等于提取跨会话的长期记忆,那需要另外设计存储、写入和检索链路。
最后,怎样看 dsh 的创新
我更看重的是这些机制组合起来后的结果。
核心运行组件可以替换,依赖和生命周期有统一管理,公共能力与局部配置能够分层,会话记录又为上下文变化保留了来源。开发者于是有机会调整 Agent 的运行方式,同时沿用已有的接口、记录和控制机制。
这里有来自 Cordis 的底层能力,也有 dsh 面向 Agent 场景作出的具体设计。将它们贯穿成一套可组合的运行架构,是 dsh 最值得研究的地方。
接下来值得观察的,是这套设计能否经受真实扩展的检验:换掉一个核心组件后,其他部分还能否沿着原有接口工作;加入新插件后,依赖、状态和退出行为是否仍然说得清楚。
这些问题,比插件数量更能说明架构的成色。