WEBVTT

1
00:00:00.000 --> 00:00:04.793
规则是：账户处于活跃状态，并且是所有者或员工时，才可以编辑文档，

2
00:00:04.793 --> 00:00:05.991
其余情况都不行。

3
00:00:06.333 --> 00:00:08.785
这是课程编写的一个候选实现。

4
00:00:08.785 --> 00:00:12.463
只用一个活跃的员工账户去试，结果一切正常。

5
00:00:12.800 --> 00:00:14.762
可是，一个不活跃的所有者能编辑吗？

6
00:00:15.800 --> 00:00:17.702
在问模型之前，先写下你自己的评审。

7
00:00:17.702 --> 00:00:21.059
Python 里 and 的优先级高于 or，所以这句读作：

8
00:00:21.059 --> 00:00:23.857
owner，或者 staff 并且 active。

9
00:00:24.200 --> 00:00:26.254
只要是所有者，结果就为真。

10
00:00:26.254 --> 00:00:29.889
于是一个不活跃的所有者也能编辑，而约定说不行。

11
00:00:30.233 --> 00:00:35.505
一条评审要写明：位置、一个具体的输入、预期结果和实际结果，

12
00:00:35.505 --> 00:00:37.141
以及它造成的后果。

13
00:00:38.182 --> 00:00:39.386
接着要复现它。

14
00:00:39.386 --> 00:00:41.449
公开检查一共跑七个用例。

15
00:00:41.449 --> 00:00:45.058
在候选实现上，有两个失败：不活跃的所有者，

16
00:00:45.058 --> 00:00:48.840
以及一个同时是所有者和员工、但不活跃的账户。

17
00:00:49.882 --> 00:00:55.867
只有到这时，才去读模型的评审，比如活动一里课程编写的两条说法，

18
00:00:55.867 --> 00:01:00.308
并把每一条都当作假设，拿约定和一个输入去检验。

19
00:01:00.649 --> 00:01:03.728
它可能找到一个有用的边界，可能漏掉一个缺陷，

20
00:01:03.728 --> 00:01:07.368
也可能凭空加出一条需求，比如接受字符串 false，

21
00:01:07.368 --> 00:01:10.448
而一份只接受布尔值的约定从来没有这样要求过。

22
00:01:10.782 --> 00:01:15.814
人也可能漏掉同一个优先级错误，或者太轻易地跟着模型走。

23
00:01:15.814 --> 00:01:17.678
两者都不能保证正确。

24
00:01:18.715 --> 00:01:21.143
一个修订提议让两条角色路径都检查 active，

25
00:01:21.143 --> 00:01:23.975
写成 active 并且（owner 或 staff）。

26
00:01:24.315 --> 00:01:27.171
不要因为它看起来更清楚就接受它。

27
00:01:27.171 --> 00:01:32.347
重跑同一组检查：七个全部通过，覆盖了三个布尔值的八种组合。

28
00:01:32.347 --> 00:01:37.702
还要检查测试源码本身，因为候选实现可能改动它自己那一份副本。

29
00:01:38.748 --> 00:01:44.178
把一次改动交给智能体，里面其实藏着三份工作：决定做什么、去做，

30
00:01:44.178 --> 00:01:45.930
以及检查做得对不对。

31
00:01:45.930 --> 00:01:50.660
本周的委派脚本让每份工作各自一次运行、各自一个工作区。

32
00:01:50.998 --> 00:01:52.764
规划者写出一份计划。

33
00:01:52.764 --> 00:01:55.414
实现者提出一个文件和一份差异。

34
00:01:55.414 --> 00:02:00.537
评审者只看得到 issue、差异、检查输出和你的评审规则。

35
00:02:00.537 --> 00:02:02.480
它从来看不到那份计划。

36
00:02:02.814 --> 00:02:07.554
在我们的运行里，一个还放着计划的评审工作区被拒绝了，

37
00:02:07.554 --> 00:02:10.288
那时还什么都没读、什么都没发。

38
00:02:10.288 --> 00:02:13.751
读过计划的评审者，会继承计划里的假设。

39
00:02:14.098 --> 00:02:16.589
但新的上下文不等于独立。

40
00:02:16.589 --> 00:02:22.195
同一个模型可能和实现者有同样的盲点；只看差异的评审者，

41
00:02:22.195 --> 00:02:24.894
也发现不了差异里缺了什么。

42
00:02:24.894 --> 00:02:29.046
所以要把它叫作模型评审，绝不叫独立评审，

43
00:02:29.046 --> 00:02:31.745
并把每一条意见都当作声明。

44
00:02:32.780 --> 00:02:35.118
交接也可以无人值守地进行。

45
00:02:35.118 --> 00:02:40.691
一个 issue 文件写明一个目标文件、验收标准、要跑的检查，

46
00:02:40.691 --> 00:02:42.489
以及哪些不在范围内。

47
00:02:42.489 --> 00:02:46.445
然后一次运行先规划、再实现，中间没有人参与。

48
00:02:46.780 --> 00:02:49.684
issue 是数据，来自任何能写它的人。

49
00:02:49.684 --> 00:02:53.168
它的措辞没法扩大这次运行能做的事，这由脚本决定。

50
00:02:53.168 --> 00:02:54.330
在我们的运行里，

51
00:02:54.330 --> 00:02:58.249
它拒绝了一个还写着 BASE_SHA 的 issue，

52
00:02:58.249 --> 00:03:01.443
也拒绝了一个以检查文件为目标的 issue。

53
00:03:01.780 --> 00:03:07.101
它也不能创建分支、推送、访问网络，或者运行验证命令。

54
00:03:07.101 --> 00:03:12.217
分支由你来创建，检查由你来跑，而合并只能由人来做。

55
00:03:13.263 --> 00:03:15.485
课程写了一份这类运行的示例。

56
00:03:15.485 --> 00:03:17.866
它的计划加了一步没人要求的事：

57
00:03:17.866 --> 00:03:21.676
把 true 和 false 当作字符串来接受，

58
00:03:21.676 --> 00:03:23.898
理由是调用方可能会传表单值。

59
00:03:24.246 --> 00:03:26.524
评审者从来看不到这份计划。

60
00:03:26.524 --> 00:03:30.202
它看到的是差异，差异里多了两行字符串解析。

61
00:03:30.546 --> 00:03:33.794
再看证据：我们应用了这个改动，跑了检查。

62
00:03:33.794 --> 00:03:34.931
七个全部通过。

63
00:03:34.931 --> 00:03:38.991
这些检查传入的都是布尔值，所以看不到多出来的范围，

64
00:03:38.991 --> 00:03:43.050
尽管 issue 已经把字符串解析列为不在范围内。

65
00:03:44.096 --> 00:03:45.835
所以你要写评审规则。

66
00:03:45.835 --> 00:03:48.095
每条规则都写明它从哪里来，

67
00:03:48.095 --> 00:03:51.747
以及评审者怎样对照差异或一次运行去检查它。

68
00:03:52.096 --> 00:03:55.461
规则三：输入保持为 Python 布尔值。

69
00:03:55.461 --> 00:04:00.267
它的来源是约定，以及 issue 里写明不在范围内的那一行。

70
00:04:00.267 --> 00:04:04.914
它的检查是：差异里没有加入字符串处理、类型转换或默认参数。

71
00:04:05.263 --> 00:04:09.851
“代码应该整洁”不是规则：没有人能证明某个差异违反了它。

72
00:04:09.851 --> 00:04:13.948
而且规则只告诉评审者该查什么，它本身什么也不强制；

73
00:04:13.948 --> 00:04:16.733
强制的是安全启动器和你的合并规则。

74
00:04:17.779 --> 00:04:22.622
一条评审意见，哪怕引用了某条规则，也仍然只是一个声明。

75
00:04:22.622 --> 00:04:26.209
决定它结论的是复现证据，而不是规则编号。

76
00:04:26.209 --> 00:04:28.182
每条意见只给一个结论。

77
00:04:28.529 --> 00:04:31.326
证实：复现证据显示问题确实存在，

78
00:04:31.326 --> 00:04:36.396
比如一个输入及其实际结果、差异中的一行，或者证据中的一行。

79
00:04:36.396 --> 00:04:41.816
证伪：可运行的证据表明这条意见不对，或者它引用的规则已经满足。

80
00:04:41.816 --> 00:04:46.536
无法判断：你手上的文件没法判定它，所以要写明缺少什么。

81
00:04:46.879 --> 00:04:49.369
来看针对这份示例的两条意见。

82
00:04:49.369 --> 00:04:53.280
第一条引用规则三：第六、七行加了字符串解析。

83
00:04:53.280 --> 00:04:57.015
差异里的这一行就能说明问题，所以它被证实。

84
00:04:57.362 --> 00:05:01.001
第二条引用规则一：没有检查会拒绝不活跃的员工。

85
00:05:01.001 --> 00:05:04.007
用这个输入跑一下：它返回 False，

86
00:05:04.007 --> 00:05:06.538
而且不活跃员工的那项检查通过了。

87
00:05:06.538 --> 00:05:07.962
所以这一条被证伪。

88
00:05:08.996 --> 00:05:13.584
当任务彼此独立、有自己专属的文件，验收标准和检查都已写下，

89
00:05:13.584 --> 00:05:18.173
等待本来会占掉大部分时间，并且有人能抽空评审返回的结果时，

90
00:05:18.173 --> 00:05:20.072
无人值守的运行才有帮助。

91
00:05:20.412 --> 00:05:22.767
当任务含糊不清时，它帮不上忙，

92
00:05:22.767 --> 00:05:27.319
因为无人值守的规划者会拿猜测去填空；当评审本来就是瓶颈时，

93
00:05:27.319 --> 00:05:28.574
它同样没有帮助。

94
00:05:28.912 --> 00:05:33.893
而且每次运行都会用掉模型请求，不论你留不留它的输出；

95
00:05:33.893 --> 00:05:36.767
超出范围的运行还要花评审时间。

96
00:05:36.767 --> 00:05:41.939
一直重跑到检查通过，是在挑选一个输出，而不是在验证它。

97
00:05:42.978 --> 00:05:46.426
后台智能体把 issue 运行又往前推了一步。

98
00:05:46.426 --> 00:05:50.023
一个触发条件，比如给 issue 加上一个标签，

99
00:05:50.023 --> 00:05:52.572
就会让它在自己的工作区里开始工作，

100
00:05:52.572 --> 00:05:57.069
然后交回一个分支、一个附带描述的拉取请求，以及一份运行报告。

101
00:05:57.069 --> 00:05:58.268
中间没有人看着。

102
00:05:58.611 --> 00:06:01.230
课程写了一次这类运行的回放。

103
00:06:01.230 --> 00:06:05.908
issue 42：正在运行的构建会让耗时汇总崩溃。

104
00:06:05.908 --> 00:06:09.837
要把它们排除在中位数之外，加一个回归测试，

105
00:06:09.837 --> 00:06:13.017
并保持每个已有测试及其预期值不变。

106
00:06:13.017 --> 00:06:14.888
显示格式不在范围内。

107
00:06:15.228 --> 00:06:19.415
描述说测试都通过了，报告说检查的退出码是零。

108
00:06:19.415 --> 00:06:22.269
你是维护者，这两样都只是声明。

109
00:06:22.269 --> 00:06:25.885
先预测一下：如果检查一通过它就被合并，

110
00:06:25.885 --> 00:06:30.263
默认分支里会不会只有 issue 要求的改动？

111
00:06:31.311 --> 00:06:34.620
所以要读改动本身，而不是读对它的描述。

112
00:06:34.620 --> 00:06:39.844
第五周的检查器会把报告和交接单对照，也和差异里的文件名对照。

113
00:06:40.194 --> 00:06:42.199
在我们的运行里，它停下了，

114
00:06:42.199 --> 00:06:45.283
但只是因为报告没有列出它哪些东西没检查。

115
00:06:45.283 --> 00:06:49.909
两个改动过的文件都是 issue 允许的，而报告里的退出码，

116
00:06:49.909 --> 00:06:51.142
检查器照单全收。

117
00:06:51.477 --> 00:06:53.765
它读不了差异块，所以要由你来读。

118
00:06:53.765 --> 00:06:57.339
有一个差异块改了 format_duration，

119
00:06:57.339 --> 00:06:59.912
而 issue 把它列为不在范围内。

120
00:06:59.912 --> 00:07:02.771
另一个差异块改写了一个已有测试的预期值。

121
00:07:02.771 --> 00:07:05.630
文件列表通过了，不等于差异没有超出范围。

122
00:07:06.677 --> 00:07:09.212
现在亲自重跑它声称跑过的测试。

123
00:07:09.212 --> 00:07:14.281
在智能体的分支上，用它自己的测试文件跑了六个测试，全部通过。

124
00:07:14.627 --> 00:07:18.590
但是拿基础提交里的测试文件去跑智能体的代码，

125
00:07:18.590 --> 00:07:20.751
已有的格式测试就失败了。

126
00:07:20.751 --> 00:07:23.813
显示方式变了，而分支之所以能通过，

127
00:07:23.813 --> 00:07:26.875
只是因为同一个改动改写了那个测试。

128
00:07:27.210 --> 00:07:30.757
接着在智能体的代码上跑 issue 自带的复现命令。

129
00:07:30.757 --> 00:07:34.304
它输出 60，而在基础代码上，这一行会抛出类型错误。

130
00:07:34.643 --> 00:07:36.690
现在逐条标记描述里的说法。

131
00:07:36.690 --> 00:07:39.525
“耗时的显示方式没有变化”：被证伪，

132
00:07:39.525 --> 00:07:42.201
依据是那个差异块和那个失败的测试。

133
00:07:42.201 --> 00:07:46.452
“七个测试全部通过”：被证伪，因为你的运行只跑了六个，

134
00:07:46.452 --> 00:07:48.657
而且用的是智能体自己的文件。

135
00:07:48.993 --> 00:07:51.830
“面板现在能显示中位数”：无法判断，

136
00:07:51.830 --> 00:07:54.825
因为你做过的运行里没有一次渲染了面板。

137
00:07:54.825 --> 00:07:56.716
要写明哪次运行能判定它。

138
00:07:56.716 --> 00:08:00.499
另外两条说法和描述漏掉的那项要求都留给你来判断。

139
00:08:01.543 --> 00:08:02.819
再看流水线本身。

140
00:08:02.819 --> 00:08:07.926
智能体的令牌可以推送 agent 开头的分支，可以打开拉取请求，

141
00:08:07.926 --> 00:08:10.479
可以修改包括测试在内的任何文件，

142
00:08:10.479 --> 00:08:13.830
还能在必需检查通过时合并它自己的拉取请求。

143
00:08:13.830 --> 00:08:17.181
而这个检查跑的，是智能体分支上的测试文件。

144
00:08:17.526 --> 00:08:20.031
顺着最后两项授权走一遍这次运行。

145
00:08:20.031 --> 00:08:25.042
智能体改写了一个测试，而必需检查跑的正是这个测试，结果顺利通过。

146
00:08:25.042 --> 00:08:29.739
这样，智能体就会把自己的改动连同显示方式的变化一起合并进去，

147
00:08:29.739 --> 00:08:30.992
没有任何人看过。

148
00:08:30.992 --> 00:08:35.219
那个绿色的检查只说明，它的改动和它自己的测试是一致的。

149
00:08:35.559 --> 00:08:37.517
笔记给出的做法有三点。

150
00:08:37.517 --> 00:08:40.364
检查用的测试文件要取自基础提交。

151
00:08:40.364 --> 00:08:44.279
智能体的授权只保留 issue 需要的部分。

152
00:08:44.279 --> 00:08:45.880
合并则留给人来做。

153
00:08:46.226 --> 00:08:48.775
即便这样，为什么还要停在人这里？

154
00:08:48.775 --> 00:08:51.962
描述和报告都是声明；而检查可能全部通过，

155
00:08:51.962 --> 00:08:55.307
差异却加进了 issue 明确排除的东西，

156
00:08:55.307 --> 00:08:57.857
前面示例运行的七个通过就是这样。

157
00:08:57.857 --> 00:09:00.565
所以要亲自跑检查、读每一个差异块，

158
00:09:00.565 --> 00:09:04.070
再决定是合并、要求修改还是关闭这个拉取请求。

159
00:09:05.108 --> 00:09:08.115
所以，先自己评审，并复现你的发现。

160
00:09:08.115 --> 00:09:13.599
把每一条模型评审意见、每一份交回的结果都当作要逐条标记的声明，

161
00:09:13.599 --> 00:09:16.253
并测试你真正要合并的那个版本。

162
00:09:16.591 --> 00:09:17.989
来试一个问题吧。

163
00:09:17.989 --> 00:09:22.707
一个仪表盘 issue 要求编辑守卫拒绝已归档的构建，

164
00:09:22.707 --> 00:09:24.979
并记录每一次被拒绝的编辑。

165
00:09:24.979 --> 00:09:28.299
模型评审者只看到了差异，报告没有发现。

166
00:09:28.299 --> 00:09:31.969
这个差异修好了守卫，却没有加任何记录调用。

167
00:09:31.969 --> 00:09:34.416
这个“没有发现”能说明什么？

168
00:09:34.416 --> 00:09:36.688
哪一项检查能决定是否接受？
