---
title: "账号级信号（agatha、bdsm、user-cred-v2）"
description: "三类给账号打分的离线与流式系统：举报/拉黑相对点赞的比率、行为序列模型、关注图 PageRank；本页列出它们的公式与阈值"
---

> Documentation Index
> Fetch the complete documentation index at: https://x-algorithm.vergego.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 账号级信号（agatha、bdsm、user-cred-v2）

## 这一页回答什么

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

三者都不在请求路径上。打标签规则或处置服务把它们的输出转成账号标签，再由 [可见性过滤](https://x-algorithm.vergego.ai/concepts/visibility-filtering/) 读取。

## 核心结论

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（见 [可见性过滤](https://x-algorithm.vergego.ai/concepts/visibility-filtering/)）。
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`）：

```text
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`）。

### 从标签到 Blink 分

```mermaid
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`）：

```text
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`）：

```text
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`）：

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

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

分数换算（`user-cred-v2/UserCredV2.scala:10-19`）：

```text
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`）。公开的打标签规则普遍跳过这类账号，见 [打标签规则](https://x-algorithm.vergego.ai/concepts/labeling-rules/)。
- 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 到分数

## 相关页面

- [打标签规则](https://x-algorithm.vergego.ai/concepts/labeling-rules/) —— 读取这些分数的 botmaker 规则
- [处置与标签聚合](https://x-algorithm.vergego.ai/concepts/enforcement/) —— 消费 BDSM 结果的处置服务
- [可见性过滤](https://x-algorithm.vergego.ai/concepts/visibility-filtering/) —— 账号标签在推荐中的效果
- [Under the Hood 透明度报告](https://x-algorithm.vergego.ai/concepts/under-the-hood/) —— 用户能看到哪些账号标签

Source: https://x-algorithm.vergego.ai/concepts/account-signals/index.mdx
