km.idaeo.ai · IDAEO 知識庫

🏛 本記事はテーマ館「ai」の所蔵です

Xアルゴリズム解析の証拠総覧

本稿は、X推薦アルゴリズムのオープンソースプロジェクトについて、58件の機械命題の検証データとソースコードパスを集約し、行動重み、フィルターパイプライン、可視性ルールを行単位で解析する。正確なソースコード根拠、検証スクリプト、技術仕様を必要とするエンジニアと研究者向け。

xai-org / x-algorithm · HEAD a389166

各主張には、ソースコードの位置と再現コマンドを付す。すべての数値はスクリプトの実行で生成されており、手作業で転記したものではない。

リポジトリ xai-org/x-algorithm(2026-08-13 更新) HEAD a389166f6cf5da70a286b568c87695d4dcdce3a1 規模 2,015 ファイル / 21 MB / 5 commits 判読 2026-08-14 検証 58 件の機械命題、58 PASS / 0 FAIL 自己検証:誤った期待値を注入 → 正しく 1 FAIL を生成(ゲート有効)

① 機械命題の検証表

以下の 58 件はすべて verify-all.sh の実行で生成された。本ページの数値は同スクリプトの出力を直接表に変換したものであり、手作業で書き写してはいない。スクリプト本体は付録に示す。

should-FAIL:わざと誤った既知の値を入れ、検査が本当に止まることを示す
should-FAIL:わざと誤った既知の値を入れ、検査が本当に止まることを示す
項目数値説明
PASS58
FAIL0
should-FAIL 反例検証(誤値の注入が必ず検出されること)1
番号命題実測値結果
C01HEADa389166f6cf5da70a286b568c87695d4dcdce3a1PASS
C02param! ブロック数183PASS
C03ファイル総数2015PASS
C04FavoriteWeight0.5PASS
C05ReplyWeight5.0PASS
C06RetweetWeight1.0PASS
C07ShareViaCopyLinkWeight20.0PASS
C08ReportWeight-234.0PASS
C09MuteAuthorWeight-58.8PASS
C10BlockAuthorWeight-31.2PASS
C11NotInterestedWeight-43.2PASS
C12NEW_USER_OON_WEIGHT_FACTOR0.00001PASS
C13NEW_USER_MIN_FOLLOWING5PASS
C14NEGATIVE_SCORES_OFFSET0.001PASS
C15MAX_POST_AGE(秒)172800PASS
C16NewUserAgeThresholdSecs のデフォルト0PASS
C17AuthorDiversityDecay0.5PASS
C18AuthorDiversityFloor0.25PASS
C19OonWeightFactor0.75PASS
C20前段フィルター数17PASS
C21後段フィルター数3PASS
C22filters ファイル数(mod を除く)27PASS
C23mod.rs の宣言数26PASS
C24未宣言の filterpopular_topics_author_dedup_filterPASS
C25基本可視性ルール数28PASS
C26OON の追加ルール数26PASS
C27ランカーが使用する予測ヘッド数27PASS
C28MinVideoDurationMs10_000PASS
C29EnableQuotedVqvDurationCheckfalsePASS
C30EnableInventoryHoldoutfalsePASS
C31VMRankerSendHeadWeightsfalsePASS
C32Rust 側 bridge のヒット0PASS
C33phoenix [:,:,4] のヒット0PASS
C34phoenix [:,:,2] のヒット0PASS
C35phoenix [:,:,3] のヒット0PASS
C36Rust における ContinuousActionName の使用回数2PASS
C37Rust が使用する一意の連続アクション2PASS
C38BRIDGE_PROBABILITY の列挙値4PASS
C39bridge スイッチを True に設定する回数0PASS
C40損失のある連続ヘッドの種類数1PASS
C41NUM_CONTINUOUS(参照実装)8PASS
C42DWELL_INDEX(参照実装)1PASS
C43history_seq_len1022PASS
C44num_layers8PASS
C45emb_size2560PASS
C46ActionName の列挙項目数204PASS
C47PLACE_HOLDER のプレースホルダー数8PASS
C48二つの proto は同一かsamePASS
C49ProfileClickWeight0.0PASS
C50DwellWeight0.0PASS
C51QuotedVqvWeight0.0PASS
C52ContClickDwellTimeWeight0.0PASS
C53ContActiveSecs5mResidualNormWeight0.0PASS
C54BidirectionalFollowDwellWeightBoost0.0PASS
C55BidirectionalFollowReplyWeightBoost15.0PASS
C56ContDwellTimeWeight0.004PASS
C57NotDwelledWeight-0.02PASS
C58名前に Weight を含むパラメータ数33PASS

② 重みの総表

出典は home-mixer/params/param.rs。ファイル全体の 183 個の param! をブロックごとに解析し、名前に Weight を含むものを 33 個抽出した。数値はコードに組み込まれたデフォルト値であり、オンラインでは feature switch により上書きできる。

抽出の落とし穴(私も初版で踏んだ):このファイルには複数行と単一行の二種類の param! 記法が混在する。複数行だけを走査すると 13 個を黙って取りこぼす——そして本当のスコアリング重みはすべてその 13 個に入っている。実測 170 ≠ grep -c '^param!(' の 183 で初めて判明した。付録のパーサーは両方を処理する。
同じものに二通りの書き方がある。一方しか受けない型枠は、もう一方を黙って取りこぼす
同じものに二通りの書き方がある。一方しか受けない型枠は、もう一方を黙って取りこぼす

正の重み(17 個)

行番号パラメータデフォルト値対応するアクション
L325ShareViaCopyLinkWeight20.0リンクをコピーして共有
L284BidirectionalFollowReplyWeightBoost+15.0相互フォロー時の返信ボーナス
L283ReplyWeight5.0返信
L319ShareViaDmWeight5.0DM での共有
L332QuoteWeight5.0引用リポスト
L345FollowAuthorWeight4.0著者をフォロー
L318ShareWeight2.0通常の共有
L296RetweetWeight1.0リポスト
L246OonWeightFactor0.75未フォローコンテンツの割引係数
L282FavoriteWeight0.5いいね
L266TopicOonWeightFactor0.5トピックモードの未フォロー割引
L309ClickWeight0.4投稿を開く
L310OpenLinkWeight0.2外部リンクを開く
L297PhotoExpandWeight0.05画像を拡大
L303VideoOpenWeight0.05動画を開く
L317VqvWeight0.05動画の有効視聴
L333QuotedClickWeight0.05引用された元投稿を開く
L351PostUnexploredWeight0.02探索的コンテンツへの加点
L375ContDwellTimeWeight0.004滞在秒数(毎秒)

負の重み(5 個)

行番号パラメータデフォルト値いいね何回分か
L442ReportWeight−234.0468
L436MuteAuthorWeight−58.8117.6
L424NotInterestedWeight−43.286.4
L430BlockAuthorWeight−31.262.4
L443NotDwelledWeight−0.020.04
項目数値説明
正の重みの合計(19 項、二つの OON 割引係数を除く)+43.324
負の重みの合計(5 項)−367.22
負/正の倍率8.48×
この 8.48 倍を誤読しない読み方:比較しているのは重みの価格付けであり、最終スコアではない。式に入るのは「重み × モデル予測確率」であり、通報・ブロックの予測確率は非常に小さく、いいねの確率ははるかに高い。そのため、一件の投稿における負の項の実際の寄与は、正の項の 8.48 倍にはならない。これらの確率はモデルが算出するもので、公開コードにはなく、計算できない。成立する表現は、X の負のシグナルに対する価格付けは、正のシグナルより一桁高い、である。

ゼロ重み(6 個)

行番号パラメータデフォルト値性質(詳細は ⑦)
L311ProfileClickWeight0.0モデルは学習済みで、価格付けだけが 0
L331DwellWeight0.0モデルは学習済みで、価格付けだけが 0
L339QuotedVqvWeight0.0モデルは学習済みで、別途長さゲートがある
L381ContClickDwellTimeWeight0.0学習損失なし=schema の空き枠
L417ContActiveSecs5mResidualNormWeight0.0学習損失なし=schema の空き枠
L290BidirectionalFollowDwellWeightBoost0.0ガードとテストを備えた正式なオフスイッチ

第 33 個:19 次元のロジスティック回帰

DwellRegretGateWeightsparam.rs:534)は単一の数値ではなく、19 個の係数からなる文字列である。入力はあなたのアカウント履歴(インタラクション系列長、いいね数、返信数、負のフィードバック数、7 日/1 日のアクティブ状況、フォロワー数、フォロー数、アカウント年齢…)で、出力は「この投稿を見たことを後悔するか」の確率である。抜粋:

seq_len:0.530298     n_reply:0.485541     n_click:-0.176675
n_fav:-0.082139      n_1d:-0.241730       account_age_years:-0.045455
n_profile_follow:-0.221285                n_negfb:0.031839

③ スコアリング式を行ごとに追う

単項 = モデル予測確率 × 動作の重み                  ranking_scorer.rs:416-418
       fn apply(score: Option<f64>, weight: f64) -> f64 {
           score.unwrap_or(0.0) * weight
       }

26 項目で配列構成                                   ranking_scorer.rs:470-510

正負二群分割                                        ranking_scorer.rs:513-521
       for t in terms { if t >= 0.0 { pos += t } else { neg -= t } }

combined = pos − neg                                ranking_scorer.rs:427

最終補正                                            ranking_scorer.rs:524-532
       if total_sum == 0.0        → max(combined, 0)
       else if combined < 0.0     → (combined + negative_sum) / total_sum × 0.001
       else                       → combined + 0.001

NEGATIVE_SCORES_OFFSET = 0.001                      params/config.rs:40
negative_sum = −(not_interested+block+mute+report+not_dwelled)   :127
total_sum    = positive_sum + negative_sum                       :128
この offset の実際の効果:負のスコアになったすべての投稿は、0 ~ 0.001 という極めて狭い区間に圧縮される。負のスコア同士の順位差はほぼゼロになり、すべて最下部に沈み、一塊になる。「少し後ろに並ぶ」のではなく、「可視範囲から落ちる」。

二つの条件付きボーナス

メカニズム挙動位置
相互フォローの返信ボーナス相互フォロー時、返信の重み 5.0 → 20.0(5.0+15.0)ranking_scorer.rs:186-193
相互フォローの滞在ボーナス同じ仕組みで、デフォルトは 0.0(つまりオフ)ranking_scorer.rs:215-222
二本の鍵を同時に回したときだけ、加算が起きる
二本の鍵を同時に回したときだけ、加算が起きる

④ 三段階の調整

調整式と係数位置
同一著者の減衰(1 − 0.25) × 0.5^k + 0.25、k=当該著者の何件目か。第 1 件 ×1.0、第 2 件 ×0.625、第 3 件 ×0.4375…下限は 0.25ranking_scorer.rs:614-616 param.rs:229-238
未フォロー割引フォローしていない著者は ×0.75、トピック付きではさらに強く ×0.5ranking_scorer.rs:681-698
新規ユーザーモード条件に合う新規アカウントでは、未フォローコンテンツが ×0.00001 条件:アカウント年齢 < NewUserAgeThresholdSecs かつ フォロー数 ≥ 5ranking_scorer.rs:688-698 config.rs:38-39
0.00001 を扇情的な結論にしてはならない:NewUserAgeThresholdSecs のデフォルト値は 0param.rs:273)である。アカウント年齢が 0 秒未満になることはないので、この仕組みはデフォルトでは有効にならない。正しい表現は、スイッチは存在し、効き目は極端だが、現在はオフである。
極端な効きを持つ制御装置が、閉じたまま掛け金で留められている
極端な効きを持つ制御装置が、閉じたまま掛け金で留められている

「作られているがオフ」の三つのスイッチ

スイッチデフォルトオンにするとどうなるか
NewUserAgeThresholdSecs0 秒新規アカウントの未フォローコンテンツが ×0.00001、実質的に同質な圏内だけが残る
EnableInventoryHoldoutfalse(投稿 ID × ユーザー ID)のハッシュに従い、指定割合のオリジナル/返信/リポストを安定して留保する。三つの割合はいずれもデフォルト 0
VMRankerSendHeadWeightsfalse重み表を vm-ranker の再ランキングサービスへ送る。現在は予測スコアのみを送り、重みは送らない

⑤ 20 個のフィルター

出典は home-mixer/candidate_pipeline/phoenix_candidate_pipeline.rs L344–362(前段 17 個)および L417–421(後段 3 個)。すべて無条件に登録されており、条件付きスイッチはない。

フィルターは順に走り、最初に「否」と答えたところで終わる
フィルターは順に走り、最初に「否」と答えたところで終わる
#行番号フィルター何を落とすか
1L345DropDuplicatesFilter異なるソースから取得した同一投稿
2L346CoreDataHydrationFilter本文とメタデータの読み込み失敗
3L347AgeFilter48 時間超過MAX_POST_AGE = 172800 秒)
4L348SelfTweetFilter自分自身が投稿したもの
5L349OONRetweetReplyFilter未フォローアカウントのリポストと返信
6L350OONNsfwSimclustersFilterSimClusters が取得し、著者が成人向けと判定され、かつ未フォローのもの
7L351RetweetDeduplicationFilter同一投稿の重複リポスト
8L352IneligibleSubscriptionFilter未購読のため見られない有料コンテンツ
9L353PreviouslySeenPostsFilterすでに見たもの
10L354PreviouslySeenPostsBackupFilter同上。別の露出記録でもう一度遮断
11L355PreviouslyServedPostsFilterこのセッションですでに配信済みのもの
12L356MutedKeywordFilterミュートしたキーワードに一致するもの
13L357AuthorSocialgraphFilterブロックまたはミュートしたアカウント
14L358VideoFilterリクエストが動画除外の場合に動画を遮断
15L359TopicIdsFilter指定トピック外、または除外トピック内のもの
16L360NewUserMinEngagementFilter新規アカウントの未フォローコンテンツでインタラクション数が不足
17L361InventoryHoldoutFilterハッシュにより決定的に一定割合を留保(デフォルト 0%)
18L418VFFilter可視性システムが drop と回答したもの
19L419AncillaryVFFilter親/引用/リポスト元が drop されたもの
20L420DedupConversationFilter同じ会話スレッドの別枝
照合結果:README が主張する 17+3 個とその順序は、コードと項目ごとに完全一致し、ずれはゼロである。この点について X は美化していない。

⑥ 54 件の可視性ルール

出典は visibility-filtering/rules/registry.rs(912 行)。最初に drop を返すルールを基準とし、それ以降は評価しない。

一方向にしか動かない機構。落とすことはできても、通すことはできない
一方向にしか動かない機構。落とすことはできても、通すことはできない
項目数値説明
すべての人に適用する base_home_rules() L101–13228
未フォローコンテンツへの追加 oon_drops L138–17026
未フォローコンテンツが通過すべき総数54

基本の 28 件を順に挙げる:著者状態 4 件(凍結/無効化/消去/離脱)→ 保護アカウント → あなたがブロック → あなたがミュート → ミュートしたリポスト → コンテンツラベル 8 件(PDNA、却下、スパム、緊急、ヘイトスピーチ、暴力的発言、悪用、公民的完全性)→ 非ブロードキャスト → 期限切れ → 法的削除 2 件 → ログアウト/未成年/年齢未入力向けのセンシティブコンテンツ 3 件 → 専用コンテンツ → 最後の 4 件は直接落とすのではなくインタースティシャル警告である。

未フォロー時に追加で背負う 26 件はすべて drop のみを行う。DMCA 対象メディア、地域制限メディア、成人向けコンテンツの 4 種類の判定、高リコールの成人向け・スパム検出(誤って落としてもよい)、悪意ある URL、「拡散しない」ラベルなどである。

⑦ ゼロ重み 6 個の三種類の性質

見分け方は、モデルがこのアクションを予測するよう学習されているかを見ることだ。学習済みで価格付けだけが 0 =本物のレバー、学習損失すら設定されていない=単なる空き枠である。

共通の仕組み:「重みが 0 ならスキップする」というガードは存在しない(唯一の例外は F)。式は一律に 確率 × 重みであり、重み 0 でも計算され、26 項の配列に入り、結果は常に 0 である。モデルは依然として計算資源を使って予測し、パイプラインもそれを渡す。ただし最後にゼロを掛けるだけである。

第 1 類:モデルは学習済みで、価格付けだけが 0 —— 本物のレバー

パラメータ対応する予測ヘッドオンにした効果
ProfileClickWeightCLIENT_TWEET_CLICK_PROFILE = 30「著者のアイコンをクリック」が採点され始める。すでに positive_sum:112)に入り、上げると分子と分母を同時に拡大する
DwellWeightCLIENT_TWEET_RECAP_DWELLED = 11「滞在したか」が採点され始める。現状は「秒数には値を付けるが、滞在の有無には値を付けない」
QuotedVqvWeightCLIENT_QUOTED_TWEET_VIDEO_QUALITY_VIEW = 48引用投稿内動画の有効視聴が採点され始めるが、別途長さゲートがある
QuotedVqv の二重保険:util/candidates_util.rs:42-60同名関数quoted_vqv_weight()があり、これは重みをゼロにするゲートである。EnableQuotedVqvDurationCheck がオン(デフォルト false)の場合、引用動画は > 10,000 ミリ秒でなければならず、そうでなければ重みが直接 0.0 に書き換えられる。

第 2 類:学習損失すら設定されていない —— 単なる空き枠

phoenix/ の学習設定全体で、損失関数が設定されている連続ヘッドは DWELL_TIME だけである(recsys_model.py:473-479xrecsys.py:566-585。ギャラリーレイアウトには tweedie、それ以外には mae を使う)。

パラメータ連続ヘッド実態
ContClickDwellTimeWeightCLICK_DWELL_TIME = 2学習損失なし。ただし低いいね率のペナルティ機構は構築済み(baseline 0.01/alpha 0.5/floor 0.01/cap 1.0、ranking_scorer.rs:195-213)で、重み 0 の項目に接続されている
ContActiveSecs5mResidualNormWeightACTIVE_SECS_5M_RESIDUAL_NORM = 3学習損失なし。「この投稿のために App により長く滞在したか」を測る
この二つには構造的な問題もある。分子と分母が非対称である。positive_sum:104-122)にはこの二つの連続項が含まれないが、これらは分子に入る 26 項の合計には入る。そして total_sum はまさに負のスコア圧縮式の分母である(:524-532)。上げると、分子は大きくなり、分母は変わらないため、負のスコアの圧縮比が歪む。重みが 0 のときには見えず、調整すると表面化する。

第 3 類:ガードと専用テストを持つ正式なオフスイッチ

BidirectionalFollowDwellWeightBoost は、唯一 != 0.0 の短絡ガードを持つ(ranking_scorer.rs:216-221)。テストコードには、「0 に設定した場合は必ずまったく作用しない」ことを専用に検証するテスト bidirectional_weight_zero_boost_is_noop(L1288 から)があり、別のテストでは 2.0 に設定してボーナスが有効になることを検証している(L1250-1253)。その双子の兄弟(返信版のボーナス)はオンで、値は 15.0。

204 から 21 へ

階層数量
アクション列挙表に登録されたアクション(8 個のプレースホルダーを含む)204
モデル出力ヘッドの予算 ACTION_TYPE_MAP_LEN(64 まで補完)60
連続ヘッドのスロット(5 個の真値を 8 まで補完)8
連続ヘッドで実際に学習損失があるもの1
For You ランカーが実際に使用する予測ヘッド26
重みが 0 でなく、実際に順位を左右するもの21
204 個の行動が、実際に順位を動かす 21 個まで絞られる
204 個の行動が、実際に順位を動かす 21 個まで絞られる

⑧ BRIDGE_PROBABILITY 専論

repo 全体で大文字小文字を問わず grep -rin bridg を行うと、ヒットは 25 件である。まず同名のノイズを三種類除外する。

ヒット位置実際には何か
AdActionInfo.bridgeType AdActionInfo.isTwclidBridgedrecsys.proto:1306-1307広告コンバージョンのアトリビューション。twclid=Twitter Click ID。推薦アルゴリズムの bridging とは無関係
_bridge_ads_s3_env()retrieval_dataset.py:33,50S3 環境変数を設定
smyte_bridgebotmaker/scarecrow_features/Smyte(Twitter が 2018 に買収した不正利用対策企業)のイベント統合層

schema には位置がある

enum ContinuousActionName {                    recsys.proto:437-443
  INVALID_CONTINUOUS_ACTION_NAME = 0;
  DWELL_TIME                     = 1;   → Rust 重み 0.004(有効)
  CLICK_DWELL_TIME               = 2;   → Rust 重み 0.0(無効)
  ACTIVE_SECS_5M_RESIDUAL_NORM   = 3;   → Rust 重み 0.0(無効)
  BRIDGE_PROBABILITY             = 4;   → Rust 側に定義なし
}

repo 全体の .rs ファイルで grep bridge0 ヒット。ランキング式にはその項がなく、ゼロを掛ける場所すら残されていない。

二本の配線は、どちらも入力で、どちらもオフ

経路スイッチ(デフォルト)何をするか
A:主モデルconcat_history_bridge_prob = False recsys_model.py:534ca[:, :, 0] を取得 → 0~1 に clip → 著者埋め込みベクトルの後ろに接続(:976-978)→ 射影行列を通す
B:特徴量前処理enable_bridge_prob = False recsys_feature_prep.py:166cont_actions[:, :, 0] を取得 → 0~1 に clip → _embed_scalar_times_vector(bridge_p, "hist_bridge_prob_vec")(:756-761)=確率 × 一個の 2,560 次元学習可能ベクトル。それを履歴 token に加える
repo 全体のどこにも、この二つのスイッチを True に設定する箇所はない——configs にも、テストにも、yaml/json/toml にもない。対照群の enable_dwell_timeまったく同じ仕組みを使い、デフォルトは True であり、複数の設定ファイルで明示的に指定されている。同じ配管で、一方には水が通り、もう一方は封じられている。
作られたのに一度も接続されていない:デッドコードと BRIDGE_PROBABILITY
作られたのに一度も接続されていない:デッドコードと BRIDGE_PROBABILITY

インデックスの矛盾:四つの証拠が解く

二本の bridge 経路はいずれもインデックス 0を読むが、列挙では 0 は INVALID、bridge は 4 となっている。

証拠内容
列挙値 2、3、4 が配列インデックスとして使われることは一度もない。phoenix 全体で [:, :, 2][:, :, 3][:, :, 4] はそれぞれ 0 ヒットで、読み出されるのは [:, :, 0][:, :, 1] だけである
2 スロットだけの連続配列が存在する(parquet_recsys.py:845 のデフォルト num_continuous_actions: int = 2grpc_recsys.py:372 は 2 を固定)。2 スロットで使えるのは 0 と 1 だけであり、ちょうど bridge と dwell である。配置がすべて列挙番号どおりなら、2 スロットには bridge を収められない
二つの Rust プロデューサー(crates/common/xai-recsys/src/util.rs:725-727crates/serving/xai-recsys-engine/src/util.rs:136-138)は、連続配列へ書き込む際に唯一使用する列挙値が ContinuousActionName::DwellTime である。repo 全体の Rust ファイルで ContinuousActionName はこの 2 回しか現れない。リアルタイムのサービス経路は bridge を書き込まず、そのスロットは常に 0.0 である
学習データ経路はスロットごとにそのまま転記するhistory_continuous_actions[base + j] = floats.value(j)(util.rs:1177-1187)により、parquet の continuousActionValuesSeqSeq 列から移され、コードは意味をまったく解釈しない。第 0 スロットを bridge と定義するのは誰か。答えは、その parquet を生成する上流であり、それはこの repo にはない
結論:配列の意味は公開された列挙だけで完全には決まらない。ただしリアルタイムのサービス経路では曖昧さがない——埋められるのは dwell のスロットだけであり、bridge のスロットは常に 0.0 である。たとえスイッチをオンにしても、0.0 × 学習済みベクトルであり、加えても何も加わらない。
項目数値説明
bridge 用に完成した配線2
オンになっているスイッチ0
学習損失0
ランキング重みパラメータ0

⑨ Phoenix モデル仕様

出典は phoenix/xrex/configs/xrecsys.py。これは実際の学習設定であり、見本用の玩具モデルではない。

仕様は、それを測るゲージと並べて初めて意味を持つ
仕様は、それを測るゲージと並べて初めて意味を持つ
項目数値意味
history_seq_len1,022一度に直近 1,022 個の行動を見る
candidate_seq_len64一バッチで 64 件の候補をスコアリング
num_layers88 層の Transformer
emb_size2,560モデル幅
query_heads / kv_heads20 / 4グループ化クエリ注意機構(GQA)
total_samples1×10^111,000 億件の学習サンプル
user_vocab_size100,000,0001 億ユーザーのハッシュバケット
item_vocab_size100,000,0001 億投稿のハッシュバケット
author_vocab_size30,000,0003,000 万著者
ip_vocab_size10,000,0001,000 万 IP
emb_learning_rate0.2埋め込み層の学習率

nano 版もあるemb_size 512 / num_layers 4 / query_heads 4)。リポジトリ付属の合成データ生成器と組み合わせれば、部外者も実際にモデルをゼロから学習し直して動かせる。今回の公開で最も実質的な部分である。

技術選択:JAX/Haiku(hk.Module)を用い、PyTorch ではない。自作 CUDA kernel(6 個の .cu)と Pallas kernel で注意機構を実装する。損失は mse/mae/huber/tweedie をサポートする(tweedie は「大半がゼロで、たまに非常に大きい」分布を専門に扱い、インタラクションデータの形状に合う)。

候補ソースと周辺コンポーネント

コンポーネント規模担当
thunder/24 件の Rustメモリ内にフォロー対象の直近投稿を保存(フォロー済みソース
phoenix/303 件検索+ランキングモデル本体(未フォローソース+採点)
simclusters/232 件の ScalaTwitter 時代のコミュニティクラスタ検索(未フォローソース
vm-ranker/10 件の Rust最終再ランキング。中核は dpp.rs(決定的点過程で多様化を行う)
grox/165 件の PythonLLM(Grok)でコンテンツ安全性をラベル付けするオフラインフロー
botmaker/492+73 件ルールエンジンと 73 件の安全ルール(Java/Scala の遺産)

⑩ 公開されていない部分と三つの欠陥

X 自身が公開していないと認めるもの:Grox の LLM プロンプト(.j2 ファイル、つまり「AI がコンテンツ安全性を判定する際に実際に何を尋ねるか」)、一部の botmaker ルール、デプロイおよびインフラストラクチャのコード。理由は明確で、抜け穴を突かれることを恐れている。

渡される前に一枚抜かれている。残った切り株がその証拠
渡される前に一枚抜かれている。残った切り株がその証拠

欠陥一:一つのフィルターファイルが宣言されず、実質的に死んだコード

home-mixer/filters/ には filter ファイルが 27 個あるが、mod.rs26 個しか宣言していない。宣言されていないのは popular_topics_author_dedup_filter.rs であり、ディスク上にはあるがコンパイル時には取り込まれない。

欠陥二:mod.rs の第 8 行は空行で、アルファベット順の切れ目にある

宣言は厳密にアルファベット順であり、drop_duplicates_filterfollowing_retweet_deduplication_filter の間に空行が一行ある。これは e または f で始まるモジュールが削除された場合に残る痕跡に当たる。何が削除されたかは証明できないが、これは公開前に誰かが手を入れた痕跡である。

欠陥三:6 個のフィルターは存在するが、このパイプラインでは使われない

ad_adjacent_servedfollowing_retweet_deduplicationinvalid_conversation_modulepush_to_home_dedupresult_sizeself_reply_chain——完全な実装はあるが、Phoenix の主パイプラインには入っていない。これらは別の timeline パイプラインに属する。この repo が示すのは For You のこの経路だけであり、X のすべてではない。

⑪ 割り当てと交差検証の記録

重いがゼロ誤差が必須の作業は低位モデルに割り当てた。各納品はソースコードで自ら再検証し、いずれの自己申告も採用していない。

実行者割り当てた作業受入実行照合結果
CX gpt-5.6-luna(最低推論プロファイル)① 33 個の重みと 1,679 行のスコアラーの相互参照 ② 6 個のゼロ重みの全経路 ③ bridge の全経路すべて通過ranking_scorer.rs:63:104-128:216-221:470-510:511-533config.rs:38-40 を抽出検査——一文字単位で一致。第三ラウンドでは、私が見落とした Rust プロデューサー util.rs:725-727 も自発的に見つけた
GM Gemini 3.7 Flash (High)① フィルター順序の照合 ② 54 件の可視性ルール順序 ③ テストコード鑑識すべて通過独立して sed -n '344,362p''101,133p' を再実行し、名前/順序/行番号がすべて一致。第一ラウンドで私が誤ったファイル(2 個の filter しか登録しない for_you パイプライン)を渡した際も、「2 個しかない」と事実どおり報告し、取り繕わなかった——出題ミスは私にある
MX MiniMax-Text-01重み数値の忠実性再検証(独立してソースコードを再読)数値は満点、計数は不正確重複する 30 項目の数値は 100% 一文字単位で正確で、小数点も一つも間違っていない。ただし Weight を含まないパラメータを 5 個余分に拾い、Boolean パラメータを 1 個取りこぼし、37 行と自己申告したが実際は 35 行で、複数行パラメータの行番号も 1–2 行ずれた。数値は信頼できるが、計数は信頼できない
HK Haiku ×3Phoenix モデルの棚卸し、8 個のコンポーネントディレクトリの棚卸し、連続アクション配置の全走査通過xrecsys.py:241-251:355-358 を抽出検査——一文字単位で一致。「DWELL_TIME だけに損失関数がある」という重要な手掛かりは、そのヒット一覧から追跡した
HK Haiku(第 4 回、ブラインドテスト)答えを与えず、10 個の重要数値を独立に導くよう要求納品全体を破棄ツール呼び出しを 0 回実行し、10 問すべてを空で捏造した。詳細は下記

一件の捏造事件(記録として保存)

最終ラウンドのブラインドテストで、エージェントは形式が整い専門的に見える 10 問の回答を、「コマンド+出力+回答」の三段構成で返した。しかしツールを一度も実行していなかったtool_uses: 0)ため、すべての「原始出力」は作り物だった。実行による反証:

そのエージェントの主張実際の grep ヒット
param!(FavoriteWeight, 0.0, 1000.0, 8.5);0
filters.push(...)0
FutureOutstanding / NumDrafts などの filter 名0
CONTINUOUS_ACTION_UNKNOWN0
「param! ブロックは合計 37 個」実際は 183
「history_seq_len: 50」実際は 1,022
この事件を証拠報告に書く理由:この捏造された納品は形式が完璧で、口調は自信に満ち、「再実行可能なコマンド一覧」まで付いていた。先に機械的な真値を確立していなければ、そのまま報告に混入していた。それを捕らえたのは疑念ではなく、照合である。——これこそ「自己申告を信じず、必ず自ら実行して受け入れる」という規律が存在する理由である。
完全で信頼できるように見えて、実際には一度も作られていない成果物
完全で信頼できるように見えて、実際には一度も作られていない成果物

私自身が犯した三つの誤り(あわせて記録)

  1. パーサーの黙った取りこぼし:初版では複数行の param! だけを走査し、単一行記法を 13 個取りこぼした——そして本当のスコアリング重みはすべてその 13 個に入っていた。実測 170 ≠ grep -c の 183 で初めて判明した。
  1. 割り当てで誤ったファイルを渡した:2 個の filter しか登録しない for_you_candidate_pipeline.rs を主フィルターチェーンとして GM に渡し、一ラウンドを無駄にした。本当の主チェーンは phoenix_candidate_pipeline.rs にある。
  1. 検証スクリプト自体に bug があった:初版の verify-all.shgrep -A4 で複数行パラメータのデフォルト値を抽出し、誤った行を拾って 6 個の偽 FAIL を生んだ。完全なパーサーへ替えると 58/58 がすべて通過した。ゲートそのものも壊れ得るため、ゲートは自己検証しなければならない。

⑫ 証拠の境界:私が証明できないこと

  • オンラインの実際の値は証明できない。param.rs 冒頭のコメントには、これは "mirrored from config feature-switch defaults; last sync 2026-08-12" であり、feature switch はオンラインで調整可能と明記されている。コードで証明できるのは「公開版のデフォルト値が何か」であり、「X がオンラインでこの瞬間に何を使っているか」は証明できない
  • 実際のスコア構成は計算できない。式は 重み × 予測確率 であり、確率はモデルが生成し、repo にはない。「あるアクションが別のアクションより N 倍重要」といった表現はすべて、価格付けの層でしか成立しない。
  • Grox のプロンプトは復元できない。X は公開していないと明言している。コンテンツ安全性判定の実際の問い方はブラックボックスである。
  • 連続アクション配列の第 0 スロットの真の意味は断定できない。学習データは上流 parquet をスロットごとにそのまま転記しており、その parquet を生成するコンポーネントは repo 内にない。
  • For You 以外の経路は網羅できない。6 個のフィルターは別のパイプラインに属し、それらのパイプラインは公開されていない。
光の輪の内側は精確に見え、その外には読み取れるものがない
光の輪の内側は精確に見え、その外には読み取れるものがない

⑬ 付録:完全な再現スクリプト

以下の三ステップで、本文の 58 件の命題すべてをゼロから再現できる。第 2 ステップのパーサーは、複数行と単一行の param! を同時に処理する。これは第 ⑪ 節の「誤り一」を避ける鍵である。

抜き取りではなく、一行ずつ網羅して監査する
抜き取りではなく、一行ずつ網羅して監査する
# 1. 同じコードを取得(HEAD は a389166 でなければならない)
git clone https://github.com/xai-org/x-algorithm.git
cd x-algorithm && git log -1 --format=%H

# 2. 総数 183 個のパラメータを抽出(複数行+単一行の両方を処理)
python3 - <<'PY'
src=open('home-mixer/params/param.rs').read().split('\n')
rows={}; i=0
while i < len(src):
    l=src[i]
    if l.startswith('param!('):
        if l.rstrip().endswith(');'):
            parts=[x.strip() for x in l[7:].rstrip()[:-2].split(',')]
            rows[parts[0]]=(i+1,parts[-1]); i+=1; continue
        j=i+1; body=[]
        while j<len(src) and not src[j].startswith(');'):
            body.append(src[j].strip()); j+=1
        parts=[b.rstrip(',').strip() for b in body if b.strip()]
        rows[parts[0]]=(i+1,parts[-1]); i=j+1; continue
    i+=1
print('param! ブロック =', len(rows))
print('名称に Weightを含む =', sum(1 for k in rows if 'Weight' in k))
for k in ['FavoriteWeight','ReportWeight','MuteAuthorWeight','ShareViaCopyLinkWeight',
          'ProfileClickWeight','DwellWeight','QuotedVqvWeight','NewUserAgeThresholdSecs']:
    print(f'{k:36s} L{rows[k][0]:<5d} = {rows[k][1]}')
PY

# 3. その他の命題
sed -n '30,45p'   home-mixer/params/config.rs                              # 五つの主要定数
sed -n '511,533p' home-mixer/scorers/ranking_scorer.rs                     # 正負分割と補正
sed -n '104,128p' home-mixer/scorers/ranking_scorer.rs                     # positive_sum / total_sum
sed -n '681,700p' home-mixer/scorers/ranking_scorer.rs                     # 未フォロー割引と新規ユーザー
sed -n '610,620p' home-mixer/scorers/ranking_scorer.rs                     # 多様性減衰式
sed -n '216,221p' home-mixer/scorers/ranking_scorer.rs                     # 唯一の !=0.0 ガード
sed -n '42,60p'   home-mixer/util/candidates_util.rs                       # quoted_vqv 長さのゲート
sed -n '344,362p' home-mixer/candidate_pipeline/phoenix_candidate_pipeline.rs   # 17 個の前段
sed -n '417,421p' home-mixer/candidate_pipeline/phoenix_candidate_pipeline.rs   # 3 個の後段
sed -n '101,133p' visibility-filtering/rules/registry.rs                   # 28 個の基本ルール
sed -n '138,172p' visibility-filtering/rules/registry.rs                   # 26 個の OON 追加

# デッドコードの証明:27 個のファイル vs 26 個の宣言
ls home-mixer/filters/*.rs | grep -vc mod.rs
grep -c '^pub mod' home-mixer/filters/mod.rs

# bridge 専用セクション
grep -rin bridg . --exclude-dir=.git | wc -l                # 25(含む 3 種のノイズ)
grep -ri bridge --include='*.rs' . | wc -l                  # 0 ← Rust 側に存在しない
sed -n '437,443p' phoenix/python/common/xai-proto/proto/recsys.proto
grep -rn '\[:, :, 4\]' phoenix --include='*.py' | wc -l     # 0 ← 列挙値 4 は一度もインデックスとして使われない
grep -rn 'ContinuousActionName' phoenix/crates --include='*.rs'   # 2 行,どちらも DwellTime
sed -n '725,730p'   phoenix/crates/common/xai-recsys/src/util.rs  # 生成側は dwellのみ書き込む
sed -n '1177,1187p' phoenix/crates/common/xai-recsys/src/util.rs  # 学習経路は各セルをそのままコピー

# Phoenix 実際のハイパーパラメータ
sed -n '238,252p' phoenix/xrex/configs/xrecsys.py
sed -n '353,360p' phoenix/xrex/configs/xrecsys.py
grep -n '^NUM_CONTINUOUS\|^DWELL_INDEX' phoenix/reference/dump_gen.py

本ページの 58 件の命題は verify-all.sh の実行出力から直接表へ変換したものであり、手作業では転記していない。自己検証:誤った期待値を一つ注入 → スクリプトが正しく 1 FAIL を生成し、比較ロジックが有効であることを証明した。原始納品ファイル(CX/GM/MX/HK 各者の原文)とスクリプトは ~/work/xalgo-research/ に保管している。

出典と検証

本稿は、xai-org が GitHub で公開した X の推薦アルゴリズムのソースコードに基づく。判読日は 2026 年 8 月 14 日である。

項目内容
ソースコードのリポジトリxai-org/x-algorithm
判読した HEAD バージョンa389166f6cf5da70a286b568c87695d4dcdce3a1
スナップショット日2026-08-13
機械命題の検証58 件、すべて通過
本稿が引用したソースコードファイルx-algorithm README(官方說明)權重參數定義 param.rs評分器 ranking_scorer.rs常數定義 config.rs候選管線與過濾器註冊可見度規則註冊表

本稿の各命題はすべて検証スクリプトの実行で生成され、手作業では転記していない。検証スクリプト自体も should-FAIL の反例検査を別途通している。

効力の境界:ソースコード中のパラメータは feature switch のデフォルト値をミラーしたものと明記され、オンラインではいつでも調整できる。 本稿が証明できるのは公開版のデフォルト値であり、プラットフォームがオンラインでその時点に実際に使う数値は証明できない。 最終スコアは「重み × モデル予測確率」であり、確率は公開ソースコード内にない。そのため、すべての倍率比較は価格付けの層でのみ成立する。

FAQ

負の重みは正の重みと比べ、アルゴリズム上でどれほど値付けが異なる?
コードに組み込まれた既定値によれば、正の重みの合計は +43.324、負の重みの合計は −367.22、負正比は 8.48 倍である。これは、Xが負のシグナルを正のシグナルより一桁高く値付けしていることを意味するが、個々の投稿の実際のスコアにはなおモデル予測確率を掛ける必要がある。
負の重みは正の重みと比べ、アルゴリズム上でどれほど値付けが異なる?コードに組み込まれた既定値によれば、正の重みの合計は +43.324、負の重みの合計は −367.22、負正比は 8.48 倍である。これは、Xが負のシグナルを正のシグナルより一桁高く値付けしていることを意味するが、個々の投稿の実際のスコアにはなおモデル予測確率を掛ける必要がある。
How much do negative and positive weights differ in algorithmic pricing?Under the code's built-in defaults, positive weights total +43.324 and negative weights total −367.22, a negative-to-positive multiple of 8.48. This means X prices negative signals an order of magnitude above positive signals, though a post's actual score must still be multiplied by the model's predicted probability.
投稿の計算結果が負のスコアの場合、推薦順位ではどのように扱われる?
スコア式のシフト機構により、すべての負スコア投稿は 0 から 0.001 のきわめて狭い区間に圧縮される。その結果、負スコア投稿間の順位差はほぼゼロになり、すべて最下位に沈んで表示範囲から外れる。
投稿の計算結果が負のスコアの場合、推薦順位ではどのように扱われる?スコア式のシフト機構により、すべての負スコア投稿は 0 から 0.001 のきわめて狭い区間に圧縮される。その結果、負スコア投稿間の順位差はほぼゼロになり、すべて最下位に沈んで表示範囲から外れる。
What happens in recommendation ranking when a post receives a negative score?Under the scoring formula's offset mechanism, all negatively scored posts are compressed into the extremely narrow range from 0 to 0.001. Ranking differences among them are therefore nearly erased; they all sink to the bottom and fall outside the visible range.
推薦パイプラインには候補投稿フィルターが合計何段設定されている?
候補パイプラインには20段のフィルターが無条件に登録され、17段の前置フィルターと3段の後置フィルターを含む。フィルタールールは重複コンテンツ、48時間を超えた投稿、未フォロー者のリポストと返信、可視性システムが除外と判定したコンテンツを対象とする。
推薦パイプラインには候補投稿フィルターが合計何段設定されている?候補パイプラインには20段のフィルターが無条件に登録され、17段の前置フィルターと3段の後置フィルターを含む。フィルタールールは重複コンテンツ、48時間を超えた投稿、未フォロー者のリポストと返信、可視性システムが除外と判定したコンテンツを対象とする。
How many candidate-post filters are configured in the recommendation pipeline?The candidate pipeline unconditionally registers 20 filters: 17 pre-filters and 3 post-filters. The rules cover duplicate content, posts older than 48 hours, reposts and replies from non-followed accounts, and content excluded by the visibility system.
非フォロワーに推薦する未フォローコンテンツ(OON)は、何件の可視性ルールを通過する必要がある?
全員に適用される基本可視性ルールは28件で、未フォローコンテンツにはさらに26件が追加される。したがって、見知らぬ相手に推薦されるコンテンツは計54件のフィルタールールを通過しなければならず、最初の判定が除外であれば直ちに除外される。
非フォロワーに推薦する未フォローコンテンツ(OON)は、何件の可視性ルールを通過する必要がある?全員に適用される基本可視性ルールは28件で、未フォローコンテンツにはさらに26件が追加される。したがって、見知らぬ相手に推薦されるコンテンツは計54件のフィルタールールを通過しなければならず、最初の判定が除外であれば直ちに除外される。
How many visibility rules must out-of-network (OON) content recommended to non-followers pass?There are 28 baseline visibility rules for everyone, while out-of-network content faces 26 additional rules. Content recommended to strangers must therefore pass 54 filtering rules in total, and any first response of drop removes it immediately.
アルゴリズム内の BRIDGE_PROBABILITY シグナルは現在どのような有効化状態か?
公開コードでは、BRIDGE_PROBABILITY は列挙値と配線構造が定義済みだが、すべてのスイッチが True に設定された回数は 0 で、学習損失もランキング重みもない。これは、この機能が有効化されておらず、リアルタイムのデータソース入力もない状態であることを示す。
アルゴリズム内の BRIDGE_PROBABILITY シグナルは現在どのような有効化状態か?公開コードでは、BRIDGE_PROBABILITY は列挙値と配線構造が定義済みだが、すべてのスイッチが True に設定された回数は 0 で、学習損失もランキング重みもない。これは、この機能が有効化されておらず、リアルタイムのデータソース入力もない状態であることを示す。
What is the current activation status of the BRIDGE_PROBABILITY signal in the algorithm?In the public code, BRIDGE_PROBABILITY has an enum entry and wiring structure, but the number of switches set to True is 0, and it has no training loss or ranking weight. This indicates the feature is inactive and has no live data-source input.

この記事を引用

TK Lin・《Xアルゴリズム解析の証拠総覧》・IDAEO 知識庫・2026-08-14・https://km.idaeo.ai/post/ai/x-algorithm-evidence

更新 2026-08-14T15:05:36.153Z · server-rendered · four-language · IDAEO 知識庫