Loading...
正在加载...
请稍候

智柴论坛推荐系统技术文档

✨步子哥 (steper) 2025年11月12日 06:06

目录

  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

职责:推荐服务的入口,负责缓存管理和参数验证

关键方法

// 获取用户推荐
getRecommendationsForUser(int {{LATEX:0}}limit = 10): array

// 获取话题相关推荐
getRelatedTopics(int {{LATEX:1}}limit = 10): array

配置参数

  • DEFAULT_LIMIT = 10:默认推荐数量
  • MAX_LIMIT = 20:最大推荐数量
  • CACHE_TTL = 900:缓存有效期(15分钟)
  • WAIT_TIMEOUT_MS = 2500:等待异步结果超时(2.5秒)

2. RecommendationEngine

职责:纯计算引擎,实现推荐算法核心逻辑

关键方法

// 混合推荐得分计算(用户推荐)
buildHybridScores(
    int {{LATEX:2}}targetInteractions,      // 目标用户与话题的交互权重
    array {{LATEX:3}}topicContents,           // 话题内容数据
    array {{LATEX:4}}limit = 10
): array

// 话题相似度排序(话题推荐)
rankRelatedTopics(
    array {{LATEX:5}}candidateDocs,           // 候选话题
    int {{LATEX:6}}pdo, array {{LATEX:7}}pdo, array {{LATEX:8}}pdo, int {{LATEX:9}}pdo, array &{{LATEX:10}}excludeUserId): array

推荐算法

1. 用户推荐算法(混合推荐)

1.1 数据收集

用户交互权重

  • 发布话题:权重 3.0
  • 回复话题:权重 1.0

数据范围

  • 用户发布的话题:最近 200 条
  • 用户回复的话题:最近 400 条
  • 全局话题数据:最近 1000 条(用于协同过滤)
  • 全局回复数据:最近 2000 条(用于协同过滤)

1.2 协同过滤(Collaborative Filtering)

算法:基于用户的协同过滤(User-Based CF)

步骤

  1. 计算用户相似度

    similarity(userA, userB) = cosine_similarity(
        userA_interactions,  // 用户A与话题的交互向量
        userB_interactions    // 用户B与话题的交互向量
    )
    
  2. 推荐得分计算

    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. 构建用户兴趣向量

    user_vector = Σ(topic_vector × (1 + interaction_weight))
    

    其中:

    • topic_vector:话题的 TF-IDF 向量
    • interaction_weight:用户对该话题的交互权重
  2. 计算候选话题相似度

    similarity(topic) = cosine_similarity(user_vector, topic_tfidf_vector)
    
  3. TF-IDF 计算

    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 混合得分计算

公式

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. 相似度计算

    similarity(topic) = cosine_similarity(query_tfidf_vector, candidate_tfidf_vector)
    
  4. 排序与筛选

    • 按相似度降序排序
    • 返回前 N 个话题(N = limit × 2,用于后续过滤)

3. 分词算法

分词策略

  1. 文本预处理

    • 转换为小写
    • HTML 实体解码
  2. 正则匹配

    /[\p{L}\p{N}]+/u
    
    • 匹配所有字母和数字的连续序列
  3. 中文处理

    • 如果 token 包含中文且长度 > 1,拆分为单字
    • 例如:"人工智能" → ["人", "工", "智", "能"]
  4. 停用词过滤

    • 过滤常见停用词(中英文)
    • 过滤单字符 token(除非是中文单字)
  5. 停用词表

    // 英文停用词
    '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}

缓存结构

{
  "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

消息格式

{
  "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. 缓存填充超时:返回空数组(前端不显示推荐)

热门话题算法

排序规则

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. 在话题详情页展示推荐

// TopicController.php
{{LATEX:11}}relatedTopicsRaw = {{LATEX:12}}topicId, 5);

// 转换为 Topic 对象
foreach ({{LATEX:13}}relatedTopicData) {
    if (isset({{LATEX:14}}relatedTopic = {{LATEX:15}}relatedTopicData['id']);
        // ... 添加到推荐列表
    }
}

2. 获取用户推荐

// 获取用户推荐(最多10个)
{{LATEX:16}}recommendations = {{LATEX:17}}userId, 10);

foreach ({{LATEX:18}}rec) {
    echo "推荐话题: {\(rec['title']} (得分: {\)rec['score']})\n";
    echo "  - 协同过滤得分: {\(rec['cf_score']}\n";
    echo "  - 内容得分: {\)rec['content_score']}\n";
}

3. 前端展示

<!-- views/topic_content.html -->
<?php if (!empty({{LATEX:21}}data['RelatedTopics'] as $relatedTopic): ?>
                    <div class="col-12 col-sm-6 col-md-4 col-lg">
                        <!-- 推荐卡片 -->
                    </div>
                    <?php endforeach; ?>
                </div>
            </div>
        </div>
    </div>
</div>
<?php endif; ?>

未来改进方向

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
2025-11-12 07:35

已将 emoji 纳入推荐权重计算。总结如下:

实现内容

1. Emoji 权重映射

定义了 7 种 emoji 的权重,反映不同含义:

  • ❤️ (爱心):5.0 - 非常喜欢
  • 🚀 (火箭):4.0 - 支持/推荐
  • 👍 (点赞):3.0 - 喜欢
  • ✅ (对勾):2.0 - 认可
  • 👀 (眼睛):0.5 - 浏览/关注
  • 👎 (点踩):-2.0 - 不喜欢(负权重)
  • ❌ (叉):-3.0 - 反对(负权重)

2. 核心方法

  • getEmojiWeights():返回 emoji 权重映射
  • getTopicEmojiWeight():获取单个话题的 emoji 权重总和
  • getTopicsEmojiWeights():批量获取多个话题的 emoji 权重
  • getUserEmojiWeightsForTopics():获取特定用户对特定话题的 emoji 权重

3. 集成到推荐流程

  • 用户推荐:在 collectUserInteractionsForRecommendations() 中,获取用户对所有话题的 emoji 权重,归一化后累加到交互权重
  • 全局交互:在 collectGlobalInteractionsForRecommendations() 中,为每个用户获取其对话题的 emoji 权重,同样进行归一化处理

4. 权重计算方式

  • 归一化:emoji 权重除以 10,使其与基础权重(1.0-3.0)在同一量级
  • 累加公式:final_weight = base_weight + (emoji_weight_sum / 10.0)
  • 负权重处理:👎 和 ❌ 会降低话题的推荐得分,避免推荐用户不喜欢的内容

5. 文档更新

  • 在“推荐算法”部分添加了 emoji 权重说明
  • 新增“Emoji 权重系统”章节,详细说明设计理念、权重映射、计算方式和效果

效果预期

  1. 更准确反映用户偏好:❤️ 和 🚀 比 👍 更能体现强烈兴趣
  2. 避免推荐不喜欢内容:负权重会降低不感兴趣话题的得分
  3. 提升推荐质量:结合 emoji 的推荐更符合用户真实意图
  4. 性能优化:使用批量查询,减少数据库访问次数

系统已集成 emoji 权重计算,每次推荐都会考虑用户的 emoji 表态。

推荐
智谱 GLM-5 已上线

我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。

领取 2000万 Tokens 通过邀请链接注册即可获得大礼包,期待和你一起在 BigModel 上畅享卓越模型能力
登录