Files
john 4e21ca937a
Deploy Documentation / deploy (push) Has been cancelled
Canary / Prepare Version (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Canary / bundle-desktop-windows-cuda (push) Has been cancelled
Canary / Release (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
CI / Check MSRV (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
CI / H5 Plaza Tests and Build (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Add TKMind platform extensions, H5/MindSpace stack, and deployment tooling.
Fork goose with custom MCP widgets, platform extensions (aider, git, web, search),
MindSpace H5 backend/frontend, Plaza/Ops UIs, and deploy scripts for tkmind.cn.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-14 21:30:20 +08:00

6.8 KiB
Raw Permalink Blame History

sidebar_position, title, sidebar_label, description
sidebar_position title sidebar_label description
6 Feed 算法 Feed 算法 广场热度排序公式、算法演化路径、Redis 计数策略和推荐迁移时机

Feed 算法

设计原则

  • 初期不做个性化推荐:冷启动时用户行为数据不足,协同过滤效果差于热度排序。
  • 热度排序足够用到 10 万帖子:时间衰减 + 多维互动权重,工程实现简单,效果可预期。
  • 透明可调:热度公式的权重系数写在配置表里,运营可以实时调整,不需要发版。
  • 推荐算法是独立演化的模块:等数据量够了再接入,接口协议从第一天就预留好。

热度排序公式(初期)

hot_score = (V × w_v + L × w_l + C × w_c + S × w_s) / (T + decay_base) ^ decay_exp
变量 含义 默认权重
V view_count 浏览量 w_v = 0.1
L like_count 点赞数 w_l = 3.0
C comment_count 评论数 w_c = 5.0
S collect_count 收藏数 w_s = 4.0
T 帖子年龄(小时),NOW() - published_at -
decay_base 时间衰减基数 默认 2
decay_exp 时间衰减指数 默认 1.5

示例

一篇帖子发布 3 小时后,获得 500 次浏览、20 个点赞、3 条评论、5 个收藏:

分子 = 500×0.1 + 20×3 + 3×5 + 5×4 = 50 + 60 + 15 + 20 = 145
分母 = (3 + 2)^1.5 = 5^1.5 ≈ 11.18
hot_score ≈ 12.97

同样互动量、发布 24 小时后:

分母 = (24 + 2)^1.5 = 26^1.5 ≈ 132.6
hot_score ≈ 1.09

时间衰减效果明显,保证新内容有机会上热门。


权重配置表

CREATE TABLE plaza_algorithm_config (
    key         VARCHAR(100) NOT NULL,
    value       DOUBLE       NOT NULL,
    description VARCHAR(200) NOT NULL DEFAULT '',
    updated_at  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO plaza_algorithm_config VALUES
  ('w_view',      0.1,  '浏览量权重',   NOW()),
  ('w_like',      3.0,  '点赞权重',     NOW()),
  ('w_comment',   5.0,  '评论权重',     NOW()),
  ('w_collect',   4.0,  '收藏权重',     NOW()),
  ('decay_base',  2.0,  '时间衰减基数', NOW()),
  ('decay_exp',   1.5,  '时间衰减指数', NOW());

后台定时任务读取配置表后计算,运营修改配置后下次计算周期生效。


计算时机

触发条件                          执行动作
──────────────────────────────    ──────────────────────────────
每 10 分钟定时任务                重新计算过去 48 小时内发布帖子的 hot_score
新帖发布到广场                    立即计算初始 hot_score(基于 0 互动 + 年龄 0)
帖子互动量发生变化                不立即重算,等下一次定时任务

为什么不实时重算:每次点赞都重算 hot_score 并回写 MySQL,在高并发下会造成锁竞争。Redis 实时累计计数,定时批量回写 + 重算是更稳定的方案。


Redis 计数策略

# 每次浏览
INCR plaza:post:{id}:view_count

# 点赞
INCR plaza:post:{id}:like_count

# 取消点赞
DECR plaza:post:{id}:like_count

# 防止负数
if GET plaza:post:{id}:like_count < 0:
    SET plaza:post:{id}:like_count 0

定时同步任务(每 5 分钟)

1. 维护 Redis SET plaza:sync:post_ids(每次 INCR 时将 post_id SADD 入集合)
2. SMEMBERS plaza:sync:post_ids,批量 GET 计数
3. 批量 UPDATE plaza_posts … WHERE id IN (...)
4. SREM 已同步的 post_id

避免对 plaza:post:* 做全量 SCAN;帖子量大时 SCAN 会阻塞 Redis。Sprint 3 前帖子少时可临时 SCAN,上线前必须改为 SET 增量同步。

Redis 数据持久化:开启 AOF,每秒 fsync,允许最多 1 秒的计数损失。


Feed 缓存策略

广场首页热门 Feed(全部分类):
  缓存 Key:  plaza:feed:hot:all:page:1
  TTL:       5 分钟
  生成时机:  定时任务重算 hot_score 后刷新

分类热门 Feed
  缓存 Key:  plaza:feed:hot:{category_slug}:page:1
  TTL:       5 分钟

最新 Feed
  缓存 Key:  plaza:feed:new:all:cursor:{cursor}
  TTL:       60 秒(最新列表变化快)

精选位(Sprint 5):
  缓存 Key:  plaza:featured:{position}
  TTL:       5 分钟
  合并规则:  见 04-backend-api「精选与 Feed 合并规则」

新帖 status 变为 published 后:
  DEL plaza:feed:hot:* plaza:feed:new:*(下次请求重建)

Feed 接口降级策略

场景 降级行为
Redis 不可用 直接查 MySQL ORDER BY hot_score DESC,性能下降但不中断
热度未计算(新帖) published_at DESC 兜底
分类无帖子 返回全局热门帖子,前端提示"该分类暂无内容"

算法演化路径

第一阶段:热度排序(现在)

纯公式,无需用户行为数据,工程简单。

第二阶段:兴趣标签匹配(月活 1 万后)

  • 从用户的浏览历史和点赞行为中推断兴趣标签(与帖子的 tags 字段匹配)。
  • 在热度排序基础上,对匹配用户兴趣的帖子 hot_score 乘以权重系数(1.21.5)。
  • 不需要机器学习模型,纯 SQL 可实现。

第三阶段:协同过滤(月活 10 万后)

  • 引入独立 Python 推荐服务。
  • 基于用户行为矩阵(浏览、点赞、收藏)进行 item-based 协同过滤。
  • 后端 /api/plaza/v1/feed 接口保持不变,内部调用推荐服务获取帖子 ID 列表。
  • 接口协议从现在就预留:响应中的 posts 数组顺序即为推荐顺序,客户端无需感知算法类型。

第四阶段:实时流推荐(月活 100 万后)

  • Flink/Spark Streaming 实时处理行为流。
  • 向量召回 + 精排模型。
  • A/B 测试框架。

防刷策略

初期使用简单规则,不上机器学习:

  • 同一 IP 对同一帖子,24 小时内只计 1 次有效浏览。
    • 实现:Redis SET plaza:view:{ip_hash}:{post_id} 1 EX 86400 NXSET 成功才 INCR 计数。
  • 同一用户重复点赞(已有 UNIQUE 约束):数据库层保证幂等。
  • 短时间内大量来自同一 IP 的点赞:限流中间件 + 运营人工核查。
  • 评论刷量:同一用户对同一帖子每小时最多发 10 条评论。

新帖冷启动

新帖发布后 hot_score = 0,会排在热门列表末尾,导致新帖永远没有曝光机会。

解决方案:「最新」Tab 独立于「热门」存在,新帖默认进入「最新」Feed,以 published_at DESC 排序。

「最新」是新内容的曝光入口,「热门」是经过验证的内容的沉淀展示。这两个 Tab 服务不同的用户动机。