Skip to content

账号级信号(agatha、bdsm、user-cred-v2)

三类给账号打分的离线与流式系统:举报/拉黑相对点赞的比率、行为序列模型、关注图 PageRank;本页列出它们的公式与阈值

Updated

这一页回答什么

  • agatha/ 怎样用“拉黑、举报、垃圾举报相对点赞”的比率和图特征,为每个账号计算垃圾、成人内容等分数。
  • bdsm/ 的行为序列模型读取什么、输出什么,怎样决定挑战或封禁。
  • user-cred-v2/ 怎样在关注图和互动边上运行 PageRank,并把质量(mass)换算成 0-100 的账号分。
  • 哪些系统把这些分数用作阈值或豁免条件。

三者都不在请求路径上。打标签规则或处置服务把它们的输出转成账号标签,再由 可见性过滤 读取。

核心结论

  1. Agatha 的基础标签是平滑比率。 分数 = (互动数 + 0.1) / (互动数 + 站外点赞数 + 0.1 / 先验)。这里的互动指拉黑、举报或垃圾举报(agatha/scalding/labels/rate_based_labels/RateBasedLabels.scala:29-57、:60)。
  2. Agatha 再用图特征把这些标签推广到所有活跃账号。 它计算每个图特征与标签的 PMI(点互信息,衡量两者共现程度),按账号汇总成 Blink 分,再按活跃用户的分布做百分位校准(agatha/scalding/blink/BlinkScoreJobTemplate.scala:127-128、:184-244)。
  3. Agatha 分数的使用阈值写在 botmaker 规则中。 例如:SpamSuspended > 0.99 或举报比分数 > 0.999 时,规则给账号打标签 AGATHA_SPAM_TOP_USER,有效 7 天(botmaker-rules/scarecrow/bot/ProductionAgatha__FilterOutOrganicNotifications.bot:11-33)。这个标签只影响通知,不影响 For You(见 可见性过滤)。
  4. BDSM 是行为序列 Transformer。 它输入最近 512 个动作,输出 8 个头的分数。开源版把执法阈值替换为不可能触发的 9.99(bdsm/README.md:102-115、bdsm/runtime/sink_policy.yaml:4-40)。
  5. user-cred-v2 分数 = clamp(165.2 + 7.07 × ln(mass), 0, 100)(user-cred-v2/UserCredV2.scala:10-19)。mass 来自关注图上的 PageRank。跳转概率开源默认为 0.2;跳转目标一半在认证账号间均分,一半按点赞/转发加权(user-cred-v2/UserCredV2Config.scala:30-35、user-cred-v2/UserCredV2App.scala:141-193)。
  6. user-cred-v2 分数 ≥ 54 的账号视为“高 PageRank 账号”,多数自动打标签规则豁免这类账号(botmaker-rules/scarecrow/derived-feature/HighPageRankThreshold.df:1-2、IsHighPageRankUser.df:3-18)。

Agatha:举报/拉黑相对点赞

基础标签(率值标签)

标签名 分子(互动) 时间窗 锚点
BlocksPerFav 被拉黑(拉黑图中状态为 0 或 2 的边,排除已知人造账号) 本批次日期范围 RateBasedLabels.scala:120-140
ReportsPerFav 经滥用分诊工具(AbuseTriageTool)提交的举报 180 天 RateBasedLabels.scala:65-66、:142-159
SpamReportsPerFav 被判为有效的垃圾举报(私信、举报为垃圾、举报用户、举报帖子) 本批次日期范围 RateBasedLabels.scala:67-68、:161-184
AllSpamReportsPerFav 全部垃圾举报(不区分是否有效) 本批次日期范围 RateBasedLabels.scala:69-70

分母中的“点赞”只计站外点赞,即点赞者与作者之间没有任何方向的关注关系(agatha/scalding/labels/favs/Favs.scala:15-19、:51-68)。

计算步骤如下:

  1. 每个发起者最多计 100 个目标账号,超出部分用水库抽样截断(agatha/scalding/labels/LabelUtil.scala:12-23)。例如,一个人拉黑一万个账号,只按 100 个计。
  2. 计算先验:先验 = 全体账号的 Σ互动 / Σ(互动 + 点赞)。单个账号的 (互动 + 点赞) 超过 1000 时,按比例缩到 1000 再参与计算(LabelUtil.scala:25-44)。
  3. 计算账号分数(RateBasedLabels.scala:47-55):
score = (I + 0.1) / (I + F + 0.1 / prior)
I = 互动数,F = 站外点赞数,0.1 = 平滑参数(RateBasedLabels.scala:60)

没有任何互动和点赞的账号,分数等于先验。样本越多,分数越接近真实比率。

另有两类二值标签:

  • SpamSuspended:正例是过去窗口内因 spam 或 other 理由被封禁、且备注不含 #chaq 的账号(agatha/scalding/util/GetSuspendedUsers.scala:12-27)。负例是其余活跃账号(LabelUtil.scala:46-61)。
  • NSFW:正例是带 NsfwHighPrecision、NsfwAvatarImage 或 NsfwBannerImage 账号标签的账号(agatha/scalding/labels/nsfw/NSFWLabelExtraction.scala:16-30)。
flowchart LR
    G["图特征: 关注 / 点赞 / 转发 / 拉黑 / 举报 / 列表 / 简介词 等"] --> B["Build: 每个特征与标签的 PMI"]
    L["标签: 率值 / SpamSuspended / NSFW"] --> B
    B --> P["Predict: 留一法 PMI, 按特征类别取矩"]
    P --> S["Blink 分 = (Σm0·m1 + ln 先验) / (Σm0 + 1)"]
    S --> C["按活跃用户分布做百分位校准"]
    C --> MH["写入 Manhattan, 供 GetUserHealthScores 读取"]
  • 图特征来源有 24 种,包括关注、点赞、转发、回复、提及、拉黑、静音、举报、私信、列表、话题标签和简介词(agatha/hub/agatha/thrift/featureSource.thrift:3-28)。
  • Build:对每个 (特征, 标签) 对计算 PMI,即 ln[P(特征, 标签) / (P(特征)·P(标签))](agatha/hub/pmi/math/Math.scala:95-101、agatha/hub/agatha/builder/Builder.scala:16-37)。训练窗口为 30 天(agatha/scalding/RateBasedBuildJob.scala:22-29、SpagathaBuildJob.scala:21-24)。
  • Predict:对每个账号,用留一法取其各特征的 PMI,再按特征类别汇总成矩(moments)。预测窗口为 14 天(agatha/hub/agatha/predict/Predict.scala:17-50、agatha/scalding/AgathaFeaturesPredictJob.scala:26-36)。
  • Blink 分:对每个账号的每个标签,把各特征类别的 m0(计数)与 m1(均值)累加,再按下式合成(BlinkScoreJobTemplate.scala:121-182):
blink = (Σ m0·m1 + ln(prior)) / (Σ m0 + 1)
  • inbound 子类特征只计入总分,不计入“外向”分(BlinkScoreJobTemplate.scala:123-125)。
  • 部分标签排除与自身同源的特征类别。例如,ReportsPerFav 不用拉黑图和举报图(BlinkScoreJobTemplate.scala:287-313)。推测目的是防止标签泄漏。
  • 校准:以活跃账号为参照,分 1000 个分区做百分位校准(BlinkScoreJobTemplate.scala:184-244)。校准后的分数可理解为百分位,0.99 约等于前 1%(推测)。
  • 输出:任务把 AllSpamReportsPerFav、ReportsPerFav、SpamSuspended、SpamReportsPerFav 四个标签的校准分写入 Manhattan(BlinkScoreJobTemplate.scala:315-320)。botmaker 规则通过 GetUserHealthScores 读取 AgathaSpamSuspended 等名称的分数(bot/ProductionAgatha__FilterOutOrganicNotifications.bot:11-14)。二者的对应关系由名称推断,源码未直接写明。

Agatha 分数的使用阈值(开源默认值)

规则 条件 结果 锚点
bot 21278 AgathaSpamSuspended > 0.99 或 AgathaAllSpamReportsPerFav > 0.999 或 AgathaSpamReportsPerFav > 0.999;非高 PageRank、非灰标、不在跳过名单 账号标签 AGATHA_SPAM_TOP_USER,7 天 botmaker-rules/scarecrow/bot/ProductionAgatha__FilterOutOrganicNotifications.bot:4-33
bot 20998 发帖时 AgathaAllSpamReportsPerFav > 0.9975 或 AgathaSpamSuspended > 0.98;同样豁免 帖子标签 AGATHA_SPAM,7 天 botmaker-rules/scarecrow/bot/AgathaSpamProduction__ApplySearchTopTweetLabel.bot:3-28

AGATHA_SPAM_TOP_USER 只在通知相关的安全级别中触发丢弃(visibility-filtering/rules/levels.rs:89-100)。For You 两个级别的规则不读取 AGATHA_SPAM 帖子标签(visibility-filtering/rules/levels.rs:298-361);规则名提示它用于搜索。

BDSM:行为序列模型

BDSM 全称 Behavioral Detection Sequence Model(bdsm/README.md:1-5)。

  • 模型:双向 Transformer 编码器,读取账号最近的动作序列。位置编码用按时间戳归一化的 RoPE(旋转位置编码),因此能表示动作间隔(bdsm/README.md:17-25、bdsm/runtime/model.py:62)。
  • 规格:256 种动作类型、8 个分类头、序列长度 512、嵌入宽度 1024(bdsm/README.md:102-103)。
  • 每个动作的特征:动作类型、产品界面、停留时长、设备/客户端信号、互动目标哈希等(bdsm/README.md:26-29)。
  • 8 个头:FollowBot、LikeBot、EngagementAmplifier、ReplySpamBot、TweetSpamBot、RTBot、MultiActionBot、LegitimateUser(bdsm/README.md:30-34、bdsm/runtime/heads.py:115-118)。
  • 训练:主干先做自监督训练(掩码属性预测,加每 6 步一次的对比学习),然后冻结;任务头单独训练(bdsm/README.md:42-51)。
  • 流水线:Kafka 动作事件 → Rust 累加器去重并入队 → 预取序列 → GPU 推理输出 8 维分数 → 结果汇(sink)按阈值决定动作(bdsm/README.md:53-67)。

结果汇的判定结构

阈值是二元组 [tau, lambda]。某个机器人头的分数 ≥ tau,且 LegitimateUser 分数 ≤ lambda 时,才算命中(bdsm/runtime/score_results_sink_focal.py:851-857)。

通道 涉及的头 命中结果 锚点
硬执法 FollowBot、EngagementAmplifier、RTBot 标记 enforcement_threshold_reached,交给处置服务 score_results_sink_focal.py:821-980、sink_policy.yaml:17-20
临界(cusp) EngagementAmplifier 分数在阈值附近 ± cusp_delta 时发挑战标签(Arkose、验证码或活体检测) score_results_sink_focal.py:981-1035、:187-205
暂停-活体检测 LikeBot、MultiActionBot 活体检测 score_results_sink_focal.py:1038-1098、sink_policy.yaml:26-28
垃圾弹回 TweetSpamBot、ReplySpamBot 弹回并挑战 score_results_sink_focal.py:1100-1160、sink_policy.yaml:30-36

硬执法前有以下豁免门(score_results_sink_focal.py:836-920):

  • 动作数少于 min_actions_for_enforcement 的账号不执法。开源版把该值替换为 999999。
  • 发帖动作占比 ≥ 50% 的账号不走硬执法(:836-841)。
  • FollowBot:取关多于关注时不算命中(:853-854);开启年龄门时,新账号不算命中(:860-869);登录后短时间内通过入门推荐包完成的关注不算命中(:871-889);关注过入门推荐包的账号改为发挑战(:891-905、:946-961)。
  • 开启检查时,跳过客户端停留数据缺失(dwell dropout)的账号(:907-913)。

开源版的全部阈值与 min_actions_for_enforcement 都是占位值。生产值未公开(bdsm/README.md:104-115)。

user-cred-v2:关注图 PageRank

图与参数(开源默认值)

项 值 锚点
节点 未受限、未停用、未封禁、未抹除的账号 user-cred-v2/ValidUserInfo.scala:21-41
边 关注边,质量从关注者流向被关注者;只有非 near-zero 状态的关注者能传出质量 user-cred-v2/UserCredV2App.scala:287-318
去除的边 账号扩展调查(AccountExpansionInvestigations)认定的关联账号之间的边 UserCredV2App.scala:102-118
跳转概率 α 0.2 user-cred-v2/UserCredV2Config.scala:30
收敛阈值 0.001(L1 差) UserCredV2Config.scala:31、UserCredV2App.scala:256-257
最大迭代 50 UserCredV2Config.scala:32
互动跳转权重 β 0.5 UserCredV2Config.scala:35
互动窗口 7 天,点赞与转发 UserCredV2Config.scala:25、UserCredV2App.scala:79-100

跳转分布

跳转(teleport)目标不是在全体账号上均匀分布(UserCredV2App.scala:141-193):

teleport(u) = (1 - β) × uniform_premium(u) + β × engagement(u)
uniform_premium:在非 near-zero 的付费/认证账号(蓝标、灰标、金标、认证机构及其关联号)上均分
engagement(u) = Σ_e mass_prev(e) × count(e→u) / count(e→·),再归一化

含义:未认证账号拿不到均匀跳转质量。它们只能通过被关注、被有质量的账号点赞或转发来获得质量(依代码推断)。

迭代与分数

每轮迭代的计算如下(UserCredV2App.scala:320-390):

mass'(u) = (1 - α) × Σ_{f 关注 u} mass(f) / outdeg(f) + teleport(u) × (α + (1 - α) × 遗失质量)

遗失质量指没有出边的节点未能分出去的质量。任务用上一轮快照做热启动(UserCredV2App.scala:200-215)。

分数换算(user-cred-v2/UserCredV2.scala:10-19):

score = min(100, max(0, 165.2 + 7.07 × ln(mass)))

由公式反推(计算值,不是源码常量):score = 54 对应 mass ≈ 1.48e-7;score = 100 对应 mass ≈ 9.88e-5。

发布保护

结果先通过快照安全门。安全门检查三项:行数、分数 ≥ 55 的账号数(vitThreshold)与上一版相比变化不超过 5%;质量总和在 [0.99, 1.005] 之间。检查不通过时,任务把结果写入隔离区并失败退出(UserCredV2Config.scala:40-44、UserCredV2App.scala:392-430、user-cred-v2/SnapshotSafeguard.scala:21-64)。生产任务从 2026-04-27 起每天运行(UserCredV2App.scala:518-525)。

分数的使用方

  • botmaker:IsHighPageRankUser = 分数 ≥ 54 或带 PREDICTED_HIGH_PAGE_RANK 标签;没有分数时,改用粉丝数 > 25000 判断(botmaker-rules/scarecrow/derived-feature/IsHighPageRankUser.df:3-18)。公开的打标签规则普遍跳过这类账号,见 打标签规则。
  • abuse-enforcement-service:规则中有 cred.is_high || cred.score >= ... 跳过条件,开源值是占位值 12.34(abuse-enforcement-service/service-lib/rules/enforcement_user.yaml:26-33)。
  • safety-label-user-agg:使用 highPageRankOrGreyBadge 排除条件(safety-label-user-agg/safetyLabelToUserLevelAggregationV2Processor.strato:31-48)。

设计决策

决策 理由
比率用站外点赞做分母 熟人圈的点赞容易刷;站外点赞更能代表陌生人的真实反应
每个发起者最多计 100 个目标 防止少数人通过大批量拉黑或举报左右他人分数
贝叶斯平滑 + 先验 样本少的账号不会因一两次举报得到极端分数
Blink 分做百分位校准 阈值可以写成“前 1%”这类稳定口径;模型更新不改变阈值含义
PageRank 跳转偏向认证账号与互动 新注册的批量账号无法靠互相关注凭空获得质量
去除关联账号之间的边 已认定的关联账号不能互相抬分
BDSM 阈值不公开 公开阈值和动作数下限会暴露规避边界

源码锚点

  • agatha/scalding/labels/rate_based_labels/RateBasedLabels.scala:29-118 —— 率值标签与平滑公式
  • agatha/scalding/labels/LabelUtil.scala:12-61 —— 抽样截断、先验、正负例
  • agatha/scalding/blink/BlinkScoreJobTemplate.scala:121-320 —— Blink 分、校准、写入
  • botmaker-rules/scarecrow/bot/ProductionAgatha__FilterOutOrganicNotifications.bot:11-33 —— Agatha 阈值
  • bdsm/README.md:17-115 —— 模型、流水线、阈值脱敏说明
  • bdsm/runtime/score_results_sink_focal.py:821-1160 —— 各执法通道
  • user-cred-v2/UserCredV2App.scala:141-390 —— 跳转分布与 PageRank 迭代
  • user-cred-v2/UserCredV2.scala:10-19 —— mass 到分数

相关页面

Type to search…

↑↓ navigate↵ selectEsc close