1
00:00:00,100 --> 00:00:06,432
本内容改编自小山学堂《学 AI 产品，从入门到精通》，为二次演绎配音版。

2
00:00:06,382 --> 00:00:09,206
这是 DeepSeek Harness 系列的又一集。

3
00:00:09,206 --> 00:00:16,802
这一集要解决的工程问题看起来很朴素，发布文里说的那些话，在源码里到底对不对得上。

4
00:00:16,802 --> 00:00:18,629
为什么值得关心。

5
00:00:18,629 --> 00:00:22,572
我们读一篇产品的发布文，最容易犯两种错。

6
00:00:22,572 --> 00:00:27,800
一种是把宣传词当废话略过，结果错过了真正的架构信号。

7
00:00:27,800 --> 00:00:36,322
另一种是把它当设计文档全盘接受，结果照着一句文案去找实现，找了半天发现自己被一个名字骗了。

8
00:00:36,322 --> 00:00:42,463
真相通常在中间，宣传词里有真东西，也有些词只在文案层活着。

9
00:00:42,463 --> 00:00:53,305
这一集做的事很笨也很有效，把发布文里的四种模式、插件生态，一条条对到仓库里的具体文件，看它们到底叫什么、在哪一行。

10
00:00:53,305 --> 00:00:55,144
先把结论摆出来。

11
00:00:55,144 --> 00:00:58,461
内核只管装卸，业务全是插件。

12
00:00:58,461 --> 00:01:03,149
官宣的四种模式，在源码里只是四份插件清单。

13
00:01:03,149 --> 00:01:10,757
而那个听起来很厉害的 PTC 模式，源码里压根没有叫这个名字的实现，它的真名叫 Code Mode。

14
00:01:10,757 --> 00:01:12,536
这一集分四块。

15
00:01:12,536 --> 00:01:23,942
先讲内核为什么能缩到那么小，再讲模式这份清单长什么样，然后逐张看完四种模式的卡片，最后横向对比三家产品，收五条能带走的原则。

16
00:01:23,942 --> 00:01:25,540
还有一句提醒。

17
00:01:25,540 --> 00:01:35,384
这一集里出现的行数、包数、插件数，都是二零二六年八月十三日对源码做本地统计的结果，你今天去翻可能已经变了。

18
00:01:35,384 --> 00:01:40,480
别把数字当结论背，把「去哪一行找」这件事记住就够了。

19
00:01:40,612 --> 00:01:42,124
先说内核。

20
00:01:42,074 --> 00:01:52,411
DeepSeek Harness 把一套叫 Cordis 的框架直接 vendor 进了仓库，也就是把依赖的源码整份拷进自己的代码树，而不是当外部依赖去装。

21
00:01:52,411 --> 00:01:54,983
这个框架只做三件事。

22
00:01:54,983 --> 00:01:58,468
第一件，把插件装进共享上下文。

23
00:01:58,468 --> 00:02:00,932
第二件，把插件卸下来。

24
00:02:00,932 --> 00:02:07,507
第三件，把每一次注册记成可逆的副作用，插件卸载的时候自动回滚。

25
00:02:07,507 --> 00:02:09,622
业务逻辑一行都没有。

26
00:02:09,622 --> 00:02:12,555
官方文档的原话值得念一遍。

27
00:02:12,555 --> 00:02:20,512
Cordis 是这套系统底层的框架，插件向共享上下文贡献服务、类型化事件和可逆的副作用。

28
00:02:20,512 --> 00:02:31,762
产品的每一部分都是插件，包括模型适配器、工具注册表、会话日志，以及智能体循环本身，因此每一部分都可以从配置替换。

29
00:02:31,762 --> 00:02:42,374
不存在需要打补丁的特权内核，扩展的方式是把插件挂载到其他插件旁边，而各项注册都是副作用，会在其插件卸载时撤销。

30
00:02:42,374 --> 00:02:46,485
这段的出处是架构文档的第十一和十三行。

31
00:02:46,485 --> 00:02:52,555
根目录下的 AGENTS 点 md 第三行，用加粗英文写着一句话，一切皆插件。

32
00:02:52,555 --> 00:02:55,956
为什么这段描述值得单独拎出来讲。

33
00:02:55,956 --> 00:03:01,737
因为「不存在需要打补丁的特权内核」不是一句修辞，它是一个有代价的承诺。

34
00:03:01,737 --> 00:03:13,913
它承诺你永远不会遇到这种处境，想改一个能力，翻进去一看它在内核里，改不动，只能 fork 仓库，然后官方每发一版你都要重新合并一次补丁。

35
00:03:13,913 --> 00:03:20,439
要兑现这个承诺，内核就必须足够小，小到只装得下装卸机制这一件事。

36
00:03:20,439 --> 00:03:26,004
换个角度说，这个设计把「可定制」从一个功能变成了一条不变量。

37
00:03:26,004 --> 00:03:29,682
功能可以砍，可以延后，可以这版不做。

38
00:03:29,682 --> 00:03:43,096
不变量不一样，一旦有人为了省事把某个业务能力塞回内核，这条承诺就开始漏水，而且漏水的地方往往要很久之后才被发现，因为短期内没人会去替换那个能力。

39
00:03:43,096 --> 00:03:48,985
所以看这类项目的时候，值得盯一眼内核的行数有没有在长。

40
00:03:49,132 --> 00:03:51,161
那业务逻辑去哪了。

41
00:03:51,111 --> 00:03:53,563
全部搬进了 packages 目录。

42
00:03:53,563 --> 00:04:01,315
二零二六年八月十三日的本地统计是四十九个分组、二百一十九个包，全部以插件形式存在。

43
00:04:01,315 --> 00:04:03,407
模型适配器是插件。

44
00:04:03,407 --> 00:04:04,957
工具是插件。

45
00:04:04,957 --> 00:04:07,108
会话日志是插件。

46
00:04:07,108 --> 00:04:10,907
连智能体循环的主循环本身也是插件。

47
00:04:10,907 --> 00:04:13,238
注意最后这一条的分量。

48
00:04:13,238 --> 00:04:21,928
主循环在很多产品里是最神圣、最不可碰的那块代码，在这里它只是挂在插件树上的一个普通包。

49
00:04:21,928 --> 00:04:28,407
你想换掉整个智能体的思考方式、调度方式，不需要 fork 仓库，换一个包就行。

50
00:04:28,407 --> 00:04:41,904
日志格式想换也是同理，回想一下开头说的那个场景，在单体架构里改个日志格式要 fork、要改、要重建、要长期跟官方补丁打架，在这里只是替换一个插件。

51
00:04:41,904 --> 00:04:45,702
想给系统加能力，同样不用碰它的仓库。

52
00:04:45,702 --> 00:04:56,940
官方仓库之外的插件叫树外插件，用 dsh plugin 命令指定 profile 和目标包名就能装进去，运行时挂载，卸载时注册的副作用自动回滚。

53
00:04:56,940 --> 00:05:04,092
官方的 README 还定了一条规矩，插件仓库统一打 dsh-plugin 话题标签，方便互相发现。

54
00:05:04,092 --> 00:05:09,284
所以发布文里那句邀请大家共建生态，别当成客套话。

55
00:05:09,284 --> 00:05:17,757
装卸机制、profile、话题标签，这套东西本来就是为第三方准备的，机制是现成的，缺的只是人。

56
00:05:17,757 --> 00:05:21,411
这里有个容易忽略的细节值得单独点一下。

57
00:05:21,411 --> 00:05:30,077
副作用自动回滚这件事，听起来只是卸载时的清理工作，其实它决定了插件能不能被安全地反复装卸。

58
00:05:30,077 --> 00:05:39,969
如果注册不是可逆的，装一次卸一次就会留下一堆残留状态，热切换模式这种事根本没法做，只能重启进程再来一次。

59
00:05:39,969 --> 00:05:44,044
可逆性不是锦上添花，它是热插拔的前提。

60
00:05:44,188 --> 00:05:47,419
插一句为什么这个思路长期成立。

61
00:05:47,369 --> 00:05:50,494
微内核思想比这份代码老得多。

62
00:05:50,494 --> 00:05:58,222
操作系统课上的 Mach 和 L4，浏览器的扩展体系，VS Code 的插件生态，走的都是同一条路。

63
00:05:58,222 --> 00:06:02,789
变化快的能力放外圈，几乎不变的装卸机制放核心。

64
00:06:02,789 --> 00:06:05,253
这么分层的好处很具体。

65
00:06:05,253 --> 00:06:11,672
哪天官方把智能体循环整个重写一遍，Cordis 的装卸逻辑一行不用动。

66
00:06:11,672 --> 00:06:16,804
换个语言把这套 harness 再写一遍，这个分层照样管用。

67
00:06:16,804 --> 00:06:20,554
所以记这门课的结构比记它的代码划算。

68
00:06:20,554 --> 00:06:26,744
代码只是这个思路的某一版实现，明年可能就变了，结构才是能带走的东西。

69
00:06:26,744 --> 00:06:32,056
一句话收住，变化快的住在插件里，几乎不变的住在内核里。

70
00:06:32,056 --> 00:06:33,523
再补一层。

71
00:06:33,523 --> 00:06:38,583
判断什么该进内核，别问它重不重要，要问它变不变。

72
00:06:38,583 --> 00:06:41,780
会话日志很重要，但它会变。

73
00:06:41,780 --> 00:06:44,881
工具注册表很重要，它也会变。

74
00:06:44,881 --> 00:06:48,595
装卸机制看起来最不起眼，可它几乎不变。

75
00:06:48,595 --> 00:06:56,768
按变化频率分层，比按重要性分层靠谱得多，因为重要性是主观的，变化频率是可以事后验证的。

76
00:06:56,908 --> 00:06:59,069
现在讲第二种思路。

77
00:06:59,019 --> 00:07:08,394
多数产品的模式是硬编码的，代码里散着一堆判断分支，比如判断当前是不是精简模式，然后走不同的路径。

78
00:07:08,394 --> 00:07:12,324
模式数量在写代码的那一刻就定死了。

79
00:07:12,324 --> 00:07:16,988
想新增一个模式，要改代码、过测试、等发版。

80
00:07:16,988 --> 00:07:21,363
想微调某个模式里的一个能力，还是这套流程。

81
00:07:21,363 --> 00:07:25,954
用户更没得选，只能在官方给的几个套餐里挑一个。

82
00:07:25,954 --> 00:07:27,937
这里的做法不一样。

83
00:07:27,937 --> 00:07:34,476
一个模式就是配置目录下 agent-presets 里的一个子目录，目录里只有两个文件。

84
00:07:34,476 --> 00:07:41,495
第一个文件叫 preset.yml，只有三行，管的是界面上的展示名和排序，跟能力无关。

85
00:07:41,495 --> 00:07:45,738
第二个文件叫 agent.cordis.yml，是插件清单。

86
00:07:45,738 --> 00:07:51,411
启动时框架照着清单逐行挂载，输出一个组装好的智能体。

87
00:07:51,411 --> 00:07:56,170
于是切换模式这件事，变成了换一份清单重新组装。

88
00:07:56,170 --> 00:08:00,618
源码里没有任何模式分支，一个 if 判断都没有。

89
00:08:00,618 --> 00:08:04,908
四种模式的差异有多大，看四张卡片就够了。

90
00:08:04,908 --> 00:08:07,048
顺带提醒一句口径。

91
00:08:07,048 --> 00:08:19,608
下面说的插件数都是 macOS 视角，仅 Windows 启用的 tool-pwsh 和两条处于 disabled 状态的委派行没有算进去，所以你自己在 Windows 上数出来的数字可能不一样。

92
00:08:19,756 --> 00:08:21,881
第一张，标准模式。

93
00:08:21,831 --> 00:08:25,569
目录名叫 standard，清单二百五十一行。

94
00:08:25,569 --> 00:08:29,511
给谁用，日常写代码的人，这是默认档。

95
00:08:29,511 --> 00:08:36,314
二十三个插件全开，文件编辑、shell、检索、计划、子代理、工作流都在。

96
00:08:36,314 --> 00:08:41,927
它真正的意义是当基准，其余三个模式全部描述成对它的加减。

97
00:08:41,927 --> 00:08:48,994
背后的思路是，先定义一个能力齐全的参照系，别的组合才有资格用一句话说清自己。

98
00:08:48,994 --> 00:08:52,408
出处是 standard 目录下的清单全文。

99
00:08:52,408 --> 00:08:54,475
第二张，PTC 模式。

100
00:08:54,475 --> 00:08:57,853
目录名叫 code，清单二百六十二行。

101
00:08:57,853 --> 00:09:03,273
给谁用，跑多步长任务、嫌一次一个工具调用往返太慢的人。

102
00:09:03,273 --> 00:09:11,182
它比标准多了什么，标准那部分一行不动，末尾多挂一个叫 tool-presentation 的插件，配置里 mode 设为 code。

103
00:09:11,182 --> 00:09:19,223
效果是模型改写一段 TypeScript 小程序，用 run_code 一次执行掉原本要五次往返的操作。

104
00:09:19,223 --> 00:09:27,180
注意这里改的只是工具的呈现方式，能力本身一点没变，所以这个差异只配拥有清单里的一行。

105
00:09:27,180 --> 00:09:35,785
出处是 code 目录下清单的第二百五十九到二百六十二行，第三行的注释还专门写明，standard 全量未动。

106
00:09:35,785 --> 00:09:37,985
第三张，极简模式。

107
00:09:37,985 --> 00:09:40,833
目录叫 minimal，清单六十二行。

108
00:09:40,833 --> 00:09:44,151
给谁用，跑模型基准测试的人。

109
00:09:44,151 --> 00:09:45,833
只剩六个插件。

110
00:09:45,833 --> 00:09:52,119
persona 一句话写死，complete 设为 true，别的插件想追加提示词也加不进去。

111
00:09:52,119 --> 00:09:58,586
工具只剩持久 bash 和 str_replace_editor 两个，没有 compaction。

112
00:09:58,586 --> 00:10:05,533
背后的思路很干净，把 harness 这层的变量全部排干净，剩下的表现就是模型本身。

113
00:10:05,533 --> 00:10:11,374
出处是 minimal 目录下的清单全文六十二行，persona 在第八到十三行。

114
00:10:11,374 --> 00:10:13,562
第四张，创造模式。

115
00:10:13,562 --> 00:10:17,047
目录叫 cordis，清单也是二百六十二行。

116
00:10:17,047 --> 00:10:20,389
给谁用，想让智能体造智能体的人。

117
00:10:20,389 --> 00:10:27,744
标准全套之上，加一个 tool-cordis 工具集，加一个教组合写法的 skill，persona 也换了版本。

118
00:10:27,744 --> 00:10:35,172
于是智能体能在运行时检查并挂载自己的插件，写出来的组合还能存成一份新的预设。

119
00:10:35,172 --> 00:10:41,350
背后的思路是，组装器自己也是一个插件，所以它天然能开放给智能体用。

120
00:10:41,350 --> 00:10:47,204
文件头的注释还提醒了一句，把这个模式的会话当 shell 权限对待。

121
00:10:47,204 --> 00:10:54,824
出处是 cordis 目录下清单的第二百四十五到二百四十六行，权限提醒在第九到十二行。

122
00:10:54,964 --> 00:10:57,269
顺带点破一个营销词。

123
00:10:57,219 --> 00:11:02,279
code 目录下的 preset.yml，第一行写着 name，PTC 模式。

124
00:11:02,279 --> 00:11:11,366
但目录名字叫 code，源码注释和文档里这套机制叫 Code Mode，你把整个仓库翻一遍，找不到任何名为 PTC 的实现。

125
00:11:11,366 --> 00:11:15,092
也就是说，PTC 只活在界面文案这一层。

126
00:11:15,092 --> 00:11:18,614
跟人聊源码的时候说 Code Mode 才对得上号。

127
00:11:18,614 --> 00:11:23,169
这件事本身不大，但它是一个很好用的判断标准。

128
00:11:23,169 --> 00:11:29,839
看到一个花哨的名字，先去仓库里搜一遍，搜得到就是机制，搜不到就是文案。

129
00:11:29,839 --> 00:11:37,219
花三十秒能省掉后面一整场误会，也能避免你把一句宣传语写进自己的设计文档。

130
00:11:37,372 --> 00:11:41,564
一切皆插件的价值，放到同行里才看得清。

131
00:11:41,514 --> 00:11:48,726
同一个问题，想给智能体加一个新能力，需不需要动它的仓库源码，三家给出三种答案。

132
00:11:48,726 --> 00:11:52,380
第一家，DeepSeek Harness，不需要改仓库。

133
00:11:52,380 --> 00:11:59,747
能力就是一个官方仓库之外的 npm 包，用命令装进 profile，运行时挂载，卸载自动回滚。

134
00:11:59,747 --> 00:12:03,558
模式级差异也只是 YAML 里增删几行。

135
00:12:03,558 --> 00:12:08,990
出处是 bundle 包的 README 第十三行，以及架构文档第十三行。

136
00:12:08,990 --> 00:12:11,827
第二家，Claude Code，看情况。

137
00:12:11,827 --> 00:12:19,339
还原出来的是一棵 TypeScript 单体源码树，入口在 main.tsx，改内建功能要动产品源码。

138
00:12:19,339 --> 00:12:27,584
对外留了 hooks、MCP、Skills 这些扩展口，能加工具和拦截点，但换不掉会话日志这类深层实现。

139
00:12:27,584 --> 00:12:30,673
第三家，Grok Build，需要改仓库。

140
00:12:30,673 --> 00:12:37,680
根 Cargo.toml 的 members 数组列了七十九个 workspace 成员，能力按 crate 切分，编译期组合。

141
00:12:37,680 --> 00:12:41,827
新增能力要新建 crate、改根清单、重新编译。

142
00:12:41,827 --> 00:12:43,846
三家没有绝对优劣。

143
00:12:43,846 --> 00:12:54,014
Grok 用编译期组合换 Rust 的类型和性能保证，Claude Code 用单体换产品迭代速度，DeepSeek Harness 用插件树换运行时可拔插。

144
00:12:54,014 --> 00:13:03,702
只是如果你的目标是做一个可替换、可审计的运行时，三家里只有它让第三方无需 fork 仓库就能替换深层能力。

145
00:13:03,844 --> 00:13:07,135
最后收五条原则，外加一道练习。

146
00:13:07,085 --> 00:13:10,174
第一条，内核只装装卸机制。

147
00:13:10,174 --> 00:13:17,554
判断标准很简单，如果一个能力的变化频率高于装卸机制，它就不该待在内核里。

148
00:13:17,554 --> 00:13:20,426
第二条，把差异收敛进清单。

149
00:13:20,426 --> 00:13:24,344
想知道两个模式差在哪，diff 两份 YAML。

150
00:13:24,344 --> 00:13:27,385
想造新模式，复制目录改几行。

151
00:13:27,385 --> 00:13:30,186
出了问题，回滚清单就行。

152
00:13:30,186 --> 00:13:40,967
这条思路有个通行名字，配置即架构，Kubernetes 用 YAML 声明集群，Docker 用 Dockerfile 声明镜像，同一件事在不同层面反复出现。

153
00:13:40,967 --> 00:13:47,409
架构问题一旦降维成文本问题，就变得可以比较、可以回滚、可以评审了。

154
00:13:47,409 --> 00:13:51,412
第三条，警惕只在文案层活着的名字。

155
00:13:51,412 --> 00:13:57,554
搜不到实现的名词，不要写进你的设计文档，也不要拿它去跟工程师对齐。

156
00:13:57,554 --> 00:14:00,859
第四条，选架构就是选代价。

157
00:14:00,859 --> 00:14:08,888
编译期组合、单体、插件树，各有各的账，先想清楚你要的是性能、迭代速度还是可替换性。

158
00:14:08,888 --> 00:14:11,532
这三样很难同时拿满。

159
00:14:11,532 --> 00:14:14,849
第五条，先定义基准再谈差异。

160
00:14:14,849 --> 00:14:22,926
没有能力齐全的参照系，别的组合就只能靠长篇描述来说清自己，长篇描述又是分歧的开始。

161
00:14:22,926 --> 00:14:24,344
留一道练习。

162
00:14:24,344 --> 00:14:34,969
把 standard 清单里 compaction 那个分组删掉，也就是第一百三十七到一百五十五行，得到的会话和极简模式在上下文压力下表现等价吗。

163
00:14:34,969 --> 00:14:38,756
再对照 minimal 清单里 persona 的三个字段想一想。

164
00:14:38,756 --> 00:14:51,111
答案是除了压缩之外还差两点，一是 persona 被 complete 锁死，别的插件追加不进提示词，二是工具只剩持久 bash 和 str_replace_editor 两个。

165
00:14:51,111 --> 00:14:55,667
想明白这两点，四份清单就算真的读透了。

