第2077篇:AI驱动的客户成功——用数据和LLM提升客户留存
2026/4/30大约 8 分钟
第2077篇:AI驱动的客户成功——用数据和LLM提升客户留存
适读人群:做ToB/SaaS产品的工程师和产品经理 | 阅读时长:约18分钟 | 核心价值:掌握如何将AI能力应用于客户成功场景,包括流失预警、自动化跟进、智能诊断
客户成功(Customer Success)一直是一个劳动密集型工作:CS团队需要主动跟进客户、发现使用问题、提供解决方案。
当客户规模达到数千时,人工跟进变得不可能,但这时候客户流失的风险也最高。
AI可以在这里发挥关键作用——用数据预测风险,用LLM生成个性化的跟进内容。
客户成功AI的核心场景
客户健康评分
/**
* 客户健康评分系统
* 基于多维度行为数据计算健康分
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomerHealthScoreService {
private final CustomerUsageRepository usageRepo;
private final CustomerEngagementRepository engagementRepo;
private final ChatLanguageModel llm;
/**
* 计算客户健康评分(0-100分)
*/
public CustomerHealthScore calculateScore(String customerId) {
// 获取过去30天的使用数据
UsageMetrics usage = usageRepo.getMetrics(customerId, 30);
EngagementMetrics engagement = engagementRepo.getMetrics(customerId, 30);
// 多维度评分
double usageScore = calculateUsageScore(usage); // 使用量
double adoptionScore = calculateAdoptionScore(usage); // 功能采用率
double engagementScore = calculateEngagementScore(engagement); // 互动质量
double valueScore = calculateValueScore(usage); // 价值实现
// 加权综合
double overallScore =
usageScore * 0.30 +
adoptionScore * 0.25 +
engagementScore * 0.25 +
valueScore * 0.20;
HealthStatus status = classifyStatus(overallScore);
List<String> riskFactors = identifyRiskFactors(usage, engagement, overallScore);
List<String> opportunities = identifyOpportunities(usage);
return CustomerHealthScore.builder()
.customerId(customerId)
.overallScore(overallScore)
.usageScore(usageScore)
.adoptionScore(adoptionScore)
.engagementScore(engagementScore)
.valueScore(valueScore)
.status(status)
.riskFactors(riskFactors)
.opportunities(opportunities)
.calculatedAt(LocalDateTime.now())
.build();
}
private double calculateUsageScore(UsageMetrics metrics) {
// DAU/MAU比率(粘性指标)
double dauMauRatio = metrics.getMau() > 0 ?
(double) metrics.getDau() / metrics.getMau() : 0;
// 与同期对比的趋势
double trend = metrics.getUsageTrend(); // 正数增长,负数下降
double score = dauMauRatio * 60 + (trend > 0 ? 40 : Math.max(0, 40 + trend * 2));
return Math.min(100, score);
}
private double calculateAdoptionScore(UsageMetrics metrics) {
// 已使用功能数 / 总功能数
double featureAdoptionRate = (double) metrics.getUsedFeatureCount() /
metrics.getTotalFeatureCount();
// 核心功能使用率(关键功能)
double coreFeatureRate = metrics.getCoreFeatureUsageRate();
return (featureAdoptionRate * 0.4 + coreFeatureRate * 0.6) * 100;
}
private double calculateEngagementScore(EngagementMetrics engagement) {
double score = 0;
// 支持请求:多但快速解决是正面信号
if (engagement.getSupportTicketCount() > 0) {
double resolutionRate = engagement.getResolvedTicketRate();
score += resolutionRate * 30;
} else {
score += 30; // 没有问题也是好的
}
// 培训/文档使用
score += Math.min(30, engagement.getDocPageViews() / 10.0);
// 主动沟通次数(CSM联系)
score += Math.min(40, engagement.getCsmContactCount() * 5);
return score;
}
private double calculateValueScore(UsageMetrics metrics) {
// 价值实现指标(业务结果)
return metrics.getValueRealizationScore();
}
private HealthStatus classifyStatus(double score) {
if (score >= 80) return HealthStatus.HEALTHY;
if (score >= 60) return HealthStatus.AT_RISK;
if (score >= 40) return HealthStatus.DETERIORATING;
return HealthStatus.CRITICAL;
}
private List<String> identifyRiskFactors(UsageMetrics usage,
EngagementMetrics engagement, double score) {
List<String> risks = new ArrayList<>();
if (usage.getUsageTrend() < -0.2) {
risks.add("使用量显著下降(" + String.format("%.0f%%", usage.getUsageTrend() * 100) + ")");
}
if (usage.getDau() == 0 && usage.getMau() > 0) {
risks.add("本周无活跃用户,但月活仍有");
}
if (engagement.getSupportTicketCount() > 5) {
risks.add("本月支持工单数量较多(" + engagement.getSupportTicketCount() + "个)");
}
if (usage.getUsedFeatureCount() < usage.getTotalFeatureCount() * 0.3) {
risks.add("功能采用率偏低(仅" + usage.getUsedFeatureCount() + "/" +
usage.getTotalFeatureCount() + "个功能)");
}
return risks;
}
private List<String> identifyOpportunities(UsageMetrics metrics) {
List<String> ops = new ArrayList<>();
if (metrics.getUnusedHighValueFeatures().size() > 0) {
ops.add("有" + metrics.getUnusedHighValueFeatures().size() +
"个高价值功能未使用,可以推荐");
}
return ops;
}
public enum HealthStatus { HEALTHY, AT_RISK, DETERIORATING, CRITICAL }
@Data @Builder
public static class CustomerHealthScore {
private String customerId;
private double overallScore;
private double usageScore;
private double adoptionScore;
private double engagementScore;
private double valueScore;
private HealthStatus status;
private List<String> riskFactors;
private List<String> opportunities;
private LocalDateTime calculatedAt;
}
}AI生成个性化跟进内容
/**
* 基于客户数据生成个性化跟进邮件
* 这是AI在客户成功中最直接的价值
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class PersonalizedFollowupGenerator {
private final ChatLanguageModel llm;
private final CustomerHealthScoreService healthService;
private final CustomerRepository customerRepo;
/**
* 为高风险客户生成跟进邮件草稿
*/
public FollowupEmail generateAtRiskFollowup(String customerId) {
CustomerHealthScore health = healthService.calculateScore(customerId);
Customer customer = customerRepo.findById(customerId)
.orElseThrow(() -> new CustomerNotFoundException(customerId));
String prompt = String.format("""
你是一名资深的客户成功经理,正在给一个有风险的客户写跟进邮件。
目标:提高客户的产品参与度,防止流失
客户信息:
- 公司名:%s
- 联系人:%s(%s)
- 使用时长:%d个月
- 健康评分:%.0f分(%s)
具体问题:
%s
未充分使用的高价值功能:
%s
要求:
1. 邮件要真诚、具体,不要模板化套话
2. 主动承认观察到的使用下降,表示关心
3. 提供具体的、可操作的建议
4. 邀请他们参加一对一的产品深度使用指导
5. 语气:关心而不焦虑,专业而不生硬
6. 长度:150-200字,简洁有力
请输出邮件正文(不包括主题和称呼):
""",
customer.getCompanyName(),
customer.getContactName(),
customer.getContactRole(),
customer.getMonthsAsCustomer(),
health.getOverallScore(),
health.getStatus().toString(),
String.join("\n- ", health.getRiskFactors()),
String.join("\n- ", health.getOpportunities())
);
String emailBody = llm.generate(prompt);
String subject = generateSubject(customer, health);
return FollowupEmail.builder()
.customerId(customerId)
.toEmail(customer.getContactEmail())
.subject(subject)
.body(emailBody)
.healthScore(health.getOverallScore())
.generatedAt(LocalDateTime.now())
.status(FollowupStatus.DRAFT) // 需要CS经理审核后发送
.build();
}
private String generateSubject(Customer customer, CustomerHealthScore health) {
String[] subjectTemplates = {
"关于您的" + customer.getProductName() + "使用——我有几点想与您分享",
"想聊聊如何从" + customer.getProductName() + "获得更多价值",
customer.getContactName() + ",我们注意到您的使用情况,想提供些帮助"
};
// 根据健康状态选择合适的主题
return switch (health.getStatus()) {
case CRITICAL -> subjectTemplates[2]; // 更直接
case DETERIORATING -> subjectTemplates[0];
default -> subjectTemplates[1];
};
}
@Data @Builder
public static class FollowupEmail {
private String customerId;
private String toEmail;
private String subject;
private String body;
private double healthScore;
private LocalDateTime generatedAt;
private FollowupStatus status;
}
public enum FollowupStatus { DRAFT, SENT, REPLIED, RESOLVED }
}流失预测模型接入
/**
* 将ML流失预测模型和LLM结合
* ML做评分,LLM做解释和建议
*/
@Service
@RequiredArgsConstructor
public class ChurnPredictionService {
private final ChurnPredictionModel mlModel; // 传统ML模型
private final ChatLanguageModel llm;
/**
* 综合流失风险评估
*/
public ChurnRiskAssessment assess(String customerId, CustomerFeatures features) {
// 1. ML模型预测流失概率
double churnProbability = mlModel.predict(features);
// 2. 识别关键风险特征
Map<String, Double> featureImportance = mlModel.getFeatureImportance(features);
List<String> topRiskFeatures = featureImportance.entrySet().stream()
.sorted(Map.Entry.<String, Double>comparingByValue().reversed())
.limit(5)
.map(e -> e.getKey() + "(贡献度" + String.format("%.0f%%", e.getValue() * 100) + ")")
.toList();
// 3. 用LLM生成可理解的解释和建议
String explanation = generateExplanation(customerId, churnProbability,
topRiskFeatures, features);
ChurnRisk riskLevel = classifyRisk(churnProbability);
return ChurnRiskAssessment.builder()
.customerId(customerId)
.churnProbability(churnProbability)
.riskLevel(riskLevel)
.topRiskFactors(topRiskFeatures)
.explanation(explanation)
.recommendedActions(generateActions(riskLevel, topRiskFeatures))
.assessedAt(LocalDateTime.now())
.build();
}
private String generateExplanation(String customerId, double probability,
List<String> riskFeatures, CustomerFeatures features) {
String prompt = String.format("""
基于以下数据,用2-3句话解释这个客户的流失风险,使用业务语言(不要技术术语)。
流失概率:%.0f%%
主要风险因素:
%s
客户近期特征:
- 最近30天活跃天数:%d天
- 功能使用率变化:%+.0f%%
- 未处理的支持工单:%d个
请用2-3句话解释风险,重点说明对业务的影响:
""",
probability * 100,
String.join("\n", riskFeatures),
features.getActiveDays(),
features.getFeatureUsageChange() * 100,
features.getOpenTickets()
);
return llm.generate(prompt);
}
private List<String> generateActions(ChurnRisk risk, List<String> riskFactors) {
// 基于风险等级和因素生成行动建议
List<String> actions = new ArrayList<>();
switch (risk) {
case CRITICAL:
actions.add("立即由高级CS经理接手,安排CEO级别的关怀电话");
actions.add("评估是否需要提供特殊支持或合同调整");
break;
case HIGH:
actions.add("本周内安排深度健康检查通话");
actions.add("提供专属的成功经理1对1支持");
break;
case MEDIUM:
actions.add("发送个性化的功能建议和使用指南");
actions.add("邀请参加产品使用技巧培训");
break;
case LOW:
actions.add("纳入定期健康检查日历");
break;
}
return actions;
}
private ChurnRisk classifyRisk(double probability) {
if (probability > 0.7) return ChurnRisk.CRITICAL;
if (probability > 0.5) return ChurnRisk.HIGH;
if (probability > 0.3) return ChurnRisk.MEDIUM;
return ChurnRisk.LOW;
}
public enum ChurnRisk { LOW, MEDIUM, HIGH, CRITICAL }
@Data @Builder
public static class ChurnRiskAssessment {
private String customerId;
private double churnProbability;
private ChurnRisk riskLevel;
private List<String> topRiskFactors;
private String explanation;
private List<String> recommendedActions;
private LocalDateTime assessedAt;
}
@Data
public static class CustomerFeatures {
private int activeDays;
private double featureUsageChange;
private int openTickets;
// ... 更多特征字段
}
}自动化批量跟进流程
/**
* 自动化的客户成功工作流
* 每天自动识别高风险客户,生成跟进内容
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomerSuccessAutomationService {
private final CustomerHealthScoreService healthService;
private final ChurnPredictionService churnService;
private final PersonalizedFollowupGenerator followupGenerator;
private final FollowupRepository followupRepo;
private final NotificationService notificationService;
/**
* 每日自动运行:识别风险客户,生成跟进建议
*/
@Scheduled(cron = "0 8 * * * ?") // 每天早8点
public void dailyRiskScan() {
log.info("开始每日客户风险扫描");
// 获取所有活跃客户
List<String> customerIds = customerRepo.findAllActiveCustomerIds();
List<CustomerHealthScore> atRiskCustomers = new ArrayList<>();
for (String customerId : customerIds) {
try {
CustomerHealthScore score = healthService.calculateScore(customerId);
if (score.getStatus() == CustomerHealthScore.HealthStatus.AT_RISK ||
score.getStatus() == CustomerHealthScore.HealthStatus.CRITICAL) {
atRiskCustomers.add(score);
}
} catch (Exception e) {
log.warn("计算客户{}健康评分失败: {}", customerId, e.getMessage());
}
}
// 按健康评分升序(最差的优先处理)
atRiskCustomers.sort(Comparator.comparingDouble(CustomerHealthScore::getOverallScore));
log.info("发现{}个风险客户", atRiskCustomers.size());
// 为前20个最高风险客户生成跟进邮件草稿
int processCount = Math.min(20, atRiskCustomers.size());
for (int i = 0; i < processCount; i++) {
CustomerHealthScore customer = atRiskCustomers.get(i);
try {
PersonalizedFollowupGenerator.FollowupEmail draft =
followupGenerator.generateAtRiskFollowup(customer.getCustomerId());
followupRepo.save(draft);
} catch (Exception e) {
log.error("生成客户{}跟进邮件失败: {}", customer.getCustomerId(), e.getMessage());
}
}
// 通知CS团队
notificationService.notifyCsTeam(
"今日风险客户扫描完成",
"发现" + atRiskCustomers.size() + "个风险客户," +
"已为前" + processCount + "个生成跟进邮件草稿,请在CS仪表板查看。"
);
}
@Autowired
private CustomerRepository customerRepo;
}AI在客户成功领域的真正价值,不是替代CSM团队,而是把CSM的精力从"找出有风险的客户"解放到"真正帮助客户解决问题"。
健康评分告诉你该关注谁,LLM生成的内容帮你把关注变成行动——这两者结合起来,是CS团队效率提升的关键。
