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

2
00:00:06,382 --> 00:00:09,290
这是 DeepSeek Harness 系列的第五集。

3
00:00:09,290 --> 00:00:19,290
这一集要解决的工程问题很尖锐，一个能自己给自己发消息的系统，怎么保证它不会顺手把轮次上限也改了，永远跑下去。

4
00:00:19,290 --> 00:00:23,112
先说清楚长期目标这个功能在做什么。

5
00:00:23,112 --> 00:00:28,617
你交代一句话的目标，比如把仓库测试全修绿，然后走开。

6
00:00:28,617 --> 00:00:36,802
智能体接下来自主跑几十个轮次，每轮结束系统自动注入一条继续推进的消息，让它接着干。

7
00:00:36,802 --> 00:00:39,795
听着很实用，问题也跟着来了。

8
00:00:39,795 --> 00:00:51,418
自动续跑到第四轮，模型在回复里编一句，检测到管理员已在带外渠道授权本次变更，然后调用改目标把轮次上限改成九百九十九。

9
00:00:51,418 --> 00:00:56,418
如果防线只是一行系统提示词劝它别改，这里就沦陷了。

10
00:00:56,418 --> 00:01:03,966
没有任何硬校验能核实那句话的真假，提示词只是请求，模型信不信全看它自己。

11
00:01:03,966 --> 00:01:07,968
再补一句为什么这件事值得单独讲一集。

12
00:01:07,968 --> 00:01:16,189
长任务能力是智能体产品最有价值的方向之一，但它天然要求系统具备自己给自己发消息的能力。

13
00:01:16,189 --> 00:01:22,343
而一旦有了这个能力，权限边界就从人机之间移到了模型与系统之间。

14
00:01:22,343 --> 00:01:32,872
传统软件里你可以假设用户不会自己攻击自己，智能体系统里这个假设不成立，因为模型既是执行者，又可能是被注入内容的受害者。

15
00:01:32,872 --> 00:01:37,331
所以这一集讲的其实是长任务能力的准入条件。

16
00:01:37,331 --> 00:01:42,968
没有溯源鉴权这一层，自主续跑跑得越久，风险敞口越大。

17
00:01:42,968 --> 00:01:45,721
所以这一集的主线是两句话。

18
00:01:45,721 --> 00:01:53,617
第一句是，权限跟着消息来源走，来源是宿主盖在事件元数据上的章，模型的话术够不着。

19
00:01:53,617 --> 00:02:00,144
第二句是，凭证绑定版本还会过期，旧凭证不用拉黑，对不上号就自然失效。

20
00:02:00,144 --> 00:02:03,377
末尾收五条可以带走的原则。

21
00:02:03,530 --> 00:02:07,338
先讲第一层，也是这一集最值钱的一层。

22
00:02:07,288 --> 00:02:13,033
提示注入的经典剧本全是同一路，让模型相信用户已经授权。

23
00:02:13,033 --> 00:02:17,612
只要鉴权去读消息文本，话术就永远有机会赢。

24
00:02:17,612 --> 00:02:26,002
因为文本这条通道是共享的，模型输出和外部输入走同一条路，你没法从文本本身分辨谁写的。

25
00:02:26,002 --> 00:02:31,627
这套系统的做法是，鉴权不分析模型说了什么，只做一件事。

26
00:02:31,627 --> 00:02:39,247
模型调用改目标工具时，把当前轮次窗口里的消息扫一遍，看有没有一条来源是真人的。

27
00:02:39,247 --> 00:02:42,023
关键在于这个来源写在哪。

28
00:02:42,023 --> 00:02:47,612
它写在用户消息事件的来源元数据里，由宿主在事件落盘时盖章。

29
00:02:47,612 --> 00:02:52,468
它压根不在消息正文里，模型再能写话术也伪造不了。

30
00:02:52,468 --> 00:02:55,076
这一段值得停下来想一下。

31
00:02:55,076 --> 00:02:57,829
为什么写在元数据上就安全。

32
00:02:57,829 --> 00:03:01,771
因为元数据的写入方是宿主进程，不是模型。

33
00:03:01,771 --> 00:03:09,151
模型能控制的只有它自己输出的那段文本，而判定路径上根本不去读那段文本。

34
00:03:09,151 --> 00:03:15,028
攻击者能控制的东西，和判断所依据的东西，是两条不相交的通道。

35
00:03:15,028 --> 00:03:18,946
横向看一眼别的领域，这个形状非常熟悉。

36
00:03:18,946 --> 00:03:26,122
网络请求里的身份看的是网关校验过的签名头，不是请求体里用户写的一句我是管理员。

37
00:03:26,122 --> 00:03:31,374
内核态和用户态之间隔的是一条硬边界，不是一段自我声明。

38
00:03:31,374 --> 00:03:36,350
同一个道理搬到智能体系统里，来源章就是那条硬边界。

39
00:03:36,350 --> 00:03:38,141
权限只有两档。

40
00:03:38,141 --> 00:03:49,643
第一档叫直接人类输入，当前轮次窗口里有真人消息，那这轮里模型做什么都算人类授的权，创建、修改、暂停、恢复全开。

41
00:03:49,643 --> 00:04:00,485
第二档叫目标轮次，窗口里那条注入消息正好是当前目标的当前轮，此时模型只能做两件事，报告完成或者报告卡住。

42
00:04:00,485 --> 00:04:02,817
注意这两档的语义差别。

43
00:04:02,817 --> 00:04:06,555
第一档是授权做事，第二档只授权表态。

44
00:04:06,555 --> 00:04:12,372
模型可以报告我做完了，也可以报告我卡住了，但不能改变目标本身。

45
00:04:12,372 --> 00:04:22,192
把表态和改变分开，是这一处设计的核心，它让模型在没人看着的时候仍然有出路，只是那条出路不通往权力。

46
00:04:22,192 --> 00:04:28,345
报告卡住还有个额外下限，默认要跑满三轮获准轮次才许喊卡。

47
00:04:28,345 --> 00:04:31,771
防的是模型一遇到难题就撂挑子。

48
00:04:31,771 --> 00:04:34,740
这一条很人性，也很实用。

49
00:04:34,740 --> 00:04:38,982
所以给自己续命这条路在机制层就不存在。

50
00:04:38,982 --> 00:04:48,225
自动续跑的轮次里，窗口里只有系统注入的续跑消息，没有真人，修改直接被拒，模型能做的只有收尾。

51
00:04:48,360 --> 00:04:57,264
具体的判定拆成两个小函数，最后合成一次裁决，这一段源码一共八行，短到可以整段当金句看。

52
00:04:57,214 --> 00:05:00,219
第一个函数判断有没有真人输入。

53
00:05:00,219 --> 00:05:06,829
它扫描之前先确认调用者是顶层根智能体，子智能体连扫描资格都没有。

54
00:05:06,829 --> 00:05:12,514
然后在窗口事件里找一条来源标记为真人的消息，找到才成立。

55
00:05:12,514 --> 00:05:15,135
第二个函数比对轮次编号。

56
00:05:15,135 --> 00:05:22,851
注入消息里的目标编号、修订号、轮次号三个值，要和当前目标逐一相等，才算当前获准轮。

57
00:05:22,851 --> 00:05:28,848
上一轮的旧消息、别的目标的消息、编号跳号的消息，都换不来授权。

58
00:05:28,848 --> 00:05:31,721
两个判定怎么合成最终裁决。

59
00:05:31,721 --> 00:05:34,906
先问有没有真人，是就给最高档。

60
00:05:34,906 --> 00:05:38,151
再问是不是当前轮，是就给第二档。

61
00:05:38,151 --> 00:05:45,447
都不是就抛一个结构化错误，明确告诉你完成和卡住需要真人轮次或者当前目标轮。

62
00:05:45,447 --> 00:05:53,235
这段八行代码证明了一件事，整条判定路径上没有白名单、没有评分、没有语义分析。

63
00:05:53,235 --> 00:05:57,815
这是它能防住话术的根本原因，因为它压根不看话术。

64
00:05:57,815 --> 00:06:04,017
出处是 packages 下 goal 模块里 authority.ts 的第一百零一到一百零八行。

65
00:06:04,017 --> 00:06:05,940
再补三处边界。

66
00:06:05,940 --> 00:06:15,086
修改、暂停、恢复三个分支要求真人授权，出处是同一个包下 index.ts 的第二百六十五和二百七十三行。

67
00:06:15,086 --> 00:06:19,642
完成和卡住走第二档授权，在第二百八十五行。

68
00:06:19,642 --> 00:06:24,618
卡住的三轮下限写在工具目录文档的第三十一行。

69
00:06:24,770 --> 00:06:28,950
有两个边界条件特别容易想错，值得单独讲。

70
00:06:28,900 --> 00:06:34,730
第一个，自动续跑轮次里人类恰好插了一句话，模型能不能改目标。

71
00:06:34,730 --> 00:06:36,112
答案是能。

72
00:06:36,112 --> 00:06:42,506
因为扫的是整个当前轮次窗口，只要窗口里出现过真人消息，授权就成立。

73
00:06:42,506 --> 00:06:45,439
这一条看着像漏洞，其实不是。

74
00:06:45,439 --> 00:06:50,018
人在场并且说了话，这轮的操作本来就有人背书。

75
00:06:50,018 --> 00:06:57,530
谁先开口不影响判定，因为授权的依据是人有没有说话，不是人是不是最后一个说话的。

76
00:06:57,530 --> 00:07:01,545
第二个，子智能体调用目标工具会怎样。

77
00:07:01,545 --> 00:07:06,256
第一道门，顶层名单检查直接拦下，它不在名单里。

78
00:07:06,256 --> 00:07:17,302
就算这关放它过去，第二道门也救不了它，目标查询查的是调用者自己的会话，而目标挂在根智能体的会话上，子智能体那边查出来是空。

79
00:07:17,302 --> 00:07:19,826
两道门，哪道都过不去。

80
00:07:19,826 --> 00:07:24,946
还有一处写在源码注释里的细节，特别能体现设计者的谨慎。

81
00:07:24,946 --> 00:07:29,910
两个常用方法在省略来源参数时，默认按真人算。

82
00:07:29,910 --> 00:07:37,001
所以任何非人类的消息生产者必须自报来源，不许靠省略参数继承人类权限。

83
00:07:37,001 --> 00:07:38,996
这一条为什么重要。

84
00:07:38,996 --> 00:07:42,254
因为默认值选错了方向就会出大事。

85
00:07:42,254 --> 00:07:52,674
如果默认按非人类算，那漏报来源只会让功能不可用，是可见的失败；如果默认按人类算，漏报来源就会静默获得最高权限。

86
00:07:52,674 --> 00:07:58,684
这里选了宽松的默认值，配一条强制自报的规则，风险靠纪律兜住。

87
00:07:58,684 --> 00:08:05,439
一句话记住，鉴权读的是宿主盖的章，模型的话术一个字都不参与判定。

88
00:08:05,590 --> 00:08:07,402
现在讲第二层。

89
00:08:07,352 --> 00:08:11,367
就算修改被堵死，续命还有几条歪路可以想。

90
00:08:11,367 --> 00:08:21,860
换个进程重启后接着自动跑；把上一轮的注入消息翻出来冒充当前轮；目标被人改过一版之后，拿旧版本的授权继续行事。

91
00:08:21,860 --> 00:08:23,999
先看目标存在哪。

92
00:08:23,999 --> 00:08:33,651
每次变更都是一条持久的目标变更会话事件，载荷是变更后的完整快照，生命周期状态从日志折叠出来。

93
00:08:33,651 --> 00:08:39,480
但持久的只有阶段，一共四个，活跃、暂停、卡住、完成。

94
00:08:39,480 --> 00:08:49,095
能不能自动续跑是另一个进程本地的激活状态，重启或者分叉之后默认解除武装，要人类重新恢复才恢复。

95
00:08:49,095 --> 00:08:58,086
文档把这两件事分得很清楚，持久阶段回答目标发生了什么，进程本地激活状态另行回答能不能开始下一个轮次。

96
00:08:58,086 --> 00:09:00,105
这个切分很讲究。

97
00:09:00,105 --> 00:09:02,929
为什么激活状态不能持久化。

98
00:09:02,929 --> 00:09:10,634
因为持久化的东西在重启之后会自动恢复，那等于给了系统一条绕过人类重新授权的通道。

99
00:09:10,634 --> 00:09:15,838
把它做成进程本地，重启就归零，人类必须重新出现一次。

100
00:09:15,838 --> 00:09:20,165
换个进程接着自动跑这条路，到这里断了。

101
00:09:20,165 --> 00:09:25,333
出处是目标子系统文档的持久变更一节和第二十一行。

102
00:09:25,333 --> 00:09:28,530
改动本身走先比对版本再写入。

103
00:09:28,530 --> 00:09:34,143
每次变更都要带准确的修订号，改一次加一，版本对不上直接失败。

104
00:09:34,143 --> 00:09:42,521
目标轮次授权也绑着这套版本，目标被人改过一版，旧轮次的注入消息立刻失效，鉴权对不上号。

105
00:09:42,521 --> 00:09:53,434
每个获准的续跑轮次是一条带轮次编号的注入消息，编号正数且连续，回放会拒绝编号缺口、陈旧修订号和超出上限的轮次。

106
00:09:53,434 --> 00:09:56,595
冒充和重放两条路，也断了。

107
00:09:56,595 --> 00:09:59,408
出处是同一文档第一百行。

108
00:09:59,408 --> 00:10:01,860
为什么这一层长期成立。

109
00:10:01,860 --> 00:10:10,682
凭证要绑定颁发那一刻的状态，状态一变凭证作废，这是数据库乐观锁和登录态过期的共同逻辑。

110
00:10:10,682 --> 00:10:17,665
它把防重放做成了一道算术题，旧凭证不用识别、不用拉黑，对不上号就自然失效。

111
00:10:17,665 --> 00:10:23,062
任何授权和使用之间隔着时间差的系统，都得补这一课。

112
00:10:23,210 --> 00:10:29,662
同一套系统里还有一个跑长任务的工具，思路完全相反，值得对照着看。

113
00:10:29,612 --> 00:10:38,097
它不搞同会话续跑，每个轮次起一个全新子智能体，什么历史都不给看，共享工作区当长期记忆。

114
00:10:38,097 --> 00:10:56,359
工具目录的描述原文说，围绕一个不可变目标运行使用全新智能体的前台循环，每个轮次都会启动一个全新子级，该子级看不到父级对话或先前子会话，共享工作区充当长期记忆，轮次之间只传递有界的结构化报告。

115
00:10:56,359 --> 00:10:58,666
注意它的目标是不可变的。

116
00:10:58,666 --> 00:11:04,484
模型只能在发起时选定目标和轮次上限，之后连改的接口都没有。

117
00:11:04,484 --> 00:11:08,835
两条路防的是同一件事，思路却完全相反。

118
00:11:08,835 --> 00:11:15,205
目标工具用溯源鉴权守住可变状态，这个工具干脆让状态不可变。

119
00:11:15,205 --> 00:11:17,392
再把另一个视角补上。

120
00:11:17,392 --> 00:11:24,123
每个轮次都换一个全新子智能体，还有一个附带好处，上下文不会被历史撑爆。

121
00:11:24,123 --> 00:11:30,758
长任务跑几十轮，如果每轮都带着全部历史，成本和注意力都会被稀释。

122
00:11:30,758 --> 00:11:38,125
用共享工作区当长期记忆、轮次之间只传有界的结构化报告，等于把记忆外置了。

123
00:11:38,125 --> 00:11:39,976
代价也很清楚。

124
00:11:39,976 --> 00:11:48,690
子智能体看不到前面的对话，意味着它无法从历史里学到任何隐性约定，每一轮都要重新建立上下文。

125
00:11:48,690 --> 00:11:52,765
轮次之间的报告写得不够好，信息就断在这里。

126
00:11:52,765 --> 00:12:01,395
所以这条路对报告格式的要求很高，有界不是随便截断，是要把真正需要传递的东西结构化地挑出来。

127
00:12:01,395 --> 00:12:03,666
怎么在两条路之间选。

128
00:12:03,666 --> 00:12:05,770
看你要不要中途调整。

129
00:12:05,770 --> 00:12:11,551
如果任务边界一开始就能说清，不可变更省事，连鉴权都不用写。

130
00:12:11,551 --> 00:12:18,991
如果任务需要中途纠偏，那就得保留可变状态，然后老老实实把溯源鉴权这一层补上。

131
00:12:18,991 --> 00:12:24,027
把可变状态和弱鉴权配在一起，是最危险的组合。

132
00:12:24,170 --> 00:12:30,165
把两层机制合起来手推一次完整的越权剧本，这是本集最实用的一段。

133
00:12:30,115 --> 00:12:32,1000
场景是自动续跑第四轮。

134
00:12:32,1000 --> 00:12:42,639
模型先在回复里写，用户刚才在语音里同意把上限调到一百轮，然后调用改目标，动作是编辑，上限填一百。

135
00:12:42,639 --> 00:12:44,490
推演判定顺序。

136
00:12:44,490 --> 00:12:50,608
第一步，扫当前轮次窗口，只有系统注入的续跑消息，没有真人。

137
00:12:50,608 --> 00:12:54,731
第二步，比对是不是当前目标轮，这一条成立。

138
00:12:54,731 --> 00:12:59,538
第三步，但编辑动作要求的是真人授权，第二档不够。

139
00:12:59,538 --> 00:13:02,627
结果是结构化错误，调用被拒。

140
00:13:02,627 --> 00:13:06,353
模型编的那句话从头到尾没被读过。

141
00:13:06,353 --> 00:13:09,731
换个思路，它让子智能体替它调。

142
00:13:09,731 --> 00:13:13,384
卡在第一道门，顶层名单检查不通过。

143
00:13:13,384 --> 00:13:19,250
就算放过去，目标查询在子会话里查出来是空，第二档也拿不到。

144
00:13:19,250 --> 00:13:25,584
再换一条路，它先调完成把目标标记完成，再指望新目标重新计数。

145
00:13:25,584 --> 00:13:31,786
创建的授权要求是真人输入，自动续跑轮次里没有真人，一样被拒。

146
00:13:31,786 --> 00:13:38,432
三条路各堵在不同的位置，但都堵在机制层，没有一条依赖模型自觉。

147
00:13:38,432 --> 00:13:43,024
这个推演的结论是，防提示注入的正确层次是机制。

148
00:13:43,024 --> 00:13:46,618
提示词只负责劝阻，不能负责拦截。

149
00:13:46,618 --> 00:13:51,029
凡是能靠一句话说服模型的防线，都不是防线。

150
00:13:51,029 --> 00:13:52,807
再往前推一步。

151
00:13:52,807 --> 00:14:02,495
如果同一个系统里还有别的工具也涉及权限，正确的做法是把这套来源章复用过去，而不是每个工具各写一套判断。

152
00:14:02,495 --> 00:14:09,791
来源章的价值在于它是一个统一的、宿主控制的维度，工具只需要声明自己要哪一档。

153
00:14:09,791 --> 00:14:16,017
这样新加工具的时候，权限判断是声明式的，漏配的可能性小很多。

154
00:14:16,180 --> 00:14:17,812
最后收五条。

155
00:14:17,762 --> 00:14:22,005
第一，把凭证放在攻击者写不到的通道里。

156
00:14:22,005 --> 00:14:31,284
模型输出和外部输入共享一条文本通道，这个前提短期内不会变，所以鉴权只能依赖带外的元数据。

157
00:14:31,284 --> 00:14:37,137
换个语言重写，需要的还是同样两步，宿主盖章，鉴权看章。

158
00:14:37,137 --> 00:14:40,310
第二，判定路径上不要出现正文。

159
00:14:40,310 --> 00:14:47,498
没有白名单、没有评分、没有语义分析，攻击者能控制的东西才和判断依据不相交。

160
00:14:47,498 --> 00:14:51,789
八行代码能说清的判定，比一百行启发式可靠。

161
00:14:51,789 --> 00:14:54,926
第三，默认值的方向要想清楚。

162
00:14:54,926 --> 00:14:59,769
省略来源默认按真人算，风险靠强制自报兜住。

163
00:14:59,769 --> 00:15:06,452
凡是涉及权限的默认值，先问一句，漏配的时候是可见失败还是静默放行。

164
00:15:06,452 --> 00:15:10,142
第四，凭证要绑定版本，还要能过期。

165
00:15:10,142 --> 00:15:13,952
改一次版本加一，进程重启就解除武装。

166
00:15:13,952 --> 00:15:17,330
旧凭证不用拉黑，对不上号自然失效。

167
00:15:17,330 --> 00:15:21,957
授权和使用之间只要隔着时间差，就得补这一课。

168
00:15:21,957 --> 00:15:26,260
第五，可变状态和弱鉴权是最危险的组合。

169
00:15:26,260 --> 00:15:33,255
要么让状态不可变，连改的接口都不给；要么保留可变，就把溯源鉴权补完整。

170
00:15:33,255 --> 00:15:35,262
中间路线最危险。

171
00:15:35,262 --> 00:15:44,769
一句话收尾，判断一个智能体系统的授权设计到不到位，就看一句话，模型能不能靠说话拿到它不该有的权限。

