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

2
00:00:06,382 --> 00:00:13,665
今天这一集，我想把两个看起来八竿子打不着、其实是一枚硬币两面的工程问题放在一起讲。

3
00:00:13,665 --> 00:00:21,862
第一个问题是，当你把代码执行权交给了 Agent，你怎么保证它不会顺手把你服务器上的密钥也交出去。

4
00:00:21,862 --> 00:00:30,132
第二个问题是，当对话长到快要撑爆上下文窗口的时候，你怎么在不丢关键信息的前提下把它压短。

5
00:00:30,132 --> 00:00:34,495
这两个问题的答案，最后会指向同一套思维方式。

6
00:00:34,495 --> 00:00:38,846
前一半是结构性安全，靠隔离，不靠自觉。

7
00:00:38,846 --> 00:00:43,329
后一半是分治算法，靠切小再合并，不靠硬撑。

8
00:00:43,329 --> 00:00:47,295
我们先说第一个，因为它更紧急，也更致命。

9
00:00:47,295 --> 00:00:49,675
一句话先把结论立住。

10
00:00:49,675 --> 00:00:54,603
Agent 生成的代码和用户的密钥，永远不能在同一个地方。

11
00:00:54,603 --> 00:01:01,189
这句话听着像安全口号，其实是一条架构硬约束，与设计偏好无关。

12
00:01:01,348 --> 00:01:03,978
先看看传统做法长什么样。

13
00:01:03,928 --> 00:01:06,632
一个容器，装下所有东西。

14
00:01:06,632 --> 00:01:13,098
代码执行在这里，API 密钥在这里，OAuth 令牌在这里，会话凭证也在这里。

15
00:01:13,098 --> 00:01:15,995
全部共存于同一个运行环境。

16
00:01:15,995 --> 00:01:18,399
麻烦就出在这个「同一个」上。

17
00:01:18,399 --> 00:01:22,437
Agent 能看到的一切，恶意代码同样能看到。

18
00:01:22,437 --> 00:01:26,248
这不是一个概率问题，而是一个结构问题。

19
00:01:26,248 --> 00:01:28,159
攻击链短得吓人。

20
00:01:28,159 --> 00:01:38,531
攻击者只需要通过提示词注入，说服 Agent 执行一行命令，把这行命令的输出打印出来，整个环境变量里的密钥就全走了。

21
00:01:38,531 --> 00:01:43,279
不需要提权，不需要绕过什么沙箱，因为根本没有沙箱。

22
00:01:43,279 --> 00:01:51,692
整个过程甚至不需要攻击者懂多少安全技术，他只要能让模型相信「这条命令是任务的一部分」就够了。

23
00:01:51,692 --> 00:01:55,695
更麻烦的是，模型越强，这件事越严重。

24
00:01:55,695 --> 00:02:03,531
能力更强的 Agent，意味着更强大的工具调用能力，而在旧架构下，这也意味着更大的攻击面。

25
00:02:03,531 --> 00:02:08,483
模型的进步不会自动解决这个问题，反而会让它恶化。

26
00:02:08,483 --> 00:02:10,935
这句话值得多念一遍。

27
00:02:10,935 --> 00:02:16,308
安全架构不能依赖模型自觉，必须通过结构性设计来保证。

28
00:02:16,308 --> 00:02:20,286
哪怕模型被完全控制，攻击者也拿不到凭证。

29
00:02:20,286 --> 00:02:22,702
这才是我们要的目标。

30
00:02:22,852 --> 00:02:24,616
那具体怎么隔离。

31
00:02:24,566 --> 00:02:27,643
素材里给了两种模式，我分别说。

32
00:02:27,643 --> 00:02:29,855
第一种叫绑定到资源。

33
00:02:29,855 --> 00:02:36,514
令牌在使用的时候被嵌入到资源的访问路径里，它从来不作为一个独立变量存在。

34
00:02:36,514 --> 00:02:39,338
Agent 能用它，但看不见它。

35
00:02:39,338 --> 00:02:42,908
举个最直观的例子，克隆代码仓库。

36
00:02:42,908 --> 00:02:48,917
令牌在克隆的那一刻被注入到远端地址里，变成一串带令牌的网址。

37
00:02:48,917 --> 00:02:58,761
之后沙箱里的 Agent 可以正常地拉取和推送，但当它想把这个令牌读出来、打印出来、发到别处去的时候，它读不到。

38
00:02:58,761 --> 00:03:04,687
因为这个令牌藏在了配置的深层，不是一个可以随手枚举的环境变量。

39
00:03:04,687 --> 00:03:07,667
这个设计的精髓在于「可用不可见」。

40
00:03:07,667 --> 00:03:12,271
Agent 的能力一点没少，但窃取的路径被物理切断了。

41
00:03:12,271 --> 00:03:14,554
第二种叫 Vault 代理模式。

42
00:03:14,554 --> 00:03:21,381
令牌存在一个安全的保险库服务里，Agent 的每一次接口调用都经过一个代理转发。

43
00:03:21,381 --> 00:03:30,480
代理拿到请求，根据会话编号去保险库里查对应的凭证，把令牌注入请求头，再转发给真正的目标服务。

44
00:03:30,480 --> 00:03:34,290
Agent 全程只知道一件事，调用成功了。

45
00:03:34,290 --> 00:03:37,595
它从来没有见过令牌的任何一个字符。

46
00:03:37,595 --> 00:03:41,814
哪怕它把整个请求日志翻一遍，也翻不出凭证。

47
00:03:41,814 --> 00:03:45,684
这两种模式有个共同点，我建议你记下来。

48
00:03:45,684 --> 00:03:51,033
它们都不是在教 Agent 别偷东西，而是让 Agent 根本没有东西可偷。

49
00:03:51,033 --> 00:03:55,864
这就是结构性安全和提示词安全的根本分野。

50
00:03:56,020 --> 00:04:00,849
光有凭证隔离还不够，你还得限制代码本身能做什么。

51
00:04:00,799 --> 00:04:06,244
素材里给了三层，文件系统、网络、进程，我一层一层说。

52
00:04:06,244 --> 00:04:14,129
文件系统隔离，意思是 Agent 只能访问工作目录下的文件，宿主机上其他路径它一概读不到。

53
00:04:14,129 --> 00:04:18,131
凭证、配置、系统文件，全部不可见。

54
00:04:18,131 --> 00:04:25,174
网络隔离，意思是沙箱限制它访问网络的范围，它没法向任意外部服务发送数据。

55
00:04:25,174 --> 00:04:30,679
这一层是关键补充，因为就算它真的拿到了凭证，也送不出去。

56
00:04:30,679 --> 00:04:34,489
拿得到和外传得了，是两件完全不同的事。

57
00:04:34,489 --> 00:04:41,496
进程隔离，意思是 Agent 的代码跑在独立进程空间里，碰不到宿主机的其他进程。

58
00:04:41,496 --> 00:04:44,874
既读不了别人的内存，也发不了信号。

59
00:04:44,874 --> 00:04:50,415
这三层加上凭证隔离，还有一个维度要补上，就是信任的层级。

60
00:04:50,415 --> 00:04:56,605
素材里把它分成三层，我觉得这个划分很实用，可以直接抄进你的系统设计里。

61
00:04:56,605 --> 00:04:59,573
最细的一层是工具级信任。

62
00:04:59,573 --> 00:05:06,520
某些特定工具每次使用都要人工审批，比如删除文件、写数据库、发邮件。

63
00:05:06,520 --> 00:05:12,061
而安全的只读操作，比如搜代码、读文件，就自动放行。

64
00:05:12,061 --> 00:05:15,883
粒度细到这里，安全性和体验才能兼得。

65
00:05:15,883 --> 00:05:18,432
中间一层是会话级信任。

66
00:05:18,432 --> 00:05:26,701
用户在开一次会话的时候，授予 Agent 某个范围的权限，比如可以读写源码目录，但不能改配置目录。

67
00:05:26,701 --> 00:05:31,797
整个会话期间这个范围不变，会话结束权限自动回收。

68
00:05:31,797 --> 00:05:36,316
最外一层是全局级策略，也就是组织级别的红线。

69
00:05:36,316 --> 00:05:46,569
无论用户在会话里授予了什么权限，Agent 都不能违反全局策略，比如永远不能访问生产数据库，不能向外部域名发请求。

70
00:05:46,569 --> 00:05:49,045
这是兜底的那一道防线。

71
00:05:49,045 --> 00:05:51,785
三层合起来，逻辑是这样的。

72
00:05:51,785 --> 00:05:57,350
工具层管单次操作，会话层管一段时间，全局层管永远。

73
00:05:57,484 --> 00:06:00,799
安全讲完了，我们把视角切到算法。

74
00:06:00,749 --> 00:06:08,465
为什么要在同一集里讲算法，因为后面讲上下文压缩的时候，你会发现它用的就是这副骨架。

75
00:06:08,465 --> 00:06:10,064
先玩一个游戏。

76
00:06:10,064 --> 00:06:15,593
我想了一个一到一百之间的数，你来猜，我只告诉你大了还是小了。

77
00:06:15,593 --> 00:06:17,420
最优策略是什么。

78
00:06:17,420 --> 00:06:19,355
是每次都猜中间。

79
00:06:19,355 --> 00:06:28,045
一百个候选，砍一半剩五十，再砍剩二十五、十二、六、三、一，砍七次必然只剩一个。

80
00:06:28,045 --> 00:06:32,696
这里真正值钱的不是「七次」这个数字，而是它的性质。

81
00:06:32,696 --> 00:06:39,006
乱猜运气差要几十次，运气好也要看命，而二分给的是最坏情况的保证。

82
00:06:39,006 --> 00:06:42,083
工程上最值钱的就是确定性。

83
00:06:42,083 --> 00:06:44,174
数据量再放大看看。

84
00:06:44,174 --> 00:06:47,648
十亿条数据，二分最多也就三十次。

85
00:06:47,648 --> 00:06:52,095
数据量翻一万倍，次数只是从七涨到二十出头。

86
00:06:52,095 --> 00:06:54,234
这就是对数级的威力。

87
00:06:54,234 --> 00:06:57,756
但有个前提，二分的门票是「先排好序」。

88
00:06:57,756 --> 00:07:03,609
猜数字能玩，是因为数字天然有大小顺序，「大了」这个提示才有意义。

89
00:07:03,609 --> 00:07:12,203
换成本页码被打乱的字典，你翻到中间发现是「猫」，你根本不知道「狗」在左边还是右边，二分当场失效。

90
00:07:12,203 --> 00:07:16,879
所以想享受对数级的查找，你得先付出排序的成本。

91
00:07:16,879 --> 00:07:19,006
那排序本身贵不贵。

92
00:07:19,006 --> 00:07:20,821
看你用什么套路。

93
00:07:20,821 --> 00:07:25,472
素材里安排了一场赛跑，冒泡排序对上快速排序。

94
00:07:25,472 --> 00:07:35,244
冒泡永远只跟隔壁比，一格一格往右挪，一轮下来最大的必然冒到最右边，但 n 个数要扫 n 轮，总账就是 n 平方。

95
00:07:35,244 --> 00:07:46,218
快排是随手指定一个基准，比它矮的甩左边，比它高的甩右边，一趟下来基准就位，剩下两堆各自重复这个动作，平均是 n 乘 log n。

96
00:07:46,218 --> 00:07:49,583
十根柱子的时候，两边看起来差不多。

97
00:07:49,583 --> 00:07:54,487
六十根柱子的时候，快排的比较次数只有冒泡的零头。

98
00:07:54,487 --> 00:08:03,501
说句实话，真实工程里没人手写排序，一行内置的 sort 就解决了，而且语言内置的实现比你我手写的都好。

99
00:08:03,501 --> 00:08:05,124
那学这个干嘛。

100
00:08:05,124 --> 00:08:06,771
为了两种手感。

101
00:08:06,771 --> 00:08:14,234
一是看懂为什么有的代码要跑一整晚，多半是有人在百万级数据上用了平方级的套路。

102
00:08:14,234 --> 00:08:18,730
二是亲身体会平方级和 n 乘 log n 到底差多少。

103
00:08:18,730 --> 00:08:22,852
尺子有了，验收 AI 写的代码才有底气。

104
00:08:22,996 --> 00:08:27,501
排序比的是数字大小，但 AI 世界里排的是相关性。

105
00:08:27,451 --> 00:08:32,908
RAG 从知识库捞回来一堆段落，谁排前面谁就能挤进上下文。

106
00:08:32,908 --> 00:08:37,258
排错了，模型就会一本正经地引用错误资料。

107
00:08:37,258 --> 00:08:40,744
这个打分加重排的环节，叫 Rerank。

108
00:08:40,744 --> 00:08:42,583
设想一个场景。

109
00:08:42,583 --> 00:08:47,186
用户在客服机器人里问，你们的退货政策是什么。

110
00:08:47,186 --> 00:08:54,602
粗排从知识库捞回五条候选，每条有三个分数，向量相似度，意思是意思相不相近。

111
00:08:54,602 --> 00:08:58,328
关键词命中，意思是字面匹不匹配。

112
00:08:58,328 --> 00:09:01,405
新鲜度，意思是文档新不新。

113
00:09:01,405 --> 00:09:04,025
总分是三项的加权平均。

114
00:09:04,025 --> 00:09:05,972
有意思的事情来了。

115
00:09:05,972 --> 00:09:11,694
如果初始只重视相似度，排在第一的其实是二零二三年的旧版政策。

116
00:09:11,694 --> 00:09:17,090
你只要把新鲜度的权重加上去，正确的现行条款就会登顶。

117
00:09:17,090 --> 00:09:20,191
这一幕说明了一件很重要的事。

118
00:09:20,191 --> 00:09:22,138
权重就是价值观。

119
00:09:22,138 --> 00:09:26,778
你给哪个维度多少权重，就等于宣告你更在意什么。

120
00:09:26,778 --> 00:09:33,893
只看相似度，系统就会把旧资料捧上来，而旧资料在政策类问题里往往就是错误答案。

121
00:09:33,893 --> 00:09:38,016
再看整个流程的形状，它是一个两阶段漏斗。

122
00:09:38,016 --> 00:09:44,350
知识库全部文档，先过粗排，用向量近邻，快而糙，砍到几十条。

123
00:09:44,350 --> 00:09:51,045
再过精排，也就是 Rerank，逐条细看，贵而准，最后剩几条塞进上下文。

124
00:09:51,045 --> 00:09:53,112
为什么不全用精排。

125
00:09:53,112 --> 00:09:54,350
因为贵。

126
00:09:54,350 --> 00:10:02,186
精排要把问题和段落成对送进模型逐条细读，一百万条都这么读，等一小时，烧一笔钱。

127
00:10:02,186 --> 00:10:06,189
所以先用便宜的粗排把一百万砍到五十。

128
00:10:06,189 --> 00:10:08,244
为什么不全用粗排。

129
00:10:08,244 --> 00:10:09,530
因为不准。

130
00:10:09,530 --> 00:10:16,682
粗排只看向量距离，分不清现行政策和旧版政策，刚才那一幕你已经亲眼见过了。

131
00:10:16,682 --> 00:10:20,804
所以最后五十条要请贵的模型逐条把关。

132
00:10:20,804 --> 00:10:25,768
把这两句并排放在一起，你会发现一条通用的工程原则。

133
00:10:25,768 --> 00:10:29,061
工程就是在漏斗的每一层花对钱。

134
00:10:29,061 --> 00:10:38,004
快而糙的算法负责海选，贵而准的模型负责决赛，两层各干各的，总成本和总质量才能同时达标。

135
00:10:38,004 --> 00:10:44,734
这个分层花钱的思路，你以后在缓存、推荐、审核系统里会反复见到。

136
00:10:44,884 --> 00:10:47,478
现在补上分治的另一半。

137
00:10:47,428 --> 00:10:48,798
先说递归。

138
00:10:48,798 --> 00:10:52,127
老板丢来一句话，给奶茶店做个官网。

139
00:10:52,127 --> 00:10:55,216
这活没法直接动手，但可以拆。

140
00:10:55,216 --> 00:11:02,296
首页、菜单、门店地图，一层一层往下拆，拆到一个能直接动手的小活为止。

141
00:11:02,296 --> 00:11:08,197
你发现没有，做官网和做首页，其实是同一种问题，只是规模更小。

142
00:11:08,197 --> 00:11:14,976
拆「做官网」的那套办法，分几块、每块继续拆，原封不动地用在了「设计页面」上。

143
00:11:14,976 --> 00:11:20,144
用同一套办法处理越拆越小的同一类问题，这就是递归。

144
00:11:20,144 --> 00:11:25,288
递归最神奇的地方在于，规则只有一条，结果可以无限复杂。

145
00:11:25,288 --> 00:11:31,490
画一根枝，在末端分两杈，每一杈是缩小版的自己，然后回到开头。

146
00:11:31,490 --> 00:11:35,529
就这么一句话，八层就是两百五十五根枝。

147
00:11:35,529 --> 00:11:39,579
以后你验收任何一段递归，就用三件套去卡。

148
00:11:39,579 --> 00:11:45,925
第一，同样的事，拆出来的子问题必须是同一类问题，能用同一套拆法。

149
00:11:45,925 --> 00:11:52,139
第二，规模更小，每拆一层问题必须变小一圈，不变小就永远拆不完。

150
00:11:52,139 --> 00:11:56,923
第三，终止条件，拆到能直接动手的叶子就得停。

151
00:11:56,923 --> 00:11:59,904
这三条里最要命的是第三条。

152
00:11:59,904 --> 00:12:05,372
递归每深一层就在栈上摞一层，没有终止条件就是爆栈。

153
00:12:05,372 --> 00:12:09,663
这个姊妹篇讲栈的时候讲过，这里正好接上。

154
00:12:09,663 --> 00:12:12,344
和 AI 的关系再点透一层。

155
00:12:12,344 --> 00:12:24,002
你让 Agent 重构整个项目，它先列出子任务，发现重构登录模块还是太大，就继续往下拆，直到每一项都是改一个文件这种能直接执行的动作。

156
00:12:24,002 --> 00:12:29,303
判断「够小了，能动手了」的那一刻，就是它的终止条件。

157
00:12:29,452 --> 00:12:33,789
上一课是拆，这一课补上另一半，拆完再合。

158
00:12:33,739 --> 00:12:36,791
这就到了本集开头说的第二个问题。

159
00:12:36,791 --> 00:12:42,416
对话太长装不下的时候，AI 会做一次压缩，把旧对话压成摘要。

160
00:12:42,416 --> 00:12:46,972
之前我们把它比作搬家前把杂物打包，现在揭开包装纸。

161
00:12:46,972 --> 00:12:51,046
它是一个有两千年历史的算法套路，叫分治。

162
00:12:51,046 --> 00:12:52,753
亲手跑一遍。

163
00:12:52,753 --> 00:13:01,311
一段你和 AI 讨论装修的完整对话，十二条消息，大约三千六百 token，眼看要把上下文窗口塞爆。

164
00:13:01,311 --> 00:13:02,921
动作分三幕。

165
00:13:02,921 --> 00:13:05,638
第一幕是切，分成三段。

166
00:13:05,638 --> 00:13:09,376
第二幕是治，每段各自收成一条摘要。

167
00:13:09,376 --> 00:13:12,777
第三幕是合，三条摘要并成一条。

168
00:13:12,777 --> 00:13:17,885
素材里有个细节特别值得注意，那就是「保留最近四条不压缩」。

169
00:13:17,885 --> 00:13:20,421
这正是真实 Agent 的做法。

170
00:13:20,421 --> 00:13:28,727
最近这几条对话往往就是你正在办的事，最金贵，一个字都不动，只把久远的部分压成摘要。

171
00:13:28,727 --> 00:13:35,313
你会发现 token 数没有压到两百那么狠，因为保真和省空间，永远在做交换。

172
00:13:35,313 --> 00:13:38,282
这三幕有个正式名字，叫分治。

173
00:13:38,282 --> 00:13:42,008
教科书里它最出名的代言人是归并排序。

174
00:13:42,008 --> 00:13:46,371
把一列数切成小段，每段排好，再两两合并。

175
00:13:46,371 --> 00:13:51,034
归并排序排的是数，压缩压的是话，骨架一模一样。

176
00:13:51,034 --> 00:13:53,991
为什么不一口气让 AI 总结全文。

177
00:13:53,991 --> 00:14:00,397
因为总结一万字本身就会把上下文塞爆，这不就是我们要解决的那个问题吗。

178
00:14:00,397 --> 00:14:08,077
分治聪明的地方在于，把解不动的大问题变成一堆肯定解得动的小问题，再花点力气拼装。

179
00:14:08,077 --> 00:14:15,529
Coding Agent 读大代码库也是这么干的，一个文件一个文件地读，各自记要点，最后汇总。

180
00:14:15,529 --> 00:14:18,835
段还太多怎么办，递归再来一轮。

181
00:14:18,835 --> 00:14:21,070
最后必须说清楚代价。

182
00:14:21,070 --> 00:14:23,895
摘要是有损压缩，细节会丢。

183
00:14:23,895 --> 00:14:29,412
原文说预算八万以内，我特别不喜欢红色，全屋别出现红色。

184
00:14:29,412 --> 00:14:33,294
压缩之后变成，讨论了预算和配色偏好。

185
00:14:33,294 --> 00:14:36,106
具体数字和红色禁令都没了。

186
00:14:36,106 --> 00:14:41,623
压缩完 AI 还知道聊过预算，但你再问它我预算多少，它只能猜。

187
00:14:41,623 --> 00:14:45,313
下次它给你配个红沙发，你都没处说理。

188
00:14:45,460 --> 00:14:48,715
把这一集压缩成五条能带走的东西。

189
00:14:48,665 --> 00:14:52,235
第一条，安全靠结构，不靠自觉。

190
00:14:52,235 --> 00:14:59,242
凭证和执行环境物理隔离，让攻击在结构上不可能发生，而不是寄望于模型不犯错。

191
00:14:59,242 --> 00:15:07,222
能用到今天的一个动作是，去查一遍你的 Agent 运行环境里，密钥到底是不是和代码在同一个容器里。

192
00:15:07,222 --> 00:15:10,408
第二条，凭证设计追求可用不可见。

193
00:15:10,408 --> 00:15:18,268
绑定到资源、代理注入，两种模式都是在保证能力不变的前提下，让窃取路径物理消失。

194
00:15:18,268 --> 00:15:22,571
这条可以直接变成你下一次架构评审的检查项。

195
00:15:22,571 --> 00:15:25,275
第三条，信任要分三层。

196
00:15:25,275 --> 00:15:30,552
工具层管单次操作，会话层管一段时间，全局层管永远。

197
00:15:30,552 --> 00:15:33,857
三层缺一层，就有一层没人兜底。

198
00:15:33,857 --> 00:15:39,867
第四条，算法选型是生死线，而工程是在漏斗的每一层花对钱。

199
00:15:39,867 --> 00:15:45,215
平方级和 n 乘 log n 在百万级数据上的差别，是跑一晚和一秒。

200
00:15:45,215 --> 00:15:49,110
粗排和精排的分工，是成本和质量的双赢。

201
00:15:49,110 --> 00:15:55,600
验收 AI 写的代码时，先问它在这个数据规模上用了什么复杂度的套路。

202
00:15:55,600 --> 00:15:59,891
第五条，压缩是有损的，关键事实必须另存。

203
00:15:59,891 --> 00:16:08,749
预算数字、硬性要求、已经做出的决定，这些要另外记进长期记忆或者文档里，不能指望摘要替你留着。

204
00:16:08,749 --> 00:16:13,028
摘要负责大概聊过什么，档案负责铁板钉钉的事。

205
00:16:13,028 --> 00:16:17,475
把这五条串起来看，你会发现它们其实是同一件事。

206
00:16:17,475 --> 00:16:25,311
无论是隔离凭证、分层信任、两阶段漏斗，还是切分再合并的压缩，它们都在做同一个动作。

207
00:16:25,311 --> 00:16:29,350
承认单点扛不住，然后用结构去分解压力。

208
00:16:29,350 --> 00:16:33,220
这大概就是工程师最核心的一种手艺。

