搞运维和设备管理的都知道,工单系统里的报障记录是一座矿:每天几十上百条,藏着故障规律、设备弱点和流程问题。但矿是矿,没开采工具就是土堆------工单的描述是维修工各写各的,同一类故障能写出七八种说法:泵异响、水泵有怪声、循环泵声音不对、泵体噪音大。人工从这堆自由文本里找规律,翻到眼花也翻不出全貌。
今年我们给工单系统加了异常自动聚类:机器把工单按内容相似度自动分组,同类问题一屏看清。上线三个月,靠它逮住了两个反复出现但一直被当独立故障处理的真问题。这篇讲讲这个功能的技术实现,重点是工程上的取舍------不是搞科研,是解决实际问题。
一、为什么要聚类,不要分类
先说一个设计决策:我们做的是聚类,不是分类。区别在哪:分类是预先定义好类别(机械故障、电气故障、仪表故障),把每条工单塞进格子------格子是死的,新问题类型没有格子可去,会被硬塞进不合适的类。聚类是无监督的:让数据自己长出分组,出现新的故障模式,它自己成一类,直接浮出水面。
对我们的场景,聚类的价值恰在发现未知:工单分类表是我们已知的故障知识,聚类发现的是我们不知道自己在重复发生的事。前者管理已知,后者暴露未知------设备管理做久了,最值钱的恰恰是后者。
二、技术链路:四步走
2.1 文本预处理
工单文本很脏,直接聚类效果差。预处理三件事:去停用词(的、了、请、尽快这类无信息量的词);统一术语(这是个关键活,后面细说);拼接结构化字段(设备编号、故障类别、处理时长------光靠文本聚类会丢掉这些信号,拼进来一起算)。
术语统一是效果的决定因素。老师傅们对同一部件的叫法五花八门,我们整理了一份车间术语对照表,八百多条,是拉着两位老维修工花两天时间梳理的------这个表本身就成了车间的知识资产。有了它,泵异响和水泵怪声才能在向量空间里真正靠近。
2.2 向量化
文本转向量用现成的中文 embedding 模型,每条工单的描述文本转成一个几百维的向量。这一步没做任何定制,通用模型对短文本的语义编码已经够用------我们在十万条历史工单上验证过:同一故障的工单,向量相似度明显高于不同故障的,区分度足够。
python
def embed_tickets(tickets, term_map):
texts = []
for t in tickets:
desc = normalize_terms(t["description"], term_map)
text = f"{t['device_id']} {t['category']} {desc}"
texts.append(text)
return embedding_model.encode(texts)
2.3 聚类算法的选择
算法我们试了三种:KMeans 要预设类数,不合适(新故障类型的数量未知);层次聚类直观但十万条规模跑不动;最后选了 HDBSCAN------密度聚类,不用预设类数,能自动把密度不够的点标成噪音(孤立的、不成模式的工单,本来就不该硬分组)。
python
import hdbscan
def cluster_tickets(vectors):
clusterer = hdbscan.HDBSCAN(
min_cluster_size=8, # 少于8条不成类
min_samples=3,
metric="euclidean",
)
labels = clusterer.fit_predict(vectors)
# labels == -1 的是噪音点,单独放"待观察"池
return labels
min_cluster_size 定成 8 是调过的:太小(3以下),随机巧合的工单凑成组,组多而碎,看不过来;太大(20以上),真问题因为样本少被当噪音埋了。8 这个值是拿历史工单回测出来的------历史上有记录的真实重复故障,最少的发生过 9 次。
2.4 类别的自动命名
聚类出来的组是没名字的(第 37 组是什么鬼?),人工起名太累。我们让大模型给每个组做命名:把组内工单的设备编号分布和描述高频词喂给它,生成一个概括名。比如某组的命名:3 号线灌装机------夹瓶星轮卡阻(22 单,近 90 天),组名直接带上了设备、故障特征、频次、时间窗------一屏扫过去,轻重缓急自现。
三、上线后的两个战果
3.1 战果一:星轮卡阻的 22 张散单
上线头一周,聚类就揪出一个组:22 张工单分布在 90 天里,涉及同一台灌装机的夹瓶星轮卡阻------每次的处理都是现场清理复位,平均四十分钟。单张看,每张都像偶发;聚起来看,每四天卡一次的设备,这不叫偶发,叫慢性病。拿聚类报告找设备科会诊,结论是星轮磨损到临界值,换件一劳永逸。换件后三个月,该类工单零新增。
这 22 张单如果靠人工发现,需要有人连续翻三个月的工单记录,还得在不同写法里认出是同一件事------不是没人有能力做,是没人有这个时间。聚类干的就是这个苦活:把散落的珍珠串成线,让该被看见的规律浮上来。
3.2 战果二:换班交叠时段的重复报障
第二个发现更意外:某类巡检类工单高度集中在早上八点到九点------聚类报告的时间分布统计里跳出来的。查了才知道:夜班收尾时发现的小故障,夜班报一单,早班接班复核又报一单------同一故障两张单,间隔半小时。这事对故障本身没影响,但对数据的影响不小:这类重复单占该时段工单的一成七,统计口径全被带歪了(故障率虚高)。处理很简单:报障流程加了三十分钟内的相似单合并提示。数据干净了,后面所有的分析才有地基。
四、工程上的三个坑
4.1 别追求完美聚类
头两周我犯了完美主义:调参数调到每组都纯净------每组里都是百分之百同类工单。做到后来发现路走偏了:越调越碎,组从 40 个变成 200 个,每组三五条,全景感全无。想通了聚类报告的用途:它是给人看全景、发现异常的,不是做精确归档的。容忍每组里有两成不太纯的样本,换来组数稳定在四五十个、重点组一眼可辨------这才是要的。工具的精度要跟用途匹配,不是越精越好。
4.2 定期重跑比实时更重要
起初我想做成实时聚类,后来发现没必要:故障模式的变化以周为单位,每天凌晨全量重跑一次,三十万条工单十几分钟跑完,足够新鲜。实时化省下的那点时效,对发现问题没有意义,对系统复杂度却是成倍的增加。定时批处理,简单可靠------工程上的克制,有时候比能力更稀缺。
4.3 报告的形态决定使用率
聚类结果最初的形态是一张 JSON 大表,没人看。后来改成了一页看板:头部是组数和新增组的高亮,中间是异常组的排序(按频次和时间趋势),每组点开是工单明细。就这个改动,报告的打开率从没人看变成每周例会必看。数据产品的最后一公里是呈现------再好的分析,看不到人眼里就是零。这一课我记下了:做数据功能,先想清楚给谁看、在什么场合看、他想从里面一眼看到什么。技术人容易栽在这:把呈现当装饰,殊不知对用户来说,呈现就是产品的全部。
常见问题
问:工单量少,几千条,聚类有意义吗?
答:量少有量少的做法:min_cluster_size 降到 5,时间窗口拉长到半年,让稀疏的规律有机会显形。我们的历史数据里,那个星轮卡阻在头一年也就十几单,照样聚得出来。聚类对量的要求没有想象中苛刻------它是找相似,不是建模型,几百条里如果有十来条是同一个事,就能浮上来。
问:聚类结果怎么验证是不是真规律?
答:三道验证:让一线维修工看组名和样本,问这组是不是同一回事------人的领域判断是金标准;对着设备的维修历史和更换记录,看时间上对不对得上;小范围试点处理(比如换件、改流程),看后续工单量有没有掉。第三道最硬:规律是真的,处理掉了,数字就说话。星轮那个案例就是这么闭环的------22 单换一个件,这个验证成本太值了。
问:这个思路能用在工单以外吗?
答:能,本质是自由文本的相似度分组:客服的投诉记录、质量部门的不良描述、IT 的故障工单,同构场景全部适用。我们有位同行做餐饮连锁的,拿这招聚类顾客差评,发现了某几家电的同一台冰柜老出问题------道理相通:散在文字里的规律,人眼看不见,向量和密度看得见。数据不缺规律,缺的是让规律现形的那层透镜。
问:会不会聚类出太多噪音组,反而干扰判断?
答:噪音的量取决于两个参数的耐心调校和术语表的维护质量。我们的经验:噪音组(点了发现是巧合凑的)稳定在两成以内,且特征明显------组内工单的设备型号跨得离谱、描述的共同词极少。看板设计上给噪音组降权(灰色折叠,不占注意力),让人头一眼先看高置信组。噪音就像淘金的沙子,不可能完全消除,但可以让金子排在沙子前面------呈现的排序设计,就是干这个的。