Files
abot/plugins/value_rank/README.md
liuwei 7d82557699 docs(value_rank): 补充当前实现状态与操作指南
- 新增功能完整度说明,明确已上线命令、定时任务和数据链路状态

- 增加快速上手步骤:配置检查、模板检查、表结构检查、命令验证顺序

- 增加常见问题排查,覆盖社交关系图、社交榜单、趋势数据不足场景
2026-04-21 14:28:07 +08:00

466 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Value Rank 插件设计文档(优化版)
> 目标:基于你现有插件生态(`point_trade` / `daily_ranking` / `message_sign` / `inactive_rank` / `stats_collector`)实现一个可落地、可运营、可扩展的“群成员身价排行”插件。
## 0. 当前实现状态2026-04-21
当前插件核心闭环已完成,可直接在群里使用:
1. 排行与报告能力
- `我的身价`
- `身价排行 [名次]`
- `身价周报`
- `我的趋势 [天数]`
- `身价说明`
- `重算身价`(管理员)
2. 社交图谱能力
- `社交热度榜 [名次]`
- `搭子榜 [名次]`
- `社交桥梁榜 [名次]`
- `社交关系图 [人数]`(基于 HTML 模板渲染为图片)
3. 定时任务能力
- 每日重算身价快照
- 每 10 分钟批处理一次 @ 关系(默认只处理“当前前 20~10 分钟”的消息窗口)
- 每周自动推送周报
4. 数据链路状态
- `messages.mentioned_user_ids`:已用于标记“是否处理过 @ 抽取”,`[]` 代表已处理但无 @。
- `messages.raw_payload`:已支持存储完整原始消息载荷(不脱敏)。
- 社交明细/边/日汇总:已落 `t_message_mentions``t_social_edges_daily``t_value_rank_social_daily`
## 0.1 操作指南(快速上手)
### 步骤 1确认配置与命令
检查 [config.toml](/D:/learn/abot/plugins/value_rank/config.toml)
1. `enable = true`
2. `command` 包含下列命令:
- `我的身价`
- `身价排行`
- `社交热度榜`
- `搭子榜`
- `社交桥梁榜`
- `社交关系图`
- `我的趋势`
- `身价周报`
- `身价说明`
- `重算身价`
### 步骤 2确认社交关系图模板
`社交关系图` 命令强依赖模板文件:
1. 配置项:
- `social_graph_template_path = "plugins/value_rank/templates/social_graph.html"`
2. 模板文件:
- [social_graph.html](/D:/learn/abot/plugins/value_rank/templates/social_graph.html)
说明:当前不再兼容内嵌模板;模板缺失会导致关系图生成失败。
### 步骤 3确认数据库结构
至少需要以下表/字段可用:
1. `t_value_rank_snapshot`
2. `t_value_rank_social_daily`
3. `t_message_mentions`
4. `t_social_edges_daily`
5. `messages.mentioned_user_ids`
6. `messages.raw_payload`
### 步骤 4群内验证命令
建议按以下顺序验证:
1.`重算身价`(管理员)确保当天快照可用。
2.`我的身价``身价排行 10` 验证基础分与排行。
3.`社交热度榜 10``搭子榜 10``社交桥梁榜 10` 验证社交统计。
4.`社交关系图 12` 验证模板渲染与发图链路。
5.`我的趋势 7``身价周报` 验证历史快照与趋势计算。
### 步骤 5定时任务观察点
1. `@` 抽取任务:每 10 分钟跑一次,只处理指定时间窗口内、`mentioned_user_ids` 为空的消息。
2. 每日消息快照:用于第二天趋势与周报对比。
3. 每周周报推送:依赖当日快照和近 7 天对比数据。
### 常见问题
1. `社交关系图` 无图返回
- 优先检查模板路径和模板文件是否存在。
- 再检查近 `social_window_days` 是否有足够社交边数据。
2. 社交榜单无数据
- 检查 `@` 批处理任务是否正常执行。
- 检查消息是否被标记为已处理(`mentioned_user_ids = []` 或有值)。
3. `我的趋势` 数据过少
- 说明历史快照不足,等待每日重算积累后会逐步完整。
## 1. 先说结论(对当前方案的评估)
你原文档的方向是对的,但当前版本存在 4 个落地风险:
1. 指标可用性不一致
- 你设计了 `被@次数`,但当前代码链路里没有稳定落库这个指标,直接作为核心权重会导致实现卡住。
2. 分值可刷问题
- 如果直接用“发言量线性加分”,会被短消息刷屏轻易拉爆,排行榜失真。
3. 指标量纲不统一
- 积分、发言数、签到天数的数值范围差异大,直接加权会导致大数指标统治结果。
4. 结果不可追溯
- 如果只算“当前分”,没有“每日快照”,后续很难解释“为什么今天涨跌”。
因此建议:
- **V1 先用现成数据做稳定可用版**(积分 + 发言 + 活跃天数 + 潜水惩罚)。
- **V2 再接入社交中心度(被@**,但前提是先补齐数据采集链路。
---
## 2. 与现有插件/数据库的复用关系
### 2.1 可直接复用的数据来源
1. 积分存量
- 来源:`db/points_db.py` -> `PointsDBOperator.get_user_points()` / `get_points_ranking()`
- 表:`t_user_points`(兼容旧数据迁移)
2. 发言活跃
- 来源:`db/message_storage.py`
- 可用接口:
- `get_group_member_message_ranking(group_id, start_time, end_time, limit)`
- `get_member_active_dates(group_id, wxid, days)`
- 或直接 SQL 聚合 `messages`
3. 连续签到/签到数据
- 来源:`db/sign_in.py` + `plugins/message_sign`
- 表:`t_sign_record``signin_streak``last_sign_date` 等)
4. 潜水信息
- 来源:`plugins/inactive_rank`(内部用 `ContactsDBOperator.get_inactive_members_rank`
- 可作为负向修正项
### 2.2 暂时不可直接复用(需补链路)
1.@次数(社交中心度)
- 当前 `messages` 里虽有 `message_xml`,但还没有统一抽取与聚合逻辑。
- 建议放到 V2不阻塞 V1 上线。
---
## 3. 推荐算法V1 可直接上线)
## 3.1 指标定义
按群独立计算(`group_id` 维度):
1. `P`:用户当前积分(财富存量)
2. `M7`:近 7 天有效发言量
3. `A30`:近 30 天活跃天数(有发言记 1 天)
4. `I`:潜水惩罚项(最近发言距今天数)
> “有效发言”建议过滤:长度过短、纯命令消息、纯重复刷屏消息。
## 3.2 归一化与评分
建议将所有指标归一化到 `[0,1]` 再加权,避免量纲污染。
- `P_norm = min(log1p(P) / log1p(P95), 1)`
- `M_norm = min(M7 / M95, 1)`
- `A_norm = min(A30 / 30, 1)`
- `I_penalty = min(inactive_days / 30, 1)`
其中 `P95``M95` 为群内当日 95 分位,用于抗极端值。
最终分数:
```text
score = 1000 * (
0.35 * P_norm +
0.45 * M_norm +
0.20 * A_norm
) - 150 * I_penalty
```
边界处理:
- `score < 0` 则记为 `0`
- 新成员冷启动:若无积分但有发言,可获得基础活跃分,避免“永远垫底”
## 3.3 为什么比原线性模型更优
1. 抗刷屏:`M7` 上限由分位数截断,极端刷量收益递减
2. 抗土豪统治:积分用 `log1p`,防止高积分一票否决
3. 可解释:每个分项都能单独展示(资产、热度、活跃、惩罚)
---
## 4. 称号系统(可运营版)
不建议用固定分数区间,建议用“分位段位”,因为不同群体量级差异很大。
- Top 1%`群之巨鳄`
- Top 5%`社交名流`
- Top 20%`活跃中产`
- 20%~80%`稳定居民`
- Bottom 20%`潜力新人`
- Bottom 10% 且 `inactive_days >= 30``潜水观察员`
这样跨群也更公平,且无需频繁调阈值。
---
## 5. 数据库存储设计(建议新增)
## 5.1 每日快照表(核心)
```sql
CREATE TABLE IF NOT EXISTS t_value_rank_snapshot (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stat_date DATE NOT NULL COMMENT '统计日期',
group_id VARCHAR(100) NOT NULL COMMENT '群ID',
user_id VARCHAR(100) NOT NULL COMMENT '用户ID',
score DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '身价分',
rank_no INT NOT NULL DEFAULT 0 COMMENT '名次',
title VARCHAR(50) NOT NULL DEFAULT '' COMMENT '称号',
points_total INT NOT NULL DEFAULT 0 COMMENT '当前积分',
msg_count_7d INT NOT NULL DEFAULT 0 COMMENT '7日有效发言数',
active_days_30 INT NOT NULL DEFAULT 0 COMMENT '30日活跃天数',
inactive_days INT NOT NULL DEFAULT 0 COMMENT '距今未发言天数',
score_detail_json JSON NULL COMMENT '分项得分详情',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_day_group_user (stat_date, group_id, user_id),
KEY idx_group_day_rank (group_id, stat_date, rank_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
> 这个表是关键:支持“涨跌解释”“历史回溯”“趋势展示”。
## 5.2 社交图数据层(建议直接上)
```sql
CREATE TABLE IF NOT EXISTS t_value_rank_social_daily (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stat_date DATE NOT NULL,
group_id VARCHAR(100) NOT NULL,
user_id VARCHAR(100) NOT NULL,
mentioned_count INT NOT NULL DEFAULT 0 COMMENT '被@次数(入度)',
mention_others_count INT NOT NULL DEFAULT 0 COMMENT '@他人次数(出度)',
unique_interactors INT NOT NULL DEFAULT 0 COMMENT '互动去重人数',
interaction_score DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '社交影响力分',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uniq_day_group_user (stat_date, group_id, user_id),
KEY idx_group_day_score (group_id, stat_date, interaction_score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
## 5.3 消息表结构化字段(提升提取效率)
`messages` 表新增字段:
- `raw_payload LONGTEXT`:完整原始消息(已支持)
- `mentioned_user_ids LONGTEXT`:该消息里被 @ 的用户 ID 清单JSON 数组字符串)
示例值:
```json
["wxid_abc", "wxid_xyz"]
```
> 设计目的:避免每次统计都扫 `raw_payload`,在入库阶段就把最常用的社交特征结构化。
## 5.4 社交关系明细与边表(用于关系网图)
```sql
CREATE TABLE IF NOT EXISTS t_message_mentions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
message_id VARCHAR(32) NOT NULL,
group_id VARCHAR(100) NOT NULL,
sender_id VARCHAR(100) NOT NULL,
mentioned_user_id VARCHAR(100) NOT NULL,
stat_date DATE NOT NULL,
msg_time DATETIME NOT NULL,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_message_sender_mentioned (message_id, sender_id, mentioned_user_id),
KEY idx_group_date (group_id, stat_date),
KEY idx_mentioned_group_date (mentioned_user_id, group_id, stat_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS t_social_edges_daily (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stat_date DATE NOT NULL,
group_id VARCHAR(100) NOT NULL,
from_user_id VARCHAR(100) NOT NULL,
to_user_id VARCHAR(100) NOT NULL,
mention_count INT NOT NULL DEFAULT 0,
reply_count INT NOT NULL DEFAULT 0,
interaction_score DECIMAL(10,2) NOT NULL DEFAULT 0,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_day_group_edge (stat_date, group_id, from_user_id, to_user_id),
KEY idx_group_day_score (group_id, stat_date, interaction_score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
---
## 5.5 社交图可产出图表(周报/日报)
1. 群社交关系网图(节点=成员,边=互动强度)
2.@热度榜Top10
3. 最强搭子榜(双向互动最强的成员对)
4. 社交桥梁榜(连接不同圈层的关键成员)
5. 个人影响力趋势图7天/30天
> 这些图表都基于 `t_social_edges_daily` + `t_value_rank_social_daily` 即可生成,不需要回扫全量原始消息。
---
## 6. 插件交互设计(命令与输出)
## 6.1 命令建议
1. `我的身价`
- 查询自己今日身价报告
2. `身价排行 [N]`
- 默认前 10最大 50
3. `身价说明`
- 返回算法权重与统计周期说明,减少争议
4. `重算身价`(管理员)
- 手动触发当前群重算(用于调试/补数据)
## 6.2 输出模板
### 个人报告
```text
📊 [昵称] 的身价报告2026-04-21
总身价786.4
群内排名:第 4 / 126社交名流
资产分:+210.2(积分 13240
热度分:+365.87日发言 188
活跃分:+142.030日活跃 24 天)
潜水惩罚:-31.62 天未发言)
较昨日:+5.8%
```
### 排行榜
```text
🏆 身价排行榜Top10
1. 张三 942.1 群之巨鳄
2. 李四 901.3 社交名流
3. 王五 860.0 社交名流
...
```
---
## 7. 插件实现方案(按你项目结构)
推荐文件:
```text
plugins/value_rank/
├─ __init__.py
├─ main.py
├─ config.toml
└─ README.md
```
## 7.1 `main.py` 关键职责
1. 继承 `MessagePluginInterface`
2. 注册 feature 权限(如 `VALUE_RANK`
3. 处理命令:`我的身价` / `身价排行` / `身价说明` / `重算身价`
4. 提供定时任务:每天 `04:00` 全量重算
5. 复用:`PointsDBOperator``MessageStorageDB`、联系人昵称管理
## 7.2 调度逻辑(对齐 `daily_ranking`
- `get_schedule_actions()` 返回 `value_rank_daily_recompute`
- `run_scheduled_action()` 按启用群批量执行
- 每群执行:
1. 拉取候选成员(近 30 天有消息或有积分记录)
2. 计算分项与总分
3. 生成 rank + title
4. upsert 到 `t_value_rank_snapshot`
5. (可选)推送 TopN
---
## 8. 防刷与风控建议(必须做)
1. 发言去噪
- 过滤超短文本、纯命令、重复消息
2. 单日贡献上限
- 单用户单日“发言贡献值”设上限(例如 200 条)
3. 冷启动保护
- 新人有发言即可入榜,但不会瞬间冲顶
4. 异常波动检测
- 日涨跌超过阈值(如 ±40%)写日志,便于排查
---
## 9. V2 增量(社交中心度:被@
当你准备上线“被@”指标时,建议:
1. 在消息入库时同步解析 `@`,并直接写 `messages.mentioned_user_ids`
2. 同步写 `t_message_mentions` 明细,方便追溯和反查
3. 日聚合写入 `t_social_edges_daily``t_value_rank_social_daily`
3. 新增权重项:
```text
score = 1000 * (0.30*P_norm + 0.35*M_norm + 0.20*A_norm + 0.15*C_norm) - penalty
```
其中 `C_norm` 为被@次数归一化值
---
## 10. 最小可行上线计划(建议)
1. 第 1 天:建表 + 插件骨架 + `我的身价` 查询
2. 第 2 天:`身价排行` + 定时重算 + 排名持久化
3. 第 3 天:防刷规则 + 涨跌展示 + 管理员重算命令
4. 第 4 天:灰度 1~2 个群,观察一周后再全量
---
## 11. 你原方案中建议调整的点(明确结论)
1. “被@权重 30%”
- 暂不建议直接上,先补采集链路,否则会造成空值偏差。
2. “倒数群友自动踢出警告”
- 建议改为轻量提醒,避免群体验冲突。
3. “打击低身价用户(禁言/掠夺)”
- 建议改为娱乐化、低惩罚玩法,避免强负反馈导致活跃进一步下降。
4. 固定阈值称号
- 建议改为分位阈值,更适配不同规模群。
---
如果你愿意,下一步我可以直接把 `plugins/value_rank/main.py``config.toml` 的首版骨架按这份文档落地出来(含详细中文注释、可直接运行)。