聊聊DSH,感觉不是传统意义的harness

muxiesan 2026-08-15 21:55 1

从 Pi 到 DSH:一个可以动态重组的 Agent 运行时



这次我们从论文、源码一路看到 GUI 和自定义模型,顺手把结果整理了下来。



DeepSeek Harness(下面简称 DSH)乍看也是一个 Coding Agent。实际跑过以后,我们发现它和 Pi、Codex 这类产品的重点不太一样。


DSH 先提供一个插件运行时,再用插件拼出具体的 Agent。模型、工具、权限和 Agent Loop 都在这套插件系统里。创造模式(ID 为 cordis)甚至允许 Agent 检查和修改当前运行时。


我们觉得最好用的类比还是:



Pi 像 Vim,DSH 像 Emacs。



这个比喻不严格,但能说明两边的设计取向。下面聊聊我们实际确认了什么。


DSH 还在快速迭代。文中的结论以我们检查时的仓库和运行时为准。


DSH 先搭运行时,再组装 Agent


多数 Coding Agent 都围绕一个固定的 Agent Loop 展开:


读取用户输入
→ 调用模型
→ 执行工具
→ 把结果交还模型
→ 继续循环

模型、工具、权限和界面都接在这个循环上。


DSH 连 Agent Loop 也做成了 Cordis 插件。不过在当前 Web 组装里,并不是所有插件都放进 Preset。Agent Loop、模型路由、会话持久化、Sandbox 和 Approval 等进程级能力留在 Host;Preset 主要负责一个 Agent 面向模型的那一层。


结构大致如下:


DSH Host(进程级共享)
├─ Agent Loop / Agent Factory
├─ 模型路由与 Provider
├─ Session / Persistence
├─ Sandbox / Approval
├─ 工具、Skills、Subagent 等注册表
└─ Agent Preset(按 Agent scope 生效)
├─ Persona 与提示词段
├─ 文件、Shell、Web 等工具
├─ Skills 入口
├─ Plan、Goal、Compaction
├─ 子 Agent 与工作流工具
└─ 可选的 Cordis 动态工具

所以,DSH 里的 Agent 仍然是一组插件的组合,但 Preset 管的是其中按会话变化的部分。进程级基础设施仍由 Host 统一提供。


Cordis 论文到底在讲什么


Cordis 的论文叫 A Programming Paradigm for Spatiotemporal Composability。它要解决的问题很具体:组件在运行时加入、退出或替换以后,系统怎么保持干净、稳定?


论文把动态组装分成两个方向:



  • 时间可组合性(temporal composability):一个组件离开以后,它曾经给系统造成的影响能否被完整撤销。

  • 空间可组合性(spatial composability):组件之间的依赖关系发生变化以后,系统能否自动重新连接、激活或停用受影响的组件。


这里的“空间”指依赖图,不是机器或进程的位置。“时间”指组件从加载到卸载的生命周期。


时间组装:插件卸载后要收拾干净


很多插件系统都有 dispose()unload()。问题是,少清一个事件监听器或定时器,插件虽然停了,副作用还留在进程里。


Cordis 把一次副作用写成:


e : Γ → Γ × (Γ → Γ)

Γ 是运行时能管理的上下文。副作用 e 改变上下文时,还要返回一个逆操作。


如果加载时按 A → B → C 注册了工具、监听器和定时器,卸载时就按 C⁻¹ → B⁻¹ → A⁻¹ 清理。工程上就是一个 LIFO 清理栈。


Cordis 的卸载会撤销插件通过上下文登记的影响,不动其他插件。这里说的恢复是“观测等价”:不用把内存逐字节还原,只要外部看到的服务和行为恢复了即可。


空间组装:运行时一直维护依赖图


effect 记录组件改了什么,coeffect 记录组件需要什么。论文把依赖环境表示成一张从键到服务值的表:


Σ = (k : K) ⇀ Vₖ

组件声明依赖 d 和供给 p。只有 d 全部满足时,组件才会激活。服务加入、移除或更换提供者时,运行时会重新检查相关组件。


放到 DSH 里看就很直观了。Agent Loop 需要模型、工具和会话服务。模型适配器还没加载时,Loop 不启动;适配器加载后,Loop 自动激活;适配器被替换时,依赖它的组件重新绑定;适配器退出时,依赖者先卸载。


普通依赖注入通常在启动时做一次解析。Cordis 会在整个运行期维护这张依赖图。


时间和空间怎么接到一起


论文把状态、逆操作和依赖表放进同一个递归上下文:


Γ∞ = μΓ. Γ × (Γ → Γ) × Σ

一个上下文同时保存当前状态、清理函数和可用依赖。它还能派生子上下文,最后形成一棵组件树。


论文把组件实例叫作 fiber。每个 fiber 都记录自己的依赖、状态和清理函数。加载组件时执行 effect,卸载时执行 recover;父组件卸载时,子组件也跟着清理。


提供一个服务本身也是可逆 effect。插件卸载时,服务被撤回,依赖它的 fiber 随之停用。时间和空间就是这样接起来的。


论文把一个组件概括为三元组:


Component = Dependencies × Provisions × RevertibleEffect

也就是:我需要什么、我提供什么、运行时会改什么,以及怎么撤销。


在没有运行失败、逆操作正确、依赖声明完整等前提下,最终状态只取决于最后的组件配置,不取决于中间的加载和替换顺序。可以把它理解成:折腾完以后,运行时应该和“从一开始就按最终配置启动”基本一致。


这些公式怎么落到代码里


论文里的概念和 Cordis API 基本是一一对应的:





















































论文中的概念 工程含义 Cordis 中的落点
Γ∞ 可递归组合的运行时上下文 ctx 与上下文树
effect 与 inverse 一次状态变化及其撤销方法 ctx.effect(callback);回调返回或逐步产出 disposer
accumulator / recover 按逆序合成全部清理动作 fiber.dispose 与卸载流程
Σgetset 服务供给、查询与撤回 ctx.get()ctx.set()set 本身也受 effect 跟踪
dpe 组件的依赖、供给和副作用 fiber.inject、组件的 provide、fiber.apply
fiber 一个组件的运行时实例 ctx.use() 创建的实例及其生命周期状态
target / committed view 希望绑定的依赖与当前已提交依赖 refresh 驱动激活、卸载和重新加载
声明式组件树 系统希望达到的最终组合 Cordis loader 的配置协调与热更新;DSH 在更上层用预设描述 Agent 组合

开发者要做两件事:给副作用配上逆操作,并把依赖声明清楚。清理顺序、依赖变化和生命周期切换交给 Cordis。


当然,这套保证有边界:



  • Cordis 不会自动证明 inverse 写对了。

  • 没有通过 ctx.effect 等机制登记的修改,Cordis 管不到。

  • 已经发出的网络请求和外部消息不会被自动撤回。


所以 DSH 的“自进化”不是随意改写整个进程。Agent 改的是组件图,Cordis 负责把变化局部地应用到运行时,并在需要时撤销。


Preset 到底控制什么


在当前实现里,一个 Agent Preset 就是一个目录,核心文件是 agent.cordis.yml。文件里是一组 Cordis 插件行。某个会话选择 Preset 后,DSH 会把这组插件挂到该 Agent 的 scope 上。


更具体一点,每个 Preset 代际在进程内只挂载一次,使用它的会话通过 scope 加入这份常驻组合。插件内部仍按 Session 或 Agent 保存状态,所以多个会话不会混在一起。修改 Preset 文件时,DSH 会为后续会话建立新代际,已经加入旧代际的会话不会被中途换掉。


它主要控制这些内容:



  • Persona、Agent Instructions 和其他系统提示词段

  • 模型能看到、也能调用的工具

  • 工具以普通 schema 还是 Code Mode SDK 的方式呈现

  • 本地 Skills 的发现和加载

  • Plan、Goal、Todo、Compaction 等工作方式

  • Subagent、Workflow、Ralph 等委派能力

  • 是否开放 Cordis 运行时检查和动态插件工具


Preset 会直接改变请求里的系统提示词和工具 schema,所以它不只是界面上的一个模式名称。选择不同 Preset,模型拿到的能力、提示词长度、工具调用方式和长期会话行为都会变化。


它也有明确的边界。当前 Web 部署中,下面这些能力留在 Host,不由 Agent Preset 决定:



  • 使用哪个模型和 Provider

  • Agent Loop 与各类注册表

  • Session 持久化、Credentials 和 Settings

  • Sandbox、Approval 和 Permission Preset

  • Subagent 的底层 spawn、fork 等进程级后端


所以“Agent Preset”“模型选择”和“权限预设”是三件事。Agent Preset 决定 Agent 有什么能力,模型选择决定由谁来推理,权限预设决定工具执行时受到什么限制。


四种内置 Preset






































ID 界面名称 基础能力 适合场景
standard 标准模式 完整的原生工具集 日常编码
code PTC 模式 标准模式 + Code Mode 工具呈现 多步骤、可编程的工具编排
minimal 极简模式 持久 Bash + str_replace_editor 最小实验、模型基线测试
cordis 创造模式 标准模式 + Cordis 自修改工具与创作 Skills 创建 Preset、开发和调试插件

标准模式:日常编码的默认组合


标准模式不是一两个常用工具的集合,它基本装上了 DSH 当前完整的模型侧编码能力:



  • Linux 和 macOS 使用 Bash,Windows 使用 PowerShell

  • 文件读取、编辑和文件系统搜索

  • 后台任务的查询与停止

  • 本地 Skills 发现、目录查询和 Skill 加载

  • Goal、Plan Mode 和 Todo

  • 自动上下文压缩、手动 /compact,以及长工具结果裁剪

  • Subagent 查询、spawn、fork

  • Workflow 和 Ralph 循环

  • 向用户提问

  • 网页搜索


标准模式还会读取工作区里的 Agent Instructions,当前上限是 64 KiB。它带有完整的 Compaction 组合,长对话不会像极简模式那样一直堆上下文。当前配置还会裁剪超过阈值的长工具结果,保留头尾供模型继续判断。


组装文件中已经放了 Codex 和 Claude Code 子代理工具的模板,但默认是 disabled: true。也就是说,标准模式不会自动获得这两个产品;复制成自定义 Preset 后,可以按需打开对应行。


这套组合最适合日常使用。我们创建自己的 Code Preset 时,也应该优先从 standard 复制,而不是从空文件开始。


PTC 模式:能力不变,工具调用方式变了


PTC 模式的 agent.cordis.yml 基本是标准模式的完整副本,额外增加了一行:


- id: tool-presentation
name: '@deepseek-ai/dsh-agent-tool-presentation'
config:
mode: code

这行不会增加新的文件或 Shell 能力,它改变的是模型看到工具的方式。


在标准模式下,模型直接看到 bash、文件工具、搜索工具、Subagent 等原生 schema。在 PTC 模式下,模型在协议层唯一可以直接调用的工具是 run_code,同时在系统提示词里拿到一份根据当前工具目录生成的 TypeScript SDK。模型可以写一段异步 TypeScript:



  • 连续调用多个工具

  • 并发执行互不依赖的查询

  • 根据前一步结果做分支

  • 在程序里过滤和汇总结果,只把必要内容交回模型


原来需要五轮“模型—工具—模型”往返的操作,有机会压成一次 run_code。内部工具仍由 DSH 的工具注册表分发,原来的 Sandbox、Approval 和调用记录不会因为套了一层 TypeScript 就消失。


这段代码是一次 run_code 调用的函数体,可以使用顶层 awaitreturn。程序结束后,只把日志和返回值交回模型。它是一次性的工具编排,不是常驻插件,也不会自动写进 Preset。


代价也很明显。一次程序可以做更多事情,执行范围比单个原生工具调用大,审查代码也更重要。它还依赖 Host 提供 Code Runtime;没有 TypeScript Runtime 的部署无法挂载这个 Preset。


PTC 模式适合搜索多个文件、批量读取结果、串联查询和修改等多步骤任务。简单改一个文件时,标准模式通常更直观。


极简模式:真的只剩两个工具


极简模式不是“标准模式关掉几个按钮”,而是一份单独的最小组装。它只向模型提供:



  • 一个状态可跨调用保留的持久 Bash

  • 一个 str_replace_editor


它使用固定的完整 Persona:You are a helpful software engineer assistant.,并关闭运行时上下文快照。普通的 Agent Instructions、Web 引导和后续提示词贡献不会继续叠加。


它没有标准模式中的文件搜索、Web、Skills、Plan、Compaction、Todo、Subagent 和 Workflow。持久 Bash 的状态会在多次调用间保留,当前超时是五分钟;工具说明也明确写着不能通过它访问互联网。编辑器直接使用本地文件系统,并要求绝对路径。


这个模式适合观察最小 Agent Loop、比较模型基础能力,或者做范围很小的编码任务。长会话和复杂仓库工作不太适合它,尤其是它没有 Compaction。


创造模式:标准模式加上运行时创作能力


创造模式的 ID 是 cordis。它保留标准模式的主要能力,又增加了三部分内容。


第一部分是专门的 Persona。它会告诉 Agent:



  • DSH 分成 Host plane 和 Agent Preset plane

  • 哪些服务应该全局共享,哪些插件应该放进单个 Preset

  • 用户 Preset 在哪里

  • 不要修改随 DSH 安装的四个内置 Preset


第二部分是 Cordis 动态工具:



  • cordis_inspect_listcordis_inspect_querycordis_inspect_self

  • cordis_definecordis_runcordis_stopcordis_undefine


它们可以检查当前服务、事件、工具和动态 Package,也可以在当前 DSH 进程中定义、运行、停止和删除临时插件。


第三部分是两份内置 Skill:



  • editing-cordis-compositions:教 Agent 创建、修改和验证 Preset

  • cordis-plugin-development:教 Agent 检查接口并开发动态 Cordis 插件


创造模式适合“帮我做一个新的 Agent”或“先在运行时试验一个插件”。它不适合作为默认日常模式:额外的工具 schema 和说明会增加上下文,而且动态 JavaScript 的权限接近 Shell 访问。临时 Cordis Package 也只存在于当前进程,重启后不会自动变成正式插件。


怎么创建自己的 Preset


DSH 当前采用“复制后修改”,不在网页里提供一个从空白开始的 YAML 编辑器。这是有意的:一个可工作的 Preset 往往包含多个插件行、隔离 realm、Skills 和其他资源,从已知可用的组合复制更稳。


方法一:在 DSH Web 中复制


操作路径是:



  1. 打开“设置”。

  2. 进入“Agent 预设”。

  3. 在最接近需求的 Preset 上点击“复制”。

  4. 填写标识符和可选的显示名称。

  5. 创建完成后,打开新 Preset 的目录修改文件。远程或容器部署无法直接打开宿主目录时,页面会显示路径供手动复制。


标识符会直接成为目录名,只允许小写字母、数字和连字符,规则是:


[a-z0-9][a-z0-9-]*

例如 backend-reviewer 可以,Backend Reviewermy_agent../test 都不行。标识符创建后不能在界面里改名,也不能与内置或已有 Preset 重名。复制操作不会覆盖已有目录。


默认的用户目录是:


${DSH_HOME:-$HOME/.dsh}/.agent-presets/<id>/

一个自定义 Preset 通常长这样:


backend-reviewer/
├─ preset.yml
├─ agent.cordis.yml
├─ skills/ # 可选
└─ 其他插件或资源 # 可选

两个 YAML 文件的职责不同:



  • preset.yml 只放界面元数据,主要是 namedescription

  • agent.cordis.yml 才是实际的插件组装,里面每一行都有 id、包名和可选配置。


preset.yml 可以很简单:


name: 后端审查模式
description: 基于标准模式,保留代码检索与审查工具,关闭不需要的委派能力。

复制会带走来源目录里的组装文件、Skills 和资源。系统会保留来源的 description,但去掉来源的 name 和内置排序值,避免副本在列表里看起来和原版一模一样。


四个内置 Preset 属于 system,只能查看和复制,不能直接编辑或删除。用户创建的 Preset 属于 user,可以打开目录、继续复制或删除。


方法二:让创造模式帮你做


DSH Web 的“用「创造模式」创作自定义预设”会开启一个使用 cordis Preset 的空白会话。可以直接描述目标,例如:



基于 standard 创建一个 backend-reviewer。保留文件读取、搜索、Plan 和 Todo,去掉 Ralph 与普通 Subagent,加入一份后端代码审查 Skill。完成后验证它能够正常挂载。



正常流程应该是:



  1. Agent 加载 editing-cordis-compositions Skill。

  2. 查看当前 Preset 名单和运行时接口。

  3. 通过 Preset 服务复制一个内置组合。

  4. 修改用户目录下的 preset.ymlagent.cordis.yml

  5. 按 Host plane、Agent plane 和 isolate realm 规则检查插件位置。

  6. 实际挂载一次做验证。

  7. 让用户新建会话,检查最终工具列表和提示词是否符合预期。


用户 Preset 目录通常不在当前项目工作区里,写文件时可能需要额外批准。运行动态 Cordis 代码也可能触发批准。这些都是正常的权限边界。


当前 Waku 主要支持选择和使用 Preset,还没有 DSH Web 这套复制、删除和打开目录的完整管理界面。可以在 DSH Web 中完成复制,也可以在 Waku 里进入创造模式,通过对话让 Agent 创建。我们给 Waku 补的“选择器打开时刷新”,就是为了让新 Preset 能及时出现在列表中。


Preset 什么时候生效


Preset 是会话创建期配置,不是随时可以切换的皮肤。



  • 新会话页面可以选择一次 Preset;设置里的默认值只影响以后创建的会话。

  • 空白会话还可以切换。只要已经产生过对话或工具历史,Host 就会拒绝切换。

  • 这样限制是因为历史中的工具调用属于原来的工具 schema。中途换组合,新的 Agent 可能连旧工具名都不认识。

  • 修改 agent.cordis.yml 后,新会话会使用新的组装代际,已经运行的会话继续使用旧版本。

  • 删除一个自定义 Preset,也不会强行停止已经由它创建的会话。

  • Subagent 会继承父 Agent 正在使用的同一份 Preset 代际,不会重新读取磁盘上的最新版。


Host 每次读取 Preset 名单时都会重新扫描目录,所以新建和删除在服务端是实时可见的。客户端如果自己缓存名单,仍然需要主动刷新;我们之前在 Waku 遇到的就是这个问题。


还有一个当前限制:DSH 主要用 agent.cordis.yml 的修改时间和大小判断是否产生了新代际。只修改旁边的 Skill 或资源文件,未必会让新会话立即拿到新版本;这时需要同时触碰组装文件,或者重启 DSH。


最后要注意信任级别。user 只表示“这是本地创作的 Preset”,不代表它运行在低权限沙箱里。Preset 的实际权限取决于它引用了哪些插件。拿到别人分享的 Preset 后,应该先读 agent.cordis.yml,再决定是否使用。


“自进化”目前能做到哪一步


在创造模式(cordis)下,Agent 可以获得一组运行时工具,用来:



  • 检查当前服务、事件、工具和插件契约

  • 定义新的动态 Package

  • 启动或更新 Package

  • 停止动态 Run

  • 回滚到旧版本

  • 删除动态插件


这些工具确实能修改当前运行时,但不会直接改仓库源码,动态 Package 重启后也不一定还在。


目前比较实际的用法是:


运行时实验
→ 检查效果
→ 更新或回滚
→ 确认有效
→ 固化成正式插件

需要长期保存或分享时,再把验证过的代码整理成正式插件或 Preset。运行动态代码通常还要经过用户批准。


Agent Loop 被拔掉怎么办


真把 Agent Loop 停掉,Agent 当然就不能继续对话了。插件化只说明组件可以替换,不代表任意组合都能工作。


一个可用的 Coding Agent 至少需要:



  • 一个模型适配器

  • 一个 Agent Loop

  • 一组消息与会话能力

  • 最基本的输入输出路径


目前 Cordis 的动态工具主要管理动态定义的 Package,并不能随便拆掉整个宿主。


不过,DSH 还缺少一套很清楚的保护规则:哪些插件不能拔,哪些可以热替换,哪些修改只能下次会话生效。当前实现里,这部分还有完善空间。


Cordis 就是 DSH 的 Core 吗


可以这么理解。更准确一点,Cordis 是 DSH 的运行时 Core。


传统 Agent 的核心直接规定:



  • 循环怎么运行

  • 工具怎么调用

  • 模型怎么接入

  • 状态怎么保存


DSH 固定下来的部分更少,主要负责:



  • 启动进程

  • 建立 Cordis Context

  • 加载插件

  • 提供服务和事件契约

  • 管理生命周期


具体组装出什么 Agent,由后面的 Preset 和插件决定。这就是我们之前说的“Core 更后置”:核心只管怎么组装,不提前写死组装结果。


这套架构有先例吗


有,而且不少:



  • Emacs:编辑器本身也是一个可编程 Lisp 环境

  • Smalltalk Image:系统可以在运行时检查和修改自身

  • Eclipse/OSGi:功能通过服务化插件动态组合

  • Erlang:支持运行时升级和热代码替换

  • 微内核系统:核心只保留最小机制,其余能力放到模块中

  • 浏览器扩展系统:宿主提供生命周期和权限,扩展决定具体能力


DSH 的新意是把这些做法放进 Agent 场景,还让模型参与插件的检查、生成和运行。以前插件系统主要给开发者用,现在 Agent 自己也开始操作插件系统。能力更灵活,权限、回滚和稳定性也会更难做。


Pi 和 DSH 的差别


两者都支持自定义工具和 Provider,但重心不一样。


Pi 先把 Agent Loop 和 TUI 做稳,再让用户通过 Extension 扩展它。DSH 先做运行时,让 Agent Loop、服务和工具都参与组装。


所以我们用了 Vim 和 Emacs 的类比:



  • Pi 像 Vim:一个稳定的编辑核心,加上强大的扩展生态

  • DSH 像 Emacs:核心之外还是一个可以继续编程和重组的运行环境


Pi 的扩展也可以很强,DSH 也有固定核心。这个比喻只说明设计重心,不比较谁更强。


日常怎么用 DSH


日常写代码不需要一直开创造模式。我们建议这样分:



  • 日常编码使用标准模式、PTC 模式或自己的 Code Preset

  • 编写普通仓库插件时仍使用日常编码 Preset

  • 需要检查运行时、快速试验动态能力时使用创造模式

  • 动态插件验证成功后,再固化成正式插件或预设


创造模式更像实验室。工作流和普通开发差不多:


想法
→ 运行时原型
→ 快速验证
→ 回滚或迭代
→ 固化为工程代码

它省掉的是“改完代码、重启整个 Agent、再验证”这段来回折腾。


Waku、Web 和 ACP


DSH 自带 Web 界面,但写代码时,桌面客户端或 TUI 通常更顺手。我们检查了 Waku 的 DSH 接入,它走的是 DSH Web 后端的 HTTP 和 WebSocket,不是 ACP。


连接关系如下:


Waku GUI
↓ HTTP / WebSocket
DSH Host

Preset / Agent Loop / Cordis / Tools

Cordis 和插件都跑在 DSH Host 里,自进化能力也在服务端。客户端只要能:



  • 展示会话

  • 发送消息

  • 渲染工具调用

  • 处理用户批准

  • 刷新模型和预设目录


DSH Web 和 Waku 都能使用同一套运行时能力。


ACP 的价值在于统一客户端协议。以后要接更多编辑器或 TUI,可以考虑 ACP;目前用 Waku 验证 DSH 没有问题,没必要先重写一套接入。


动态后端也需要动态 GUI


我们在 DSH 里创建了新 Preset,Waku 的选择器却没有更新。Preset 创建成功了,问题出在 Waku 缓存了 Provider 目录,只在启动或 Provider 重载时读取一次。


后来我们给选择器加了“打开时刷新”,并把修改提交给上游。


这个问题不复杂,但很典型:后端的模型、Preset 和工具会动态变化,客户端就不能一直拿启动时的缓存。还要考虑目录刷新、事件更新和能力失效。


自定义 Provider 的模型能力要自己补


DSH 可以通过通用的 llm-pi-ai 适配器接入 OpenAI-compatible Provider。


接入过程并不复杂:



  • 设置 Provider ID

  • 填写 Base URL

  • 选择协议

  • 保存 API Key

  • 获取远端模型列表


我们接入 CPA 后,通过 /models 成功拉到了模型列表,但界面里没有 thinking level。


原因是 OpenAI-compatible 的模型列表通常只包含:



  • 模型 ID

  • 显示名称

  • 上下文窗口

  • 最大输出长度


这里没有统一的推理能力描述,DSH 也不会根据模型名字猜 thinking level。


下面这项配置:


compat:
supportsReasoningEffort: true

只说明 API 接受 reasoning_effort,具体模型还要单独声明:


models:
- id: custom-reasoner
reasoningEfforts:
off:
low: low
medium: medium
high: high

协议能接什么参数,和模型实际开放什么能力,是两份配置。DSH 当前要求部署者手动补齐模型能力元数据。


DeepSeek 模型名不会触发专用适配


DSH 按 Provider 路由选适配器,不看模型名称。


如果请求是:


provider: custom-gateway
model: deepseek-xxx

上面的请求仍然走通用 llm-pi-ai。只有 deepseek-official 路由会进入 llm-deepseek


DeepSeek 专用适配器主要处理:



  • DeepSeek 原生 thinking 参数

  • off/high/max 推理等级映射

  • 工具调用轮次的 reasoning_content 回传

  • DeepSeek 工具消息格式

  • DeepSeek SSE 流式响应

  • Cache token 统计

  • DeepSeek 错误分类

  • 模型上下文和输出默认值


这些主要保证协议处理正确,不会额外增强模型能力。


如果自定义网关已经把 DeepSeek 协议完整转换成 OpenAI-compatible API,日常使用的差别可能不大。差异通常出现在:



  • 长时间多轮工具调用

  • 大量 reasoning 内容

  • 上下文压缩

  • 流式响应边界

  • 网关没有完整透传 DeepSeek 特有字段


普通对话和简单改代码时,两条路径可能感觉不出区别。多轮工具调用和长 reasoning 场景下,专用适配器会更稳一些。


最后怎么理解 DSH


现在我们更愿意把 DSH 看成一个 Agent 运行时:



  • Cordis 用可逆 effect 管理组件在时间上的进入与退出

  • Cordis 用响应式 coeffect 管理组件在空间依赖图中的连接与重组

  • Preset 决定 Agent 的组装方式

  • Plugin 提供具体能力

  • Agent Loop 只是其中一个组件

  • GUI 是运行时的观察和控制界面

  • ACP 或 WebSocket 是客户端接入协议

  • 创造模式让 Agent 开始参与自身能力的构建


DSH 现在的优势不一定是“写代码比别的 Agent 更快”,而是可以更灵活地拆分、替换和试验 Agent 能力。


它还不成熟,下面这些问题都没有完全解决:



  • 动态能力的持久化与分享

  • 必要插件的保护机制

  • 前端对动态目录的完整支持

  • 自定义模型能力元数据

  • 权限和审批边界

  • 运行时实验如何转化为可维护代码


Pi 更关注把一个小而稳定的 Agent 做好,DSH 更关注怎样继续组装出不同的 Agent。回到开头的类比:一个更像 Vim,一个更像 Emacs。

最新回复 (13)
  • Uncle Money 08-15 22:02
    1

    有过深度开发 Agent 经验的,很容易就能理解 DSH 的价值所在,也能很快理解究竟有多自由。


    不过,话说回来,真要想看到 DSH 一骑绝尘的领先,或是跑出一个确定性的优势,目前的 LLM 能力还是有点不够看。

  • blless 08-15 22:10
    2

    怎么看着像后端微服务一些架构设计的依赖注入设计,自动管理依赖,自动生成依赖链,生命周期管理。。感觉这个架构设计很像后端架构设计底子的人搞的。。

  • huo0 08-15 22:11
    3

    还真是这个感觉

    大约去年此时我就在搓类似的运行时

    感觉是skill还没流行的时候

    不过后来弃了

  • Fayilor 08-15 22:11
    4

    感觉更像Nix一点,声明式依赖图,可逆副作用,最终状态只跟最终配置有关。


    但是跟Emacs还是有不一样的,Emacs几乎不区分插件和运行时,一切都是Lisp(这一点,与nix相似的guix也继承下来了,毕竟都是gnu旗下的emm),而且也几乎所有能力都通过函数提供,编辑器能用的,人也基本能调用。


    DSH目前还是Host和Preset分层,虽然创造模式已经能做很多事了,但本质上仍是一个设计精巧的插件系统(指cordis)。

  • 谢大佬 08-15 22:12
    5

    目前体验下来感觉又是一个极客的玩具,理念和Pi有一些相似,开发者给了一个基座,可以通过这个基座扩展出各种可能。但是跳脱开coding这个单一场景的话,个人感觉DSH似乎野心更大,毕竟是梁圣背书,看好它的未来。

  • Fayilor 08-15 22:12
    6

    去看看cordis就知道了,人家已经是很成熟的架构了,dsh直接拿来用了

  • Zcb 08-15 22:13
    7

    感觉是一个能够影响接下来Coding Agent走向的一个东西。目前个人来看可玩性很高。

  • blless 08-15 22:21
    8

    cordis



    看了一下 最早提交4年前,但是依赖注入这套,不说java spring吧,golang 的dig依赖注入都是7年前的玩意了。。这玩意真不是啥新东西啊,还发论文了,有点绷不住 ^-^

  • Fayilor 08-15 22:28
    9

    原理当然是很早就有了,但是各领域的实践确实是不一样的,所以不神化,正常使用就好。


    所有的系统都只是管理自己想要管理的一部分,不相关那部分就形成了外溢的熵。


    dsh这套大概能预想到同时使用大量插件的管理问题和生态混乱问题,另外可能插件能力参差不齐,非开发者安装一大堆推荐插件后没有能力甄别用ai来管越来越乱 (

  • aeo 08-15 22:37
    10

    其实我去年在 skill 还没这么火的时候,也尝试做过类似的机制,是热插拔的插件形式。


    但是实际用久了之后,想增删东西,或者说当初立项时没有考虑得这么全面,就不可避免会有卸载残留:即便卸载插件,依然会出现残留子进程、临时文件没有回收的情况。

    不是 DSH 独有的问题,是几十年动态插件系统反复遇到的固有矛盾。从早年 OSGi/Eclipse 的 bundle 陈旧引用泄漏、Emacs Lisp 插件卸载后钩子残留,再到 VSCode 扩展卸载后孤儿子进程存活,包括 JS 生态 Koishi 这套同样主打可逆插件的框架,都遇到同类问题

  • 老文 08-15 22:48
    11

    看你了好几遍,但是发现有点看不懂,但是我感觉这样插件化其实很好,舍弃所有全部交给插件来完成

  • xsmingerfan 08-15 22:53
    12

    如果每个步骤是可逆的,在这套框架内可以保证没有残留。但是问题是可逆的保证是一个很强的保证,插件的行为不一定能保证这种可逆性

  • edapan 08-15 22:53
    13

    好文章,通读下来,原本以为Pi Coding Agent已经够自由了,但是没想到DSH比Pi还自由。


    但是DSH把Agent loop也可以定制化,那么没有一个稳定的Agent loop,DSH搭建出来的Agent质量可能参差不齐,全靠使用者的设计理念和LLM的能力吗?


    现在各家的coding agent 的 agent loop 虽然大同小异,但是细微优化还是有差距,这方面还得看deepseek怎么设计底线了。

* 帖子来源Linux.do
返回