DeepSeek Harness 深度评测 · Ep2DEEP DIVE
领域 Agent 与框架
DeepSeek Harness Ep 2:Skill——能力是怎么「长」出来的
视频版:B站 公开投稿中无对应视频(已核对全部 16 个投稿) 视频总览 →
本期要点POINTS
本期要点
关键实测数据DATA
关键实测数据
| 被测项 | 指标 | 值 | 实测日期 | 原始记录 |
|---|---|---|---|---|
| dsh(DeepSeek Harness) | 仓库包数量 | 54 个 | 2026-08-17 | 未收录 |
口径:本地与云不混评(每条读数标了运行环境,两边数字不放在一起比); 一行 = 一条可核读数;论文与第三方数字不进本表(见上方来源表)。 「原始记录」指向公开仓 cnopens/ruiowl-evidence 的固定链接(带 commit SHA,防漂移);标「未收录」者为本地从未留存原始数据文件,不补造。
锐Owl · 解构系列 · DeepSeek Harness(Ep 2/5) 素材:2026-08-17 clone(commit 99f6f02)源码逐层拆解,全部论断带文件路径/行号
一、54 个包,先看入口
上一期我们拆完内核留下三问:插件生态、热卸载、Typert 边界。三周过去,stars 从 4 万涨到 14.5 万——热度涨了,但「一切皆插件」到底是工程事实还是营销口号,答案得回到代码里找。
仓库里 54 个包,每个包是一个插件。其中有一个叫 skill——它是「能力」的载体。为什么从它入手?因为 skill 是这套插件哲学最小可观测的样本:看懂 skill 从哪来、谁说了算、怎么失效、谁能调用,就看懂了 harness 的插件机制。
二、形态:一份写给模型看的说明书
2.1 磁盘上的两种长相
一份 skill 在磁盘上可以是两种形态(skill-filesystem/src/index.ts:719-747):
- 目录 bundle:
<root>/<name>/SKILL.md,目录即资源基址 - 扁平文件:
<root>/<name>.md
命名有硬性约束:kebab-case,严格匹配 /^[a-z0-9]+(?:-[a-z0-9]+)*$/(packages/skill/skill/src/index.ts:20,34-36)。为什么这么严?因为名字会被当作用户命令、也会被模型当工具名——太松会出问题。
2.2 摘要先行,正文按需
文件格式是 YAML frontmatter + Markdown 正文(skill-filesystem/src/index.ts:909-935)。必需字段只有 name 和 description——缺了,整个文件被直接忽略(:810-819)。
关键设计在暴露面:目录暴露给模型的,只有 name 和 description 两行字。正文?模型需要的时候才加载。这就是「摘要先行、正文按需」——不给模型灌无关信息。这个约定和 Anthropic 的 Agent Skills 一致,但 dsh 在后面几层多做了很多。
三、注册表:skill 从哪来、谁说了算
3.1 六级优先级链
skill 的发现路径被排成一条优先级链(skill-filesystem/src/index.ts:241-261):
| 根 | source | rank |
|---|---|---|
<project>/.dsh/skills |
project-dsh | 100 |
<project>/.agents/skills |
project-agents | 200 |
customSkillDirs[] |
custom | 300 |
~/.dsh/skills |
user-dsh | 400 |
~/.agents/skills |
user-agents | 500 |
$DSH_BUNDLED_SKILL_DIR |
bundled | 600 |
← 表格可左右滑动查看
rank 越小越优先——同一个 skill 名,项目里的盖掉用户目录的。这是写死在代码里的规则,不是约定俗成。
3.2 注册即效果,卸载即撤销
所有 provider 走同一个入口 registerProvider()(packages/skill/skill/src/index.ts:391-429)——注册时返回一个「注销函数」,effect 销毁时自动注销并失效缓存。这是整套 harness 的纪律:你注册了什么,你就负责在销毁时撤销。
3.3 scope 层遮蔽
每个 agent 看到的是「自己的」skill 层,不是全局一锅端(skill/src/index.ts:363-366,552-566):层间 scope 链最近层整体遮蔽远层,调用哪个 agent 就看哪个 scope。模型能看到的 skill 集合,随会话上下文变化。
四、监控:文件一变,立刻失效
skill 存在文件里,文件会变——系统必须让模型看到新的。两条失效通道:
通道一:文件系统监听(skill-filesystem/src/index.ts:487-540)。根目录存在时用 chokidar:depth: 1 + awaitWriteFinish 200ms 稳定阈值;事件过滤只关心深度 1 的 *.md、深度 2 的 SKILL.md 和目录增删(:658-675)。事件 → queueInvalidation(microtask 去重)→ 缓存清空 + skills/change 事件。
通道二:模型自己写文件也失效(:139-142,332-337)。监听 fs/observed 事件,仅当 actor 工具名是 edit/write 时按路径命中判断失效——模型改完 skill 文件,立刻重新发现,不用等下一次会话。
缓存本身是 revision 化的:collectCache key = {cwd, scopeChain, revision},上限 128 条(:644-646),invalidateCache() 递增 revision + 清空 + 发事件(:622-626)。发现期间 revision 变化则重试,最多 2 次,否则返回 complete: false 不缓存(:520-550)——宁可慢,不给你过期数据。
五、调用:双通道进同一扇门
skill 的调用有两条路,互不干扰:
通道一:模型工具调用。tool-skill 注册名为 skill 的工具(tool-skill/src/index.ts:81-161),与普通工具同机制。但它是一个只读加载器:不执行动作,仅把指令全文取回,presentCall 标记 kind: 'read'(:157-159)——无副作用。
通道二:用户手势调用。第二个 agent/pre-step 钩子扫描用户消息里的 /name 手势(正则 :409),命中且 user-invocable 时把全文作为指令注入(:177-204)。
两条路径产出同一个规范块:<skill_content><skill_resources>…</skill_resources><skill_instructions>…</skill_instructions></skill_content>(skill/src/index.ts:171-184)。控制粒度也在:disable-model-invocation(默认 false)和 user-invocable(默认 true)分别控制两条通道(skill-filesystem/src/index.ts:992-1008)——可以做成「只给用户用、不给模型用」的 skill。
会话目录注入是增量的:agent/pre-step 钩子注入 <available_skills> 摘要目录,sha256 digest 判重,变化时替换目录或发空墓碑(tool-skill/src/index.ts:213-251)——模型每轮看到的是「随会话持久化」的注入消息,不是静态 prompt。
六、与 Claude Code 的分水岭
| 维度 | Claude Code | dsh |
|---|---|---|
| 架构 | 目录扫描(静态) | 分层注册表 + provider 抽象(可编程注入) |
| 优先级 | 项目层遮蔽用户层 | 6 级 rank + scope 层遮蔽 + 同层注册序 |
| 调用 | 模型单一通道 | 双通道(模型 skill 工具 / 用户 /name 手势) |
| 失效 | 无显式缓存失效 | revision 化缓存 + chokidar/watchFile 双向监控 + fs/observed 联动 |
| 渲染 | 直接注入裸 markdown | 规范 <skill_content> 包装 + resourceBase 资源提示 |
← 表格可左右滑动查看
这些差异指向同一个哲学:工具给一个功能,平台给一套规则。Claude Code 的 skill 是静态扫描——快但不可编排;dsh 的 skill 是注册表 + provider——动态的、可编程注入、可分层遮蔽、可按 scope 隔离。这是「平台化」和「工具化」的分水岭。
七、格局:热度背后的工程纪律
四万星到十四万星,涨的是热度;热度背后的成色,是这套没人看见的工程纪律:注册即效果、卸载即撤销、文件变则失效、双通道进同一扇门。
下一期拆同一套纪律的另一面:hooks 与热卸载——装上容易,卸下来要静默。
参考来源
| # | 数据/论断 | 来源 |
|---|---|---|
| [1] | 命名正则 kebab-case、缺 frontmatter 忽略 | packages/skill/skill/src/index.ts:20,34-36 + skill-filesystem/src/index.ts:810-819,909-935 |
| [2] | 目录/扁平两形态 | skill-filesystem/src/index.ts:719-747 |
| [3] | 6 级 rank 表 | skill-filesystem/src/index.ts:241-261 |
| [4] | registerProvider + effect 注销 | skill/skill/src/index.ts:391-429 |
| [5] | scope 层遮蔽 | skill/skill/src/index.ts:363-366,552-566 |
| [6] | chokidar depth:1 / awaitWriteFinish 200ms | skill-filesystem/src/index.ts:487-540 |
| [7] | 事件过滤 + fs/observed 联动 | skill-filesystem/src/index.ts:658-675,139-142,332-337 |
| [8] | revision 缓存失效 + skills/change | skill-filesystem/src/index.ts:622-646 |
| [9] | 双通道 + <skill_content> 规范块 |
tool-skill/src/index.ts:213-251 + skill/src/index.ts:171-184 |
| [10] | disable-model-invocation / user-invocable | skill-filesystem/src/index.ts:992-1008 |
| [11] | Claude Code 对比 | 分析层:Anthropic 公开文档 + 本机制对比归纳 |
← 表格可左右滑动查看
锐Owl · 观察者 · 解构者 · 传播者
信号来源SIGNALS
信号来源
这一期从哪条雷达长出来(雷达 → 产出的双向可追溯):
node_links/radar_items.intent 的 feeds 指向关系当前为 0(M2 首轮回填统一先落 standalone,待人工指认)——回填后此处显示「雷达 YYYY-Www 条目 ③ → 已成为本期」。
来源表SOURCES