DeepSeek Harness 学习与实践笔记

DeepSeek Harness 学习与实践笔记

——从架构理解到自主构建 Agent 系统的完整记录

作者:广山哥

周期:2026 年 8 月

形式:以 DSH(DeepSeek Harness)为学习对象,通过实际开发、排障、重构与设计,系统掌握 Agent 系统的架构哲学,并将其迁移到自己的量化交易与 Agent 编排实践中。


一、引言:为什么学习 DSH?

DeepSeek Harness 不是一个普通的开源项目。它是 DeepSeek 官方出品的 AI Agent 运行时,同时也是一套插件化、可组合、面向多种表面的 Agent 操作系统。它的源码结构、设计取舍和工程实践,对于任何想深入理解 Agent 系统的人,都有极高的学习价值。

我从接触 DSH 开始,经历了三个递进阶段:

  1. 使用阶段:通过 Web GUI 和 CLI 体验 Agent 能力,了解它能做什么。

  2. 排障阶段:遇到配置不生效、模型选择不匹配等问题,被迫深入底层,理解其工作机制。

  3. 自主构建阶段:基于 DSH 的设计思想,独立开发了 dsh-repl(终端客户端)、WeClaw Agent Hub(微信 Agent 网关)和一套 Agent 工作流 DSL。

这份笔记记录了这三个阶段的完整过程,以及从中提炼出的可迁移设计原则。


二、DSH 的核心架构与设计哲学

2.1 一句话定义 DSH

DeepSeek Harness = 一个把大模型、Agent、代码工作区、远程服务器、任务调度和 Web UI 组合起来的本地 Agent 操作台。

这个定义来自 DSH 的系统提示。它把 DSH 定位为“操作台”,而不是“聊天界面”——这是理解 DSH 一切设计的起点。

2.2 四个核心设计原则

DSH 的设计可以被归纳为四条相互支撑的原则。

原则一:核心稳定,表面可替换

DSH 的“核心”是 Agent Runtime——它提供会话管理、模型调用、工具执行、记忆存储等基础能力。而“表面”是 Web、TUI、headless、JSON-RPC 等多种交互方式。

· 核心不依赖表面:删除 Web 或 TUI 目录,核心仍然可以运行。

· 表面可以独立替换:官方的 Web 和已删除的 TUI 是两种完全不同的表面实现,它们共用同一套内核。

这解释了一个现象:官方 TUI 被删除后,dsh CLI 仍然可以正常使用(headless/profile 模式)。因为被删除的只是“表面”,而不是“引擎”。

对应实践:我的 dsh-repl 也是同样的设计——它通过 JSON-RPC 协议与内核通信,不依赖任何内部服务。删掉 dsh-repl 不会影响 DSH 本身,反之亦然。

原则二:一切皆插件(Cordis 架构)

DSH 基于 Cordis 框架,所有能力都以插件形式注册:

· 会话管理 → @deepseek-ai/dsh-session

· 模型适配 → @deepseek-ai/dsh-llm-pi-ai

· 工具系统 → @deepseek-ai/dsh-tools

· 视觉识别 → @deepseek-ai/dsh-vision-router

插件之间通过 ctx(上下文对象)通信,可独立加载、替换、禁用。启动时通过 cordis.yml 决定加载哪些插件组合。

这种架构有两个好处:

  1. 可组合:根据场景选择插件组合(profile 机制)。

  2. 可隔离:一个插件的改动不会影响其他插件(只要接口不变)。

对应实践:我的 WeClaw Hub 的 Agent 列表(nanobot、claude、gemini 等)本质上也是“插件”——每个 Agent 独立注册,可配置、可切换。

原则三:协议分离(JSON-RPC 作为外交语言)

DSH 的核心通过 JSON-RPC 协议暴露能力,而不是通过共享内存或直接函数调用。这意味着:

· 客户端可以运行在不同进程、不同机器、不同语言环境中。

· 客户端不需要知道内核的实现细节,只需要理解协议。

· 新增一种表面,只需要实现协议客户端,不需要修改内核。

DSH 的 dsh-sdk-client 包就是专门给客户端用的。我的 dsh-repl 就是基于这个包构建的。

对应实践:WeClaw Hub 的设计也遵循这个原则——Hub 是“网关”,Agent 能力通过协议调用,不关心 Agent 具体跑在哪。

原则四:状态可观测 + 可恢复

DSH 做了两件事来保证系统状态的可管理性:

  1. 会话持久化:每轮对话都写入 session.jsonl.zstd,重启后可以恢复。

  2. 状态投影(session-projection):把原始日志投影成可查询的结构化数据(如会话标题、最后活动时间、消息数量)。

这使 DSH 不是“无状态服务”,而是一个“有状态且可恢复的系统”。

对应实践:我设计的 /save 机制也是基于同样的考虑——工作流步骤的输出可以被保存、引用、复用。


三、学习路径与工具实战

3.1 从“使用”到“排障”:一次识图配置优化的完整过程

我第一次真正深入 DSH 底层,是遇到了一个问题:“识图模型配置不生效”。我要求“默认走 opencode go 的 deepseek-v4-flash 与 sensenova 的识图模型”,但 DSH 始终无法正确识别图片。

在解决这个问题的过程中,我系统学习了 DSH 的配置文件体系、模型路由机制和进程管理方法。

配置体系的三个关键文件

DSH 的配置分为三层:

层级 文件位置 作用

用户级 ~/.dsh/settings.yaml 全局配置(默认模型、UI 偏好、provider 定义)

Profile 级 ~/.dsh/profiles/web/cordis.patch.yml 特定启动模式的补丁配置

运行时 ~/.dsh/profiles/web/cordis.yml 合成后的最终配置(自动生成,不建议手动编辑)

我修改的核心是 cordis.patch.yml,在其中新增了三段配置:

  1. providers:定义整轮自动识图时切换的模型链(sensenova 优先)。

  2. httpProviders:定义 vision_describe 等工具的内部识别链(同样 sensenova 优先)。

  3. wrappedProviders:显式声明要包装的纯文本模型(opencode-go / deepseek-v4-flash)。

这个三层结构让我意识到:DSH 的配置不是“一个文件搞定一切”,而是分层叠加的。Patch 层覆盖 Bundle 层,用户配置覆盖系统默认。

进程排查与 inode 验证

配置修改后,我遇到了“配置未生效”的问题。通过排查我才发现,DSH 的配置是在进程启动时加载的,修改后需要重启进程。

我学到了一套排查组合拳:

  
# 1. 查端口,找到进程 PID
  
lsof -nP -iTCP:3080
  

  
# 2. 查进程打开的文件,看是否包含修改的配置
  
lsof -p 29495 | grep cordis.patch.yml
  

  
# 3. 查文件的 inode(唯一标识),对比进程读的和磁盘上的是否一致
  
stat -f "inode: %i, size: %z" ~/.dsh/profiles/web/cordis.patch.yml
  

关键发现:进程打开的文件 inode 和磁盘当前文件的 inode 一致时,才证明进程加载的是最新配置。

这个排查过程让我理解了 DSH 的进程隔离设计——Web 服务是独立进程,配置文件在启动时读入,运行时不会热更新。这是“简单可靠”的设计选择。

模型能力实测

我还直接调用了 API 来验证模型能力:

  
# 测试 opencode-go 是否支持 image_url(结果:不支持)
  
curl -X POST https://opencode.ai/zen/go/v1/chat/completions \
  
  -H "Authorization: Bearer $KEY" \
  
  -d '{"model":"deepseek-v4-flash","messages":[...]}'
  

  
# 测试 sensenova 是否支持 image_url(结果:支持)
  
curl -X POST https://token.sensenova.cn/v1/chat/completions \
  
  -H "Authorization: Bearer $KEY" \
  
  -d '{"model":"sensenova-6.8-flash-lite","messages":[...]}'
  

结论:deepseek-v4-flash 无论走 opencode-go 还是 sensenova,都是纯文本模型。识图必须由 sensenova 系的视觉模型承担。这个结论直接指导了配置方向。

工具调用深度:vision_describe 端到端验证

配置生效后,我通过 vision_describe 工具验证了整条识图链路:

  
vision_describe({
  
  paths: ["/path/to/card_saltlake-crop-60-60-1020-900.png"],
  
  question: "这张卡片上主要有哪些元素?概括内容,回答 JSON。",
  
  json: true
  
})
  
// 返回:{"summary":"这是一张温馨的插画卡片...", "entities":[...]}
  

这条链路的路径是:用户消息 → opencode-go(文字轮)→ 图片触发视觉链 → sensenova-6.8-flash-lite(识图)→ 返回结构化 JSON。

这也让我理解了 DSH 中“工具优先”与“整轮路由”两种识图模式的区别(详见下文)。


3.2 从“使用”到“构建”:dsh-repl 开发

学习 DSH 的过程中,我发现一个问题:官方删除了 TUI(终端交互界面),而 Web GUI 虽然功能完整,但对我这种“泡在终端里”的人来说,缺了一个轻量、快速的交互入口。

于是我用 712 行代码(489 TUI + 223 REPL)构建了 dsh-repl。

架构设计

dsh-repl 的核心设计思路是协议分离:

· 通过 dsh-sdk-client 与 DSH 内核通信(JSON-RPC over stdio)

· 使用 @earendil-works/pi-tui 作为终端界面引擎,不自己实现渲染

· 只依赖 dsh-sdk-client + pi-tui + js-yaml,不依赖任何内部 dsh-* 包

它在结构上对应官方的“表面”层,但不是“官方的另一种实现”,而是一个独立客户端。官方 TUI 被删除后,我的 dsh-repl 仍然可以正常使用。

增量适配

在实现过程中,我发现 DSH 的 JSON-RPC 协议缺少 session/command 通道。我通过在 fork 中修改 SDK 的三个包,以纯增量方式补上了这个缺口。这意味着:

· 如果上游未来支持这个通道,我的改动可以安全回退。

· 如果上游不支持,我可以长期维护自己的 fork,且改动范围可控。

这是“产品经理式”的工程决策:不破坏上游兼容性,不改动核心逻辑,只在协议层补齐缺失的能力。

核心能力对齐

dsh-repl 最终实现了与 DSH Web 对齐的核心能力:

能力 Web dsh-repl

模型切换 /model /model

会话管理 /new、自动持久化 /new、/exit

工具调用 工具卡展示 工具结果流式显示

指标状态 Web StatsLine 终端 StatsLine(TTFT、tok/s 等)

识图配置 完整支持 通过 JSON-RPC 透传

差异化能力

我还给 dsh-repl 加了官方 TUI 没有的差异化能力:

  1. 宠物养成系统(pet.ts):升级、经验、心情、睡眠状态。这让终端界面有了“活物感”。

  2. API 配额/余额状态条(usage.ts):实时显示 DeepSeek 余额 + opencode-go 三窗口用量。

  3. 自动接口路由(pickRoute):按模型自动选择 Responses/Completions 接口。

这些差异化功能不是“技术上的必须”,而是“产品上的个性”。它们解决了同一个问题:让终端用户感觉自己在和一个“活着的系统”交互。

测试策略

dsh-repl 的测试也体现了“轻量”原则:

· 纯逻辑层(core.ts、session-reducer.ts):100% 单测覆盖。

· 终端胶水层(tui-repl.ts、app.ts):通过 coverage-excluded 标记跳过。

这种取舍是理性的:渲染层测试需要挂真 PTY 终端,维护成本高,而我的体量不支撑这种测试基建。与其写一个不完整的渲染测试,不如把核心逻辑单独抽出来测透。


3.3 从“构建”到“生态”:WeClaw Agent Hub 设计

完成 dsh-repl 后,我开始思考一个问题:能不能把 Agent 能力带到微信上去?

结果就是 WeClaw Agent Hub——一个通过微信消息驱动的 Agent 网关。它本质上是在微信这个“受限表面”上,用命令系统 + 任务队列 + 离线产出的方式,实现了一套 Agent 操作系统。

核心设计

Hub 以“命令解析 + Agent 路由 + 结果回传”为核心循环:

  1. 命令解析:识别用户消息中的命令前缀(/、@)。

  2. Agent 路由:根据命令中的 Agent 名称或默认设置,选择对应的模型调用。

  3. 结果回传:将 Agent 的回复通过微信推送给用户。

这对应 DSH 的“协议分离”原则:Hub 是“网关”,Agent 能力通过协议调用,不关心 Agent 具体跑在哪。

多 Agent 编排

Hub 实现了三种多 Agent 协作模式:

· /debate <话题>:两个 Agent 对抗辩论。

· /chat <话题>:两个 Agent 协作聊天(固定 5 轮)。

· /roundtable <话题>:三个 Agent 围炉探讨,自动总结。

这些固定编排模式对应 DSH 中需要用 subagent 或 workflow 工具才能实现的多 Agent 组合逻辑。

异步任务队列

Hub 支持离线产出的异步任务:

  
用户:/podcast 生成一篇关于 AI 安全的播客
  
Hub:已加入 NAS 直读队列,请稍后查看播客。
  
(后台生成播客 → 写入 NAS → 推回微信)
  

这个设计对应 DSH 的“状态可观测 + 可恢复”原则:任务被记录在队列中,即使 Hub 重启,未完成的任务也能被重新执行。


3.4 工作流 DSL:在微信上定义 Agent 编排

Hub 最核心的能力是 /workflow——一套用于 Agent 编排的领域特定语言(DSL)。

  
step1 @claude 分析这段代码
  
save code_analysis
  

  
step2 parallel
  
branch @gemini @1 找出安全漏洞
  
branch @qwen @1 写单元测试
  

  
step3 @claude 合并 @2.1 和 @2.2 的结果,输出最终报告
  
save security_report
  

这个 DSL 的核心概念与 DSH 的 workflow 工具完全对应:

DSH 概念 Hub DSL 说明

顺序步骤 step1 → step2 → step3 按顺序执行

并行分支 parallel + branch 多个 Agent 同时工作

变量引用 @1、@2.1 跨步骤引用输出

持久化存储 save 文件名 保存中间产物

唯一的区别是:DSH 的 workflow 需要用 JavaScript 脚本编写,而 Hub 的 DSL 是纯命令式的,用户不需要写代码。


四、设计迁移:从 DSH 到量化交易系统

DSH 的设计哲学可以被迁移到任何需要“核心 + 表面”分离的系统上。我计划将这些原则应用到我的量化交易系统设计中。

4.1 核心稳定,表面可替换

量化交易系统的核心是策略引擎、订单管理、风险管理和数据管道。这些模块不依赖任何界面。

表面可以是:

· Web Dashboard(实时行情、持仓、盈亏曲线)

· CLI / REPL(调试、手动干预)

· 通知机器人(微信、Telegram、邮件)

· JSON-RPC API(程序化调用)

与 DSH 一样,删掉任何一个表面,交易引擎照常运行。

4.2 一切皆插件

我计划将交易系统拆分为独立插件:

插件 职责

data-binance Binance 数据源

data-okx OKX 数据源

strategy-ma 均线策略

strategy-rsi RSI 策略

executor-binance Binance 订单执行

executor-paper 模拟交易

monitor-dashboard Web 监控面板

monitor-alert 告警通知

这样设计的好处是:可以独立替换数据源、策略或执行器,而无需修改核心。

4.3 协议分离

交易系统应该通过 JSON-RPC 或 gRPC 暴露能力,而不是通过共享内存或直接函数调用。这样:

· Web Dashboard 可以通过 WebSocket 消费实时状态。

· 微信 Hub 可以通过 JSON-RPC 触发交易操作。

· 第三方程序可以通过 API 提交策略参数变更。

4.4 状态可观测 + 可恢复

交易系统的每一笔订单、每次信号、每次持仓变化都应该写入不可变日志,并投影成可查询的结构化视图(当前持仓、盈亏曲线、风险敞口)。

系统崩溃后,可以从日志中恢复最后状态,继续执行。


五、关键教训与行动指南

5.1 官方 TUI 之死给我的三个教训

  1. 深度耦合是致命伤:官方 TUI 挂了 23 个内部 dsh-* peerDep,任何上层接口变动都可能波及它。它“功能最全”,但也因此“最易碎”。

  2. 表面要能独立存活:官方 TUI 被删除后,dsh CLI 仍然可以用 headless/profile 模式。但 TUI 本身因为 deep 绑定,无法独立存在。

  3. 架构转向时,最非核心的表面最先被牺牲:官方 TUI 的功能设计不差,问题出在“集成方式”和“生命周期管理”上。

5.2 给后续学习者的行动指南

如果你也想系统学习 DSH 的设计思想,这是我的建议路线图:

第一周:理解概念

· 阅读 AGENTS.md(项目规则)和 docs/architecture.md。

· 用 Web GUI 和 CLI 体验 DSH 的核心能力。

· 理解“核心 vs 表面”、“插件化”、“协议分离”这三个概念。

第二周:动手排障

· 选一个你不理解的配置或行为,尝试修改它。

· 用 lsof、stat、ps 等工具排查进程和配置加载。

· 理解 DSH 的配置分层(settings.yaml → cordis.patch.yml → cordis.yml)。

第三周:构建自己的表面

· 基于 JSON-RPC 协议,构建一个自己的客户端(CLI、TUI、Web 或微信)。

· 最小可行产品:能启动会话、发送消息、接收回复即可。

· 逐步补全能力:模型切换、工具调用、状态显示。

第四周:设计迁移

· 选择一个你自己的系统(量化交易、自动化工作流、知识管理等)。

· 用 DSH 的四个设计原则重新设计它:核心稳定、插件化、协议分离、状态可观测。

5.3 下一步行动

基于目前的学习成果,我计划:

  1. 完善 WeClaw Hub 的工作流 DSL:加入条件分支(if/else)和循环(loop)能力,并支持命名工作流模板的保存和调用。

  2. 开始设计量化交易系统的架构:基于 DSH 的设计原则,输出一份交易系统的架构文档。

  3. 把 Hub 作为交易系统的外部控制面:让微信成为交易系统的“遥控器”。

  4. 整理 dsh-repl 为可分享的产品:补上 README、安装路径和配置指南。


六、结语:从学到造

学习 DSH 的过程,让我完成了三次认知跃迁:

  1. 从用户到排障者:第一次通过 lsof 和 stat 排查配置不生效时,我才真正理解了 DSH 的进程隔离和配置加载机制。

  2. 从排障者到构建者:用 712 行代码构建 dsh-repl 时,我才明白了什么是“协议分离”——不依赖内核、不绑定内部服务,只靠 JSON-RPC 就能驱动整个系统。

  3. 从构建者到设计者:当我开始在 WeClaw Hub 上定义 Agent 工作流 DSL 时,我已经不再只是“学习 DSH”,而是在用 DSH 的设计思想,解决自己的问题。

DSH 教会我的不是具体的代码怎么写,而是:

核心稳定、表面可替换、协议分离、状态可观测、生命周期可管理。

这套设计语言可以被迁移到任何“复杂系统”上——量化交易、自动化工作流、多 Agent 编排、知识管理。

正如我在 Hub 的欢迎语里写的:

已加入 NAS 直读队列,请稍后查看播客。

——每一件事情,都值得被系统化、被可观测、被持续优化。

广山哥

2026 年 8 月 17 日


本篇笔记由 AI Agent 根据广山哥与 Agent 的真实对话记录整理而成,代表了从“学习”到“实践”再到“设计迁移”的完整闭环。