☰
DeepSeek Harness多端架构:CLI/Web/桌面端如何共享一套Agent引擎
2026/11/3 7:49:56 网站建设 项目流程

我的日常大概是这样的:上午在终端里用dsh run把批量任务丢给 DeepSeek Harness,CLI 一动就不用管;中午想看进度,懒得回去敲命令,直接打开浏览器访问dsh web打印出来的本地地址;下午整理 skill 包和插件时,又切到桌面端图形界面去拖拽编排。三个端来回切换,我最怕的就是每个端各搞一套逻辑、各存一份状态,体验还互相打架。

后来把 DeepSeek Harness 的多端架构拆开看,发现它的设计思路其实很朴素:多端不是把同一个软件做三遍,而是把内核收敛成一套可嵌入的引擎,CLI、Web、桌面端都只是它的一件外套。这篇文章就把这套多端架构的分层逻辑、关键实现,以及我实际踩过的几个坑完整讲一遍。如果你想自己部署一套本地 Agent 工具,或者正在纠结"多端产品怎么避免重复造轮子",这篇应该能给你一个能直接落地的参考。

1. 先说清楚一件事:多端不是做三个软件,而是做一套内核

很多人一听到"多端架构"就以为是要把 UI 层做适配,像移动端那套跨端框架一样,用一套代码编译到三个平台。但 DeepSeek Harness 这类 AI Agent 编排工具的核心价值根本不在界面上,而在 Agent 运行时本身:会话怎么管理、模型怎么调用、工具怎么注册、插件怎么加载、多智能体怎么协作。这些能力如果每个端都重新实现一遍,那才是真正的灾难。

1.1 三个端分别要回答什么产品问题

三个端的目标用户和交互场景其实是错开的,产品诉求完全不同。

端核心诉求典型使用方式
CLI效率、脚本化、自动化dsh run批量任务、CI/CD 接入、SSH 远程执行
Web随时访问、无安装、轻监控浏览器看日志、查看任务状态、分享面板给同事
桌面端系统集成、完整 GUI、配置管理管理插件/skill、可视化编排、本地模型端点设置

CLI 面向的是"熟手",Web 面向的是"临时看一眼的人",桌面端面向的是"需要把复杂状态可视化的操作者"。三者注定不是同一套交互逻辑,但底层能力完全可以是同一份。

1.2 为什么必须共享一套内核

如果三个端各自实现 Agent 循环,维护成本会随着功能迭代指数级上涨:修一个模型调用 Bug 要改三处,新增一种工具协议要同步三端,插件开发者还得为不同端写不同版本的插件。

DeepSeek Harness 的做法是反过来:交互层随便换,但能力层只保留一份。任务状态放在引擎层,三端都是"接同一个 session";插件体系也是统一的,写一次插件,CLI 能加载、Web 能加载、桌面端也能加载。这样用户从终端切到 Web 面板时,任务进度、日志、中间产物都不丢。这才是多端架构存在的意义。

2. 内核层:Agent 引擎如何做到"可嵌入、可常驻"

拆开 DeepSeek Harness 之后你会发现,它并没有一个叫做"内核"的独立的进程,而是把引擎设计成了两种形态:一种是库模式,被dsh run、dsh board这类一次性命令直接加载,跑完就退出;另一种是服务模式,由dsh web和桌面端的后台进程拉起,常驻监听请求。同一个引擎,两种形态,这是整个多端架构能成立的基础。

2.1 会话与事件流设计

所有端之间共享的最核心抽象是 Session。一个 Session 里包含任务上下文、Agent 消息历史、工具调用记录、中间产物,以及当前状态(排队中、运行中、等待输入、已完成、已失败)。

引擎层会为每个 Session 产生一条统一的事件流,事件类型大致包括:

  • agent.message:某个 Agent 输出的文本消息
  • tool.call:Agent 准备调用某个工具
  • tool.result:工具执行完毕并返回结果
  • session.status:Session 状态切换
  • artifact.ready:产生了一个可下载的中间产物

CLI 拿到事件流后渲染成终端里的流式文本,Web 端拿到同样的事件流渲染成浏览器的实时日志面板,桌面端拿到事件流渲染成图形化的状态卡片。三端面向的是同一条数据管道,只是呈现方式不同。这也是为什么"从 CLI 发起任务,再到 Web 端附加进去看进度"能够无缝衔接。

2.2 多智能体编排:引擎层最复杂的一块

多智能体是 DeepSeek Harness 这类工具经常被提到的能力。多个 Agent 之间不是简单的前后调用,而是需要在同一个 Session 里共享上下文、互相传递中间结果。

我理解它的编排模型是:一个 Session 里可以有多个 Agent 角色,比如规划器、执行器、审查器。规划器先拆解任务,执行器按步骤干活,审查器对产出做检查。配置方式类似这样:

# dsh.agents.yaml 意图示例 agents: planner: model: deepseek-chat system: 你是规划器,负责把任务拆解为可执行的步骤清单 executor: model: deepseek-reasoner system: 你是执行器,负责按步骤完成代码编写和修改 reviewer: model: deepseek-chat system: 你是审查器,负责检查产出质量并给出结论

这些 Agent 的编排图、消息流转、当前执行到哪一步,全部是引擎层的状态,天然可以被三端读取。CLI 里你看到的是文本形式的步骤清单,Web 面板里你看到的是可视化流程,桌面端里你看到的是一张可拖拽的编排画布。底层跑的是同一套东西。

2.3 插件系统如何定义跨端边界

插件是多端架构里最容易翻车的一块。很多人一开始把插件理解成"一段脚本",随便挂个钩子就能跑。但一个跨端插件至少要区分两类能力:一类是运行时能力,比如"我可以执行代码";另一类是 UI 扩展能力,比如"我要在 Web/桌面端渲染一个自定义面板"。

DeepSeek Harness 的插件包结构大致是这样:

@linxin666/dsh-p/ manifest.json cli.js web.js desktop.js

manifest.json声明插件元数据,cli.js提供命令行端的运行时入口,web.js和desktop.js是可选的 UI 扩展入口。引擎在加载插件时会根据当前运行形态选择对应的 entry,而不是一股脑全部加载。

这也解释了为什么你可能会看到类似 "failed to load plugins web boot: 2 entries did not activate @linxin666/dsh-p" 的报错:某个插件在 CLI 下能用,但它没有声明 Web 端的 entry,或者 Web entry 初始化失败了。引擎会跳过这类插件,不至于让整个服务起不来,但这种半激活状态恰恰是插件跨端兼容性最容易踩的坑。

3. CLI 端:朴素界面背后藏着架构的"参考实现"

CLI 往往是最不起眼的端,却是对架构约束最严格的一个端。因为没有鼠标、没有图形按钮、没有任何样式,所有交互都要靠 stdin/stdout 完成,这倒逼内核接口必须足够干净。如果 CLI 端能流畅完成一次完整的 Agent 任务,说明引擎的能力边界划分是正确的。

3.1 dsh run 到 dsh web:从一次性命令到常驻服务

CLI 的命令体系我实际用下来大致分几类:

  • dsh run "任务描述":启动一个一次性任务
  • dsh board:列出当前所有 Session,查看状态
  • dsh web:启动本地 Web 服务
  • dsh plugin:插件管理
  • dsh doctor:环境自检

dsh run是库模式的最好例子。它启动引擎、创建 Session、执行任务、把结果输出到终端,然后退出。整个过程不常驻系统,不占资源,适合做自动化脚本。

dsh web则是服务模式的例子。它会拉起一个本地 HTTP 服务,打印出访问地址,然后进程一直挂着。执行dsh run需要的是引擎一次性调用,执行dsh web需要的是可复用的常驻引擎服务。两种形态能在同一个 CLI 里共存,靠的正是"引擎可嵌入"这个设计。

3.2 CLI 端的细节体验与工程取舍

CLI 端的工程细节看起来琐碎,实际影响很大。我举几个例子:

输出双轨制。人类可读的终端渲染和机器可读的 JSON 输出要分开。比如dsh run "修复测试" --json能输出结构化结果,方便接入 CI 或脚本;不带参数的默认输出则是带颜色、带缩进的终端友好格式。这个双轨制如果设计晚了,后面想接外部工具会很痛苦。

中断与恢复。长任务跑了一半,按 Ctrl+C 不能直接把进程杀了,而是应该优雅地给引擎发送一个中断信号,让当前工具调用收尾,然后保存 Session 快照。下次用dsh board --resume <session-id>可以从断点接着跑。Session 快照的序列化格式是跨端复用的关键,这里必须设计成稳定版本化的 schema。

终端兼容性。某些 CI 环境没有 TTY,ANSI 颜色会变成乱码。CLI 要么检测NO_COLOR环境变量,要么提供--plain参数。这类问题在架构文档里根本不值一提,但实际使用频率非常高。

3.3 安装部署与路径规划

很多人装 DeepSeek Harness 时会纠结"装到哪个盘"。其实它跟 Node 工具链类似,安装目录本身不重要,关键是环境变量和全局数据目录的设置。

# 假设你装在 D:/deepseek-harness export DSH_HOME="D:/deepseek-harness" export PATH="$DSH_HOME/bin:$PATH" dsh doctor

我建议把安装目录和用户数据目录分开:安装目录只放程序文件,用户数据目录(Session、日志、插件、skill)默认放在用户目录下,也可以通过DSH_DATA_HOME指到别处。这样重装工具不会丢掉你的历史任务和配置。这算是我多端实践里的一个重要教训:数据目录如果不独立,卸载一次全没了。

4. Web 端:本地服务模式下的认证与实时推送

Web 端的架构核心和普通 Web 应用很不一样。它不是部署在远程服务器上,而是由dsh web在你本机拉起一个 HTTP 服务,浏览器访问的是localhost。这意味着 Web 端和 CLI 跑的是同一个引擎、同一份数据目录,不需要任何网络中转。

4.1 认证流程拆解

本地服务有一个很常见的提示:dsh web authentication required; reopen the url printed by dsh web.第一次看到这个提示的人往往会困惑:本地服务为什么还要认证?

原因很简单:dsh web监听的不一定只有 127.0.0.1,有时候为了局域网访问会绑到0.0.0.0,这时候同一局域网的人都能访问你的控制面板;另外浏览器里的恶意网页也可能通过 DNS rebinding 等手段打到 localhost 端口。所以本地 Web 服务必须有一个轻量认证机制。

流程大致是:

  1. dsh web启动时生成一个带一次性 token 的 URL,打印在终端里
  2. 浏览器打开这个 URL,服务端校验一次性 token 后返回一个长效会话 token
  3. 浏览器把会话 token 存到 localStorage,后续请求通过 Header 或 Cookie 带上
  4. 服务重启、原 URL 过期之后,浏览器再访问旧地址就会看到 "authentication required",需要重新用终端打印的新 URL 打开

这个"重新打开 URL"的设计比在浏览器里搞登录表单省事得多,因为本地服务根本没有用户体系。它追求的不是身份验证,而是"这台机器上的某个操作者授权了这个浏览器"。

4.2 实时推送选型:SSE 还是 WebSocket

Web 面板要实时显示 Agent 的输出,推送通道必须在 SSE 和 WebSocket 之间做选择。我实际接触过的实现里,WebSocket 是更合适的选择,理由有三:

对比维度SSEWebSocket
连接方向服务器单向推送双向
浏览器 POST 请求原生 SSE 无法带 body,复杂查询困难双向自由
断线重连有内置重连但事件定位弱需要自己实现心跳和续传

Agent 任务的交互不是纯日志流,用户可能需要从 Web 面板直接给任务发指令(比如中止、切换 Agent、追加输入),这已经超出 SSE 的能力边界了。WebSocket 配合心跳消息,能做到 30 秒无消息不自动断开,断线后按 Session 版本号续拉增量事件,体验比 SSE 完整得多。

4.3 Web 端能做什么、故意不做什么

Web 端不是把桌面端或 CLI 的所有功能都搬到浏览器里。我理解 DeepSeek Harness 的 Web 端是克制的:重点做任务列表、实时日志、Session 详情、线程重跑,不做复杂的 Agent 编排画布编辑。原因是浏览器端交互能力本来就有限,如果强行做复杂编辑,就要维护两套交互逻辑,架构成本高,收益却不大。

这种"故意不做"和"刻意少做"的边界,在多端架构里和"做了什么"一样重要。少划一条边界,后期多端功能漂移就会加速。

5. 桌面端:把同一套能力翻译成图形界面

桌面端在技术选型上,一般无非是 Electron、Tauri 这两个方向。DeepSeek Harness 这类工具的桌面端,从体感和启动速度上看更像是 Web 技术栈套壳的方案,因为它的前端资源大概率可以被 Web 端复用。但具体用了哪个框架不是关键,关键是桌面端的架构位置。

5.1 桌面端到底封装了什么

桌面端不是独立软件,而是"内核宿主 + 图形前端"。它的核心职责有四块:

  • 管理常驻引擎进程:桌面端启动时拉起引擎服务,退出时优雅关闭,前端崩溃时引擎保活继续执行后台任务
  • 提供 Agent 编排画布:以图形方式查看多智能体之间的协作关系
  • 插件和 Skill 管理:把@linxin666/dsh-p这类插件的安装、启用、状态查看做成 GUI,替代命令行操作
  • 本地模型端点配置:管理本地推理服务的地址、模型名称、上下文长度等参数

这些功能里,插件管理是用户感知最强的。一个插件在 CLI 下只要dsh plugin install一行命令就行,但在桌面端,你会看到插件列表、启停开关、入口类型(cli/web/desktop)、激活状态。前面说的 "2 entries did not activate" 这类问题,在桌面端会被直接可视化地标出来。

5.2 桌面端与 Web 端前端资源的复用

如果 Web 端用的是一套主流的组件框架,桌面端完全可以直接复用大部分前端代码,这是这类架构天然的优势。我建议把"页面组件"和"桌面容器能力"分成两层:组件层做任务台账、日志渲染、状态卡片;容器层做系统托盘、全局快捷键、开机自启、菜单栏。

但要注意一个隐蔽问题:桌面端切到后台或最小化时,如果还在持续渲染高频率的日志流,CPU 会莫名其妙飙升。我踩过这个坑之后的理解是,桌面端必须实现"可见性感知":窗口隐藏时降低日志刷新频率,只保留事件落盘,不做实时渲染。这在多端架构里属于"由端侧负责的优化",引擎不用管。

5.3 桌面端常见问题:登录、回调端口、进程残留

桌面端最常见的问题是 OAuth 回调端口冲突。某些模型的远程鉴权需要拉起浏览器做 OAuth 登录,回调地址默认绑定某个固定端口。如果端口被占用,登录就会一直转圈。遇到这种情况,优先确认是不是本机有另一个服务占用了端口,或者配置里指定的回调端口和实际不一致。

另一个常见问题是进程残留。桌面端已经退出,但后端引擎进程还挂在后台,导致下次启动时端口冲突。处理方式是在桌面端里加一个启动自检:启动时检查数据目录里的锁文件,发现旧的 PID 活着就先发信号让它优雅退出,实在不行再提示用户清理。

6. 多端状态同步与插件跨端加载的细节

多端架构最容易出问题的地方,不在单点功能,而在状态一致性和插件跨端适配。如果三端各自读写一份状态,用户一定会遇到"CLI 跑完的任务在桌面端看不到"这种尴尬。

6.1 状态文件与 SQLite 的选择

Session、日志、产物、配置这些状态需要被三端同时读取。如果全部用纯 JSON 文件存,并发写的问题迟早会爆:CLI 进程正在写 session 文件,桌面端同时打开同一个文件,轻则读到半截数据,重则互相覆盖。

更稳妥的方式是把状态收进一个 SQLite 数据库,开启 WAL 模式,多进程可以同时读、单进程写,写锁粒度小,不会因为一个端在写就把另外两个端卡死。我自己在本地工具开发里的体会是:只要涉及"多进程共享状态"这个场景,尽早换 SQLite 比后续做文件锁迁移的成本低得多。

6.2 插件"装了但没激活"完整排查链路

我在实际使用中遇到过一次典型的插件激活失败,报错是:

harness failed to load plugins web boot: 2 entries did not activate @linxin666/dsh-p

这种报错字面上是"Web 引导阶段有两个入口没激活",但它没有告诉你是哪两个入口、为什么没激活。我建议的排查链路是:

  1. 先跑dsh plugin list --verbose,看插件整体的激活状态
  2. 再跑dsh plugin inspect @linxin666/dsh-p,查看它的 manifest 和各 entry 文件的解析情况
  3. 检查web.js/desktop.js这类 UI entry 是否存在,文件里有没有语法错误或 import 了当前环境没有的依赖
  4. 在设置里关闭插件再重新启用,确认是否是启动时序问题

实际情况里,"web entry 激活失败"最常见的根因是插件作者只测试了 CLI 入口,Web 入口只是留了一个占位文件,但启动时的依赖检查不通过。这类问题不是 DeepSeek Harness 独有的,所有跨端插件系统都会遇到,排查思路完全可以通用。

6.3 环境变量与安装位置细节

关于"装到 D 盘"这类问题,其实只需要把两个关键路径规划清楚。一个是安装目录,放可执行文件;另一个是数据目录,放 Session、日志、插件、skill。

DSH_HOME # 安装根目录,一般不写入数据 DSH_DATA_HOME # 数据目录,默认为 ~/.dsh

如果你把两个目录混在一起,升级覆盖安装时就会连带删掉历史任务和插件配置。另一个高频坑是 PATH 没有配好,导致dsh命令找不到,或者找到的是旧版本的dsh。升级之后我一般会先跑一下dsh doctor,它会同时反馈安装路径、数据目录、插件状态和模型端点连通性,能省掉大量无头绪的排查时间。

7. 架构取舍复盘:哪些设计是对的,哪些可以再斟酌

多端架构没有标准答案。DeepSeek Harness 选择"CLI、Web、桌面端全都要",和很多只做 CLI 的 AI 工具形成了鲜明对比。像 Codex CLI、Claude CLI、Trae CLI 这类工具,核心阵地都在终端,界面只是辅助。DeepSeek Harness 把三端都作为一等公民,必然付出更高的前端维护成本,但换来的是更宽的使用场景。

7.1 与其他终端工具的对比

只做 CLI 的工具,交互闭环短、开发成本低,但很多人真的不想打开终端,尤其是配置插件、管理多智能体编排图的时候。DeepSeek Harness 把桌面端和 Web 端补上之后,相当于把同一套 Agent 能力分发到了不同习惯的用户面前。熟悉命令行的继续在终端里高效干活;习惯图形界面的用户可以在桌面端完成同样的事情。

这个取舍的代价也很明显:三个前端共享一套引擎,任何一个端的需求都可能牵动引擎接口的演进。比如为了配合 Web 端的实时推送,引擎层的事件流必须做成可订阅、可重放的形式,这就比单纯面向 CLI 的实现复杂得多。从工程实际看,这类工具更适合"先跑通 CLI,再补 Web,最后上桌面端"的推进路径。

7.2 我的实际使用建议

最后说几个真实使用建议。平时我自己会同时开着三个端,但职责是分开的:CLI 负责发起任务和跑脚本,Web 面板只在需要快速看进度时打开,桌面端则留在配置插件、查看编排图、管理模型端点时才用。不要三个端同时高频操作同一个 Session,虽然架构上支持,但没有必要。

一个小技巧:dsh web启动时如果遇到端口冲突,可以用dsh web --port 0让它自动分配一个空闲端口,终端打印出来的就是实际可用的地址。另外,dsh web打印的一次性 URL 建议直接复制到自己的私有笔记里,因为一旦服务重启,旧的 URL 就失效了,重新在终端翻历史输出比想象中麻烦得多。

多端架构这件事,说到底不是炫技,而是在保证内核能力一致的前提下,让不同习惯的人都能找到顺手的使用方式。能把这一步做稳,工具才谈得上被更多人真正用起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询