Files

Value Rank 插件设计文档(优化版)

目标:基于你现有插件生态(point_trade / daily_ranking / message_sign / inactive_rank / stats_collector)实现一个可落地、可运营、可扩展的“群成员身价排行”插件。

0. 当前实现状态2026-04-21

当前插件核心闭环已完成,可直接在群里使用:

  1. 排行与报告能力
  • 我的身价
  • 身价排行 [名次]
  • 身价周报
  • 我的趋势 [天数]
  • 身价说明
  • 重算身价(管理员)
  1. 社交图谱能力
  • 社交热度榜 [名次]
  • 搭子榜 [名次]
  • 社交桥梁榜 [名次]
  • 社交关系图 [人数](基于 HTML 模板渲染为图片)
  1. 定时任务能力
  • 每日重算身价快照
  • 每 10 分钟批处理一次 @ 关系(默认只处理“当前前 20~10 分钟”的消息窗口)
  • 每周自动推送周报
  1. 数据链路状态
  • messages.mentioned_user_ids:已用于标记“是否处理过 @ 抽取”,[] 代表已处理但无 @。
  • messages.raw_payload:已支持存储完整原始消息载荷(不脱敏)。
  • 社交明细/边/日汇总:已落 t_message_mentionst_social_edges_dailyt_value_rank_social_daily

0.1 操作指南(快速上手)

步骤 1确认配置与命令

检查 config.toml

  1. enable = true
  2. command 包含下列命令:
  • 我的身价
  • 身价排行
  • 社交热度榜
  • 搭子榜
  • 社交桥梁榜
  • 社交关系图
  • 我的趋势
  • 身价周报
  • 身价说明
  • 重算身价

步骤 2确认社交关系图模板

社交关系图 命令强依赖模板文件:

  1. 配置项:
  • social_graph_template_path = "plugins/value_rank/templates/social_graph.html"
  1. 模板文件:

说明:当前不再兼容内嵌模板;模板缺失会导致关系图生成失败。

步骤 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 是否有足够社交边数据。
  1. 社交榜单无数据
  • 检查 @ 批处理任务是否正常执行。
  • 检查消息是否被标记为已处理(mentioned_user_ids = [] 或有值)。
  1. 我的趋势 数据过少
  • 说明历史快照不足,等待每日重算积累后会逐步完整。

1. 先说结论(对当前方案的评估)

你原文档的方向是对的,但当前版本存在 4 个落地风险:

  1. 指标可用性不一致
  • 你设计了 被@次数,但当前代码链路里没有稳定落库这个指标,直接作为核心权重会导致实现卡住。
  1. 分值可刷问题
  • 如果直接用“发言量线性加分”,会被短消息刷屏轻易拉爆,排行榜失真。
  1. 指标量纲不统一
  • 积分、发言数、签到天数的数值范围差异大,直接加权会导致大数指标统治结果。
  1. 结果不可追溯
  • 如果只算“当前分”,没有“每日快照”,后续很难解释“为什么今天涨跌”。

因此建议:

  • V1 先用现成数据做稳定可用版(积分 + 发言 + 活跃天数 + 潜水惩罚)。
  • V2 再接入社交中心度(被@,但前提是先补齐数据采集链路。

2. 与现有插件/数据库的复用关系

2.1 可直接复用的数据来源

  1. 积分存量
  • 来源:db/points_db.py -> PointsDBOperator.get_user_points() / get_points_ranking()
  • 表:t_user_points(兼容旧数据迁移)
  1. 发言活跃
  • 来源: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
  1. 连续签到/签到数据
  • 来源:db/sign_in.py + plugins/message_sign
  • 表:t_sign_recordsignin_streaklast_sign_date 等)
  1. 潜水信息
  • 来源: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)

其中 P95M95 为群内当日 95 分位,用于抗极端值。

最终分数:

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 每日快照表(核心)

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 社交图数据层(建议直接上)

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 数组字符串)

示例值:

["wxid_abc", "wxid_xyz"]

设计目的:避免每次统计都扫 raw_payload,在入库阶段就把最常用的社交特征结构化。

5.4 社交关系明细与边表(用于关系网图)

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. 我的身价
  • 查询自己今日身价报告
  1. 身价排行 [N]
  • 默认前 10最大 50
  1. 身价说明
  • 返回算法权重与统计周期说明,减少争议
  1. 重算身价(管理员)
  • 手动触发当前群重算(用于调试/补数据)

6.2 输出模板

个人报告

📊 [昵称] 的身价报告2026-04-21
总身价786.4
群内排名:第 4 / 126社交名流

资产分:+210.2(积分 13240
热度分:+365.87日发言 188
活跃分:+142.030日活跃 24 天)
潜水惩罚:-31.62 天未发言)

较昨日:+5.8%

排行榜

🏆 身价排行榜Top10
1. 张三  942.1  群之巨鳄
2. 李四  901.3  社交名流
3. 王五  860.0  社交名流
...

7. 插件实现方案(按你项目结构)

推荐文件:

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. 复用:PointsDBOperatorMessageStorageDB、联系人昵称管理

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. 发言去噪
  • 过滤超短文本、纯命令、重复消息
  1. 单日贡献上限
  • 单用户单日“发言贡献值”设上限(例如 200 条)
  1. 冷启动保护
  • 新人有发言即可入榜,但不会瞬间冲顶
  1. 异常波动检测
  • 日涨跌超过阈值(如 ±40%)写日志,便于排查

9. V2 增量(社交中心度:被@

当你准备上线“被@”指标时,建议:

  1. 在消息入库时同步解析 @,并直接写 messages.mentioned_user_ids
  2. 同步写 t_message_mentions 明细,方便追溯和反查
  3. 日聚合写入 t_social_edges_dailyt_value_rank_social_daily
  4. 新增权重项:
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%”
  • 暂不建议直接上,先补采集链路,否则会造成空值偏差。
  1. “倒数群友自动踢出警告”
  • 建议改为轻量提醒,避免群体验冲突。
  1. “打击低身价用户(禁言/掠夺)”
  • 建议改为娱乐化、低惩罚玩法,避免强负反馈导致活跃进一步下降。
  1. 固定阈值称号
  • 建议改为分位阈值,更适配不同规模群。

如果你愿意,下一步我可以直接把 plugins/value_rank/main.pyconfig.toml 的首版骨架按这份文档落地出来(含详细中文注释、可直接运行)。