某大厂的好友看了我的文章系列后,建议我写一个行为分析的案例,遂有此文
导读
本章通过一个用户深度行为分析案例 ,深入展示AgentScope的Skill系统在企业级应用中的实际落地。我们将从零开始构建一个用户行为深度分析系统,涵盖:
- ✓ 业务场景分析:企业需求和痛点
- ✓ Skill定义:完整的Markdown DSL定义
- ✓ Hook监控系统:全程透明可追踪的分析过程
- ✓ 核心分析引擎:1500+行生产级代码
- ✓ 数据模型定义:完整的实体类结构
- ✓ 集成示例:Spring Boot/Agent集成
- ✓ 运行结果:真实的执行日志和输出
- ✓ 业务价值分析:可量化的效果评估
1. 业务场景分析
1.1 业务背景
公司概况:
- 某SaaS公司,提供企业协同办公平台
- 服务企业用户:5万+家
- 注册用户数:500万+
- 日活跃用户:50万+
- 每日生成行为日志:200万-500万条
产品功能:
产品矩阵:
├── 文档协同:在线编辑、多人协作、版本管理
├── 项目管理:任务跟踪、甘特图、看板视图
├── 团队沟通:即时消息、视频会议、知识库
├── 数据分析:BI看板、报表生成、数据导出
└── 工作流:自动化审批、表单提交、集成平台
1.2 业务痛点
痛点1:数据分散,难以统一分析
diff
数据源分布:
├── 应用日志:ELK Stack (Elasticsearch + Logstash + Kibana)
├── 业务数据库:MySQL集群 (10+ 实例)
├── 埋点数据:神策数据 (Sensors Analytics)
├── 用户画像:Redis + HBase
└── 行为日志:Kafka + Flink
问题:
- 数据格式不统一,难以关联
- 每次分析需要从多个源拉取数据
- 数据一致性难以保证
痛点2:分析周期长,响应慢
markdown
传统分析流程:
1. 产品经理提出需求 (1天)
2. 数据分析师确认需求 (0.5天)
3. 编写SQL/脚本抽取数据 (0.5天)
4. 数据清洗和预处理 (1天)
5. 统计分析和建模 (1天)
6. 生成报告和可视化 (0.5天)
7. 与业务方讨论和修改 (0.5天)
-----------------------------------------
总计:3-5天
问题:
- 业务变化快,等报告出来已经过时
- 分析师人力紧张,请求排队
- 重复性分析工作多
痛点3:缺乏实时预警,问题发现滞后
diff
典型场景:
场景1:用户流失
- 用户已经7天未登录,但没有预警
- 等到月报发现时,用户已经流失
- 错过最佳挽回时机
场景2:功能异常
- 某功能使用率突然下降50%
- 可能是产品问题,但无人发现
- 直到用户投诉才知道
场景3:营销效果
- 营销活动上线后效果不佳
- 缺乏实时监控,无法调整
- 造成预算浪费
痛点4:分析结果缺乏业务洞察
erlang
当前状况:
输出:大量统计数字和图表
├── DAU: 85,423
├── WAU: 387,621
├── MAU: 523,417
├── 留存率: 38.7%
└── ...更多数字
缺失:
✗ 这些数字意味着什么?
✗ 与历史数据对比如何?
✗ 存在什么问题和机会?
✗ 应该采取什么行动?
✗ 预期效果是什么?
业务方的困惑:
"数字我都看到了,然后呢?"
"我应该做什么?"
"预期能带来多少改善?"
痛点5:团队重复开发,缺乏复用
diff
现状:
产品团队:自己写SQL分析用户留存
运营团队:自己写Python脚本分析转化率
数据团队:自己开发BI看板
客服团队:自己导出Excel分析投诉率
问题:
- 相似的分析逻辑重复开发
- 代码质量参差不齐
- 计算口径不统一
- 维护成本高
1.3 解决方案设计
目标:构建基于AgentScope Skill系统的智能用户行为分析平台
核心能力:
css
┌─────────────────────────────────────────────────────────────┐
│ 用户行为分析Skill平台 │
└─────────────────────────────────────────────────────────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
┌───────▼────────┐ ┌─────────▼────────┐ ┌────────▼────────┐
│ 数据接入层 │ │ 分析引擎层 │ │ 应用服务层 │
├────────────────┤ ├──────────────────┤ ├─────────────────┤
│• 统一数据源 │ │• 用户活跃度分析 │ │• REST API │
│• 数据清洗 │ │• 用户分层聚类 │ │• 定时任务 │
│• 质量检查 │ │• 功能使用分析 │ │• 实时预警 │
│• 格式转换 │ │• 流失风险预测 │ │• 报告生成 │
└────────────────┘ │• 洞察生成 │ └─────────────────┘
│• 建议生成 │
└──────────────────┘
│
┌─────────▼──────────┐
│ Hook监控系统 │
├────────────────────┤
│• 过程追踪 │
│• 性能监控 │
│• 质量保证 │
│• 日志记录 │
└────────────────────┘
技术架构:
java
技术栈:
├── 框架:AgentScope + Spring Boot 3.2
├── 数据处理:Apache Commons Math 3.6
├── 机器学习:Smile ML 3.0
├── 数据库:MySQL 8.0 + Redis 7.0
├── 消息队列:Kafka 3.6
├── 监控:Prometheus + Grafana
└── 日志:SLF4j + Logback
模块划分:
skill-user-behavior-analysis/
├── src/main/java/
│ ├── skill/ # Skill定义
│ ├── hook/ # Hook监控
│ ├── processor/ # 分析引擎
│ ├── model/ # 数据模型
│ ├── repository/ # 数据访问
│ └── service/ # 业务服务
├── src/main/resources/
│ ├── skills/ # Skill MD文件
│ └── application.yml
└── pom.xml
2. Skill定义文件
2.1 完整的Skill定义
创建文件:skills/user_behavior_analysis.md
markdown
---
# ========== 基础信息 ==========
id: skill-user-behavior-001
name: user_behavior_deep_analysis
displayName: 用户行为深度分析
version: 1.0.0
author: Product Analytics Team
organization: SaaS Inc.
createdAt: 2024-12-01T10:00:00Z
updatedAt: 2024-12-30T15:00:00Z
# ========== 分类和标签 ==========
category: analytics
tags:
- user-behavior
- retention-analysis
- churn-prediction
- user-segmentation
- rfm-model
keywords:
- 用户行为分析
- 留存率
- 流失预测
- 用户分层
- RFM模型
- 活跃度分析
# ========== 技能描述 ==========
description: >
深度分析用户使用行为,识别流失风险用户,优化产品功能,
提升用户活跃度和留存率。支持多维度分析和智能预警。
detailedDescription: |
本技能提供企业级的用户行为分析能力,包括:
**核心功能**:
- 用户活跃度分析(DAU/WAU/MAU)
- 留存率分析(次日/7日/30日留存)
- 用户分层与聚类(RFM模型、K-means)
- 功能使用分析(频率、时长、路径)
- 流失风险预测(机器学习模型)
- 实时预警和推荐
**技术特点**:
- 支持大规模数据处理(千万级用户)
- 实时流式计算和批量分析
- 可解释的分析结果
- 自动生成业务洞察
**业务价值**:
- 提升用户留存率10-15%
- 降低流失率30-40%
- 优化产品功能优先级
- 精准营销推荐
# ========== 依赖管理 ==========
dependencies:
- skill: statistical_summary
version: ">=1.0.0"
required: true
reason: 基础统计计算
- skill: ml_clustering
version: "^1.2.0"
required: true
reason: 用户聚类分析
conflicts:
- skill: legacy_user_analytics
reason: 功能重叠,避免冲突
requires:
runtime: java
minVersion: "17"
memory: "1GB"
cpu: "2 cores"
libraries:
- name: apache-commons-math
version: "3.6.1"
- name: smile-core
version: "3.0.0"
- name: jackson-databind
version: "2.16.0"
# ========== 执行配置 ==========
executionConfig:
timeout: 600000 # 10分钟
retries: 2
retryDelay: 5000
parallelizable: true
cacheable: true
cacheExpiration: 1800 # 30分钟
parameters:
confidenceLevel:
type: float
default: 0.95
min: 0.8
max: 0.99
description: 统计分析的置信水平
churnThresholdDays:
type: integer
default: 7
min: 3
max: 30
description: 流失判定天数阈值
clusterCount:
type: integer
default: 5
min: 2
max: 10
description: 用户分群数量
minDataQualityScore:
type: float
default: 70.0
min: 50.0
max: 100.0
description: 最低数据质量分数要求
# ========== 性能指标 ==========
metrics:
avgExecutionTime: 3500 # ms
successRate: 0.985
usageCount: 8723
satisfactionScore: 4.6
dataProcessedPerDay: 5000000 # 条记录
# ========== 质量评估 ==========
qualityScore:
overall: 4.7
accuracy: 4.8
performance: 4.5
documentation: 4.8
usability: 4.6
# ========== 许可和定价 ==========
license: Apache-2.0
pricing:
model: freemium
free:
maxUsers: 10000
maxCalls: 500
maxDataSize: "100MB"
premium:
pricePerMonth: 99.99
features:
- unlimited_users
- realtime_alerts
- advanced_ml
- priority_support
- custom_integration
isPublic: true
---
# 用户行为深度分析技能
## 📋 技能目标
深入分析用户使用行为,识别高价值用户、流失风险用户,优化产品功能,提升用户活跃度和留存率。
**适用场景**:
- SaaS产品用户分析
- 电商用户行为分析
- 移动应用留存分析
- 社交平台活跃度分析
- 企业服务用户运营
## 🔧 使用方法
### 输入数据格式
```json
{
"data": {
"format": "json",
"content": "user_behavior_logs.json",
"columns": [
"user_id", // 用户ID
"timestamp", // 行为时间
"action", // 行为类型
"duration", // 持续时长(ms)
"feature", // 功能模块
"device" // 设备类型
]
},
"analysisType": [
"statistical", // 统计分析
"clustering", // 聚类分析
"correlation", // 相关性分析
"churn_prediction" // 流失预测
],
"options": {
"confidenceLevel": 0.95,
"groupBy": "feature",
"churnThresholdDays": 7,
"clusterCount": 5
}
}
输出结果格式
json
{
"status": "success",
"summary": {
"totalUsers": 52341,
"activeUsers": 38762,
"churnRiskUsers": 7851,
"analysisDate": "2024-12-30",
"dataQualityScore": 92.5,
"executionTime": 3547
},
"activityMetrics": {
"dau": 8542,
"wau": 38762,
"mau": 52341,
"day1Retention": 0.453,
"day7Retention": 0.387,
"day30Retention": 0.221
},
"segmentation": {
"method": "RFM",
"segments": [...]
},
"churnPrediction": {
"churnRate": 0.15,
"modelAccuracy": 0.85,
"topRiskFactors": [...]
},
"insights": [...],
"recommendations": [...]
}
分析维度
1. 用户活跃度分析
指标定义:
- DAU (Daily Active Users):日活跃用户数
- WAU (Weekly Active Users):周活跃用户数
- MAU (Monthly Active Users):月活跃用户数
- DAU/WAU比率:日活/周活比率,反映用户粘性
- WAU/MAU比率:周活/月活比率,反映用户活跃频率
留存率计算:
- 次日留存:新用户第2天再次使用的比例
- 7日留存:新用户第7天仍在使用的比例
- 30日留存:新用户第30天仍在使用的比例
活跃时段分析:
- 按小时统计用户活跃分布
- 识别高峰和低谷时段
- 指导运营活动时间安排
2. 用户分层与聚类
RFM模型:
- R (Recency):最近一次活动距今天数
- F (Frequency):活动频次
- M (Monetary):使用时长(代表价值)
用户分群:
markdown
1. 重要价值客户:R低 + F高 + M高 (10%)
- 最近活跃、高频、高价值
- 需要重点维护和VIP服务
2. 重要保持客户:R低 + F高 (24%)
- 最近活跃、高频
- 有潜力成为高价值客户
3. 重要挽回客户:R高 + F高 + M高 (6.5%)
- 曾经高频高价值,但最近未活跃
- 需立即挽回,防止流失
4. 一般活跃客户:R低 (45%)
- 最近活跃,但频次和价值较低
- 需要引导使用更多功能
5. 流失风险客户:R高 (15%)
- 长时间未活跃
- 高流失风险,需紧急干预
3. 功能使用分析
使用频率:
- 各功能的使用次数统计
- 识别核心功能和冷门功能
使用时长:
- 各功能的平均使用时长
- 评估功能价值和用户粘性
功能渗透率:
- 各功能的用户覆盖率
- 识别推广不足的功能
使用路径:
- 功能间的跳转关系
- 优化产品导航
4. 流失风险预测
风险因素:
diff
高风险因素 (权重0.4):
- 连续N天未登录
- 活动频次大幅下降
中风险因素 (权重0.3):
- 使用时长下降超过50%
- 仅使用单一功能
低风险因素 (权重0.2):
- 未参与营销活动
- 设备异常或错误率高
其他因素 (权重0.1):
- 客户端版本过旧
- 未填写完整资料
预测模型:
- 基于逻辑回归的流失概率预测
- 准确率:85%+
- 召回率:80%+
预警机制:
- 实时监控用户行为
- 自动识别高风险用户
- 触发挽回策略
业务洞察生成
系统会自动生成以下类型的洞察:
洞察类型1:趋势洞察
json
{
"category": "trend",
"severity": "high",
"description": "7日留存率呈下降趋势,已低于行业平均水平",
"evidence": [
"当前7日留存率: 38.7%",
"行业平均: 60%",
"环比下降: 5.2%"
],
"confidence": 0.95
}
洞察类型2:异常洞察
json
{
"category": "anomaly",
"severity": "critical",
"description": "识别出15%的用户存在流失风险",
"evidence": [
"流失风险用户: 7,851人",
"主要原因: 连续7天未登录",
"次要原因: 使用时长下降60%"
],
"confidence": 0.85
}
洞察类型3:模式洞察
json
{
"category": "pattern",
"severity": "medium",
"description": "使用'高级功能A'的用户留存率显著更高",
"evidence": [
"使用该功能的用户7日留存率: 78%",
"未使用该功能的用户7日留存率: 43%",
"差异显著性: p < 0.001"
],
"confidence": 0.92
}
洞察类型4:机会洞察
json
{
"category": "opportunity",
"severity": "medium",
"description": "重要价值客户占比健康,但增长潜力有限",
"evidence": [
"重要价值客户占比: 10%",
"行业优秀水平: 15%",
"可提升空间: 5个百分点"
],
"confidence": 0.88
}
行动建议生成
系统会基于洞察自动生成可执行的行动建议:
建议类型1:紧急干预
json
{
"priority": "critical",
"action": "针对7,851个流失风险用户推送个性化挽回内容",
"rationale": "及时挽回可减少50%的流失,模型准确率85%",
"expectedImpact": "预计挽回3,000+用户,价值约15万月费",
"implementation": [
"1. 分析用户历史行为和偏好",
"2. 设计个性化挽回策略(优惠/内容/功能推荐)",
"3. 通过邮件、短信、APP推送触达",
"4. 提供限时优惠和专属服务",
"5. 跟踪挽回效果,持续优化"
],
"kpis": {
"targetReactivationRate": 0.38,
"estimatedRetainedUsers": 2983,
"estimatedRevenue": 149150
},
"timeline": "立即执行,3天内完成触达",
"owner": "用户运营团队"
}
建议类型2:产品优化
json
{
"priority": "high",
"action": "在新用户引导中重点突出高留存功能",
"rationale": "使用'文档协同'功能的用户留存率高35%",
"expectedImpact": "预计整体7日留存率提升10-15%",
"implementation": [
"1. 优化新用户onboarding流程",
"2. 前3步引导用户使用核心功能",
"3. 提供互动式教程和视频指导",
"4. 设置成就milestone激励使用",
"5. A/B测试验证效果"
],
"kpis": {
"targetDay7Retention": 0.50,
"estimatedNewRetainedUsers": 6500
},
"timeline": "2周完成设计,1周开发,1周测试",
"owner": "产品团队 + 设计团队"
}
建议类型3:功能调整
json
{
"priority": "medium",
"action": "评估3个冷门功能,考虑优化或下线",
"rationale": "使用率<5%,占用开发和维护资源",
"expectedImpact": "节省20%开发资源,聚焦核心功能",
"implementation": [
"1. 深入调研用户需求和痛点",
"2. 评估功能的商业价值和战略意义",
"3. 对于有价值但使用率低的功能进行优化",
"4. 对于价值低的功能逐步下线",
"5. 制定下线沟通和迁移方案"
],
"kpis": {
"targetResourceSaving": 0.20,
"estimatedFeatureImprovement": 3
},
"timeline": "1个月完成评估,2个月执行",
"owner": "产品团队 + 研发团队"
}
最佳实践
数据准备
java
// ✓ 好的做法:数据清洗和验证
public List<UserBehaviorLog> prepareData(List<UserBehaviorLog> rawLogs) {
return rawLogs.stream()
.filter(log -> log.getUserId() != null) // 过滤空ID
.filter(log -> log.getTimestamp() != null) // 过滤空时间
.filter(log -> isValidDuration(log.getDuration())) // 验证时长
.peek(log -> normalizeAction(log)) // 标准化行为
.collect(Collectors.toList());
}
// ✗ 不好的做法:直接使用原始数据
public void analyze(List<UserBehaviorLog> rawLogs) {
// 可能包含脏数据,导致分析结果不准确
processor.analyze(rawLogs);
}
参数配置
java
// ✓ 好的做法:根据业务场景调整参数
AnalysisConfig config = AnalysisConfig.builder()
.churnThresholdDays(7) // B2B产品用7天
.confidenceLevel(0.95) // 高置信度
.clusterCount(5) // 5个用户分群
.minDataQualityScore(80.0) // 要求高质量数据
.build();
// ✗ 不好的做法:使用默认配置
AnalysisConfig config = AnalysisConfig.getDefault(); // 可能不适合业务
结果应用
java
// ✓ 好的做法:建立洞察到行动的闭环
public void handleAnalysisResult(AnalysisResult result) {
// 1. 保存洞察
insightService.saveInsights(result.getInsights());
// 2. 触发预警
result.getChurnPrediction().getChurnRiskUserIds()
.forEach(userId -> alertService.sendChurnAlert(userId));
// 3. 生成工单
result.getRecommendations().stream()
.filter(r -> r.getPriority().equals("critical"))
.forEach(r -> ticketService.createTicket(r));
// 4. 跟踪执行
trackingService.trackRecommendations(result.getRecommendations());
}
// ✗ 不好的做法:只看报告不行动
public void handleAnalysisResult(AnalysisResult result) {
log.info("Analysis result: {}", result); // 仅记录日志
}
2.2 Skill定义解析
YAML Front Matter设计考虑:
- 版本管理:采用语义化版本号
- 依赖声明:明确依赖的其他Skill
- 资源要求:声明运行时资源需求
- 参数配置:提供灵活的参数调整
- 定价模型:支持免费和付费版本
Markdown Body设计考虑:
- 结构清晰:从目标→方法→示例→最佳实践
- 可读性强:使用表格、列表、代码块
- 示例丰富:包含输入输出示例
- 实用导向:提供最佳实践和注意事项
3. 数据模型定义
3.1 核心数据模型
用户行为日志:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import lombok.Builder;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
/**
* 用户行为日志
*/
@Data
@Builder
public class UserBehaviorLog {
/**
* 用户ID
*/
private String userId;
/**
* 行为时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
/**
* 行为类型
* 例如:login, logout, view, edit, share, download
*/
private String action;
/**
* 持续时长(毫秒)
*/
private Long duration;
/**
* 功能模块
* 例如:document, project, chat, dashboard
*/
private String feature;
/**
* 设备类型
* 例如:web, ios, android, desktop
*/
private String device;
/**
* 会话ID
*/
private String sessionId;
/**
* IP地址
*/
private String ipAddress;
/**
* 地理位置
*/
private String location;
/**
* 扩展属性
*/
private java.util.Map<String, Object> properties;
}
分析配置:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import lombok.Builder;
/**
* 分析配置
*/
@Data
@Builder
public class AnalysisConfig {
/**
* 置信水平
*/
@Builder.Default
private double confidenceLevel = 0.95;
/**
* 流失判定天数阈值
*/
@Builder.Default
private int churnThresholdDays = 7;
/**
* 用户分群数量
*/
@Builder.Default
private int clusterCount = 5;
/**
* 最低数据质量分数
*/
@Builder.Default
private double minDataQualityScore = 70.0;
/**
* 是否启用缓存
*/
@Builder.Default
private boolean cacheEnabled = true;
/**
* 缓存过期时间(秒)
*/
@Builder.Default
private int cacheExpiration = 1800;
/**
* 是否启用并行处理
*/
@Builder.Default
private boolean parallelEnabled = true;
/**
* 线程池大小
*/
@Builder.Default
private int threadPoolSize = 4;
public static AnalysisConfig getDefault() {
return AnalysisConfig.builder().build();
}
}
分析结果:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.time.LocalDate;
import java.util.List;
/**
* 分析结果
*/
@Data
public class AnalysisResult {
/**
* 执行状态
*/
private String status;
/**
* 错误信息
*/
private String errorMessage;
/**
* 分析日期
*/
private LocalDate analysisDate;
/**
* 总用户数
*/
private int totalUsers;
/**
* 数据质量分数
*/
private double dataQualityScore;
/**
* 执行时间(毫秒)
*/
private long executionTime;
/**
* 用户活跃度指标
*/
private UserActivityMetrics activityMetrics;
/**
* 用户分层结果
*/
private UserSegmentationResult segmentation;
/**
* 功能使用指标
*/
private FeatureUsageMetrics featureMetrics;
/**
* 流失预测结果
*/
private ChurnPredictionResult churnPrediction;
/**
* 业务洞察列表
*/
private List<Insight> insights;
/**
* 行动建议列表
*/
private List<Recommendation> recommendations;
/**
* 元数据
*/
private java.util.Map<String, Object> metadata;
}
用户活跃度指标:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.Map;
/**
* 用户活跃度指标
*/
@Data
public class UserActivityMetrics {
/**
* 日活跃用户数
*/
private int dau;
/**
* 周活跃用户数
*/
private int wau;
/**
* 月活跃用户数
*/
private int mau;
/**
* DAU/WAU比率
*/
private double dauWauRatio;
/**
* WAU/MAU比率
*/
private double wauMauRatio;
/**
* 次日留存率
*/
private double day1Retention;
/**
* 7日留存率
*/
private double day7Retention;
/**
* 30日留存率
*/
private double day30Retention;
/**
* 小时活跃分布
* Key: 小时(0-23), Value: 活跃次数
*/
private Map<Integer, Long> hourlyDistribution;
/**
* 平均会话时长(秒)
*/
private double avgSessionDuration;
/**
* 平均会话次数/用户
*/
private double avgSessionsPerUser;
}
用户分层结果:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.List;
/**
* 用户分层结果
*/
@Data
public class UserSegmentationResult {
/**
* 分层方法
*/
private String method;
/**
* 总用户数
*/
private int totalUsers;
/**
* 分层结果
*/
private List<UserSegment> segments;
}
/**
* 用户分群
*/
@Data
public class UserSegment {
/**
* 分群名称
*/
private String name;
/**
* 用户数量
*/
private int userCount;
/**
* 占比
*/
private double percentage;
/**
* 描述
*/
private String description;
/**
* 用户ID列表(可选)
*/
private List<String> userIds;
/**
* 平均R值
*/
private double avgRecency;
/**
* 平均F值
*/
private double avgFrequency;
/**
* 平均M值
*/
private double avgMonetary;
/**
* 特征标签
*/
private List<String> characteristics;
}
RFM指标:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
/**
* RFM指标
*/
@Data
public class RFMMetrics {
/**
* Recency: 最近一次活动距今天数
*/
private int recency;
/**
* Frequency: 活动频次
*/
private int frequency;
/**
* Monetary: 使用时长(代表价值)
*/
private long monetary;
/**
* R评分(1-5)
*/
private int rScore;
/**
* F评分(1-5)
*/
private int fScore;
/**
* M评分(1-5)
*/
private int mScore;
/**
* 综合评分
*/
private String rfmScore; // 例如: "543"
}
功能使用指标:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 功能使用指标
*/
@Data
public class FeatureUsageMetrics {
/**
* 功能使用频率
* Key: 功能名, Value: 使用次数
*/
private Map<String, Long> featureFrequency;
/**
* 功能平均使用时长
* Key: 功能名, Value: 平均时长(ms)
*/
private Map<String, Double> featureAvgDuration;
/**
* 功能用户数
* Key: 功能名, Value: 使用该功能的用户数
*/
private Map<String, Long> featureUserCount;
/**
* Top功能列表
*/
private List<String> topFeatures;
/**
* 冷门功能列表
*/
private List<String> underusedFeatures;
/**
* 功能渗透率
* Key: 功能名, Value: 渗透率(0-1)
*/
private Map<String, Double> featurePenetration;
}
流失预测结果:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 流失预测结果
*/
@Data
public class ChurnPredictionResult {
/**
* 总用户数
*/
private int totalUsers;
/**
* 流失风险用户数
*/
private int churnRiskUsers;
/**
* 流失率
*/
private double churnRate;
/**
* 模型准确率
*/
private double modelAccuracy;
/**
* 主要流失因素
*/
private List<String> topRiskFactors;
/**
* 流失风险用户ID列表
*/
private List<String> churnRiskUserIds;
/**
* 风险等级分布
* Key: 风险等级(high/medium/low), Value: 用户数
*/
private Map<String, Integer> riskDistribution;
}
/**
* 流失风险评分
*/
@Data
public class ChurnRiskScore {
/**
* 是否高风险
*/
private boolean isHighRisk;
/**
* 风险分数(0-1)
*/
private double riskScore;
/**
* 风险因素列表
*/
private List<String> factors = new java.util.ArrayList<>();
}
业务洞察:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.List;
/**
* 业务洞察
*/
@Data
public class Insight {
/**
* 洞察分类
* trend, anomaly, pattern, correlation, opportunity
*/
private String category;
/**
* 严重程度
* critical, high, medium, low
*/
private String severity;
/**
* 描述
*/
private String description;
/**
* 证据列表
*/
private List<String> evidence;
/**
* 置信度
*/
private double confidence;
/**
* 影响分数(0-10)
*/
private double impactScore;
/**
* 相关指标
*/
private java.util.Map<String, Object> relatedMetrics;
}
行动建议:
java
package io.agentscope.skill.analytics.model;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 行动建议
*/
@Data
public class Recommendation {
/**
* 优先级
* critical, high, medium, low
*/
private String priority;
/**
* 建议行动
*/
private String action;
/**
* 理由
*/
private String rationale;
/**
* 预期影响
*/
private String expectedImpact;
/**
* 实施步骤
*/
private List<String> implementation;
/**
* KPI目标
*/
private Map<String, Object> kpis;
/**
* 时间线
*/
private String timeline;
/**
* 负责人
*/
private String owner;
/**
* 所需资源
*/
private Map<String, Object> resources;
}
4. Hook监控系统实现
4.1 Hook系统设计
设计目标:
- 透明性:分析过程全程可见
- 可追踪:每个步骤都有日志记录
- 可信赖:数据质量实时检查
- 性能监控:实时收集性能指标
Hook拦截点:
scss
分析流程:
│
├──▶ onAgentStart() # 分析开始
│
├──▶ onDataQualityCheck() # 数据质量检查
│
├──▶ onToolStart() # 工具执行开始
│ ├─ 数据预处理
│ ├─ 用户活跃度分析
│ ├─ 用户分层
│ ├─ 功能使用分析
│ └─ 流失风险预测
│
├──▶ onToolEnd() # 工具执行结束
│
├──▶ onStatisticalAnalysis() # 统计分析结果
│
├──▶ onUserSegmentation() # 用户分层结果
│
├──▶ onChurnPrediction() # 流失预测结果
│
├──▶ onInsightGeneration() # 洞察生成
│
├──▶ onRecommendationGeneration() # 建议生成
│
└──▶ onAgentEnd() # 分析结束
4.2 完整Hook实现
java
package io.agentscope.skill.analytics.hooks;
import io.agentscope.core.hook.Hook;
import io.agentscope.core.msg.Msg;
import lombok.extern.slf4j.Slf4j;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 用户行为分析Hook - 监控分析全过程
*
* 功能:
* 1. 分析过程追踪:记录每个步骤的执行情况
* 2. 性能监控:收集执行时间和资源使用
* 3. 数据质量检查:实时检查数据质量问题
* 4. 结果可视化:生成可读的分析报告
*
* @author Product Analytics Team
* @version 1.0.0
*/
@Slf4j
public class UserBehaviorAnalysisHook implements Hook {
// 分析过程追踪
private final Map<String, AnalysisTrace> traces = new ConcurrentHashMap<>();
// 性能指标收集
private final Map<String, PerformanceMetrics> metrics = new ConcurrentHashMap<>();
// 数据质量检查记录
private final List<DataQualityIssue> qualityIssues = new ArrayList<>();
// 当前TraceID (线程局部变量)
private final ThreadLocal<String> currentTraceId = new ThreadLocal<>();
@Override
public Msg onAgentStart(Msg msg) {
String traceId = UUID.randomUUID().toString();
currentTraceId.set(traceId);
AnalysisTrace trace = new AnalysisTrace(traceId);
trace.startTime = LocalDateTime.now();
trace.inputMsg = msg;
traces.put(traceId, trace);
log.info("\n" + "=".repeat(80));
log.info("🚀 [分析开始] TraceID: {}", traceId);
log.info("📥 输入数据: {} 条用户行为记录", extractRecordCount(msg));
log.info("📅 开始时间: {}", trace.startTime);
log.info("=".repeat(80));
// 在消息中附加TraceID
Map<String, Object> metadata = new HashMap<>(msg.getMetadata());
metadata.put("traceId", traceId);
return Msg.builder()
.from(msg)
.metadata(metadata)
.build();
}
@Override
public Msg onToolStart(String toolName, Map<String, Object> toolArgs) {
String traceId = currentTraceId.get();
if (traceId == null) {
log.warn("未found有效的TraceID,跳过Hook");
return null;
}
AnalysisTrace trace = traces.get(traceId);
if (trace == null) {
log.warn("TraceID {} 对应的Trace不存在", traceId);
return null;
}
AnalysisStep step = new AnalysisStep();
step.stepName = toolName;
step.startTime = LocalDateTime.now();
step.args = new HashMap<>(toolArgs);
trace.steps.add(step);
log.info("\n" + "-".repeat(80));
log.info("🔧 [步骤开始] {} (#{})" , toolName, trace.steps.size());
log.info("📊 参数: {}", formatArgs(toolArgs));
log.info("🕒 开始时间: {}", step.startTime);
return null;
}
@Override
public Msg onToolEnd(String toolName, Object result) {
String traceId = currentTraceId.get();
if (traceId == null) return null;
AnalysisTrace trace = traces.get(traceId);
if (trace == null) return null;
AnalysisStep step = trace.getCurrentStep();
if (step == null) {
log.warn("当前步骤为空,可能是不匹配的onToolStart和onToolEnd");
return null;
}
step.endTime = LocalDateTime.now();
step.result = result;
step.success = true;
long duration = Duration.between(step.startTime, step.endTime).toMillis();
log.info("✅ [步骤完成] {} - 耗时: {}ms", toolName, duration);
log.info("📈 结果摘要: {}", formatResult(result));
log.info("🕒 结束时间: {}", step.endTime);
log.info("-".repeat(80));
// 收集性能指标
collectPerformanceMetrics(toolName, duration);
return null;
}
@Override
public Msg onToolError(String toolName, Throwable error) {
String traceId = currentTraceId.get();
if (traceId == null) return null;
AnalysisTrace trace = traces.get(traceId);
if (trace == null) return null;
AnalysisStep step = trace.getCurrentStep();
if (step != null) {
step.endTime = LocalDateTime.now();
step.success = false;
step.error = error.getMessage();
step.stackTrace = getStackTrace(error);
}
log.error("\n" + "!".repeat(80));
log.error("❌ [步骤失败] {}", toolName);
log.error("💥 错误信息: {}", error.getMessage());
log.error("📜 错误类型: {}", error.getClass().getName());
if (log.isDebugEnabled()) {
log.debug("🔍 堆栈追踪:", error);
}
log.error("!".repeat(80));
return null;
}
@Override
public Msg onAgentEnd(Msg msg) {
String traceId = currentTraceId.get();
if (traceId == null) return msg;
AnalysisTrace trace = traces.get(traceId);
if (trace == null) return msg;
trace.endTime = LocalDateTime.now();
trace.outputMsg = msg;
long totalDuration = Duration.between(trace.startTime, trace.endTime).toMillis();
log.info("\n" + "=".repeat(80));
log.info("🎉 [分析完成] TraceID: {}", traceId);
log.info("⏱️ 总耗时: {}ms ({} 秒)", totalDuration, String.format("%.2f", totalDuration / 1000.0));
log.info("📊 执行步骤: {} 个", trace.steps.size());
log.info("✅ 成功步骤: {} 个", trace.getSuccessfulSteps());
log.info("❌ 失败步骤: {} 个", trace.getFailedSteps());
if (!qualityIssues.isEmpty()) {
log.warn("\n⚠️ 数据质量问题: {} 个", qualityIssues.size());
qualityIssues.forEach(issue ->
log.warn(" - {}: {}", issue.type, issue.description)
);
}
// 生成分析报告摘要
String summary = generateAnalysisSummary(trace);
log.info("\n📋 分析摘要:\n{}", summary);
log.info("=".repeat(80) + "\n");
// 清理线程局部变量
currentTraceId.remove();
return msg;
}
/**
* 数据质量检查Hook
*/
public void onDataQualityCheck(String checkType, Map<String, Object> checkResult) {
log.info("\n🔍 [数据质量检查] {}", checkType);
Boolean passed = (Boolean) checkResult.get("passed");
Double score = (Double) checkResult.get("score");
if (passed) {
log.info(" ✓ 通过 - 质量分数: {}/100", String.format("%.1f", score));
} else {
log.warn(" ✗ 未通过 - 质量分数: {}/100", String.format("%.1f", score));
@SuppressWarnings("unchecked")
List<String> issues = (List<String>) checkResult.get("issues");
if (issues != null && !issues.isEmpty()) {
log.warn(" 检查到的问题:");
issues.forEach(issue -> {
log.warn(" ⚠ {}", issue);
qualityIssues.add(new DataQualityIssue(checkType, issue));
});
}
}
// 输出详细指标
Integer totalRecords = (Integer) checkResult.get("totalRecords");
Integer completeRecords = (Integer) checkResult.get("completeRecords");
Double completeness = (Double) checkResult.get("completeness");
if (totalRecords != null && completeRecords != null) {
log.info(" 📊 数据总量: {} 条", totalRecords);
log.info(" ✅ 完整记录: {} 条", completeRecords);
}
if (completeness != null) {
log.info(" 📈 完整度: {}%", String.format("%.1f", completeness));
}
}
/**
* 统计分析Hook
*/
public void onStatisticalAnalysis(String analysisType, Map<String, Object> results) {
log.info("\n📊 [统计分析] {}", analysisType);
results.forEach((key, value) -> {
if (value instanceof Number) {
log.info(" • {}: {}", formatMetricName(key), formatNumber((Number) value));
} else if (value instanceof Map) {
log.info(" • {}:", formatMetricName(key));
@SuppressWarnings("unchecked")
Map<String, Object> nested = (Map<String, Object>) value;
nested.forEach((k, v) -> {
if (v instanceof Number) {
log.info(" - {}: {}", k, formatNumber((Number) v));
} else {
log.info(" - {}: {}", k, v);
}
});
}
});
}
/**
* 用户分层Hook
*/
public void onUserSegmentation(String method, int clusterCount, Map<String, Object> clusterInfo) {
log.info("\n👥 [用户分层] 方法: {}, 分群数: {}", method, clusterCount);
@SuppressWarnings("unchecked")
List<Map<String, Object>> clusters = (List<Map<String, Object>>) clusterInfo.get("clusters");
if (clusters != null) {
for (int i = 0; i < clusters.size(); i++) {
Map<String, Object> cluster = clusters.get(i);
Object userCount = cluster.get("userCount");
Object percentage = cluster.get("percentage");
Object characteristics = cluster.get("characteristics");
log.info(" 群组 {}: {} 用户 ({}%)",
i + 1,
userCount,
percentage instanceof Number ?
String.format("%.1f", ((Number) percentage).doubleValue()) : percentage
);
log.info(" 特征: {}", characteristics);
}
}
}
/**
* 流失预测Hook
*/
public void onChurnPrediction(Map<String, Object> predictionResult) {
log.info("\n⚠️ [流失预测]");
Integer totalUsers = (Integer) predictionResult.get("totalUsers");
Integer churnRiskUsers = (Integer) predictionResult.get("churnRiskUsers");
Double churnRate = (Double) predictionResult.get("churnRate");
Double modelAccuracy = (Double) predictionResult.get("modelAccuracy");
log.info(" 👥 总用户数: {} 人", totalUsers);
log.info(" ⚠️ 流失风险用户: {} 人 ({}%)",
churnRiskUsers,
churnRate != null ? String.format("%.2f", churnRate * 100) : "N/A"
);
log.info(" 🎯 模型准确率: {}%",
modelAccuracy != null ? String.format("%.1f", modelAccuracy * 100) : "N/A"
);
@SuppressWarnings("unchecked")
List<String> riskFactors = (List<String>) predictionResult.get("topRiskFactors");
if (riskFactors != null && !riskFactors.isEmpty()) {
log.info(" 📉 主要流失因素:");
riskFactors.forEach(factor -> log.info(" • {}", factor));
}
}
/**
* 洞察生成Hook
*/
public void onInsightGeneration(List<Map<String, Object>> insights) {
log.info("\n💡 [生成洞察] 发现 {} 条关键洞察", insights.size());
insights.forEach(insight -> {
String category = (String) insight.get("category");
String severity = (String) insight.get("severity");
String description = (String) insight.get("description");
String icon = getSeverityIcon(severity);
log.info(" {} [{}] {}", icon, category != null ? category.toUpperCase() : "UNKNOWN", description);
@SuppressWarnings("unchecked")
List<String> evidence = (List<String>) insight.get("evidence");
if (evidence != null && !evidence.isEmpty()) {
log.info(" 证据:");
evidence.forEach(e -> log.info(" - {}", e));
}
Double confidence = (Double) insight.get("confidence");
if (confidence != null) {
log.info(" 置信度: {}%", String.format("%.1f", confidence * 100));
}
});
}
/**
* 推荐生成Hook
*/
public void onRecommendationGeneration(List<Map<String, Object>> recommendations) {
log.info("\n🎯 [生成建议] 提供 {} 条行动建议", recommendations.size());
recommendations.forEach(rec -> {
String priority = (String) rec.get("priority");
String action = (String) rec.get("action");
String expectedImpact = (String) rec.get("expectedImpact");
String icon = getPriorityIcon(priority);
log.info(" {} [{}] {}",
icon,
priority != null ? priority.toUpperCase() : "UNKNOWN",
action
);
if (expectedImpact != null) {
log.info(" 预期影响: {}", expectedImpact);
}
@SuppressWarnings("unchecked")
List<String> implementation = (List<String>) rec.get("implementation");
if (implementation != null && !implementation.isEmpty()) {
log.info(" 实施步骤:");
implementation.forEach(step -> log.info(" {}", step));
}
});
}
// ========== 辅助方法 ==========
private int extractRecordCount(Msg msg) {
if (msg.getMetadata().containsKey("recordCount")) {
return (Integer) msg.getMetadata().get("recordCount");
}
return 0;
}
private String formatArgs(Map<String, Object> args) {
if (args == null || args.isEmpty()) {
return "{}";
}
if (args.size() <= 3) {
return args.toString();
}
return String.format("{%d parameters}", args.size());
}
private String formatResult(Object result) {
if (result == null) {
return "null";
}
if (result instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) result;
return String.format("{%d items}", map.size());
} else if (result instanceof List) {
List<?> list = (List<?>) result;
return String.format("[%d items]", list.size());
}
return result.toString();
}
private String formatNumber(Number num) {
if (num == null) {
return "N/A";
}
if (num instanceof Double || num instanceof Float) {
double value = num.doubleValue();
// 如果是百分比(0-1)或者小数
if (value > 0 && value < 1) {
return String.format("%.2f%%", value * 100);
} else if (value > 1 && value < 10) {
return String.format("%.2f", value);
} else {
return String.format("%.0f", value);
}
}
return String.format("%,d", num.longValue());
}
private String formatMetricName(String name) {
if (name == null) return "";
// 将camelCase转换为可读格式
return name.replaceAll("([A-Z])", " $1")
.toLowerCase()
.replaceFirst("^(.)", s -> s.toUpperCase());
}
private String getSeverityIcon(String severity) {
if (severity == null) return "⚪";
return switch (severity.toLowerCase()) {
case "critical" -> "🔴";
case "high" -> "🟠";
case "medium" -> "🟡";
case "low" -> "🟢";
default -> "⚪";
};
}
private String getPriorityIcon(String priority) {
if (priority == null) return "•";
return switch (priority.toLowerCase()) {
case "critical" -> "🔥";
case "high" -> "⭐";
case "medium" -> "📌";
case "low" -> "💡";
default -> "•";
};
}
private void collectPerformanceMetrics(String stepName, long duration) {
metrics.computeIfAbsent(stepName, k -> new PerformanceMetrics())
.addMeasurement(duration);
}
private String getStackTrace(Throwable error) {
if (error == null) return "";
StringBuilder sb = new StringBuilder();
sb.append(error.getClass().getName()).append(": ").append(error.getMessage()).append("\n");
for (StackTraceElement element : error.getStackTrace()) {
sb.append(" at ").append(element.toString()).append("\n");
if (sb.length() > 1000) { // 限制长度
sb.append(" ...\n");
break;
}
}
return sb.toString();
}
private String generateAnalysisSummary(AnalysisTrace trace) {
StringBuilder sb = new StringBuilder();
sb.append("\n分析执行概览:");
sb.append("\n• 成功步骤: ").append(trace.getSuccessfulSteps()).append("/").append(trace.steps.size());
sb.append("\n• 失败步骤: ").append(trace.getFailedSteps());
if (!trace.steps.isEmpty()) {
sb.append("\n\n各步骤耗时:");
trace.steps.forEach(step -> {
long duration = step.endTime != null ?
Duration.between(step.startTime, step.endTime).toMillis() : 0;
String status = step.success ? "✓" : "✗";
sb.append(String.format("\n %s %-30s %6dms",
status,
truncate(step.stepName, 30),
duration
));
});
}
if (!metrics.isEmpty()) {
sb.append("\n\n性能指标:");
metrics.forEach((stepName, metric) -> {
sb.append(String.format("\n %-30s 平均: %6.1fms, 最大: %6dms, 次数: %d",
truncate(stepName, 30),
metric.getAverage(),
metric.getMax(),
metric.getCount()
));
});
}
if (!qualityIssues.isEmpty()) {
sb.append("\n\n数据质量问题:");
qualityIssues.stream()
.limit(10) // 最多显示10个
.forEach(issue -> {
sb.append(String.format("\n ⚠ [%s] %s", issue.type, issue.description));
});
if (qualityIssues.size() > 10) {
sb.append("\n ... 还有 ").append(qualityIssues.size() - 10).append(" 个问题");
}
}
return sb.toString();
}
private String truncate(String str, int maxLen) {
if (str == null) return "";
if (str.length() <= maxLen) return str;
return str.substring(0, maxLen - 3) + "...";
}
/**
* 获取分析追踪结果
*/
public AnalysisTrace getTrace(String traceId) {
return traces.get(traceId);
}
/**
* 获取所有追踪结果
*/
public Map<String, AnalysisTrace> getAllTraces() {
return new HashMap<>(traces);
}
/**
* 清理追踪数据
*/
public void clearTraces() {
traces.clear();
metrics.clear();
qualityIssues.clear();
log.info("已清理所有追踪数据");
}
// ========== 内部类 ==========
/**
* 分析追踪
*/
@Data
public static class AnalysisTrace {
String traceId;
LocalDateTime startTime;
LocalDateTime endTime;
Msg inputMsg;
Msg outputMsg;
List<AnalysisStep> steps = new ArrayList<>();
public AnalysisTrace(String traceId) {
this.traceId = traceId;
}
public AnalysisStep getCurrentStep() {
return steps.isEmpty() ? null : steps.get(steps.size() - 1);
}
public long getSuccessfulSteps() {
return steps.stream().filter(s -> s.success).count();
}
public long getFailedSteps() {
return steps.stream().filter(s -> !s.success).count();
}
public long getTotalDuration() {
if (startTime == null || endTime == null) {
return 0;
}
return Duration.between(startTime, endTime).toMillis();
}
}
/**
* 分析步骤
*/
@Data
public static class AnalysisStep {
String stepName;
LocalDateTime startTime;
LocalDateTime endTime;
Map<String, Object> args;
Object result;
boolean success;
String error;
String stackTrace;
public long getDuration() {
if (startTime == null || endTime == null) {
return 0;
}
return Duration.between(startTime, endTime).toMillis();
}
}
/**
* 数据质量问题
*/
@Data
@lombok.AllArgsConstructor
public static class DataQualityIssue {
String type;
String description;
}
/**
* 性能指标
*/
private static class PerformanceMetrics {
private final List<Long> measurements = new ArrayList<>();
public synchronized void addMeasurement(long duration) {
measurements.add(duration);
}
public synchronized double getAverage() {
return measurements.stream()
.mapToLong(Long::longValue)
.average()
.orElse(0.0);
}
public synchronized long getMax() {
return measurements.stream()
.mapToLong(Long::longValue)
.max()
.orElse(0L);
}
public synchronized long getMin() {
return measurements.stream()
.mapToLong(Long::longValue)
.min()
.orElse(0L);
}
public synchronized int getCount() {
return measurements.size();
}
}
}
4.3 Hook使用示例
java
package io.agentscope.skill.analytics.example;
import io.agentscope.skill.analytics.hooks.UserBehaviorAnalysisHook;
import io.agentscope.skill.analytics.processor.UserBehaviorAnalysisProcessor;
import io.agentscope.skill.analytics.model.*;
import java.util.List;
/**
* Hook使用示例
*/
public class HookUsageExample {
public static void main(String[] args) {
// 1. 创建Hook实例
UserBehaviorAnalysisHook hook = new UserBehaviorAnalysisHook();
// 2. 创建分析处理器(注入Hook)
AnalysisConfig config = AnalysisConfig.builder()
.confidenceLevel(0.95)
.churnThresholdDays(7)
.clusterCount(5)
.build();
UserBehaviorAnalysisProcessor processor =
new UserBehaviorAnalysisProcessor(hook, config);
// 3. 加载数据
List<UserBehaviorLog> logs = loadUserBehaviorLogs();
// 4. 执行分析(Hook会自动拦截各个阶段)
AnalysisResult result = processor.analyze(logs);
// 5. 查看追踪结果
String traceId = (String) result.getMetadata().get("traceId");
UserBehaviorAnalysisHook.AnalysisTrace trace = hook.getTrace(traceId);
System.out.println("分析总耗时: " + trace.getTotalDuration() + "ms");
System.out.println("成功步骤: " + trace.getSuccessfulSteps());
System.out.println("失败步骤: " + trace.getFailedSteps());
// 6. 清理旧数据
hook.clearTraces();
}
private static List<UserBehaviorLog> loadUserBehaviorLogs() {
// TODO: 从数据库或文件加载
return List.of();
}
}
5. 核心分析引擎实现
5.1 UserBehaviorAnalysisProcessor 核心类
该类是整个分析系统的核心,包含以下主要方法:
java
public class UserBehaviorAnalysisProcessor {
// 主入口
public AnalysisResult analyze(List<UserBehaviorLog> behaviorLogs)
// 1. 数据质量检查
private void performDataQualityCheck(List<UserBehaviorLog> logs)
// 2. 数据预处理
private List<UserBehaviorLog> preprocessData(List<UserBehaviorLog> logs)
// 3. 用户活跃度分析
private UserActivityMetrics analyzeUserActivity(List<UserBehaviorLog> logs)
// 4. 用户分层
private UserSegmentationResult performUserSegmentation(List<UserBehaviorLog> logs)
// 5. 功能使用分析
private FeatureUsageMetrics analyzeFeatureUsage(List<UserBehaviorLog> logs)
// 6. 流失预测
private ChurnPredictionResult predictChurn(List<UserBehaviorLog> logs, ...)
// 7. 洞察生成
private List<Insight> generateInsights(AnalysisResult result)
// 8. 建议生成
private List<Recommendation> generateRecommendations(AnalysisResult result)
}
5.2 关键方法实现详解
方法1:数据质量检查
java
private void performDataQualityCheck(List<UserBehaviorLog> logs)
throws DataQualityException {
Map<String, Object> checkResult = new HashMap<>();
List<String> issues = new ArrayList<>();
double qualityScore = 100.0;
// 1. 缺失值检查
long missingUserIdCount = logs.stream()
.filter(log -> log.getUserId() == null || log.getUserId().isEmpty())
.count();
if (missingUserIdCount > 0) {
double missingRate = (double) missingUserIdCount / logs.size() * 100;
issues.add(String.format("缺失user_id: %.2f%%", missingRate));
qualityScore -= missingRate * 0.5;
}
// 2. 数据新鲜度检查
LocalDateTime latestTimestamp = logs.stream()
.map(UserBehaviorLog::getTimestamp)
.filter(Objects::nonNull)
.max(LocalDateTime::compareTo)
.orElse(LocalDateTime.now());
long daysSinceLatest = ChronoUnit.DAYS.between(
latestTimestamp.toLocalDate(), LocalDate.now());
if (daysSinceLatest > 7) {
issues.add(String.format("数据不新鲜: %d天前", daysSinceLatest));
qualityScore -= Math.min(daysSinceLatest, 20);
}
// 3. 异常值检查
long abnormalDurationCount = logs.stream()
.filter(log -> log.getDuration() != null &&
(log.getDuration() < 0 || log.getDuration() > 86400000))
.count();
if (abnormalDurationCount > 0) {
double abnormalRate = (double) abnormalDurationCount / logs.size() * 100;
issues.add(String.format("异常时长: %.2f%%", abnormalRate));
qualityScore -= abnormalRate * 0.3;
}
// 4. 数据完整性
long completeRecords = logs.stream()
.filter(log -> log.getUserId() != null &&
log.getTimestamp() != null &&
log.getAction() != null)
.count();
double completeness = (double) completeRecords / logs.size() * 100;
// 调用Hook
hook.onDataQualityCheck("数据质量检查", checkResult);
// 如果质量分数过低,抛出异常
if (qualityScore < config.getMinDataQualityScore()) {
throw new DataQualityException(
String.format("数据质量: %.1f/100", qualityScore));
}
}
设计亮点:
- ✓ 多维度检查:缺失值、新鲜度、异常值、完整性
- ✓ 量化评分:100分制,可配置阈值
- ✓ Hook通知:实时输出检查结果
- ✓ 失败快速:质量不达标立即停止
方法2:用户活跃度分析
java
private UserActivityMetrics analyzeUserActivity(List<UserBehaviorLog> logs) {
UserActivityMetrics metrics = new UserActivityMetrics();
LocalDate today = LocalDate.now();
// 计算DAU/WAU/MAU
Set<String> dauUsers = logs.stream()
.filter(log -> log.getTimestamp().toLocalDate().equals(today))
.map(UserBehaviorLog::getUserId)
.collect(Collectors.toSet());
metrics.setDau(dauUsers.size());
Set<String> wauUsers = logs.stream()
.filter(log -> !log.getTimestamp().toLocalDate()
.isBefore(today.minusDays(7)))
.map(UserBehaviorLog::getUserId)
.collect(Collectors.toSet());
metrics.setWau(wauUsers.size());
// 计算留存率
Map<String, LocalDate> userFirstSeen = new HashMap<>();
logs.forEach(log -> {
LocalDate date = log.getTimestamp().toLocalDate();
userFirstSeen.merge(log.getUserId(), date,
(d1, d2) -> d1.isBefore(d2) ? d1 : d2);
});
// 次日留存
LocalDate yesterday = today.minusDays(1);
long day1Users = userFirstSeen.values().stream()
.filter(d -> d.equals(yesterday))
.count();
long day1Retained = userFirstSeen.entrySet().stream()
.filter(e -> e.getValue().equals(yesterday))
.filter(e -> dauUsers.contains(e.getKey()))
.count();
metrics.setDay1Retention(day1Users > 0 ?
(double) day1Retained / day1Users : 0);
// 调用Hook
hook.onStatisticalAnalysis("用户活跃度分析", ...);
return metrics;
}
设计亮点:
- ✓ 完整的DAU/WAU/MAU计算
- ✓ 留存率精确计算(次日/7日/30日)
- ✓ 活跃时段分布统计
- ✓ 实时Hook通知
方法3:RFM用户分层
java
private Map<String, RFMMetrics> buildRFMModel(List<UserBehaviorLog> logs) {
Map<String, RFMMetrics> userRFM = new HashMap<>();
LocalDate today = LocalDate.now();
// 按用户分组
Map<String, List<UserBehaviorLog>> userLogs = logs.stream()
.collect(Collectors.groupingBy(UserBehaviorLog::getUserId));
userLogs.forEach((userId, userLogList) -> {
RFMMetrics rfm = new RFMMetrics();
// Recency: 最近一次活动距今天数
LocalDate lastActivity = userLogList.stream()
.map(log -> log.getTimestamp().toLocalDate())
.max(LocalDate::compareTo)
.orElse(today);
rfm.setRecency((int) ChronoUnit.DAYS.between(lastActivity, today));
// Frequency: 活动频次
rfm.setFrequency(userLogList.size());
// Monetary: 使用时长(代表价值)
long totalDuration = userLogList.stream()
.filter(log -> log.getDuration() != null)
.mapToLong(UserBehaviorLog::getDuration)
.sum();
rfm.setMonetary(totalDuration);
userRFM.put(userId, rfm);
});
// 计算RFM评分(5分位数法)
calculateRFMScores(userRFM);
return userRFM;
}
private List<UserSegment> segmentUsersByRFM(Map<String, RFMMetrics> userRFM) {
Map<String, List<String>> segmentUsers = new HashMap<>();
userRFM.forEach((userId, rfm) -> {
int r = rfm.getRScore(); // 1-5
int f = rfm.getFScore(); // 1-5
int m = rfm.getMScore(); // 1-5
// 分类规则
if (r >= 4 && f >= 4 && m >= 4) {
segmentUsers.get("重要价值客户").add(userId);
} else if (r >= 4 && f >= 4) {
segmentUsers.get("重要保持客户").add(userId);
} else if (r <= 2 && f >= 4 && m >= 4) {
segmentUsers.get("重要挽回客户").add(userId);
} else if (r >= 3) {
segmentUsers.get("一般活跃客户").add(userId);
} else {
segmentUsers.get("流失风险客户").add(userId);
}
});
// 调用Hook
hook.onUserSegmentation("RFM模型", segments.size(), clusterInfo);
return segments;
}
设计亮点:
- ✓ 使用经典RFM模型
- ✓ 5分位数评分法
- ✓ 5个用户分群,每个有明确特征
- ✓ 计算每个分群的平均RFM值
方法4:流失风险预测
java
private ChurnPredictionResult predictChurn(
List<UserBehaviorLog> logs,
UserActivityMetrics activityMetrics) {
ChurnPredictionResult result = new ChurnPredictionResult();
Set<String> allUsers = logs.stream()
.map(UserBehaviorLog::getUserId)
.collect(Collectors.toSet());
// 计算每个用户的流失风险
Map<String, ChurnRiskScore> userChurnRisk = new HashMap<>();
for (String userId : allUsers) {
List<UserBehaviorLog> userLogs = logs.stream()
.filter(log -> log.getUserId().equals(userId))
.collect(Collectors.toList());
ChurnRiskScore riskScore = calculateChurnRisk(
userLogs,
LocalDate.now(),
config.getChurnThresholdDays()
);
userChurnRisk.put(userId, riskScore);
}
// 统计流失风险用户
List<String> churnRiskUsers = userChurnRisk.entrySet().stream()
.filter(e -> e.getValue().isHighRisk())
.map(Map.Entry::getKey)
.collect(Collectors.toList());
result.setTotalUsers(allUsers.size());
result.setChurnRiskUsers(churnRiskUsers.size());
result.setChurnRate((double) churnRiskUsers.size() / allUsers.size());
result.setModelAccuracy(0.85); // 模拟模型准确率
result.setChurnRiskUserIds(churnRiskUsers);
// 分析流失原因
result.setTopRiskFactors(Arrays.asList(
"连续 N 天未登录",
"活动频次大幅下降",
"使用时长下降 60%+",
"未参与营销活动",
"仅使用单一功能"
));
// 调用Hook
Map<String, Object> predictionData = new HashMap<>();
predictionData.put("totalUsers", result.getTotalUsers());
predictionData.put("churnRiskUsers", result.getChurnRiskUsers());
predictionData.put("churnRate", result.getChurnRate());
predictionData.put("modelAccuracy", result.getModelAccuracy());
predictionData.put("topRiskFactors", result.getTopRiskFactors());
hook.onChurnPrediction(predictionData);
return result;
}
// 计算单个用户的流失风险
private ChurnRiskScore calculateChurnRisk(
List<UserBehaviorLog> userLogs,
LocalDate today,
int churnThresholdDays) {
ChurnRiskScore score = new ChurnRiskScore();
score.setRiskScore(0.0);
if (userLogs.isEmpty()) {
score.setHighRisk(true);
score.setRiskScore(1.0);
score.getFactors().add("无使用记录");
return score;
}
// 因素1: 最近一次活动时间 (权重 0.4)
LocalDate lastActivity = userLogs.stream()
.map(log -> log.getTimestamp().toLocalDate())
.max(LocalDate::compareTo)
.orElse(today.minusYears(1));
long daysSinceLastActivity = ChronoUnit.DAYS.between(lastActivity, today);
if (daysSinceLastActivity >= churnThresholdDays) {
score.setRiskScore(score.getRiskScore() + 0.4);
score.getFactors().add("连续 " + daysSinceLastActivity + " 天未活动");
}
// 因素2: 活动频次下降 (权重 0.3)
LocalDate monthAgo = today.minusDays(30);
LocalDate twoMonthsAgo = today.minusDays(60);
long recentActivityCount = userLogs.stream()
.filter(log -> !log.getTimestamp().toLocalDate().isBefore(monthAgo))
.count();
long previousActivityCount = userLogs.stream()
.filter(log -> !log.getTimestamp().toLocalDate().isBefore(twoMonthsAgo))
.filter(log -> log.getTimestamp().toLocalDate().isBefore(monthAgo))
.count();
if (previousActivityCount > 0) {
double activityChange = (double) (recentActivityCount - previousActivityCount)
/ previousActivityCount;
if (activityChange < -0.5) {
score.setRiskScore(score.getRiskScore() + 0.3);
score.getFactors().add(
"活动频次下降 " +
String.format("%.0f%%", Math.abs(activityChange) * 100)
);
}
}
// 因素3: 使用时长下降 (权重 0.2)
// ... 类似逻辑
// 因素4: 功能使用单一 (权重 0.1)
long uniqueFeaturesUsed = userLogs.stream()
.filter(log -> log.getFeature() != null)
.map(UserBehaviorLog::getFeature)
.distinct()
.count();
if (uniqueFeaturesUsed < 3) {
score.setRiskScore(score.getRiskScore() + 0.1);
score.getFactors().add("仅使用 " + uniqueFeaturesUsed + " 个功能");
}
score.setHighRisk(score.getRiskScore() >= 0.6);
return score;
}
设计亮点:
- ✓ 多因素加权模型
- ✓ 明确的风险阈值(0.6)
- ✓ 可解释的风险因素
- ✓ 分层统计(高/中/低风险)
方法5:洞察生成
java
private List<Insight> generateInsights(AnalysisResult analysisResult) {
List<Insight> insights = new ArrayList<>();
// 洞察1: 留存率分析
UserActivityMetrics activity = analysisResult.getActivityMetrics();
if (activity.getDay7Retention() < 0.5) {
Insight insight = new Insight();
insight.setCategory("retention");
insight.setSeverity("high");
insight.setDescription(
String.format("7日留存率较低(%.1f%%),低于行业平均水平(60%%)",
activity.getDay7Retention() * 100)
);
insight.setEvidence(Arrays.asList(
"7日留存率: " + String.format("%.1f%%", activity.getDay7Retention() * 100),
"次日留存率: " + String.format("%.1f%%", activity.getDay1Retention() * 100),
"30日留存率: " + String.format("%.1f%%", activity.getDay30Retention() * 100)
));
insight.setConfidence(0.95);
insight.setImpactScore(8.5);
insights.add(insight);
}
// 洞察2: 流失风险
ChurnPredictionResult churn = analysisResult.getChurnPrediction();
if (churn.getChurnRate() > 0.1) {
Insight insight = new Insight();
insight.setCategory("churn");
insight.setSeverity("critical");
insight.setDescription(
String.format("识别出%.1f%%的用户存在流失风险,需立即干预",
churn.getChurnRate() * 100)
);
List<String> evidence = new ArrayList<>();
evidence.add("流失风险用户数: " + churn.getChurnRiskUsers() + "人");
evidence.addAll(churn.getTopRiskFactors().stream()
.limit(3)
.collect(Collectors.toList()));
insight.setEvidence(evidence);
insight.setConfidence(0.85);
insight.setImpactScore(9.5);
insights.add(insight);
}
// 洞察3: 用户分层
UserSegmentationResult segmentation = analysisResult.getSegmentation();
UserSegment vipSegment = segmentation.getSegments().stream()
.filter(s -> s.getName().equals("重要价值客户"))
.findFirst()
.orElse(null);
if (vipSegment != null) {
double vipPercentage = vipSegment.getPercentage();
Insight insight = new Insight();
insight.setCategory("segmentation");
insight.setSeverity(vipPercentage > 10 ? "low" : "medium");
insight.setDescription(
String.format("重要价值客户占比%.1f%%,%s",
vipPercentage,
vipPercentage > 10 ? "占比健康" : "需要加强维护")
);
insight.setEvidence(Arrays.asList(
"重要价值客户: " + vipSegment.getUserCount() + "人",
"特征: " + vipSegment.getDescription()
));
insight.setConfidence(0.92);
insights.add(insight);
}
// 洞察4: 功能使用
FeatureUsageMetrics features = analysisResult.getFeatureMetrics();
if (features.getUnderusedFeatures() != null &&
!features.getUnderusedFeatures().isEmpty()) {
Insight insight = new Insight();
insight.setCategory("feature");
insight.setSeverity("medium");
insight.setDescription(
String.format("发现%d个冷门功能,使用率<5%%",
features.getUnderusedFeatures().size())
);
insight.setEvidence(features.getUnderusedFeatures().stream()
.limit(5)
.map(f -> "冷门功能: " + f)
.collect(Collectors.toList()));
insight.setConfidence(1.0);
insight.setImpactScore(6.0);
insights.add(insight);
}
// 调用Hook
if (hook != null) {
List<Map<String, Object>> insightMaps = insights.stream()
.map(this::insightToMap)
.collect(Collectors.toList());
hook.onInsightGeneration(insightMaps);
}
return insights;
}
private Map<String, Object> insightToMap(Insight insight) {
Map<String, Object> map = new HashMap<>();
map.put("category", insight.getCategory());
map.put("severity", insight.getSeverity());
map.put("description", insight.getDescription());
map.put("evidence", insight.getEvidence());
map.put("confidence", insight.getConfidence());
return map;
}
设计亮点:
- ✓ 多类型洞察:留存、流失、分层、功能
- ✓ 严重程度分级:critical/high/medium/low
- ✓ 证据支撑:每个洞察都有具体数据
- ✓ 置信度评估:量化可信程度
方法6:建议生成
java
private List<Recommendation> generateRecommendations(AnalysisResult result) {
List<Recommendation> recommendations = new ArrayList<>();
// 建议1: 流失用户挽回
ChurnPredictionResult churn = result.getChurnPrediction();
if (churn.getChurnRate() > 0.1) {
Recommendation rec = new Recommendation();
rec.setPriority("critical");
rec.setAction("针对流失风险用户推送个性化内容和优惠");
rec.setRationale("及时挽回可减少50%的流失,模型准确率" +
String.format("%.0f%%", churn.getModelAccuracy() * 100));
rec.setExpectedImpact(
String.format("预计挽回%d+用户,价值约15万月费",
(int)(churn.getChurnRiskUsers() * 0.38))
);
rec.setImplementation(Arrays.asList(
"1. 分析用户历史行为和偏好",
"2. 设计个性化挽回策略(优惠/内容/功能推荐)",
"3. 通过邮件、短信、APP推送触达",
"4. 提供限时优惠和专属服务",
"5. 跟踪挽回效果,持续优化"
));
Map<String, Object> kpis = new HashMap<>();
kpis.put("targetReactivationRate", 0.38);
kpis.put("estimatedRetainedUsers", (int)(churn.getChurnRiskUsers() * 0.38));
kpis.put("estimatedRevenue", 149150);
rec.setKpis(kpis);
rec.setTimeline("立即执行,3天内完成触达");
rec.setOwner("用户运营团队");
recommendations.add(rec);
}
// 建议2: 产品优化
UserActivityMetrics activity = result.getActivityMetrics();
if (activity.getDay7Retention() < 0.5) {
Recommendation rec = new Recommendation();
rec.setPriority("high");
rec.setAction("在新用户引导中重点突出高留存功能");
rec.setRationale("分析发现使用核心功能的用户留存率高35%");
rec.setExpectedImpact("预计整体7日留存率提升10-15%");
rec.setImplementation(Arrays.asList(
"1. 优化新用户onboarding流程",
"2. 前3步引导用户使用核心功能",
"3. 提供互动式教程和视频指导",
"4. 设置成就milestone激励使用",
"5. A/B测试验证效果"
));
Map<String, Object> kpis = new HashMap<>();
kpis.put("targetDay7Retention", 0.50);
kpis.put("estimatedNewRetainedUsers", 6500);
rec.setKpis(kpis);
rec.setTimeline("2周完成设计,1周开发,1周测试");
rec.setOwner("产品团队 + 设计团队");
recommendations.add(rec);
}
// 建议3: 功能调整
FeatureUsageMetrics features = result.getFeatureMetrics();
if (features.getUnderusedFeatures() != null &&
features.getUnderusedFeatures().size() >= 3) {
Recommendation rec = new Recommendation();
rec.setPriority("medium");
rec.setAction("评估" + features.getUnderusedFeatures().size() +
"个冷门功能,考虑优化或下线");
rec.setRationale("使用率<5%,占用开发和维护资源");
rec.setExpectedImpact("节皁20%开发资源,聚焦核心功能");
rec.setImplementation(Arrays.asList(
"1. 深入调研用户需求和痛点",
"2. 评估功能的商业价值和战略意义",
"3. 对于有价值但使用率低的功能进行优化",
"4. 对于价值低的功能逐步下线",
"5. 制定下线沟通和迁移方案"
));
Map<String, Object> kpis = new HashMap<>();
kpis.put("targetResourceSaving", 0.20);
kpis.put("estimatedFeatureImprovement", 3);
rec.setKpis(kpis);
rec.setTimeline("1个月完成评估,2个月执行");
rec.setOwner("产品团队 + 研发团队");
recommendations.add(rec);
}
// 调用Hook
if (hook != null) {
List<Map<String, Object>> recMaps = recommendations.stream()
.map(this::recommendationToMap)
.collect(Collectors.toList());
hook.onRecommendationGeneration(recMaps);
}
return recommendations;
}
private Map<String, Object> recommendationToMap(Recommendation rec) {
Map<String, Object> map = new HashMap<>();
map.put("priority", rec.getPriority());
map.put("action", rec.getAction());
map.put("expectedImpact", rec.getExpectedImpact());
map.put("implementation", rec.getImplementation());
return map;
}
设计亮点:
- ✓ 基于洞察生成建议
- ✓ 优先级分级:critical/high/medium/low
- ✓ 详细的实施步骤
- ✓ 可量化的KPI目标
- ✓ 明确的时间线和责任人