收到的答案与实际不符 - 逐项核对
目标
把进入现场之前收到的 9 项 intake 答复,与该客户公司真实系统中测得的值进行核对。把判定规则固化成文件,编写把结果判定为完全一致、范围内、不一致、没有答复、无法确认五种的核对程序,并让机器为每个对不上的项生成需要重新询问的问题。
为什么重要
intake 答复是客户的记忆,而系统才是事实。填写答复的人,通常是三年前安装该系统的人;之后保留期限缩短了,又多接了一个对接,备份周期也变了。把答复直接当作前提的计划,一开工就会崩塌。 所以核对不靠眼睛,而靠规则。先确定每一项怎么比较并写进文件,两周后再运行同一条命令,就能看到这期间变了什么。靠眼睛做的核对无法再次运行。 最重要的是把不知道的项写成不知道。把没能确认的项记为一致,就没有人会再看它;记为不一致,则会和客户白白争执。没有答复的项和无法确认的项要分别统计,并留成清单。 评分器不会相信你写下的文字。它在临时目录里摆好评分器生成的答复和事实,每次以不同的值真实运行你的核对程序,并把判定和汇总与它自己计算的值进行核对。
步骤
- 创建并运行 /root/intake/gen_site.py,生成 /root/intake/answers.json(9 项)和 /root/intake/site/(版本文件、配置、14 天的日志、生产数据库)。
- 亲自测量系统,在 /root/intake/facts.json 中写入 app_version、timezone、retention_days、log_days、daily_orders_max、integrations、backup_interval_hours 七项。
- 为每一项确定比较方法,以 fields 列表的形式写入 /root/intake/rules.json。以范围作答的项为 range,以列表作答的项为 set,以近似值作答的项为 tolerance(tolerance_pct 为 20),其余为 exact。没有可测量位置的项,把 source 设为 null。
- 创建 /root/intake/reconcile.py,让它用 exact 和 set 两种规则判定 match_exact 和 mismatch,并连同汇总一起以 JSON 输出。
- 增加 range 和 tolerance,把落在范围内的项判定为 match_in_range。边界值算范围内。
- 答复为空的项判定为 unanswered,有答复但没有测得值的项判定为 unverifiable。两者都符合的项为 unanswered。
- 增加
--questions <경로>(占位符为路径),让它写出一个 JSON 数组,其中为每个不一致的项放入一句需要重新询问的问题。 - 用真实的答复和真实的事实生成 /root/intake/report.json 和 /root/intake/questions.json,并在 /root/intake/intake_report.md 中分四节报告。
参考
- 运行契约:
python3 /root/intake/reconcile.py --answers <A> --facts <F> --rules <R> --out <보고 JSON> [--questions <질문 JSON>](占位符依次为答复文件、事实文件、规则文件、报告 JSON、问题 JSON)把一行汇总 JSON 输出到标准输出,并以退出码 0 结束。无法读取输入时为 3。 - 报告 JSON:
{"summary": {"fields": 정수, "match_exact": 정수, "match_in_range": 정수, "mismatch": 정수, "unanswered": 정수, "unverifiable": 정수}, "findings": [...]}(占位符为整数)。findings 按 field 名称升序排列,每个条目有 field、rule、answer、fact、verdict 五个键。 - 判定名称恰好是 match_exact、match_in_range、mismatch、unanswered、unverifiable 这五个。
- 规则文件:
{"fields": [{"name": …, "kind": "exact|range|tolerance|set", "source": 문자열 또는 null, "tolerance_pct": 정수}]}(占位符依次为字符串或 null、整数)。不是 tolerance 的项,可以不设置 tolerance_pct。 - range 是答复为
{"min": …, "max": …}的项,包含边界值。tolerance 在잰 값과 답변의 차이 <= 답변 × tolerance_pct / 100(韩文,意为“测得值与答复的差值 <= 答复 × tolerance_pct / 100”)时算范围内。如果测得值与答复完全相同,即使是 tolerance 项也为 match_exact。 - 事实文件中的值要亲自测量。版本读取
site/app/VERSION,配置读取site/app/app.ini(configparser),保留期限和时区读取site/data/app.db的 settings 表,日志保留天数取自site/logs中的文件名,每天最大订单数取自 orders 表,备份周期取自 backup_log 表中各时间之间的间隔。 - 常见错误:比较列表时连顺序也比较;把容许误差藏在代码里;把没能确认的项记为一致;给出指责的句子而不是提问的句子。
- tolerance_pct 为 20,以及“没有答复的项优先于无法确认”这一优先级,是本实验的假设。它们不是标准规定的值,而是团队达成一致并写进规则文件的值。
- 参考文档:RFC 2119 和 RFC 8174 讲的是用词语来明确规则语句强度的方法,RFC 3339 讲的是日期和时间的书写格式,Python json 文档和 SQLite 日期函数说明了本实验使用的工具。
掌握答复和系统
创建并运行 /root/intake/gen_site.py,生成 /root/intake/answers.json(9 项)和 /root/intake/site/。site 中包含版本文件、app.ini、14 天的日志,以及生产数据库(settings、orders、backup_log)。
在现场能拿到手的,只有一份答复文件和一台系统。这里由我们来创建这两样东西。先创建 /root/intake,然后在其中用 python3 生成文件和 sqlite 数据库。答复是客户凭记忆填写的值,所以有多项与系统不同。
直接从系统中测量
在 /root/intake/facts.json 中写入 app_version、timezone、retention_days、log_days、daily_orders_max、integrations、backup_interval_hours 七项。这些值必须全部是在 site/ 中亲自测得的,并且除这七项之外不要放入其他项。
版本取自 site/app/VERSION 这一行,时区和保留期限取自 site/data/app.db 的 settings 表,对接列表取自 site/app/app.ini 的 integrations 节。log_days 是 site/logs 中不同日期的数量,daily_orders_max 是对 orders 表按日期统计后的最大值,backup_interval_hours 是 backup_log 中相邻时间之间的间隔。
把判定规则固化成文件
在 /root/intake/rules.json 中,以 fields 列表的形式写入全部 9 项答复。以范围作答的项 kind 为 range,以列表作答的项为 set,以近似值作答的项(daily_orders_max)为 tolerance 且 tolerance_pct 为 20,其余为 exact。没有测得值的项,把 source 写为 null;有测得值的项,用字符串写明是从哪里测得的。
规则必须在核对之前确定。边核对边定规则,得到的会是自己想看到的结果。哪一项没有可测量的位置,取决于 facts.json 里有没有它的名称。source 里要写路径或表名这类之后能给客户看的依据。
区分完全一致和不一致
创建 /root/intake/reconcile.py,让它用 exact 和 set 两种规则进行判定。值相同为 match_exact,不同为 mismatch。set 不看顺序。报告 JSON 中包含 summary 和 findings。
findings 按 field 名称升序排序,每个条目放入 field、rule、answer、fact、verdict 五个键。summary 分别统计五个判定名称的数量,并在 fields 中写入全部项数。列表比较先排序再做,就不会受顺序影响。
单独统计范围内的项
增加 range 和 tolerance,把落在范围内的项判定为 match_in_range。range 是答复为 min、max 的项,包含边界值。tolerance 是差值在答复的 tolerance_pct 百分比以内的情况;如果测得值与答复完全相同,则为 match_exact。
把近似值的项全部记为不一致,真正的问题就会被同一种颜色淹没。是否包含边界值由规则决定,本实验包含。容许误差不要藏在代码里,而要读取规则文件中的 tolerance_pct 来使用。
没有答复的项和无法确认
答复为空或根本没有的项判定为 unanswered,有答复但没有测得值的项判定为 unverifiable。两者都符合的项为 unanswered。
把没能确认的项记为一致,就没有人会再看它;记为不一致,则会和客户白白争执。答复中完全没有这个键的情况,和值为 null 的情况,要同样对待。把优先级放在代码最前面,其余规则就碰不到这两种情况。
生成提问而不是指责
增加 --questions <경로>(占位符为路径),让它为每个不一致的项写出一个条目,组成 JSON 数组。条目中除了 field、verdict、answer、fact,还要有一句 ask。ask 中要包含项名称,并以问号结尾。
拿着“十二项错了”的表格过去,客户首先会防御。把同样的内容换成提问,就成了对话。每种判定要有各自的句式:不一致问哪一个才是对的,无法确认问该去哪里查看,没有答复的项问这个值由谁决定。
用真实答复运行并报告
用真实的答复和真实的事实生成 /root/intake/report.json 和 /root/intake/questions.json,并在 /root/intake/intake_report.md 中分 ## 무엇을 대조했나 ## 어긋난 칸 ## 답이 없는 칸 ## 다시 물어볼 것 四节书写(韩文标题,依次意为“核对了什么”“对不上的项”“没有答复的项”“需要重新询问的内容”)。对不上的项和没有答复的项的名称都必须出现在报告里。
报告不要手写,而是从 report.json 和 questions.json 生成。这样两周后再次运行同一条命令时,报告也会一起更新。数字直接取自汇总,项名称取自 findings。