GitHub 趋势 · 第 42 期

X 的推荐算法开源了:33,089 star,连改参数的理由都写进仓库

#推荐算法#开源审计#X

这份仓库的价值不是「你可以照着跑一个推荐系统」,而是「你可以核对 X 到底根据什么决定你看到什么」。它是可读优先、可跑其次:根目录 28 个条目里,真正配齐了构建清单、脚本和合成数据、能端到端跑通的只有 phoenix/ 那一块,而且要求 Linux + NVIDIA GPU + CUDA 12。

推荐算法被讨论得最多的一句话是「它是不是在压我」。但绝大多数讨论停在猜测,因为过去能拿到的东西只有论文、博客、二手截图和离职员工的回忆。这次 X 把请求路径的七个阶段、十七个前置过滤器、权重参数,以及一次真实线上实验的 diff,都放进了同一个仓库。

这到底是什么

仓库地址 github.com/xai-org/x-algorithm,一句话简介:Algorithm powering the For You feed on X。用 Rust 写的,Apache-2.0。

按 GitHub API 抓到的口径:33,089 star、5,384 fork、101 个未关闭 issue、296 个 watcher,创建于 2026-01-19,最近推送 2026-09-10,默认分支 main,仓库体积 4,020 KB——四兆,全是代码和文档,没有模型权重,也没有二进制。

一个必须先说的细节:它没有 Release。releases/latest 返回 404,仓库也关掉了 downloads。这意味着没有一个版本号可以锚定,你读到的默认值、行号和参数名随时可能跟 main 分支一起漂移。

home-mixer/                     For You 信息流的组装层:流水线、权重、过滤
candidate-pipeline/             流水线框架:source / hydrator / filter / scorer /
                                selector / side_effect 六种阶段,能并行就并行
thunder/                        站内候选:关注账号的近期帖子,常驻内存
phoenix/                        站外候选 + 排序模型:训练与推理,JAX + Rust
simclusters/                    站外候选:按「谁和谁互动」聚类
phoenix-rankall/                检索索引的维护与事件层
vm-ranker/                      重排服务:用行列式点过程降低邻居相似度
visibility-filtering/           能不能显示:ALLOW / INTERSTITIAL / DROP
grox/  clip/  media-model-proxy/  内容理解:文本与图片视频分类
botmaker/  scarecrow/           打标规则引擎与运行时
under-the-hood/                 账号可见性标签的报告聚合

请求路径:两条流水线,七个阶段,一次请求内跑完

For You 不是「查一批帖子再排个序」,它是每次下拉都重新组装一次。README 里的架构图把整条路摊开了,我按原文顺序列一遍:

另外一条 Blending Pipeline 包在外面,把模型排不了的东西插进来:广告、Who to Follow、提示语。广告位还会反过来重排自然内容来安排相邻关系。响应发出去之后还有 Side Effects:记录哪些帖子被服务过、刷新缓存、写广告与客户端事件日志。

打标与可见性:三个答案,独立于排序

这是整个仓库里我觉得最值得看的部分,也是它跟一般「推荐系统演示」最大的区别:排序和可见性是两件事。排序决定顺序,可见性决定「能不能出现」,两者是不同的服务、不同的输入、不同的规则。

打标路径不在请求路径上,是持续跑的:内容理解(文本与媒体分类器、图片视频模型、CLIP 嵌入、账号维度的 agatha / bdsm / user-cred-v2、成人内容分类器)产出分数和标签,打标规则(scarecrow 嵌 botmaker 当规则引擎,加 abuse-enforcement-service)落成标签存起来,请求时才被读回来。

可见性判定的三个答案含义
ALLOW正常显示
INTERSTITIAL显示,但隔一层可点开的提示页,例如成人或血腥媒体
DROP不显示

判定时会读上面那些标签,再加上观众是否拉黑、静音、关注作者,账号是否受保护、被停用、已注销,订阅专属状态,以及观众自己的设置和国家。有一条规则值得单独点出来:有些规则只在「这是来自你没关注账号的推荐」时才 DROP——比如高召回率的垃圾内容,同一篇帖子对关注者仍然放行。这也是为什么同一篇帖子在两个人的时间线上命运完全不同。

规则仓库里还有一份不常见的坦诚:botmaker-rules/ 明确写了,为降低被绕过和刷量的风险,部分规则当前不在仓库里。同时也有法律驱动的过滤器可以被逐行核对,例如巴西 2026 选举期间上线的 Brazil2026ElectionFilter,对被巴西选举法院报告的账号做过滤,除非观众自己关注了该账号,实现就在 home-mixer/filters/brazil_2026_election_filter.rs。

打分:多动作概率加权,以及那个被误读的「468 倍」

Phoenix 不给一个「相关度」分,它给每个动作一个概率,合并成总分是另一个显式步骤。动作清单按原文分五类:

Engagement    favorite . reply . repost . quote . share . share via DM . share via copy link
Clicks        post . profile . link . photo expand . video open . quoted post
Attention     video quality view . dwell . dwell time . click dwell time . active seconds
Author        follow author
Negative      not interested . mute author . block author . report . not dwelled

Final Score = Sigma (weight_i x P(action_i))

正向动作带正权重,负向带负权重,权重放在 home-mixer/params/param.rs,算式在 home-mixer/scorers/ranking_scorer.rs。求和之后还有三步调整:作者多样性衰减(同一作者的第二篇起逐篇打折,打到底线为止)、站外折扣(来自你没关注账号的帖子乘一个小于 1 的系数,关注账号的回复和转帖也打折)、新作者加成(曝光量低于阈值的作者被抬向目标位置)。

权重这件事有个流传很广的误读,官方在 README 里亲自下场纠正了。原文说,看到「举报的权重是点赞的 468 倍」就去推断「1 个举报抵 468 个赞」,是错的:权重乘的是你自己产生该动作的预测概率,不是原始互动计数。而这个概率很大程度上由你自己的行为决定。为了让读代码的人和 LLM 都不至于理解反,他们还专门在两个源文件里补了注释。

还有一个容易被忽略的细节在 VMRanker:它是排序之后的一次独立重排,用行列式点过程在帖子的嵌入上做取舍——牺牲一点分数,换取相邻帖子之间更低的相似度。也就是说,你刷到的内容变多样,是有明确代码来源的,不是玄学。

十七道前置过滤:读一遍就知道哪些帖子根本活不到打分

这些是打分之前执行的,顺序就是下表的顺序:

过滤器过滤掉什么
DropDuplicatesFilter多个来源返回的同一篇帖子
CoreDataHydrationFilter正文与元数据加载失败的帖子
AgeFilter超过 48 小时的帖子
SelfTweetFilter观众自己的帖子
OONRetweetReplyFilter未关注账号的转帖与回复,以及父帖缺失的回复
OONNsfwSimclustersFilter作者被标记为成人内容且观众未关注其账号的 SimClusters 帖子
RetweetDeduplicationFilter对同一帖子的重复转帖
IneligibleSubscriptionFilter观众无权访问的订阅专属帖
PreviouslySeenPostsFilter观众已经看过的帖子
PreviouslySeenPostsBackupFilter同上,来自第二份曝光记录
PreviouslyServedPostsFilter本次会话中已经服务过的帖子
MutedKeywordFilter命中观众静音关键词的帖子
AuthorSocialgraphFilter来自观众拉黑或静音账号的帖子
VideoFilter请求明确排除视频时的视频帖
TopicIdsFilter请求话题之外的帖子,以及在排除话题内的帖子
NewUserMinEngagementFilter新账号的互动量低于阈值的站外帖子
InventoryHoldoutFilter按配置比例确定性抽出的那部分帖子

打完分、选出前 K 之后还有三道后置过滤:VFFilter 撤掉可见性判定说 DROP 的帖子,AncillaryVFFilter 连带撤掉父帖、引用帖、被转帖已被撤掉的帖子,DedupConversationFilter 收拢同一对话的额外分支。

顺带一个设计上的取舍:已经看过的帖子处理了两遍。ThunderSource 直接拿到清单并跳过,其他来源不拿,所以它们的重复由上面那两个 PreviouslySeen 过滤器兜住。

连改参数的理由都留痕:一份持续交付的变更日志

README 顶部有一节 Notable Updates,逐日记录改动。2026-08-13 那次把可见性过滤、打标系统、以及「训练线上真正在用的 Phoenix 的代码 + 合成数据生成」一起放了进来,同时上了一个新的透明度工具 Under the Hood,让用户看到自己账号和帖子上的可见性标签的聚合统计。

更有意思的是 docs/BIDIRECTIONAL_BOOST_CHANGE.md 这份文档,它把一次真实线上实验的完整过程连 diff 一起公开了,原文的时间线是这样的:

param!(FavoriteWeight, f64, "rust_home_mixer_favorite_weight", 0.5);
param!(ReplyWeight, f64, "rust_home_mixer_reply_weight", 5.0);
param!(
    BidirectionalFollowReplyWeightBoost,
    f64,
    "rust_home_mixer_bidirectional_follow_reply_weight_boost",
    20.0          # 2026-07-13 全量上线
);
param!(RetweetWeight, f64, "rust_home_mixer_retweet_weight", 1.0);

# 2026-07-24:20.0 -> 15.0
-    20.0
+    15.0

这套东西的含金量不在代码多漂亮,而在于「这个数字为什么是 15 不是 20」有了可以查证的出处。同时 README 也说明了默认值是怎么进到代码里的:很多可调值读的是配置系统,仓库里有 cron 脚本定期把生产主值写回 param.rs 作为默认值——所以你看到的默认值,是某个时间点上的线上值,不是永久契约。

上手:先把要求读清楚,再决定要不要装

以下命令逐字取自 phoenix/QUICKSTART.md 原文。环境要求也是原文列的:

# Requirements
#   Linux with an NVIDIA GPU and CUDA 12
#   uv and Python 3.11 or newer
#   A Rust toolchain and protoc 3.15 or newer

uv sync --extra engine
export PYTHONPATH=$PWD

# Verify the install with random weights
uv run python xrex/inference/oss_bench/bench.py --smoke --service_type ranking

这一步之后可以生成确定性合成数据并训一个 nano 排序模型。同一个 seed 产出同一份数据:

# 1. Generate deterministic synthetic data
uv run python reference/world_snapshots.py --out ./synth_index --seed 20260721
export PHOENIX_INDEX_BASE=./synth_index

uv run python reference/dump_gen.py --out ./synth_dump --seed 20260721 \
  --num-rows 12288 --partitions 4 --rows-per-file 1024 \
  --sid ./synth_index/sid_snapshot/post_sid_v5_256x6.parquet --self-check

# 2. Train the ranking model
uv run python reference/train_synth.py \
  --data ./synth_dump --steps 6 --out "$PWD/checkpoints" --metrics

QUICKSTART 自己在这段旁边写了一句提醒,我原样转述:rehearsal 观察到 loss 在这份合成数据上下降,但不要把这六步当作模型质量的证据。官方口径是这套 walkthrough 只用来验证「发出去的那条链路能通」,生产数据、checkpoint、编排和规模都不在仓库里。

最后跑一次检索加排序的完整闭环:

# 5. Train retrieval and run retrieve -> rank
uv run python reference/retrieve_then_rank.py \
  --data ./synth_dump --sessions 3 --topk 16 \
  --retrieval-port 9990 --ranking-port 9988

# retrieve_then_rank: 3 session(s) completed the full loop.

我的判断

适合谁:想看清工业级大规模推荐管线长什么样的工程师——尤其是「排序与可见性为什么要拆成两个服务」「多动作概率为什么不直接合成一个分数」这类架构取舍,这里给的是一手材料而不是二手总结。做内容风控或社区治理的人值得专门啃 visibility-filtering/ 与那三个答案(ALLOW / INTERSTITIAL / DROP),这套「先打标、再按观众关系逐条判定、且只对推荐流生效」的分工,比算法本身更容易迁移到自己业务。

不适合谁:想抄一套能跑的推荐系统的人会失望——没有生产数据、没有 checkpoint、没有编排,多数模块只适合读;只有 Mac 的人会卡在第一步,QUICKSTART 要求 Linux + NVIDIA GPU + CUDA 12;想直接拿来做产品的人也要想清楚 Apache-2.0 的边界:许可是宽松的,但代码里大量 import 的是内部基础设施(xai_service_runner、xai_kafka 这类),README 明确说这些部署相关文件普遍不在仓库里。

(五条)。① 没有 Release,只能跟 main,本文引用的参数名与默认值都可能已经变了;② 仓库自己列了一份「哪些没放进来」清单:Grox 用的 LLM 提示词(j2 文件)和部分 botmaker 规则,理由是降低被绕过刷量的风险——所以透明度是分层的,不是全量;③ 默认值来自生产主值的 cron 回写,读到的默认值不等于线上永远是那个值;④ 首次跑模型要等 JAX 编译,官方说可能要几分钟;⑤ phoenix/ 目录里另带 NOTICE 与 THIRD_PARTY_NOTICES.md,你如果要用那一块,第三方声明需要单独过一遍。

仓库:xai-org/x-algorithm · github.com/xai-org/x-algorithm

数据:33,089 star / 5,384 fork / 101 open issue / 296 subscriber / 创建 2026-01-19 / 最近推送 2026-09-10 / 默认分支 main / 体积 4,020 KB / 语言 Rust

许可:Apache-2.0(LICENSE 为标准文本,逐字核对无追加自定义条款)· 无 GitHub Release(releases/latest 为 404)

本文所有架构描述、命令与参数均取自仓库内 README.md、phoenix/QUICKSTART.md、docs/BIDIRECTIONAL_BOOST_CHANGE.md 原文,未做改写;star 与 issue 数取自 GitHub API 实时快照。

数据来源:GitHub Trending 官方页面(月榜第 1 位,本月 +6,248)· 抓取时间:2026-09-11 16:40 GMT+8

你会去翻这份代码吗?如果翻了,最先想看的是那十七个过滤器,还是权重表?留言说说你的判断。

下期写 akitaonrails/ai-memory(6,496 star,本月 +5,026,Rust):给 agent 编码 CLI 做长期记忆,并为不同厂商的 agent 之间做交接。备选是 apache/maka(5,214 star,本月 +3,941,Apache 孵化,agent 全程留痕)与 modular/modular(29,677 star,本月 +3,080,Mojo/MAX)。如果核实下来某一项撑不起一期,我就换月榜下一个高动量项目,绝不硬写。

#推荐算法#开源审计#X