第2265篇:电信行业AI——网络故障诊断和客户流失预测
第2265篇:电信行业AI——网络故障诊断和客户流失预测
适读人群:电信技术工程师、Java后端开发者、运营商数字化技术团队 | 阅读时长:约15分钟 | 核心价值:解决电信行业网络运维和客户保留的核心痛点,实现智能故障根因分析和精准流失预警系统
在一家省级运营商的技术部门工作过一段时间,那里的网络运维中心是个让人震撼的地方——几十块大屏,几千个监控指标,每天产生的告警动辄几万条。
运维工程师的常态是什么?告警来了,先看是不是"老问题"(某些告警反复触发,工程师早就习惯了),如果不是,就开始手动排查:看日志、查拓扑、打电话问现场。一个中等复杂度的故障,从告警触发到根因定位,平均要1-2小时。
更头疼的是告警关联——同一个核心网元的问题,可能触发几百条下游告警,工程师光是看告警就眼花,根本不知道从哪下手。
电信行业的AI化,我认为有两个最核心的价值:一是把故障定位时间从小时级压缩到分钟级,二是在客户流失前精准识别高风险用户并干预。
电信AI系统架构
网络告警根因分析
告警压缩和关联
电信网络告警的首要问题是"告警风暴"——一个根因故障触发的级联告警可能有几百条:
@Service
public class AlarmCompressionService {
@Autowired
private NetworkTopologyRepository topologyRepo;
@Autowired
private AlarmRepository alarmRepo;
@Autowired
private OpenAIClient openAIClient;
/**
* 告警压缩:将相关告警聚合为故障组
* 核心思路:拓扑相关 + 时间相关 + 类型相关
*/
public List<AlarmGroup> compressAlarms(List<NetworkAlarm> alarms) {
// 1. 按时间窗口分组(5分钟内)
Map<Long, List<NetworkAlarm>> timeWindows = groupByTimeWindow(alarms, 5 * 60 * 1000);
List<AlarmGroup> groups = new ArrayList<>();
for (List<NetworkAlarm> windowAlarms : timeWindows.values()) {
// 2. 拓扑关联分析
List<AlarmGroup> windowGroups = correlateByTopology(windowAlarms);
groups.addAll(windowGroups);
}
// 3. 合并跨时间窗口的同源告警
return mergeOverlappingGroups(groups);
}
/**
* 基于网络拓扑关联告警
* 原理:下游网元的告警通常是上游故障传播的结果
*/
private List<AlarmGroup> correlateByTopology(List<NetworkAlarm> alarms) {
// 获取涉及的所有网元
Set<String> involvedElements = alarms.stream()
.map(NetworkAlarm::getElementId)
.collect(Collectors.toSet());
// 查询这些网元的拓扑关系
NetworkTopology topology = topologyRepo.getSubTopology(involvedElements);
// 找到根节点(没有上游故障网元的节点)
Map<String, List<NetworkAlarm>> alarmsByElement = alarms.stream()
.collect(Collectors.groupingBy(NetworkAlarm::getElementId));
List<AlarmGroup> groups = new ArrayList<>();
Set<NetworkAlarm> assigned = new HashSet<>();
// 从拓扑上游开始,逐层找下游受影响节点
for (String elementId : topology.getTopologicalOrder()) {
List<NetworkAlarm> elementAlarms = alarmsByElement.getOrDefault(elementId, List.of());
if (elementAlarms.isEmpty() || elementAlarms.stream().allMatch(assigned::contains)) {
continue;
}
NetworkAlarm rootAlarm = elementAlarms.stream()
.filter(a -> !assigned.contains(a))
.min(Comparator.comparing(NetworkAlarm::getOccurTime))
.orElse(null);
if (rootAlarm == null) continue;
// 收集所有下游受影响的告警
List<NetworkAlarm> groupAlarms = new ArrayList<>();
groupAlarms.add(rootAlarm);
assigned.add(rootAlarm);
Set<String> downstreamElements = topology.getDownstreamElements(elementId);
for (String downstreamId : downstreamElements) {
List<NetworkAlarm> downstreamAlarms = alarmsByElement.getOrDefault(downstreamId, List.of());
downstreamAlarms.stream()
.filter(a -> !assigned.contains(a))
.forEach(a -> {
groupAlarms.add(a);
assigned.add(a);
});
}
if (groupAlarms.size() > 1) {
groups.add(AlarmGroup.builder()
.rootAlarm(rootAlarm)
.correlatedAlarms(groupAlarms)
.rootCause(AlarmCause.TOPOLOGY_PROPAGATION)
.compressionRatio(groupAlarms.size())
.build());
}
}
return groups;
}
/**
* AI辅助根因诊断
*/
public RootCauseAnalysis analyzeRootCause(AlarmGroup alarmGroup) {
NetworkElement rootElement = getElementInfo(alarmGroup.getRootAlarm().getElementId());
List<String> recentChanges = changeManagementRepo.findRecentChanges(
alarmGroup.getRootAlarm().getElementId(), 24
); // 24小时内的变更
String prompt = String.format("""
请分析以下电信网络告警组,给出根因诊断。
根告警:
- 网元:%s(类型:%s)
- 告警类型:%s
- 告警时间:%s
- 告警级别:%s
- 告警描述:%s
关联告警数量:%d条(已压缩)
关联告警类型分布:%s
近24小时变更记录:%s
网元近期性能趋势:
- CPU利用率:%s
- 内存利用率:%s
- 接口流量:%s
请分析:
1. 最可能的根本原因(给出3个候选,按概率排序)
2. 排查步骤建议
3. 预计恢复时间
4. 是否影响用户(影响范围和数量估算)
5. 是否需要启动应急预案
返回JSON格式。
""",
rootElement.getName(), rootElement.getType(),
alarmGroup.getRootAlarm().getAlarmType(),
alarmGroup.getRootAlarm().getOccurTime(),
alarmGroup.getRootAlarm().getSeverity(),
alarmGroup.getRootAlarm().getDescription(),
alarmGroup.getCorrelatedAlarms().size(),
getAlarmTypeDistribution(alarmGroup),
String.join(";", recentChanges),
getPerformanceTrend(rootElement.getId(), "cpu_usage"),
getPerformanceTrend(rootElement.getId(), "memory_usage"),
getPerformanceTrend(rootElement.getId(), "traffic")
);
String jsonResult = callLLMWithJson(prompt, "gpt-4o");
return JsonUtils.parseObject(jsonResult, RootCauseAnalysis.class);
}
}客户流失预测
特征工程
流失预测的关键是特征的质量,电信行业有非常丰富的用户行为数据:
@Service
public class ChurnFeatureEngineeringService {
@Autowired
private UserBehaviorRepository behaviorRepo;
@Autowired
private CustomerServiceRepository csRepo;
@Autowired
private BillingRepository billingRepo;
/**
* 构建用户流失预测特征(基于过去90天行为)
*/
public ChurnFeatureVector buildFeatures(String userId) {
LocalDate now = LocalDate.now();
LocalDate start90 = now.minusDays(90);
LocalDate start30 = now.minusDays(30);
// 1. 使用行为特征
UsageStats usage90 = behaviorRepo.getUsageStats(userId, start90, now);
UsageStats usage30 = behaviorRepo.getUsageStats(userId, start30, now);
// 使用量趋势:近30天 vs 近90天平均
double voiceTrend = usage90.getAvgMonthlyVoiceMinutes() > 0
? usage30.getVoiceMinutes() / usage90.getAvgMonthlyVoiceMinutes() - 1 : 0;
double dataTrend = usage90.getAvgMonthlyDataMb() > 0
? usage30.getDataMb() / usage90.getAvgMonthlyDataMb() - 1 : 0;
// 2. 客服接触特征
CustomerServiceStats csStats = csRepo.getStats(userId, start90, now);
// 3. 账单特征
BillingStats billing = billingRepo.getStats(userId, start90, now);
// 4. 套餐特征
UserPlan plan = planRepo.getCurrentPlan(userId);
// 5. 网络质量特征(差网络质量是流失的重要驱动因素)
NetworkQualityStats networkQuality = networkQualityRepo.getUserStats(userId, start30, now);
return ChurnFeatureVector.builder()
// 使用量特征
.voiceMinutes30d(usage30.getVoiceMinutes())
.dataUsageMb30d(usage30.getDataMb())
.voiceUsageTrend(voiceTrend)
.dataUsageTrend(dataTrend)
.activeDataDays30d(usage30.getActiveDataDays()) // 近30天有使用流量的天数
// 客服特征
.csContactCount90d(csStats.getContactCount())
.unresolvedComplaintCount(csStats.getUnresolvedComplaints())
.lastComplaintDaysAgo(csStats.getDaysSinceLastComplaint())
.avgComplaintResolutionDays(csStats.getAvgResolutionDays())
// 账单特征
.avgMonthlyBill(billing.getAvgMonthlyBill())
.hasOverduePayment(billing.hasOverdue())
.billChangePct(billing.getBillChangePct()) // 账单金额变化比例
// 套餐特征
.planTenureMonths(plan.getTenureMonths())
.planType(plan.getType().getCode())
.planUtilizationRate(calculatePlanUtilization(usage30, plan))
// 网络质量特征
.avgCallDropRate(networkQuality.getCallDropRate())
.avgDataSpeed(networkQuality.getAvgDataSpeedMbps())
.signalWeakDays30d(networkQuality.getWeakSignalDays())
// 竞争对手特征
.daysToContractExpiry(plan.getDaysToExpiry())
.build();
}
}流失预测服务
@Service
public class ChurnPredictionService {
@Autowired
private ChurnFeatureEngineeringService featureService;
@Autowired
private ModelInferenceClient inferenceClient;
@Autowired
private OpenAIClient openAIClient;
@Autowired
private RetentionCampaignService retentionService;
/**
* 批量预测高风险流失用户(每日凌晨执行)
*/
@Scheduled(cron = "0 0 2 * * *")
public void batchChurnPrediction() {
// 获取需要评估的用户(合约到期前90天内或近期有异常行为的用户)
List<String> candidateUsers = userRepository.findChurnRiskCandidates();
log.info("Starting churn prediction for {} candidates", candidateUsers.size());
int batchSize = 1000;
for (int i = 0; i < candidateUsers.size(); i += batchSize) {
List<String> batch = candidateUsers.subList(i, Math.min(i + batchSize, candidateUsers.size()));
processBatch(batch);
}
}
private void processBatch(List<String> userIds) {
// 构建特征向量
List<ChurnFeatureVector> features = userIds.parallelStream()
.map(featureService::buildFeatures)
.collect(Collectors.toList());
// 批量推理
BatchPredictionResult predictions = inferenceClient.batchPredict(
"churn-prediction-lgbm-v6",
features
);
// 处理预测结果
for (int i = 0; i < userIds.size(); i++) {
String userId = userIds.get(i);
double churnProbability = predictions.getScore(i);
// 保存预测结果
ChurnScore score = ChurnScore.builder()
.userId(userId)
.churnProbability(churnProbability)
.riskLevel(ChurnRiskLevel.from(churnProbability))
.predictionDate(LocalDate.now())
.build();
churnScoreRepository.save(score);
// 高风险用户触发挽留流程
if (churnProbability > 0.7) {
triggerRetentionAction(userId, score, features.get(i));
}
}
}
/**
* 触发个性化挽留动作
*/
private void triggerRetentionAction(String userId, ChurnScore score,
ChurnFeatureVector features) {
// AI分析流失原因并推荐挽留方案
RetentionStrategy strategy = analyzeAndRecommendRetention(userId, score, features);
// 根据策略类型触发不同动作
switch (strategy.getType()) {
case UPGRADE_OFFER:
retentionService.sendUpgradeOffer(userId, strategy.getOffer());
break;
case OUTBOUND_CALL:
retentionService.scheduleOutboundCall(userId, strategy);
break;
case SERVICE_COMPLAINT_FOLLOWUP:
retentionService.escalateComplaint(userId, strategy);
break;
case NETWORK_QUALITY_IMPROVEMENT:
retentionService.flagForNetworkOptimization(userId, strategy);
break;
}
}
private RetentionStrategy analyzeAndRecommendRetention(String userId,
ChurnScore score,
ChurnFeatureVector features) {
UserProfile profile = userProfileRepository.findById(userId).orElseThrow();
String prompt = String.format("""
请分析这位电信用户的流失风险并推荐挽留策略。
用户基本信息:
- 用户类型:%s
- 套餐:%s
- 在网时长:%d个月
- 合约剩余:%d天
流失风险评分:%.2f(0-1,>0.7为高危)
关键风险信号:
- 通话量趋势:%+.0f%%(近30天 vs 历史均值)
- 流量使用趋势:%+.0f%%
- 近90天投诉次数:%d次
- 未解决投诉:%d条
- 网络质量评分:%.1f/10
- 套餐利用率:%.0f%%
请推荐:
1. 主要流失原因(1-2个最重要的)
2. 最合适的挽留策略(UPGRADE_OFFER/OUTBOUND_CALL/SERVICE_COMPLAINT_FOLLOWUP/NETWORK_QUALITY_IMPROVEMENT)
3. 具体挽留方案(如果是优惠,给出具体优惠建议;如果是投诉跟进,说明跟进重点)
4. 最佳联系时间和方式
5. 预计成功挽留概率
返回JSON格式。
""",
profile.getUserType(),
profile.getCurrentPlan(),
profile.getTenureMonths(),
features.getDaysToContractExpiry(),
score.getChurnProbability(),
features.getVoiceUsageTrend() * 100,
features.getDataUsageTrend() * 100,
features.getCsContactCount90d(),
features.getUnresolvedComplaintCount(),
features.getAvgDataSpeed() / 10.0,
features.getPlanUtilizationRate() * 100
);
String jsonResult = callLLMWithJson(prompt, "gpt-4o");
return JsonUtils.parseObject(jsonResult, RetentionStrategy.class);
}
}挽留效果追踪
@Service
public class RetentionEffectivenessTracker {
/**
* 追踪挽留活动效果,用于模型和策略迭代
*/
@Scheduled(cron = "0 0 6 * * 1") // 每周一执行
public void weeklyEffectivenessReport() {
LocalDate reportStart = LocalDate.now().minusWeeks(4);
LocalDate reportEnd = LocalDate.now().minusWeeks(1); // 给用户行为一周的观察期
// 查询4周前触发挽留活动的高风险用户
List<RetentionAction> actions = retentionActionRepo.findByDateRange(reportStart, reportEnd);
Map<RetentionStrategyType, RetentionStats> statsByType = new HashMap<>();
for (RetentionAction action : actions) {
// 检查用户是否实际流失
boolean churned = userRepository.hasChurned(action.getUserId(), reportEnd.plusWeeks(1));
RetentionStats stats = statsByType.computeIfAbsent(
action.getStrategyType(), k -> new RetentionStats()
);
stats.addResult(churned);
}
// 计算各策略的成功率
statsByType.forEach((strategyType, stats) -> {
double successRate = 1.0 - stats.getChurnRate();
log.info("Retention strategy {} success rate: {:.1f}% (n={})",
strategyType, successRate * 100, stats.getTotalCount());
metricsRegistry.gauge("retention.success_rate",
Tags.of("strategy", strategyType.name()), successRate);
});
}
}电信AI工程经验
1. 告警关联是ROI最高的切入点。电信运维人员天天被告警风暴折磨,告警压缩做好了,运维效率提升立竿见影。从这里入手,最容易让团队感受到AI价值并获得支持。
2. 流失预测要设置合理的决策阈值。流失概率0.6的用户和0.9的用户,值得投入的挽留成本是不同的。需要结合用户ARPU(每用户平均收入)来做成本收益计算,不是所有高概率流失用户都值得外呼挽留。
3. 网络质量数据是被低估的特征。很多运营商的流失预测模型只看账单和行为数据,忽略了网络质量。实际上,信号差、频繁掉线是非常强的流失预测信号,且这类用户的挽留方向很明确(网络优化),ROI高。
4. 自动化挽留需要严格的授权控制。自动发优惠券、自动安排外呼这类动作必须有审批流程,特别是涉及大金额优惠时。防止模型误判导致对低风险用户滥发优惠,造成不必要的成本损失。
