当一通骚扰电话拨打进来时,用户侧能看到的是“疑似骚扰”或“陌生号码”这几个字,但放在通信风控、企业通话平台或客服系统里,要处理的问题远不是挂断那么简单。系统必须在通话前判断要不要拦截,通话中判断话术和意图,通话后判断要不要进灰名单,还要尽量避免把正常号码误伤成“骚扰”,否则就会引发用户投诉和业务损失。
这篇文章就围绕“骚扰电话识别与处置”讲一条可以落地的处理链路:从呼叫事件接入、特征计算、规则引擎、模型评分,到批量任务、实时接口、验证指标和常见排查。适合通信领域的技术开发、风控工程师、客服质检负责人,以及准备做号码治理或通话安全的产品经理阅读。最值得关注的不是某一个算法,而是整条链路里那些容易翻车的位置:特征没对齐、阈值拍脑袋、批量任务没有幂等、模型上线没有影子期,每一处都可能让方案从“能用”变成“不敢用”。
1. 先说结论:骚扰电话识别不是单点功能,而是一条完整链路
很多团队第一次接这类需求时,容易把它理解成“找一个号码黑名单库,命中就拦截”。这个理解不完整。实际生产环境里,骚扰电话的形式一直在变,号码可以变更、伪装、通过不同线路外呼,单靠静态黑名单无法覆盖新增和变体。
所以我更建议把问题定义成:当一次呼叫开始之后,系统能否在有限时间内,基于可获取的数据,判断该呼叫属于“正常业务呼叫”还是“疑似骚扰”,并触发合适的处置动作。这里的核心不只是一个模型,而是一条由数据接入、特征计算、判定策略、处置动作、日志回流组成的闭路。
如果只问“骚扰电话识别准不准”,不如先问“系统能不能在一通电话发起后的几百毫秒内,把该算的特征都算完”。实时性、稳定性、可解释性,往往比单一准确率更重要。这决定了你后面的技术选型:规则引擎能不能兜底,模型该不该上,实时接口该用多高的超时,批量任务要不要做断点续跑。
1.1 一通骚扰电话从呼叫开始会发生什么
按时间线来说,一次呼叫会经历这几个关键节点。
第一个节点是呼叫事件产生。无论是运营商侧、企业通信平台还是客服系统,都会在呼叫发生时生成一条呼叫记录,至少包含主叫号码、被叫号码、发起时间、呼叫标识。这个节点最重要的是“有没有事件”和“能不能实时拿到事件”。
第二个节点是基础信息补全。系统会根据主叫号码查询历史标签、号码归属地、近期呼叫频次,也会查询被叫号码是否在此之前被其他人标记过。这里的风险在于号码字段不一致导致的漏算,比如有的记录带国家码,有的记录不带,归一化没做好,后面所有特征都会偏。
第三个节点是行为特征计算。这一层会统计短时间呼叫次数、呼叫不同被叫数量、平均通话时长、接通率、集中呼叫时段、号码生命周期等。骚扰号码通常在某个时间窗内高频外呼,且大量呼叫无法接通,这些行为特征会比静态标签更敏感。
第四个节点是内容特征提取。在合规和授权前提下,可以做通话文本转写或关键词抽取,判断是否包含“贷款”“中奖”“退款”“转账”“验证码”等高风险词,再看是否存在重复话术。内容层能发现行为层看不出的长尾骚扰。
第五个节点是判定与处置。规则和模型输出风险分,系统根据分数映射到不同动作:直接拦截、来电标记、转接提示、生成告警工单、进入人工复核队列。处置动作不是越多越好,而是要能解释清楚“为什么这条呼叫被判了高风险”。
第六个节点是数据回流。用户标记、客户投诉、复核结果、通话录音抽检结果都要回到样本和特征库,用于下一轮规则调整和模型训练。没有回流,系统会慢慢失效。
这条链路你可以在小规模环境里先简化,但不能砍掉任何一个断点。比如只做规则、不做回流,时间久了规则会被绕过;只做模型、不保留规则兜底,模型上线初期出现异常时容易失控。
1.2 适合谁看,以及最值得关注的三件事
如果你是第一次做这类系统,下面三件事最值得先记住。
第一,先把“跑通”和“调准”分开。不要一开始就追求把所有骚扰电话都识别出来,而是先用一条真实话单走通“输入 - 特征 - 判定 - 输出”的最小链路。链路通了,再逐步增加特征和策略。
第二,不要把误报当成小事。拦截十个骚扰电话当然有效,但每误伤一个正常客户电话,都可能造成真实投诉。做风控一定要同时看“拦截数量”和“误报数量”,只看命中量会让你盲目调高敏感度。
第三,默认配置只适合学习和演示。生产环境里,号码伪装、批量外呼、分段更换号码、不同时段活跃等行为都会让简单规则失灵。你需要在设计规则和模型时,保留“可解释”“可回滚”“可观测”三个能力。
2. 先理清系统链路:从呼叫事件到处置动作要经过哪几步
在写代码之前,先画一遍系统数据流。这一步花半小时,后面能省很多排查时间。
我一般会把链路分成四段:接入层、特征层、决策层、处置层。接入层负责接收呼叫事件或话单;特征层负责把原始记录计算成特征向量;决策层运行规则和模型;处置层执行拦截、标记、告警、复核等动作。每一段之间通过明确的输入输出协议连接,日志要带上同一呼叫ID,方便回溯。
2.1 呼叫事件接入与数据字段
在合规前提下,系统至少需要拿到以下字段。
| 字段 | 含义 | 在风控中的用途 |
|---|---|---|
| 呼叫 ID | 一次呼叫的唯一标识 | 贯穿日志、特征、处置全链路 |
| 主叫号码 | 发起呼叫的号码 | 号码身份识别、历史关联 |
| 被叫号码 | 接收呼叫的号码 | 判断被叫侧反馈和标记记录 |
| 呼叫时间 | 呼叫发起时间戳 | 频次、时段、时间窗计算 |
| 接通状态 | 是否接通 | 接通率、空号判断 |
| 通话时长 | 接通后的持续时长 | 区分机器空放和真实对话 |
| 挂断原因 | 主动挂断、超时、拒绝等 | 判断用户反感程度 |
| 用户标记 | 被叫方是否标记骚扰 | 回流标签和样本来源 |
| 来源线路 | 走运营商、SIP 线路还是客服平台 | 识别批量外呼、线路切换 |
有一点要特别提醒:原始字段里没有“骚扰标签”,它不是数据自带属性,而是系统计算出来的结果。所以接入层的首要任务是保证字段准确性和完整性,尤其是主叫号码归一化。号码不统一,后面所有频次类特征都是错的。
另外,涉及录音、转写、用户号码等数据时,必须遵循相应授权和合规要求。我在实际项目中见过因录音文件权限没收敛导致复核流程出问题的情况,所以建议在接入层就把数据分级和权限控制做清楚,不要在后期补救。
2.2 特征计算:号码、频次、时间分布、内容
骚扰电话识别的特征不是越多越好,而是要看“计算代价和区分度”。我一般把它们分成四类。
第一类叫静态属性特征。包括号码号段、归属地、注册时长、历史标签。这类特征稳定,但容易被绕过,因为骚扰号码也会养号。
第二类是行为特征。重点看主叫号码在特定时间窗内的外呼次数、呼叫不同被叫数量、接通率、平均通话时长、振铃时间、夜间呼叫占比。高频次、低接通、短通话,是批量骚扰最明显的信号。
第三类是关系特征。一个号码可能不单独行动,多个号码可能共用线路、设备标识、呼叫模板。通过号码聚类或共现关系,可以发现单一号码看不出的团伙特征。
第四类是内容特征。在授权前提下,对通话进行文本转写后,统计关键词命中次数、话术雷同度、重复模板。内容特征对“金融诈骗”“冒充客服”这类强意图骚扰特别有效。
特征计算层最容易踩的坑是时间窗口不统一。比如规则里写“一小时内呼叫超过20次”,但代码里用的是自然小时的开始时间,不是滑动窗口,那么跨小时边界的高频呼叫就可能漏掉。建议规范成统一的滑动时间窗口,并且把所有特征的窗口参数放在配置中心,方便后续调整。
2.3 判定与处置:拦截、标记、告警、复核
判定层不是只有一个阈值,而是把规则、模型、名单组合起来,输出一个风险等级和处置动作。
我常用的风险等级是:高、中、低、正常。高风险直接拦截或来电侧提示;中风险在用户端展示“疑似骚扰”标签;低风险记录日志然后放行;正常呼叫不受影响。对于高风险和中风险,还要生成一条供人工复核的记录,方便后续抽检。
处置动作要注意一致性。同一个呼叫不能同时被拦截又放行,处置状态也要记录清楚。如果系统做了拦截,但用户手机侧因为保护逻辑没有正常展示提示,那会引发“被拦截了为什么还能打进来”的困扰。所以处置层需要一个确认回执机制,至少要让处置结果可查。
3. 规则引擎先行:用最小可运行方案把链路跑通
第一次做骚扰电话识别,我强烈建议不要直接上模型。先把规则引擎跑通,用规则确认数据质量和处置链路没问题,再考虑模型加码。原因有两个:规则可解释,出问题时能立刻定位;规则迭代快,业务人员也能参与。
3.1 规则设计的核心维度
规则设计不用复杂,但维度要覆盖到频次、行为、内容和反馈。
频次维度可以设置“1小时内主叫呼叫超过20个不同被叫”这类规则;行为维度可以设置“接通率低于30%且平均通话时长低于10秒”;内容维度可以设置“命中高风险关键词”;反馈维度可以设置“累计被标记次数超过阈值”。每条规则命中后累加风险分,总分超过阈值则进入高风险处置。
这里要注意规则之间的相互影响。比如“呼叫次数多”和“接通率低”高度相关,如果两条规则同时加权,会给同一批次话单重复计分。建议先做规则相关性分析,把强相关规则合并,再给不同维度配置不同的权重上限。
3.2 单条呼叫判定示例
下面给一个特征计算的示意代码,目的是说明链路,不是生产实现。
# 示意代码:单条呼叫风险评分 def calculate_risk_score(call, history): score = 0 # 1 小时窗口内不同被叫数量 callee_count = history.count_distinct_callee(call.caller, window_minutes=60) if callee_count >= 20: score += 40 # 接通率 connect_rate = history.get_connect_rate(call.caller, window_minutes=60) if connect_rate < 0.3: score += 30 # 平均通话时长 avg_duration = history.get_avg_duration(call.caller, window_minutes=60) if avg_duration < 10: score += 20 # 被叫侧用户标记数 if history.get_mark_count(call.caller) >= 5: score += 10 return score这段示意里,每个阈值都是可配置的。实际生产里,history 数据通常来自 Redis 或 ClickHouse 这类存储,查询时要避免对每条话单实时全量扫描。我一般会把主叫号码的频次特征做成增量计数器,放在缓存里,减少重复计算。
3.3 规则阈值怎么定,为什么不能只看准确率
很多团队在定阈值时习惯直接看“准确率”,但准确率在骚扰电话场景里经常骗人。
假设线上10000通呼叫,只有200通是骚扰电话。如果系统把全部呼叫都判为正常,准确率也有98%。但这套系统没有任何实际作用。所以你至少要同时关注误报率、漏报率、处置准确率这几个指标。
| 指标 | 含义 | 怎么观察 |
|---|---|---|
| 准确率 | 全部判定中正确的比例 | 看混淆矩阵整体 |
| 误报率 | 正常呼叫被误判为骚扰的比例 | 用户投诉、复核抽样 |
| 漏报率 | 骚扰呼叫没有被判出的比例 | 用户后续标记、升级投诉 |
| 处置准确率 | 被拦截样本中真的属于骚扰的比例 | 人工复核确认 |
调阈值时不要只看一版结果。我会先保存一批已经标注好的历史话单,规则调整后在同一批话单上做回放,对比新旧结果,再决定是否上线。这种回放机制比“上线跑几天再看效果”要安全很多。
4. 模型识别阶段:处理规则覆盖不到的长尾
规则的好处是稳定可解释,坏处是覆盖不了长尾。骚扰号码会不断变换行为模式,新出现的模式在规则库里可能没有对应规则。这个阶段可以引入模型,但要注意引入方式。
4.1 模型解决什么问题
模型主要解决“规则没覆盖到的新模式”和“多特征非线性组合”两类问题。
比如一个号码单看频次不高,但如果它同时满足“夜间活跃”“归属地异常”“被叫用户多数在短时间内挂断”“话术中出现诱导性关键词”,规则很难用简单阈值表达,模型可以通过特征组合给出更高分。
但模型不能完全替代规则。我的建议是规则和模型并行,最终用一个分值融合策略。规则分值侧重稳定可解释因素,模型分值侧重长尾识别。融合后超过阈值,再进处置流程。
4.2 样本、特征、训练
先聊样本。正样本可以来自用户标记、客户投诉、人工复核确认的骚扰号码;负样本可以选择已确认正常的外呼号码和日常通话记录。样本数量不足时,不要急着上复杂模型,先用规则生成候选,再人工标一部分,保证正负样本都够。
再聊特征。训练特征要和线上特征保持一致,否则会出现离线评估很好、线上效果很差的问题。最典型的坑是离线用了未来信息,比如用了“通话结束后的录音结果”去预测“通话开始时是否拦截”,这属于特征泄露,必须避免。
模型选择上,第一版用 XGBoost 或 LightGBM 这类梯度提升树模型就够了。它们对表格特征友好,训练快,还方便查看特征重要性。不要一上来就上深度模型,除非你有大规模文本或音频特征需要处理。
4.3 影子模式:上线前先陪跑
模型上线前,最好的方式不是直接拦截,而是先开影子模式。影子模式的意思是:模型已经在实时接口里运行并打分,但它的分数不会影响处置动作,只会记录到日志和存储里。
影子模式跑一段时间后,你可以对比“线上规则处置结果”和“模型建议结果”,看模型新增命中哪些样本、是否包含大量误报、分数分布是否稳定。确认模型在历史回放和影子模式里都表现稳定后,再切小流量,比如让模型管理10%的呼叫处置,剩余继续走规则,观察几天再逐步放大。
我见过不少项目因为没做影子模式,模型直接上线后误报率升高,导致正常业务收到大量拦截投诉,最后只能紧急回滚。这类事故完全可以靠影子模式避免。
5. 落地实现:批量任务、接口与并发控制
当方案从验证走向落地时,你会面对两类任务:一类是离线批量处理,比如每天跑一遍全量话单,生成号码画像;另一类是实时接口,比如呼叫发生时在线打分。两者的实现方式差别很大,不要混在一起设计。
5.1 单条呼叫处理示例
先把单条处理逻辑写好,因为后续的批量任务和实时接口都会复用这部分。
下面是一个简化的处理流程:
# 示意代码:单条话单处理 def process_call(call): features = feature_service.build_features(call) rule_score = rule_engine.score(features) model_score = model_service.predict(features) final_score = fusion_score(rule_score, model_score) if final_score >= high_risk_threshold: return {"call_id": call.call_id, "risk_level": "high", "action": "block"} elif final_score >= medium_risk_threshold: return {"call_id": call.call_id, "risk_level": "medium", "action": "mark"} else: return {"call_id": call.call_id, "risk_level": "low", "action": "allow"}这里的关键是特征服务和模型服务要独立,便于后续扩展。单条跑通后,才能继续做批量和接口。
5.2 批量处理与队列设计
批量话单处理时,最忌讳一次性把全部数据加载到内存,然后循环处理。如果一天有几十万通话,每条还涉及历史特征查询,很容易把内存和数据库连接打满。
更稳的方式是分批读取、逐批处理、增量写入。比如每批读取1000条话单,处理完后写入结果表,再读取下一批。如果某一条失败了,记录失败原因,不要中断整批任务。
输出文件或结果表要带上批次标识和时间戳,方便定位。处理任务要有幂等性:同一批话单因为异常重跑时,不会产生重复结果或覆盖错误。简单做法是结果表以“呼叫ID + 任务批次”作为唯一键,重复写入时使用更新语义。
5.3 接口化和并发控制
实时接口的核心约束是超时和并发。骚扰电话识别必须在呼叫接续时间内返回结果,通常不能超过几百毫秒。如果特征服务查询很慢,就需要做缓存或预计算,而不是强行增加下游数据库压力。
并发控制上,我不建议一开始就把接口实例开到很大。先跑单实例,压测观察响应时间、内存、下游连接数,再决定扩几个实例。有一个好习惯:接口接入方要设置熔断和降级。当评分服务响应超时或异常时,默认放行呼叫,避免因为风控系统故障影响正常通信。
| 对比项 | 离线批量处理 | 实时接口 |
|---|---|---|
| 触发方式 | 定时任务或手动触发 | 呼叫事件触发 |
| 响应要求 | 分钟级或小时级 | 毫秒级 |
| 数据量 | 大批量全量扫描 | 单条或小批量 |
| 失败处理 | 可以重跑、断点续跑 | 必须熔断、降级 |
| 资源占用 | 峰值高,需要控制并发 | 稳定低延迟,需要压测 |
6. 验证标准:我用什么指标判断系统真的可用
功能上线不等于方案可用。你要有一套验证标准,能回答“它到底行不行”以及“某一版改动是变好还是变坏”。
6.1 准确率、误报率、召回率
我建议建立一套固定的评估样本集,样本来源包括历史已确认的骚扰话单、正常外呼话单、线上新产生的用户投诉和标记。每次改动后都在这套样本集上跑一遍,输出混淆矩阵。
具体看三个数:召回率要高,说明多数骚扰都被识别出来了;精度要高,说明被判为骚扰的确实有问题;误报率要低,说明正常呼叫没有被错误拦截。三者不能只看一个,否则容易顾此失彼。
另外要关注“处置准确率”。这个指标是人工复核被拦截样本后计算出来的,能够反映线上实际效果。如果被系统拦截的样本里,有相当一部分人工复核后认为不是骚扰,说明策略过度激进。
6.2 延迟、吞吐、资源占用
识别系统是典型的数据链路系统,除了算法效果,还要看运行性能。
实时接口建议统计 p50、p95、p99 延迟。p99 延迟过高的系统,在呼叫峰值时段容易引发超时。批量任务要看单批处理耗时、总任务耗时、内存峰值。资源占用要关注特征服务所在数据库的连接数,这个位置最容易成为瓶颈。
如果发现开通了识别能力后,系统整体内存上涨明显,先排查是不是特征缓存没有设置过期时间,或者批量任务一次性加载了过多话单。
6.3 从日志回看和复盘
我每周会做一次抽样复盘:从本周被拦截的样本里抽一批,从本周被放行但后来被用户标记的样本里抽一批,分别看特征分数和判定原因。
复盘的价值在于发现规则和模型没注意到的模式。比如某个时间段突然出现大量高分散步号码,或者某个被叫群体频繁投诉,说明可能有新的骚扰话术或线路变化。复盘结果要记录成文档,并转化为规则更新任务或样本补充计划。
7. 常见排查链路和边界提醒
最后讲一些实际排查时我会优先看的点。这些经验不限于一种技术方案,通常能帮你在出问题时快速缩小范围。
7.1 排查顺序
当你发现漏报增多、误报增多、接口超时或批量任务失败时,先别急着改模型,按照下面的顺序排查。
第一步看输入数据。主叫号码是否归一化,时间戳是否统一,是否包含重复呼叫ID,文件编码是否异常。数据问题会导致特征计算和判定结果一起漂移。
第二步看特征计算。取一条已知风险话单,手动核对特征值是否和预期一致。比如一小时窗口内号码呼叫次数是否正确,接通率统计是否排除无效呼叫。
第三步看判定策略。确认最近有没有调整过阈值、规则权重或模型版本。策略变更后没有回放验证,是线上指标突然变化的常见原因。
第四步看处置链路。被拦截或标记的记录是否真正写入日志,处置结果有没有被后续流程覆盖,工单是否正常生成。这里最容易出现“模型判定正确,但处置没生效”的问题。
第五步看资源占用。如果接口变慢,先看特征库的连接数和响应时间,再看评分服务所在进程的CPU和内存,不要一上来怀疑模型文件损坏。
7.2 边界提醒
要明白,任何骚扰电话识别方案都有边界,过度期待会踩坑。
号码可能被伪冒,所以不要只依赖主叫号码。用户标记有滞后性,新号码要累积到一定量才会被识别。规则和模型都会有失效期,需要持续回流和更新。低配环境能跑通批量小样本,不代表能在高并发实时场景稳定运行。支持模型不等于所有类型骚扰都覆盖,不同话术、线路、用户群可能需要单独调优。
数据合规是底线。涉及用户号码、通话记录、录音内容时,必须在授权范围内使用,并做好权限控制和数据脱敏。这些不是可选项,而是上线前必须落实的基础设施。
7.3 最后几个落地建议
如果让我给一个实施顺序,我会建议这样走:
先把单条话单评分跑通,再开放批量任务,最后接实时接口。能单条跑稳,说明基础链路没问题;能批量处理,说明边界和容错到位;能实时接口,说明性能和降级策略可靠。每一步之间都加验证,不要跨步。
参数调整时,每次只改一个变量。比如先调频次阈值,不动权重;再调权重,不动模型。否则出问题后很难定位原因。
上线前留好回滚方案。规则引擎和模型要能快速开关,最好有配置中心统一管理。真正落地时,最该盯住的不是准确率数字,而是输入数据质量、特征一致性和处置动作可观测性。这三个地方不出问题,系统就不会失控。