归因需要逐层下钻时,怎么用状态图决定往下查、往上查还是结束?
简化版
逐层下钻的特点是下一步查什么取决于上一步的检验结果,写不进一份事先列好的计划。状态图正好表达这种编排:节点做具体的事(提假设、取数检验、判定、成文、校验),条件边根据运行时的结果决定去向,环让流程可以回头再验下一条假设或回炉重写。比如判定出「只有这个板块在跌」就往下查供给和区域事件,「全区都在跌」就往上查全市,「全市都在跌」就横向比其他城市。两个关键护栏:每个环都有轮数上限,并且轮数从数据库里数而不是放在内存变量里;超上限时明确收口——没验到的假设标为未验证,结论一条都过不了就标失败,不输出空洞的报告。
详细版
一个典型的归因状态图:
假设生成 ──► 取数与检验 ──► 路由判定 ──┬──► 取数与检验(外环:验下一条假设 / 下钻 / 放宽粒度重查)
└──► 归因成文 ──► 证据校验 ──┬──► 归因报告
├──► 归因成文(内环:带改进指令回炉)
└──► 归因失败
| 组成 | 作用 |
|---|---|
| 节点 | 每个节点做一件事,具体动作由业务层注入 |
| 条件边一(路由判定之后) | 还有假设没验且外环有轮数 → 回取数检验;否则 → 去成文 |
| 条件边二(证据校验之后) | 有结论通过 → 出报告;全部驳回且内环有轮数 → 回炉;轮数用尽 → 失败 |
| 外环 | 围绕「验证假设」的循环,处理下钻、换假设、数据不足 |
| 内环 | 围绕「写对结论」的循环,处理证据校验不通过 |
下钻方向的决策示例:
本板块跌、同区其他板块不跌 → 问题在本板块 → 往下查:供给、区域事件、小区口碑
同区其他板块也在跌 → 不是本板块的问题 → 往上查:全市大盘
全市都在跌 → 横向比:其他城市同期走势
完整版教学
一、为什么固定计划不够用
计划—执行(Plan-and-Execute)适合维度可以事先列出的分析:先列好五个步骤,逐个执行。但归因往往不是这样:
第 1 步发现:A 板块近一年跌了 12%,同区平均跌 4%
→ 下一步应该查 A 板块自身:供给是否激增、有没有区域事件
第 1 步如果发现:同区平均也跌了 11%
→ 下一步应该往上查:全市是不是都在跌
同一个起点,下一步完全不同,取决于检验结果。硬写成固定计划,要么把所有分支都列进去(浪费大量取数),要么漏掉关键方向。这种「运行时分岔」正是状态图的用武之地。
二、状态图的三个原生能力
用一串 if-else 或 while 循环也能写出来,但状态图把三件事变成了显式结构:
条件边。 一个节点之后有多个去向,走哪条由运行时状态决定。判定节点可能有「成立 / 证伪 / 数据不足」三个出口,但去向只有「接着验」和「去成文」两种——出口名和去向不是一回事,出口名要单独记录下来,页面上才能反查每一步做了什么决定。
环。 验完一条假设回去验下一条,结论不合格回去重写,这些都是环。环必须有退出条件,否则就是死循环。
可中断。 一次归因要跑多轮模型调用和检验,可以按入口和停点分段推进,每次只推进一轮,页面上边跑边看。
三件事手写也能实现,但写完是一张没人看得懂的调用关系;状态图本身就是一张可读的流程图,和代码一一对应。
记忆钩子:节点回答「做什么」,条件边回答「接下来去哪」,环回答「什么时候回头」,轮数上限回答「什么时候必须停」。
三、图只管编排,不碰数据库和模型
一个好的分层是:图只负责「什么时候调谁、能不能停」,每个节点具体做什么由业务层注入。
图(编排层):节点、条件边、环、轮数上限
钩子(业务层):提假设怎么调模型、取数用哪个工具、判定用什么规则、结论怎么落库
好处是图可以脱离数据库和模型单独测试(注入假的钩子,验证各种分支走向是否正确),业务改动也不会碰到编排逻辑。
四、两个环各管什么
| 外环 | 内环 | |
|---|---|---|
| 目的 | 验证假设 | 写出能通过证据校验的结论 |
| 起点 | 路由判定之后 | 证据校验之后 |
| 回到 | 取数与检验 | 归因成文 |
| 触发 | 还有假设没验,或需要下钻、放宽粒度 | 结论全部被驳回 |
| 用尽时 | 剩余假设标为未验证,按已有结果成文 | 收口标失败,保留每轮驳回原因 |
外环:验证假设。 每一轮取一条待验证的假设,取数、检验、判定。判定结果决定下一步:成立或证伪都记下来,继续验下一条;数据不足时可以放宽一层粒度重查(小区 → 板块);还可以根据结果提出新的假设(比如往上查全市)。外环轮数用尽时,剩下的假设留在「待验证」,报告里明确写「没验到」,不假装它们成立。
内环:写对结论。 归因成文后做证据校验,全部驳回时带着改进指令回炉重写。回炉不是简单地「再来一次」:
点名式改进指令:第几条结论、正文是什么、被什么原因驳回、应该怎么改
末尾补一句:没有被点名的结论保持不变
因为成文每一轮是整份重写,不点名的话,上一轮已经过关的结论可能被顺手改坏,序号一变,报告里引用的「第几条」「第几步」就全对不上了。内环轮数用尽仍没有结论通过,就收口标失败,每一轮被驳回的原因留在各自的步骤记录里。
五、轮数为什么要从数据库里数
一次归因要跨多次请求推进,进程也可能中途重启。如果轮数存在内存变量里:
进程重启 → 计数归零 → 模型又拿到一整轮预算 → 轮数上限形同虚设
做法是:每次进入某个节点前,数一下这个任务在这个节点上已经落过几条步骤记录,加一就是本次的轮次号。步骤记录本来就要写,轮数顺带就数出来了,不需要额外的计数表,也天然支持断点续跑。
六、分段推进和失败恢复
分段推进。 同一张图可以按不同的入口和停点编译:首轮从「假设生成」进、在「取数与检验」后停;之后每次从「取数与检验」进、在「路由判定」后停,一次恰好推进一轮。续跑时直接从取数检验进入,不会让模型把同一批假设重提一遍。停点要用框架确实支持的机制实现,最好通过测试确认「真的停住了」,否则图会一路跑到底。
失败恢复。 失败后能不能重跑,要看断在哪一步:断在模型调用的环节(成文、报告),通常是调用偶发失败,可以允许重跑;断在取数、校验这些确定性环节,多半是数据、工具或代码的问题,同样的路再走一遍还是同样的结果,应该先修复原因。
七、状态里放什么
状态图的状态(State)要精简,只放决定流程走向和节点之间传递所需的信息:
任务 ID、分析对象、问题类型
当前假设、待验证假设列表
最近一步的判定结果和出口名
当前结论与校验结果
大块的取数结果、完整的步骤历史应该落库,状态里只放引用(ID)。状态太大,每次续跑重建状态的成本就高,也容易出现状态和数据库不一致。
八、常见误区与追问
- 误区:逐层下钻也可以事先列成固定计划。 下一步查什么取决于检验结果,固定计划要么浪费取数,要么漏掉方向。
- 误区:状态图就是把 if-else 换个写法。 它把条件边、环和可中断变成显式结构,流程图和代码一一对应,可读可测。
- 误区:环的计数用内存变量就行。 进程重启后计数归零,上限失效,应该从数据库的步骤记录里数。
- 误区:结论不合格就让模型整份重写。 要点名到条并声明未点名的保持不变,否则已过关的结论会被改坏。
- 误区:轮数用尽了就用现有内容凑一份报告。 没验到的假设要明确标为未验证,一条结论都没过关就收口标失败。
- 追问:什么时候用状态图,什么时候用计划—执行? 维度能事先列出用计划—执行;下一步取决于中间结果时用状态图。
- 追问:失败后都能重跑吗? 断在模型调用环节可以重跑;断在取数、校验这类确定性环节,要先修复原因。
九、加强记忆
归因下钻的下一步取决于检验结果,写不进固定计划,所以用状态图:节点做事,条件边决定去向,环用来回头,轮数上限决定何时必须停。一种可行的结构是两条条件边加两个环:外环验证假设,按结果往下查、往上查或横向比,轮数用尽剩下的标未验证;内环写对结论,全部驳回带点名指令回炉,并声明未点名的保持不变,用尽标失败。图只管编排,业务动作由钩子注入,可单测。轮数从数据库数,支持重启续跑;按入口和停点分段推进;断在模型环节可重跑,断在确定性环节先修因。