## 目录
1. [系统概述](#系统概述)
2. [架构设计](#架构设计)
3. [核心组件](#核心组件)
4. [推荐算法](#推荐算法)
5. [数据流与缓存机制](#数据流与缓存机制)
6. [降级策略](#降级策略)
7. [性能优化](#性能优化)
8. [使用示例](#使用示例)
9. [未来改进方向](#未来改进方向)
---
## 系统概述
智柴论坛推荐系统是一个**混合推荐系统**,结合了 **协同过滤(Collaborative Filtering)** 和 **基于内容的推荐(Content-Based)** 两种算法,为用户提供个性化的话题推荐。
### 核心特性
- **混合推荐算法**:协同过滤(60%)+ 内容相似度(40%)
- **异步缓存架构**:通过 Redis 缓存 + SQLite 持久化,确保高性能
- **智能降级策略**:推荐失败时自动回退到热门话题
- **实时计算**:支持用户推荐和话题相似度推荐两种场景
- **中文分词支持**:针对中英文混合内容优化的分词算法
### 推荐场景
1. **用户推荐**:基于用户历史行为(发布话题、回复)推荐感兴趣的话题
2. **话题推荐**:基于话题内容相似度推荐相关话题(用于话题详情页)
---
## 架构设计
### 整体架构图
```
┌─────────────────────────────────────────────────────────────┐
│ 前端请求层 │
│ TopicController.showTopic() │
│ └─> RecommendationService.getRelatedTopics() │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 服务层 (RecommendationService) │
│ - 缓存键管理 │
│ - 参数验证与限制 │
│ - 调用 DataService 获取数据 │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 缓存层 (DataService) │
│ - Redis 缓存查询 │
│ - 缓存未命中时发送异步填充请求 │
│ - 等待异步结果(最多 2.5 秒) │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 异步队列 (cache_fill_queue) │
│ Redis List: zhichai:cache_fill_queue │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 后台处理进程 (process_sqlite_queue.php) │
│ AsyncSQLiteWriter.executeCacheFillTask() │
│ ├─> handleUserRecommendations() │
│ └─> handleTopicRecommendations() │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 推荐引擎 (RecommendationEngine) │
│ ├─> buildHybridScores() (用户推荐) │
│ │ ├─> computeCollaborativeScores() │
│ │ └─> computeContentScores() │
│ └─> rankRelatedTopics() (话题推荐) │
│ └─> computeTfIdfSimilarities() │
└──────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ ├─> SQLite: topics, replies, user_topic_views │
│ └─> Redis: 推荐结果缓存 (TTL: 15分钟) │
└─────────────────────────────────────────────────────────────┘
```
### 关键设计原则
1. **异步优先**:推荐计算在后台进程异步执行,不阻塞主请求
2. **缓存优先**:所有推荐结果优先从 Redis 缓存读取
3. **降级保障**:推荐失败时自动回退到热门话题,确保用户体验
4. **数据分离**:推荐引擎是纯计算逻辑,不依赖外部资源,便于测试
---
## 核心组件
### 1. RecommendationService
**职责**:推荐服务的入口,负责缓存管理和参数验证
**关键方法**:
```php
// 获取用户推荐
getRecommendationsForUser(int $userId, int $limit = 10): array
// 获取话题相关推荐
getRelatedTopics(int $topicId, int $limit = 10): array
```
**配置参数**:
- `DEFAULT_LIMIT = 10`:默认推荐数量
- `MAX_LIMIT = 20`:最大推荐数量
- `CACHE_TTL = 900`:缓存有效期(15分钟)
- `WAIT_TIMEOUT_MS = 2500`:等待异步结果超时(2.5秒)
### 2. RecommendationEngine
**职责**:纯计算引擎,实现推荐算法核心逻辑
**关键方法**:
```php
// 混合推荐得分计算(用户推荐)
buildHybridScores(
int $targetUserId,
array $targetInteractions, // 目标用户与话题的交互权重
array $allUserInteractions, // 所有用户与话题的交互权重
array $topicContents, // 话题内容数据
array $excludeTopicIds = [], // 排除的话题ID
int $limit = 10
): array
// 话题相似度排序(话题推荐)
rankRelatedTopics(
array $queryTopic, // 查询话题
array $candidateDocs, // 候选话题
int $limit = 10
): array
```
**算法权重**:
- `CF_WEIGHT = 0.6`:协同过滤权重
- `CONTENT_WEIGHT = 0.4`:内容相似度权重
- `MIN_SIMILARITY = 0.01`:最小相似度阈值
### 3. AsyncSQLiteWriter
**职责**:异步处理推荐计算,负责数据收集和结果构建
**关键方法**:
```php
// 处理用户推荐请求
handleUserRecommendations(\PDO $pdo, array $payload): array
// 处理话题推荐请求
handleTopicRecommendations(\PDO $pdo, array $payload): array
// 收集用户交互数据
collectUserInteractionsForRecommendations(\PDO $pdo, int $userId): array
// 收集全局交互数据
collectGlobalInteractionsForRecommendations(\PDO $pdo, array &$topicDetails, int $excludeUserId): array
```
---
## 推荐算法
### 1. 用户推荐算法(混合推荐)
#### 1.1 数据收集
**用户交互权重**:
- 发布话题:权重 `3.0`
- 回复话题:权重 `1.0`
**数据范围**:
- 用户发布的话题:最近 200 条
- 用户回复的话题:最近 400 条
- 全局话题数据:最近 1000 条(用于协同过滤)
- 全局回复数据:最近 2000 条(用于协同过滤)
#### 1.2 协同过滤(Collaborative Filtering)
**算法**:基于用户的协同过滤(User-Based CF)
**步骤**:
1. **计算用户相似度**:
```php
similarity(userA, userB) = cosine_similarity(
userA_interactions, // 用户A与话题的交互向量
userB_interactions // 用户B与话题的交互向量
)
```
2. **推荐得分计算**:
```php
score(topic) = Σ(similarity(user, similar_user) × weight(similar_user, topic))
```
其中:
- `similar_user`:与目标用户相似的其他用户
- `weight(similar_user, topic)`:相似用户对话题的交互权重
- 只考虑相似度 ≥ 0.01 的用户
3. **排除规则**:
- 排除用户已交互过的话题
- 排除隐藏的话题
#### 1.3 内容相似度(Content-Based)
**算法**:TF-IDF + 余弦相似度
**步骤**:
1. **构建用户兴趣向量**:
```php
user_vector = Σ(topic_vector × (1 + interaction_weight))
```
其中:
- `topic_vector`:话题的 TF-IDF 向量
- `interaction_weight`:用户对该话题的交互权重
2. **计算候选话题相似度**:
```php
similarity(topic) = cosine_similarity(user_vector, topic_tfidf_vector)
```
3. **TF-IDF 计算**:
```php
TF(term, doc) = count(term in doc)
IDF(term) = log((total_docs + 1) / (docs_containing_term + 1)) + 1
TF-IDF(term, doc) = TF(term, doc) × IDF(term)
```
#### 1.4 混合得分计算
**公式**:
```php
final_score = 0.6 × normalized_cf_score + 0.4 × normalized_content_score
```
**归一化**:
- 协同过滤得分和内容得分分别归一化到 [0, 1]
- 归一化方法:`normalized_score = score / max_score`
**排序规则**:
1. 按最终得分降序
2. 得分相同时,优先协同过滤得分高的
3. 协同过滤得分也相同时,优先内容得分高的
### 2. 话题推荐算法(内容相似度)
**算法**:TF-IDF + 余弦相似度
**步骤**:
1. **候选话题筛选**:
- 排除当前话题本身
- 排除隐藏的话题
- 从最近创建的话题中选择(默认 100 条)
2. **TF-IDF 向量化**:
- 对查询话题和候选话题进行分词
- 计算每个话题的 TF-IDF 向量
3. **相似度计算**:
```php
similarity(topic) = cosine_similarity(query_tfidf_vector, candidate_tfidf_vector)
```
4. **排序与筛选**:
- 按相似度降序排序
- 返回前 N 个话题(N = limit × 2,用于后续过滤)
### 3. 分词算法
**分词策略**:
1. **文本预处理**:
- 转换为小写
- HTML 实体解码
2. **正则匹配**:
```regex
/[\p{L}\p{N}]+/u
```
- 匹配所有字母和数字的连续序列
3. **中文处理**:
- 如果 token 包含中文且长度 > 1,拆分为单字
- 例如:"人工智能" → ["人", "工", "智", "能"]
4. **停用词过滤**:
- 过滤常见停用词(中英文)
- 过滤单字符 token(除非是中文单字)
5. **停用词表**:
```php
// 英文停用词
'the', 'and', 'for', 'with', 'that', 'this', ...
// 中文停用词
'的', '了', '和', '是', '我', '我们', '你', ...
// 技术停用词
'php', 'code', 'topic', 'reply', 'forum', 'http', 'https', ...
```
---
## 数据流与缓存机制
### 数据流图
```
用户请求推荐
│
▼
RecommendationService.getRelatedTopics()
│
├─> 检查 Redis 缓存
│ ├─> 缓存命中 → 直接返回
│ └─> 缓存未命中 → 继续
│
▼
DataService.get()
│
├─> 发送异步填充请求到 cache_fill_queue
│ └─> 等待异步结果(最多 2.5 秒)
│ ├─> 收到结果 → 返回并写入缓存
│ └─> 超时 → 返回 null
│
▼
process_sqlite_queue.php (后台进程)
│
├─> 从 cache_fill_queue 读取请求
├─> 调用 AsyncSQLiteWriter.executeCacheFillTask()
│ └─> handleTopicRecommendations()
│ ├─> 从 SQLite 查询话题数据
│ ├─> 调用 RecommendationEngine.rankRelatedTopics()
│ └─> 构建推荐结果
│
▼
写入 Redis 缓存
│
└─> 缓存键: topic_recommendations:{topicId}:{limit}
TTL: 900 秒(15分钟)
```
### 缓存策略
**缓存键格式**:
- 用户推荐:`user_recommendations:{userId}:{limit}`
- 话题推荐:`topic_recommendations:{topicId}:{limit}`
**缓存结构**:
```json
{
"id": 123,
"title": "话题标题",
"snippet": "内容摘要...",
"score": 0.8523,
"cf_score": 0.6234,
"content_score": 0.4567,
"created_at": "2025-01-15 18:30:00",
"author_id": 456
}
```
**缓存 TTL**:
- 推荐结果:900 秒(15分钟)
- NULL 结果:2 秒(防止频繁查询不存在的数据)
### 异步填充机制
**队列名称**:`zhichai:cache_fill_queue`
**消息格式**:
```json
{
"type": "topic_recommendations",
"payload": {
"topic_id": 123,
"limit": 5
},
"response_key": "cache_response:req_67890abcdef"
}
```
**响应机制**:
1. 后台进程计算完成后,将结果写入 `cache_response:{requestId}`
2. 主进程通过 `BRPOP` 等待响应(最多 2.5 秒)
3. 收到响应后,将结果写入主缓存键并返回
---
## 降级策略
### 降级场景
1. **用户无交互数据**:回退到热门话题
2. **协同过滤无结果**:仅使用内容推荐
3. **内容推荐无结果**:回退到热门话题
4. **话题不存在或隐藏**:回退到热门话题
5. **缓存填充超时**:返回空数组(前端不显示推荐)
### 热门话题算法
**排序规则**:
```sql
ORDER BY (
COALESCE(rc.reply_count, 0) * 2 +
(julianday('now') - julianday(t.created_at)) * -0.1
) DESC
```
**得分公式**:
```
score = reply_count × 2 - days_since_created × 0.1
```
**特点**:
- 回复数越多,得分越高
- 时间越新,得分越高
- 平衡了热度和新鲜度
---
## 性能优化
### 1. 数据量限制
**用户推荐**:
- 用户发布话题:最多 200 条
- 用户回复话题:最多 400 条
- 全局话题:最多 1000 条
- 全局回复:最多 2000 条
**话题推荐**:
- 候选话题:最多 100 条(或 `limit × 10`)
### 2. 计算优化
**预计算候选集**:
- 话题推荐时,先筛选候选话题(排除隐藏、排除自身)
- 减少需要计算相似度的话题数量
**相似度阈值**:
- 最小相似度:0.01
- 低于阈值的结果直接丢弃
**结果限制**:
- 用户推荐:计算 `limit × 3` 个结果,最终返回 `limit` 个
- 话题推荐:计算 `limit × 2` 个结果,最终返回 `limit` 个
### 3. 缓存优化
**缓存预热**:
- 热门话题可以定期预热
- 用户推荐可以在用户活跃时预热
**缓存分层**:
- 主缓存:完整的推荐结果(TTL: 15分钟)
- NULL 缓存:防止频繁查询不存在的数据(TTL: 2秒)
### 4. 异步处理
**非阻塞设计**:
- 推荐计算在后台进程异步执行
- 主请求最多等待 2.5 秒,超时后返回空结果
**队列机制**:
- 使用 Redis List 作为消息队列
- 支持多个后台进程并发处理
---
## 使用示例
### 1. 在话题详情页展示推荐
```php
// TopicController.php
$recommendationService = new \Services\RecommendationService();
$relatedTopicsRaw = $recommendationService->getRelatedTopics($topicId, 5);
// 转换为 Topic 对象
foreach ($relatedTopicsRaw as $relatedTopicData) {
if (isset($relatedTopicData['id'])) {
$relatedTopic = $this->topicService->getTopicByID((int)$relatedTopicData['id']);
// ... 添加到推荐列表
}
}
```
### 2. 获取用户推荐
```php
// 获取用户推荐(最多10个)
$recommendationService = new \Services\RecommendationService();
$recommendations = $recommendationService->getRecommendationsForUser($userId, 10);
foreach ($recommendations as $rec) {
echo "推荐话题: {$rec['title']} (得分: {$rec['score']})\n";
echo " - 协同过滤得分: {$rec['cf_score']}\n";
echo " - 内容得分: {$rec['content_score']}\n";
}
```
### 3. 前端展示
```html
```
---
## 未来改进方向
### 1. 算法优化
**深度学习推荐**:
- 引入神经网络模型(如 Wide & Deep)
- 使用 Embedding 技术学习用户和话题的向量表示
**实时学习**:
- 基于用户实时行为(点击、浏览时长)更新推荐
- 使用在线学习算法(如 FTRL)
**多样性优化**:
- 避免推荐结果过于相似
- 引入多样性指标(如 Intra-List Diversity)
### 2. 数据增强
**行为数据**:
- 记录用户浏览时长
- 记录用户点赞、收藏行为
- 记录用户搜索关键词
**内容特征**:
- 提取话题标签
- 分析话题情感倾向
- 识别话题类别(技术、生活、讨论等)
### 3. 性能提升
**分布式计算**:
- 使用 Spark 或 Flink 进行大规模推荐计算
- 支持实时推荐和离线推荐两种模式
**缓存优化**:
- 使用 Redis Cluster 支持更大规模
- 引入多级缓存(L1: 本地缓存,L2: Redis)
**预计算**:
- 定期批量计算热门推荐
- 使用增量更新机制
### 4. 评估体系
**离线评估**:
- A/B 测试框架
- 评估指标:准确率、召回率、覆盖率、多样性
**在线评估**:
- 点击率(CTR)
- 转化率(用户从推荐进入话题详情)
- 用户满意度反馈
### 5. 冷启动优化
**新用户**:
- 基于注册信息(兴趣标签)推荐
- 基于地理位置推荐
- 推荐热门话题
**新话题**:
- 基于话题标签匹配
- 基于作者历史话题推荐
- 基于话题类别推荐
---
## 附录
### A. 配置参数
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `DEFAULT_LIMIT` | 10 | 默认推荐数量 |
| `MAX_LIMIT` | 20 | 最大推荐数量 |
| `CACHE_TTL` | 900 | 缓存有效期(秒) |
| `WAIT_TIMEOUT_MS` | 2500 | 等待异步结果超时(毫秒) |
| `CF_WEIGHT` | 0.6 | 协同过滤权重 |
| `CONTENT_WEIGHT` | 0.4 | 内容相似度权重 |
| `MIN_SIMILARITY` | 0.01 | 最小相似度阈值 |
### B. 数据库表结构
**topics 表**:
- `id`: 话题ID
- `title`: 话题标题
- `content`: 话题内容
- `author_id`: 作者ID
- `created_at`: 创建时间
- `is_hidden`: 是否隐藏
**replies 表**:
- `id`: 回复ID
- `topic_id`: 话题ID
- `author_id`: 作者ID
- `content`: 回复内容
- `created_at`: 创建时间
**user_topic_views 表**(用于推荐系统):
- `user_id`: 用户ID
- `topic_id`: 话题ID
- `view_count`: 浏览次数
- `last_viewed_at`: 最后浏览时间
### C. 相关文件
- `src/Services/RecommendationService.php`:推荐服务入口
- `src/Services/RecommendationEngine.php`:推荐算法引擎
- `src/Services/AsyncSQLiteWriter.php`:异步数据处理
- `src/Services/DataService.php`:缓存服务
- `src/Controllers/TopicController.php`:话题控制器(使用推荐服务)
- `views/topic_content.html`:话题详情页模板(展示推荐)
---
**文档版本**:v1.0
**最后更新**:2025-01-15
**维护者**:智柴论坛开发团队
相关推荐
登录后可参与表态
讨论回复
1 条回复
✨步子哥 (steper)
#1
11-12 07:35
登录后可参与表态