结论是:例外情况应当写成“可判定的条件 + 触发后的动作 + 无法判定时的去向”,而不是写成“如有特殊情况请人工处理”。前者脚本能稳定执行,后者会把判断重新推回给人。适用条件是这条经验有明确输入、明确输出,并且例外集合可以被穷举或至少能被识别为“未知”。如果经验本身依赖只有人才看得懂的语境,把它脚本化反而会增加误判。
可判定的例外,指脚本能在不询问任何人的前提下判断“这条数据属于例外”。例如一条经验是“标题长度超过某个范围就改写”,例外可能是标题里包含品牌名、包含年份、或包含一个无法拆分的专有名词。这些都可以用字符串匹配或字段存在性来判断。
不可判定的例外,指判断需要理解语义、意图或业务上下文。例如“这个标题虽然偏长,但它是活动主标题,不能动”。脚本读到这句话时,无法知道哪些标题属于活动主标题,除非这个信息已经存在于某个字段里。
取舍的代价很清楚:把不可判定的例外硬塞进脚本,会得到大量假阳性,改错不该改的页面;把可判定的例外全部交给人,脚本只能处理最干净的那部分数据,覆盖率低,人工量并没有下降多少。选择条件是:如果例外所需的判断依据已经存在于输入数据中,就写成脚本条件;如果依据不存在,就先补字段,而不是先写规则。
第一样是判定条件。不要写“标题异常”,要写清楚拿哪个字段、和什么比较、边界是否包含。例如“标题字符数大于某值且标题中不包含品牌字段”。这里的比较值应写成一个可调整的参数,而不是散落在多处。
第二样是触发后的动作。例外被识别出来后,是跳过、标记、还是走另一条处理分支?如果只写“识别出来”,脚本执行完仍然不知道下一步做什么,等于没写。
第三样是无法判定时的去向。这是最常被漏掉的一环。当输入缺失、字段为空、或匹配到多个条件时,脚本应当把这条记录放进一个待人工确认的集合,而不是默认按主流程处理。默认按主流程处理,会把“不知道”悄悄变成“当作正常”,错误会静默积累。
假设有一条人工经验:内链补充时,正文里已经出现过的目标页面不要再重复加链接。把它写成脚本需求,主流程是“扫描正文,匹配关键词,插入链接”。例外可以这样描述:
这个例子里,判定依据都来自页面本身,所以可以脚本化。反过来,如果经验是“这个链接加了会显得刻意”,那就没有可判定的依据,只能留在人工环节。
如果例外条件本身处在频繁变动中,把它写死进脚本会持续产生错误。例如某条经验依赖“当前主推内容”的范围,而这个范围每周调整,且调整结果没有落到任何结构化字段里。此时脚本里的例外描述会在调整后立刻过期,识别出的例外和实际业务意图不一致。
这种情况下,正确动作不是继续细化正则,而是先把“主推范围”变成一个脚本可读取的字段,再写例外。判断依据是:例外的判定依据是否有一个稳定的来源。没有稳定来源时,脚本化的收益为负。
另一个容易误判的现象是:改动后某类页面数量明显下降,不能单独证明例外规则写对了。搜索需求本身在变化、采集时间点不同、页面集合的定义调整,都会造成同样的数量变化。把改动前后的数据直接对比并归因于规则,是不成立的。要比较,至少要让页面集合、采集口径和时间窗口保持一致,并接受季节与需求波动带来的干扰。
具体动作是:在写脚本之前,先拿一批真实输入,人工把每一条记录标成“主流程可处理”“命中例外”“无法判定”三类,统计三类各占多少。这个动作的结果会直接决定下一步——如果“无法判定”占比高,说明缺字段,应先补数据来源;如果“命中例外”高度集中在一两个模式上,说明例外可以合并成一条条件,脚本会简单很多;如果三类分布均匀且没有规律,说明这条经验还不适合脚本化,继续保留人工更划算。
把例外写清楚,本质上是在回答“脚本不知道的时候该怎么办”。能回答这个问题,脚本才值得写;回答不了,就先别写。