TT Lab
开始
学习 学习路径 课程

调试实战

能用的客户与不能用的客户 — 用差异表排除候选原因

在 TT Lab 中继续学习

目标

把成功样本和失败样本按属性划分,制作差异表,只把存在于所有失败中、且不存在于任何成功中的值留作候选。用新样本和干预实验分别排除完全相关的假候选,并找出无法用单个属性区分的交互作用。

为什么重要

“有的客户可以,有的客户不行”听起来像是坏消息,其实是好消息。因为有一部分是正常的,就意味着有对照组。只钻研失败日志,每一行都显得可疑,但如果成功日志里也有同样的行,它就不是原因,而是背景。 本实验中难的不是制作表格的代码,而是候选剩下两个时该怎么办。如果同一天部署的两件事总是同时出现,仅凭样本无法区分。这时挑看起来更像的那个去报告,有一半的概率会白白浪费好几天。 分开的路只有两条:获取能把二者分开的更多样本,或者只改变一个属性、固定其余属性,看结果是否翻转。前者是观察,后者是实验,能谈因果的只有实验。 评分器不会相信你的结论。评分器会生成每次都把原因和假候选埋在不同位置的样本,实际运行你的工具,并把挑出的候选集合与预先埋好的答案进行核对。

步骤

  1. 创建并运行 /root/diff/gen_cases.py,生成 /root/diff/cases.json(240 条)、cases2.json(120 条)、cases3.json(180 条)和 repro.py。
  2. 在 /root/diff/table.json 中制作差异表,写下每个属性的每个值对应的失败数和成功数。
  3. 创建 /root/diff/differential.py,让它把存在于所有失败中、且不存在于任何成功中的值挑出来作为候选。
  4. 在 /root/diff/candidates.json 中,把候选列表和被排除的候选连同依据一并写下。
  5. 合并新样本重新运行,并在 /root/diff/verdict.json 中写下幸存的一个原因、被排除的候选,以及作为依据的样本编号。
  6. 给 differential.py 加上配对分析,并在 /root/diff/pairs.json 中写下 cases3.json 的配对候选。
  7. 用 repro.py 只改变一个属性,确认结果是否翻转,并写入 /root/diff/confirm.json。
  8. 用 /root/diff/summary.json 和 /root/diff/diff_report.md 分四节进行报告。

参考

拿到样本和复现器

创建并运行 /root/diff/gen_cases.py,生成 /root/diff/cases.json(240 条)、cases2.json(120 条)、cases3.json(180 条)和 repro.py。

在现场拿到手的不是结论,而是样本。把这个脚本原样保存并运行即可。暂时打开生成的 cases.json,看看成功和失败各是以什么形式记录的。

制作按属性划分的差异表

在 /root/diff/table.json 中写入 failures、successes 和 attributes。attributes 按属性名称,为每个值存放 {"fail": 건수, "ok": 건수}(占位符为数量)。case_id 和 outcome 不是属性。

先有表格。不用表格、只凭眼睛扫一遍,就分不清“在失败中出现较多的值”和“只存在于失败中的值”。属性名称要直接从样本中读取——手写的话会漏掉一个,而漏掉的属性永远不会成为候选。

做出挑选候选的工具

创建 /root/diff/differential.py,让它把存在于所有失败中、且不存在于任何成功中的 속성=값(占位符为属性名和值)输出为 candidates,把存在于所有失败中、但在成功中也存在的值输出为 eliminated。--cases 必须可以多次给出。

两次集合运算就够了。求失败样本的交集,再减去成功样本的并集。在减法中消失的值就是 eliminated。之所以用这两行,而不是让人用眼睛扫表,是因为即使属性有二十个,流程也是一样的。

连同依据写下候选和被排除的候选

在 /root/diff/candidates.json 中写下 candidates(候选列表)和 eliminated(被排除的候选)。eliminated 的每一项都是 {"attribute": "속성=값", "reason": "…"}(其中占位符为属性名和值),reason 中必须以数字写明该值出现过的成功样本的条数。

把被排除的东西记下来,是为了让下一个人不再走同样的路。“所有失败中都有 token 认证”这个事实,只有与“有几条成功中也有 token”这个事实放在一起,才有意义。把这个数字从表里取出来。

用新样本排除假候选

把 cases.json 和 cases2.json 一起放入重新运行,并在 /root/diff/verdict.json 中写入 cause(幸存的一个原因)、eliminated_by_new_batch(被排除的候选)、evidence_case_ids(作为依据的 cases2.json 中的条目编号)。

仅凭样本无法区分二者,是因为数据中没有能把二者分开的组合。新样本中就有这样的组合。作为依据的条目有两种——具有将被排除的属性却成功的条目,或者没有该属性却失败的条目。

无法用单个属性区分的情形

给 differential.py 加上 --pairs,让它寻找配对候选,在 cases3.json 上运行,并在 /root/diff/pairs.json 中写入 single_candidates 和 pair_candidates。配对写成包含两个值的列表。

有一批样本连一个单独的候选都没有。因为只有两个值同时出现时才会失败。在所有失败中都有的值里两两配对,找出没有任何一个成功同时具有这两个值的配对。如果配对中含有单独就已经是候选的值,它不带来新信息。

只改变一个属性,看是否翻转

在 /root/diff/confirm.json 中写入 claim(所主张的原因)、base(基准属性组合,共七项)、flipped_attribute、base_outcome、flipped_outcome。base 与翻转后的组合必须恰好只有一个属性不同,两个结果必须互不相同。

样本是观察,复现器是实验。固定其余所有属性,只改变所主张的那一个属性,如果结果翻转,这个属性就不是相关,而是原因。如果同时改变两件事,就无法知道是哪一个造成了翻转。

把被排除的内容也写进一页纸的报告

在 /root/diff/summary.json 中写入 week1_cases、week1_candidates、cause、eliminated_by_new_batch、pair_only_tenant、intervention,并在 /root/diff/diff_report.md 中分四节进行报告:## 무엇이 갈렸나、## 무엇을 지웠나、## 상관인가 원인인가、## 남은 위험(韩文,依次意为“什么产生了分歧”“排除了什么”“是相关还是原因”“剩余风险”)。

如果报告中只写剩下的候选,下一个人就会再走同样的路。请把被排除的候选和排除的依据一并写下。干预实验的两个结果不是数字而是词,但它们同样是证据。