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
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>
6.8 KiB
6.8 KiB
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.2–1.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 NX,SET 成功才 INCR 计数。
- 实现:Redis
- 同一用户重复点赞(已有 UNIQUE 约束):数据库层保证幂等。
- 短时间内大量来自同一 IP 的点赞:限流中间件 + 运营人工核查。
- 评论刷量:同一用户对同一帖子每小时最多发 10 条评论。
新帖冷启动
新帖发布后 hot_score = 0,会排在热门列表末尾,导致新帖永远没有曝光机会。
解决方案:「最新」Tab 独立于「热门」存在,新帖默认进入「最新」Feed,以 published_at DESC 排序。
「最新」是新内容的曝光入口,「热门」是经过验证的内容的沉淀展示。这两个 Tab 服务不同的用户动机。