基础深入理解DeepSeek Harness 架构【2】一架 dsh 是怎么被组装起来的
第 2 章 一架 dsh 是怎么被组装起来的上一章讲的是「零件」。这一章讲「装配线」当你在终端敲下npx deepseek-ai/dsh web并按下回车接下来的几秒钟里这台机器是怎么把几百个零件拼成能跑的应用的。摘要本章讲解dsh的「装配线」——当你在终端敲下npx deepseek-ai/dsh web后机器如何把几百个零件拼成能跑的应用。核心是「插件树」模型组合包是零件包profile 是装配单patch 按 id 整体替换而非深度合并。五个内置 profile 共享dsh-base内核只是外壳不同--dump-config是打印装配单、也是你的改造清单。启动统一走具名 profile由dsh-app-boot负责五步装配与失败处理桌面版与 Python SDK 只是同一套架构在不同宿主上的延伸。标签dsh、插件树、profile、组合包、patch、app-boot、--dump-config2.1 先看结果启动之后你到底得到了什么官方文档给了一句非常精确的描述值得先记住运行中的dsh是一棵插件树由启动时按序叠加的各层组合而成。不是「一个进程加一堆模块」而是一棵树。树上的每个节点是一个插件层是叠加的后叠的层可以修改先叠的层。理解这一点后面所有关于配置和覆盖的规则都会变得自然。2.2 两个核心名词profile 与组合包这两个词是这一章的关键而且很容易混。用一句话区分组合包bundle是「一包零件」。它是一个打包好的、可安装的插件集合附带一份 patch 文件。profile是「一张装配单」。它列出了这次启动要按顺序叠放哪些组合包外加你自己的一层调整。图 7组合包是零件包profile 是装配单真正的行为由「装配单 四层 patch」共同决定。2.3 五个内置 profile同一套内核五种外壳官方随发行版交付五个 profile 模板。它们的关系是理解这整套设计的捷径profile它是什么什么时候用它web浏览器应用。默认在http://127.0.0.1:3080启动 Web UI日常使用、开发调试。这也是npx deepseek-ai/dsh web默认选的那个headless不带服务器的「一次性运行器」跑批处理、脚本里临时问模型一次不需要界面sdkSDK JSON-RPC 服务器你想从自己的程序TypeScript 或 Python里驱动 dshacp仅用于自动化的 ACP 服务器接入支持 Agent Client Protocol 的编辑器或自动化客户端sdk-minimal刻意的例外一个拥有完整、显式 SDK 配置树的独立组合包不叠加 dsh-base想要一棵最小、完全可控的配置树不需要 base 那一大堆东西其中dsh-base是web、headless、sdk、acp这四个 profile 共享的第一层内容相当厚实模型适配器、工具、持久化、沙箱一个受限的执行环境。程序在里面能做什么、能碰哪些目录都由外面的策略规定与审批策略、设置、凭据、遥测。各 profile 在 base 之上再加自己那一层组合包它额外增加了什么dsh-base四个 profile 的共享第一层模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测dsh-web-app浏览器应用dsh-headless不带服务器的一次性运行器dsh-sdk-appSDK JSON-RPC 服务器dsh-acp-app仅用于自动化的 ACP 服务器dsh-sdk-minimal不应用dsh-base自己拥有完整的显式 SDK 配置树刻意的例外图 8记住这张图你就理解了「为什么 dsh 换一个外壳却像同一个产品」。2.4 patch按 id 覆盖而不是深度合并层叠机制靠的是 patch。规则很简单一条 patch 按id定位某个条目然后替换它的整个 config或者插入一个新条目。不做深度合并——所以你要覆盖某个条目时必须把你想保留的字段原样重述一遍。陷阱这是新手踩得最惨的一个坑以为 patch 是「覆盖我改的那几个字段其余保留」。不是。**它是整体替换。**官方文档在「已知限制」里专门写了一条「用户 patch 会替换匹配到的整个配置——按 id 定位的 patch 不做深度合并因此 profile 覆盖必须重述需要保留的组合包字段。」2.5 最有用的一个命令把装配单打印出来想知道你的机器上到底启动了什么官方给了现成的答案dsh--profileweb --dump-config它会打印出这次启动实际生效的条目列表并标注每一行来自哪个源文件、哪一层 patch。而且——它打印出的任何条目都可以由你自己的 patch 替换。换句话说--dump-config不只是诊断工具它是你的改造清单。两个容易混的端口Web profile 默认端口是3080Electron 桌面应用默认端口是19387且可以由 profile 配置覆盖。这两个数字经常被搞混配置文件里看到 19387 不要以为是错的。2.6 应用启动器为什么没有第二个可执行文件官方在「应用启动」一节里定了一条纪律非常能说明这套设计的性格Vendored CLI、仅用于构建和测试的可执行文件、进程内直接挂载插件以及私有浏览器 WebWorker 预览都不属于 Harness 应用启动器。还存在一个脚本verify-application-entrypoints专门把每个包 bin、可执行源码、根 demo、根脚本归入显式类别并拒绝任何绕过dsh的 Node 应用路径。这条纪律解决的是一个很现实的工程问题如果每个人都用自己方便的方式启动应用那么「这到底是哪种组合」就永远说不清楚了。现在只有一条路通过具名 profile 启动。自定义插件组合也只能由「profile 有序 patch 文件」来表达而不是另做一个可执行文件或内联一棵应用树。2.7 启动器背后做了什么app-boot真正干活的库叫dsh-app-boot。它的职责链条可以简化为五步图 9启动不是「能跑就行」而是「要么完整跑起来要么明确地失败给你看」。官方还给了一整张「失败模式 × 处理方式」的表格区分了optional 条目和required 条目的表现。这里摘出最常用的几条失败模式optional 条目required 条目模块 import 失败或求值抛异常警告继续终止启动插件配置 schema 校验失败警告继续终止启动同步apply()抛异常警告继续终止启动注入的服务不可用警告继续条目等待依赖终止启动HTTP 端口绑定失败警告继续但该端点不可用终止启动条目缺失或被显式禁用忽略忽略另外还有一句很实用的话只要 required 列表里的modules或connection有一个启用的条目失败Web 就无法成功启动。app-boot 还做了一件贴心的事如果你的应用持有终端它会在进程退出前把终端交还你的 shell 绝不会残留在 raw 模式。而且这个交还过程是有界的——卡住的清理只会延迟致命退出不会取消它。2.8 桌面版与 Python SDK同一套架构两个方向官方文档用两节说明了两条「延伸线」它们是同一套架构面对不同宿主环境的适配。Electron 桌面应用它在签名资源里携带精确匹配的 dsh 生产运行时并拥有保留的$DSH_HOME/profiles/desktop。Electron 用 Electron Node 模式启动一个私有 Desktop HostHost 调用共享的 CLI profile runner 与完整 Web 应用。窗口先立即加载打包好的 Web 资源等启动注入完成后在同一个文档里激活客户端插件。Web 负责 RPC 与流桌面载体把本地页面连接到已认证的 Host。Node IPC 承载启动注入、就绪、致命错误与关闭。与 npm CLI 共享产品数据但包、启用选择与锁文件保持独立。Python SDK运行时 wheel 把普通的dshCLI 打包成deepseek-harness-sdk-runtime-platform-arch。客户端默认以显式 Harness home 启动dsh --profile sdk。Python 侧暴露的是profile 选择与有序 patch 文件而不是完整的 Cordis 树持久外部插件通过dsh plugin安装。关键无论是 TypeScript SDK、Python SDK、桌面端还是 Web 端**「配置组合」这件事永远只有一种表达方式profile 有序 patch 文件。**不存在「另一种配置方式」。记住这一点你在任何宿主环境里都不会迷路。2.9 这一章要带走的三句话这一章要带走的三句话**运行中的 dsh 是一棵按序叠加出来的插件树。**组合包是零件包profile 是装配单。**patch 是整体替换不是深度合并。**覆盖时必须重述要保留的字段。**dsh --profile web --dump-config是你的改造清单。**打印出来的任何条目都能被你替换。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →