这一篇的灵魂不是技术,是决定。一条生产流水线建到一半,数据不够时我停了手——不是做不下去,是「算不了」是一个合法且专业的输出。宁可报「数据不足,算不了」,也不给一个看起来精确的错误价格。
项目:德莉玛淋浴房订购单生产流水线(订单 → BOM 拆单 → 玻璃图纸 → 报价)。
全量历史数据:33,176 单 / 387 个经销商 / 2,799 个型号。抽样体检 400 单,其中 97% 的 spec 为 null 或「否」(等于没有尺寸),只有 3 单有真实尺寸——而且这 3 单属于 K / Y 系列,不是 H / S / M 主流系列。
| 校验结果(实测) | 值 | 说明 |
|---|---|---|
| 型号匹配 | 96.6% | 引擎能认出型号 |
| 厚度匹配 | 95.2% | 物理参数基本齐 |
| 尺寸覆盖 | 29% | 🔴 真正卡点 |
| 客户 / 经销商匹配 | 0% | 🔴 不知道给谁报 |
相关基础篇:模块 13 · 报价引擎、模块 11 · 数据迁移实战(这批历史数据怎么落地成真库)、模块 05 · 需求诊断(这条结论的最初来源)。架构部分见 复盘 24 · 德莉玛 monorepo。
spec = 尺寸」,这是错的——把全库 spec 取值分布打出来看,装的是规格与颜色(否 / 无 / 黑色 / 黑金沙 / 预埋),没有一条是尺寸。真正装尺寸的是 room 列,但它是混字段(形状 + 尺寸 + 单值 + 163 条「以图片为准」),必须用正则认:含「数字×数字」的 8,956 单 = 27.0%。详见 28 篇第 09 节的口径更正。先把流水线画成四段。复盘一条流水线,第一件事是写清每一段的输入、输出、以及「这一段能不能单独跑」。
| 段 | 输入 | 输出 | 缺数据时能不能兜底 | 缺什么就废 |
|---|---|---|---|---|
| 1 解析 | 订单原始记录 | 系列 / 型号 / 数量 | 能(型号命中率 96.6% 兜住了大部分) | 型号词表 |
| 2 BOM | 归一化型号 | 标准件清单 | 能(BOM 模板兜底) | 模板缺失 |
| 3 图纸 | BOM + 尺寸 | 切割图 / 框线 / 开孔 | 不能 | 尺寸(spec) |
| 4 报价 | BOM + 价 | 金额区间 | 不能 | 出厂价红线 + 尺寸 |
接手一个「数据很多」的历史系统,最容易被骗的地方是:有历史 ≠ 能用。我固定按三个口径查一遍,任何一栏是空的,后面所有算法都别写。
| 口径 | 要回答的问题 | 怎么查 | 实测结果 | 结论 |
|---|---|---|---|---|
| ① 有单 | 历史上到底有多少真实订单、多少经销商、多少型号? | 全量扫 order 主表 + 关联表 | 33,176 单 / 387 经销商 / 2,799 型号 | ✅ 数量级够,够做校验 |
| ② 有字段 | 这些订单里,我要用的关键字段是不是真的填了? | 抽样 400 单查 spec(规格/尺寸)非空率 | 97% 为 null 或「否」;真实有尺寸的 3 单 | 🔴 致命 |
| ③ 有价格 | 型号 / 系列有没有对应的出厂价红线(PV/KW/K1)? | 按五个系列(H / H4 / K / M / S)逐系列查价格表 | 只有 H / S / M 有价,K 系列靠 BOM 模板兜底 | 🔴 部分缺 |
这句话我原文记在这:「别急着写算法,先做采集。」——这句话不是方法论,是被数字打出来的。
| 观察 | 我原来以为 | 真实含义 |
|---|---|---|
97% 的 spec 是 null 或「否」 | 「门店录单习惯差,通知一下就好了」 | 这不是习惯问题,是链路问题:从订单录入到生产,从来没有人要求过必填尺寸。没被要求过的字段,填不填全靠心情,100 单里能有 3 单是运气 |
| 真实有尺寸的 只有 3 单 | 「整改一个月能到 80%」 | 基数太小,不具统计意义。用它去验证「我的引擎准不准」,等于拿 3 个点验证一套算法 |
| 这 3 单属于 K / Y 系列 | —— | 这条最扎心:有尺寸习惯的是冷门系列的人,主流 H / S / M 反而没样本。也就是说,我最需要验证引擎的那批数据,恰恰一份都摸不到 |
// 我当时第一版的处理(错的) const w = spec?.width ?? 800; // 默认 800 const h = spec?.height ?? 2000; // 默认 2000 const price = base * w * h; // → 输出一个「看起来很精确」的金额
| 停手之后做什么 | 为什么是这个 |
|---|---|
| ① 录单表单改造:把尺寸做成必填项 + 校验(宽高必须为正数、厚度假数直接拒) | 解决源头。不改造表单,引擎再好也只是更准地算出空 |
| ② 把「待补尺寸」做成一档状态,而不是「算个大概」 | 让缺失可见,而不是被默认值盖住 |
| ③ 报价引擎只接「尺寸完整」的输入,其余直接走「数据不足」分支 | 把能不能算的判断权还给数据,不交给猜 |
体检结果报上去,第一反应往往是「型号 96.6%、厚度 95.2%,这不挺好?」——看错方向了。
| 指标 | 数值 | 正确读法 | 容易被误读成 |
|---|---|---|---|
| 型号匹配 | 96.6% | 引擎认得型号。但这只是第 1 段过关 | ❌「数据没问题,就快做引擎了」 |
| 厚度匹配 | 95.2% | 物理参数基本齐,属于白捡的 | ❌「参数齐了就能算价」 |
| 尺寸覆盖 | 29% | 真正卡点。第 3、4 段同时依赖它 | ❌「平均一下还有 55%,凑合」 |
| 客户匹配 | 0% | 订单 ↔ 经销商 ID 对不上,不知道给谁报 | ❌「0% 才要紧,别的还能救」 |
价格是所有数字里最不能编的一种。没有价,就只能说没有价。
| 系列 | 价格情况 | 没价时靠什么 | 报价可信度 |
|---|---|---|---|
| H | ✅ 配了价格 | 直接用 | 高 |
| H4 | ⚠️ 未配独立价 | 只能引用 H 或按模板折算 | 中低(必须标注「参考价」) |
| K | ❌ 无价格 | BOM 模板兜底(算出料,算不出钱) | 不能对外报 |
| M | ✅ 配了价格 | 直接用 | 高 |
| S | ✅ 配了价格 | 直接用 | 高 |
“算不了”听起来像一个 bug 状态。但在报价这类产品里,它必须是和「算得出」平起平坐的一等状态。不然一线就只能靠猜,或者干脆绕开系统。
| 状态 | 触发条件(实测口径) | 页面上怎么显示 | 下一步动作(要能点) |
|---|---|---|---|
| ① 算得出 (绿) | 型号命中 + 尺寸完整 + 该系列有价 | 直接显示金额区间(下沿 / 上沿),并标注所用的价 | 导出 / 复核 / 送审 |
| ② 算不出,要补数据 (黄) | 型号命中,但 spec 为空 / 该系列无价 / 经销商 ID 对不上 | 逐条列出缺哪几项:缺尺寸(第几单缺)、K 系列待补价、订单 38747 对应经销商缺失 | 一键跳到采集表单,把缺的字段补齐后重算 |
| ③ 数据本身错 (红) | 字段填了但矛盾:厚度与型号不匹配、尺寸为 0 或负数、面积与长宽不符 | 指出矛盾点在哪(例:H4-T52 标准厚度 8mm,此处填 12mm) | 回到录单源头改,不改就永远算不准 |
?? 800 这类写法在报价链路里一律禁止。我们已经有了三个接口,按理说直接演示系统就行。但我最后给干系人的是一张单文件 HTML:shower-cpq-demo.html。原因有三条,都是硬约束。
| 为什么不用系统 demo | 真实阻碍 |
|---|---|
| 干系人打不开系统 | 系统跑在内网 / 需要登录态,演示那天临时开权限,风险高、体验差 |
| 系统里有真价,不能给外部看 | 真价是核心商业数据。让干系人看真实价格表的截图,等于把底牌掀了 |
| 系统 demo 会带有噪音 | 一进系统就是列表、筛选、分页,干系人看三分钟还在找按钮,根本来不及看「引擎是什么意思」 |
| 系统 demo | 单文件演示页 | |
|---|---|---|
| 打开方式 | 内网 + 登录 + 网络 | 双击就开,断网也能开 |
| 数据 | 真价(敏感) | 假数(只用来演示逻辑) |
| 展示重点 | 功能完整度 | 引擎的三态逻辑 |
| P0 / P1 分档 | 混在一起 | 单文件里按优先级分档,先看 P0 动起来 |
| 能带走吗 | 不能 | 能,一个文件发给谁都能看 |
接口建完之后,我做的不是「按预期跑一遍」,是拿真实脏数据往里砸,看它会不会说一套做一套。
| 接口 | 反向验证做了什么 | 结论 |
|---|---|---|
/api/bom | 拿 33,176 单全量跑一遍拆单,统计命中 / 走模板兜底 / 失败三档 | 能出清单,但 K 系列只能出「料」不出「钱」——与第 05 节口径一致 |
/api/glass_dwg | 用3 份真实尺寸和一堆空尺寸分别打,看会不会出个「空图」假装成功 | 空尺寸直接返回「缺尺寸」,不返回图——三态 ② 生效,这是关键一条 |
/api/quote | 拿真单去打,看返回的是金额还是「缺什么」 | 缺尺寸 / 无价系列 / 经销商对不上时,返回结构里带缺项清单,而不是返回一个数字 |
| 解法 | 解决什么 | 为什么不用 AI 重画 |
|---|---|---|
shower-cpq-demo.html | 让干系人不开服务器就能看懂引擎逻辑 | 它根本不是图,是逻辑演示,用不上 AI 出图 |
product-grid-redraw | AI 生成的画面里门扇数画错了(四扇变六扇),只重画竖直框线 | AI 重画会把玻璃内容和背景一起改掉;程序化只重画框线,玻璃与背景一个像素都不动 |
product-frame-recolor | 效果图要出多色版本(金框改白框) | 换色必须结构 100% 不变,AI 改色等于重画一张,客户拿到的就不是同一个门了 |
| 红线 | 具体口径 |
|---|---|
| 不许用默认值把空字段填平 | 报价链路里任何 ?? 默认值 都视为伪造。缺就是缺 |
| 覆盖率按最小值报 | 四个匹配率里报最小的那个(29% / 0%),不报平均数 |
| 「算不了」是一个可交付状态 | 交付文档里必须写明「在现有数据下,能自动报价的单量约等于 X」,X 必须是用实测跑出来的,不能是估的 |
| 演示页用假数 | 给外部看的演示一律假数据;真价只存在于系统内部 |
?? 800 这种默认值兜底,并知道在报价链路里它等于伪造shower-cpq-demo.html 为什么是单文件 + 无服务器 + P0/P1 分档,并知道给不写代码的人看的产物第一原则是「打开就能看懂」product-grid-redraw / product-frame-recolor 的共同点:结构出错时只改错的那层,不重画是我把一条生产流水线停下来的那个决定。当时客户要的是「自动报价」,我已经把订单→BOM→玻璃图纸→报价四段都设计好了,但体检数据不支持开工,所以我把手停了。
数据是这样的:全量历史 33,176 单、387 个经销商、2,799 个型号,看起来很丰足。但抽样 400 单查关键规格字段 spec,97% 是 null 或者「否」,真正有尺寸的一共 3 单——而且这 3 单属于 K / Y 系列,不是 H / S / M 主流系列。也就是说:我最需要拿去验证引擎的那批主流数据,一份都摸不到。
我犯的第一个错值得单独说:我第一版写了 const w = spec?.width ?? 800,用默认值把空字段填平,让流程跑通。这段代码能跑、不报错、页面上还显示得出金额——但它输出的是错的,而且错得很体面。一线看到「系统算出 3,200 元」会直接当真拿去报给客户。一次假价,客户对系统的信任就一次性归零了。默认值在这里不是兜底,是伪造。
所以我把手停了,改成先做采集:录单表单把尺寸改成必填、加校验;把「待补尺寸」做成一档状态而不是算个大概;报价引擎只接尺寸完整的输入。「别急着写算法,先做采集」——这句话不是方法论,是被 97% 这个数字打出来的。
体检还有一组数字我特别想讲,因为它是我 shepherd 客户预期用的:四个匹配率——型号匹配 96.6%、厚度 95.2%、尺寸覆盖 29%、客户匹配 0%。前两个很好看,但一条链能跑通的比例不是平均数,是四个数里的最小值。96.6 和 0 平均是 48.3%,而实际上能算出价的单量是 0%。报「平均覆盖 48%」等于当场承诺了一个做不到的东西。所以我在交付文档里一律按最小值报。
停手之后我做了三件事,其中两件已经变成产品能力:
① 把「算不了」做成一等公民。报价必须有三态:算得出(绿,显示金额区间并标注所用价)、算不出要补数据(黄,逐条列出缺哪几项,并能一键跳到采集表单补齐后重算)、数据本身错(红,指出矛盾点,比如 H4-T52 标准厚度 8mm 此处填了 12mm)。为什么必须这么做?因为只要「算不了」不是一个正式状态,一线就只有三条路:自己估一个(最坏)、不用系统(等于白做)、回来找你(成本最高)。把它做成一等公民,等于让系统自己说「我不知道」,而不是让人替它瞎说。
② 交付物换成了一张单文件 HTML:shower-cpq-demo.html,无服务器、假数据、按 P0/P1 分档。不用系统 demo 的原因很硬:干系人打不开内网、系统里有真价不能给外部看、系统 demo 一进去就是列表筛选分页,看他三分钟还在找按钮。给不写代码的人看的产物,第一原则是「打开就能看懂」,不是「功能全」。
技术侧我做了三接口的反向验证——注意是反向:不是「给正确输入看返回对不对」,而是拿真实脏数据往里砸,看它敢不敢拒绝。/api/glass_dwg 空尺寸必须直接返回「缺尺寸」而不返回一张空图;/api/quote 缺尺寸 / 无价系列 / 经销商对不上时,返回结构里带缺项清单,而不是返回一个数字。一个报价引擎的正向测试可以 100% 通过,同时整体是个废的——因为现实里 97% 的输入是脏的。所以我现在的习惯是结论类接口先写拒绝态的用例。
还有个方法论上的收获,后来一直在用:AI 生成的效果图在「结构」上出错时(比如门扇数四扇画成六扇、金框要改成白框),修的方向不是让 AI 再生成一次,而是把错的那层剥出来,用程序化方法只改那一层——product-grid-redraw 只重画竖直框线,玻璃和背景一个像素不动;product-frame-recolor 只改框色,结构 100% 不变。重画一次你就丢掉了原图的确定性,只改一层你保住了它。
最后我怎么跟客户讲:「自动报价要三个东西:出厂价红线、真实尺寸、订单和经销商的对应关系。目前尺寸覆盖 29%,客户对应 0%。所以我建议这一期先交数据补齐工具 + 报价引擎的三态提示。硬上全量自动报价,我会给你一个每天输出些像金额的数字、然后你的业务员还得重新复核一遍的系统——那还不如你们现在的 Excel。」
这段的说服力全在真数字上。「我算不了」配上三个实测数字,比十个漂亮的功能更专业。