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 の実行で生成された。本ページの数値は同スクリプトの出力を直接表に変換したものであり、手作業で書き写してはいない。スクリプト本体は付録に示す。

| 項目 | 数値 | 説明 |
|---|---|---|
| PASS | 58 | — |
| FAIL | 0 | — |
| should-FAIL 反例検証(誤値の注入が必ず検出されること) | 1 | — |
| 番号 | 命題 | 実測値 | 結果 |
|---|---|---|---|
| C01 | HEAD | a389166f6cf5da70a286b568c87695d4dcdce3a1 | PASS |
| C02 | param! ブロック数 | 183 | PASS |
| C03 | ファイル総数 | 2015 | PASS |
| C04 | FavoriteWeight | 0.5 | PASS |
| C05 | ReplyWeight | 5.0 | PASS |
| C06 | RetweetWeight | 1.0 | PASS |
| C07 | ShareViaCopyLinkWeight | 20.0 | PASS |
| C08 | ReportWeight | -234.0 | PASS |
| C09 | MuteAuthorWeight | -58.8 | PASS |
| C10 | BlockAuthorWeight | -31.2 | PASS |
| C11 | NotInterestedWeight | -43.2 | PASS |
| C12 | NEW_USER_OON_WEIGHT_FACTOR | 0.00001 | PASS |
| C13 | NEW_USER_MIN_FOLLOWING | 5 | PASS |
| C14 | NEGATIVE_SCORES_OFFSET | 0.001 | PASS |
| C15 | MAX_POST_AGE(秒) | 172800 | PASS |
| C16 | NewUserAgeThresholdSecs のデフォルト | 0 | PASS |
| C17 | AuthorDiversityDecay | 0.5 | PASS |
| C18 | AuthorDiversityFloor | 0.25 | PASS |
| C19 | OonWeightFactor | 0.75 | PASS |
| C20 | 前段フィルター数 | 17 | PASS |
| C21 | 後段フィルター数 | 3 | PASS |
| C22 | filters ファイル数(mod を除く) | 27 | PASS |
| C23 | mod.rs の宣言数 | 26 | PASS |
| C24 | 未宣言の filter | popular_topics_author_dedup_filter | PASS |
| C25 | 基本可視性ルール数 | 28 | PASS |
| C26 | OON の追加ルール数 | 26 | PASS |
| C27 | ランカーが使用する予測ヘッド数 | 27 | PASS |
| C28 | MinVideoDurationMs | 10_000 | PASS |
| C29 | EnableQuotedVqvDurationCheck | false | PASS |
| C30 | EnableInventoryHoldout | false | PASS |
| C31 | VMRankerSendHeadWeights | false | PASS |
| C32 | Rust 側 bridge のヒット | 0 | PASS |
| C33 | phoenix [:,:,4] のヒット | 0 | PASS |
| C34 | phoenix [:,:,2] のヒット | 0 | PASS |
| C35 | phoenix [:,:,3] のヒット | 0 | PASS |
| C36 | Rust における ContinuousActionName の使用回数 | 2 | PASS |
| C37 | Rust が使用する一意の連続アクション | 2 | PASS |
| C38 | BRIDGE_PROBABILITY の列挙値 | 4 | PASS |
| C39 | bridge スイッチを True に設定する回数 | 0 | PASS |
| C40 | 損失のある連続ヘッドの種類数 | 1 | PASS |
| C41 | NUM_CONTINUOUS(参照実装) | 8 | PASS |
| C42 | DWELL_INDEX(参照実装) | 1 | PASS |
| C43 | history_seq_len | 1022 | PASS |
| C44 | num_layers | 8 | PASS |
| C45 | emb_size | 2560 | PASS |
| C46 | ActionName の列挙項目数 | 204 | PASS |
| C47 | PLACE_HOLDER のプレースホルダー数 | 8 | PASS |
| C48 | 二つの proto は同一か | same | PASS |
| C49 | ProfileClickWeight | 0.0 | PASS |
| C50 | DwellWeight | 0.0 | PASS |
| C51 | QuotedVqvWeight | 0.0 | PASS |
| C52 | ContClickDwellTimeWeight | 0.0 | PASS |
| C53 | ContActiveSecs5mResidualNormWeight | 0.0 | PASS |
| C54 | BidirectionalFollowDwellWeightBoost | 0.0 | PASS |
| C55 | BidirectionalFollowReplyWeightBoost | 15.0 | PASS |
| C56 | ContDwellTimeWeight | 0.004 | PASS |
| C57 | NotDwelledWeight | -0.02 | PASS |
| C58 | 名前に Weight を含むパラメータ数 | 33 | PASS |
② 重みの総表
出典は home-mixer/params/param.rs。ファイル全体の 183 個の param! をブロックごとに解析し、名前に Weight を含むものを 33 個抽出した。数値はコードに組み込まれたデフォルト値であり、オンラインでは feature switch により上書きできる。
抽出の落とし穴(私も初版で踏んだ):このファイルには複数行と単一行の二種類の param! 記法が混在する。複数行だけを走査すると 13 個を黙って取りこぼす——そして本当のスコアリング重みはすべてその 13 個に入っている。実測 170 ≠ grep -c '^param!(' の 183 で初めて判明した。付録のパーサーは両方を処理する。

正の重み(17 個)
| 行番号 | パラメータ | デフォルト値 | 対応するアクション |
|---|---|---|---|
| L325 | ShareViaCopyLinkWeight | 20.0 | リンクをコピーして共有 |
| L284 | BidirectionalFollowReplyWeightBoost | +15.0 | 相互フォロー時の返信ボーナス |
| L283 | ReplyWeight | 5.0 | 返信 |
| L319 | ShareViaDmWeight | 5.0 | DM での共有 |
| L332 | QuoteWeight | 5.0 | 引用リポスト |
| L345 | FollowAuthorWeight | 4.0 | 著者をフォロー |
| L318 | ShareWeight | 2.0 | 通常の共有 |
| L296 | RetweetWeight | 1.0 | リポスト |
| L246 | OonWeightFactor | 0.75 | 未フォローコンテンツの割引係数 |
| L282 | FavoriteWeight | 0.5 | いいね |
| L266 | TopicOonWeightFactor | 0.5 | トピックモードの未フォロー割引 |
| L309 | ClickWeight | 0.4 | 投稿を開く |
| L310 | OpenLinkWeight | 0.2 | 外部リンクを開く |
| L297 | PhotoExpandWeight | 0.05 | 画像を拡大 |
| L303 | VideoOpenWeight | 0.05 | 動画を開く |
| L317 | VqvWeight | 0.05 | 動画の有効視聴 |
| L333 | QuotedClickWeight | 0.05 | 引用された元投稿を開く |
| L351 | PostUnexploredWeight | 0.02 | 探索的コンテンツへの加点 |
| L375 | ContDwellTimeWeight | 0.004 | 滞在秒数(毎秒) |
負の重み(5 個)
| 行番号 | パラメータ | デフォルト値 | いいね何回分か |
|---|---|---|---|
| L442 | ReportWeight | −234.0 | 468 |
| L436 | MuteAuthorWeight | −58.8 | 117.6 |
| L424 | NotInterestedWeight | −43.2 | 86.4 |
| L430 | BlockAuthorWeight | −31.2 | 62.4 |
| L443 | NotDwelledWeight | −0.02 | 0.04 |
| 項目 | 数値 | 説明 |
|---|---|---|
| 正の重みの合計(19 項、二つの OON 割引係数を除く) | +43.324 | — |
| 負の重みの合計(5 項) | −367.22 | — |
| 負/正の倍率 | 8.48× | — |
この 8.48 倍を誤読しない読み方:比較しているのは重みの価格付けであり、最終スコアではない。式に入るのは「重み × モデル予測確率」であり、通報・ブロックの予測確率は非常に小さく、いいねの確率ははるかに高い。そのため、一件の投稿における負の項の実際の寄与は、正の項の 8.48 倍にはならない。これらの確率はモデルが算出するもので、公開コードにはなく、計算できない。成立する表現は、X の負のシグナルに対する価格付けは、正のシグナルより一桁高い、である。
ゼロ重み(6 個)
| 行番号 | パラメータ | デフォルト値 | 性質(詳細は ⑦) |
|---|---|---|---|
| L311 | ProfileClickWeight | 0.0 | モデルは学習済みで、価格付けだけが 0 |
| L331 | DwellWeight | 0.0 | モデルは学習済みで、価格付けだけが 0 |
| L339 | QuotedVqvWeight | 0.0 | モデルは学習済みで、別途長さゲートがある |
| L381 | ContClickDwellTimeWeight | 0.0 | 学習損失なし=schema の空き枠 |
| L417 | ContActiveSecs5mResidualNormWeight | 0.0 | 学習損失なし=schema の空き枠 |
| L290 | BidirectionalFollowDwellWeightBoost | 0.0 | ガードとテストを備えた正式なオフスイッチ |
第 33 個:19 次元のロジスティック回帰
DwellRegretGateWeights(param.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.25 | ranking_scorer.rs:614-616 param.rs:229-238 |
| 未フォロー割引 | フォローしていない著者は ×0.75、トピック付きではさらに強く ×0.5 | ranking_scorer.rs:681-698 |
| 新規ユーザーモード | 条件に合う新規アカウントでは、未フォローコンテンツが ×0.00001 条件:アカウント年齢 < NewUserAgeThresholdSecs かつ フォロー数 ≥ 5 | ranking_scorer.rs:688-698 config.rs:38-39 |
0.00001 を扇情的な結論にしてはならない:NewUserAgeThresholdSecs のデフォルト値は 0(param.rs:273)である。アカウント年齢が 0 秒未満になることはないので、この仕組みはデフォルトでは有効にならない。正しい表現は、スイッチは存在し、効き目は極端だが、現在はオフである。

「作られているがオフ」の三つのスイッチ
| スイッチ | デフォルト | オンにするとどうなるか |
|---|---|---|
| NewUserAgeThresholdSecs | 0 秒 | 新規アカウントの未フォローコンテンツが ×0.00001、実質的に同質な圏内だけが残る |
| EnableInventoryHoldout | false | (投稿 ID × ユーザー ID)のハッシュに従い、指定割合のオリジナル/返信/リポストを安定して留保する。三つの割合はいずれもデフォルト 0 |
| VMRankerSendHeadWeights | false | 重み表を vm-ranker の再ランキングサービスへ送る。現在は予測スコアのみを送り、重みは送らない |
⑤ 20 個のフィルター
出典は home-mixer/candidate_pipeline/phoenix_candidate_pipeline.rs L344–362(前段 17 個)および L417–421(後段 3 個)。すべて無条件に登録されており、条件付きスイッチはない。

| # | 行番号 | フィルター | 何を落とすか |
|---|---|---|---|
| 1 | L345 | DropDuplicatesFilter | 異なるソースから取得した同一投稿 |
| 2 | L346 | CoreDataHydrationFilter | 本文とメタデータの読み込み失敗 |
| 3 | L347 | AgeFilter | 48 時間超過(MAX_POST_AGE = 172800 秒) |
| 4 | L348 | SelfTweetFilter | 自分自身が投稿したもの |
| 5 | L349 | OONRetweetReplyFilter | 未フォローアカウントのリポストと返信 |
| 6 | L350 | OONNsfwSimclustersFilter | SimClusters が取得し、著者が成人向けと判定され、かつ未フォローのもの |
| 7 | L351 | RetweetDeduplicationFilter | 同一投稿の重複リポスト |
| 8 | L352 | IneligibleSubscriptionFilter | 未購読のため見られない有料コンテンツ |
| 9 | L353 | PreviouslySeenPostsFilter | すでに見たもの |
| 10 | L354 | PreviouslySeenPostsBackupFilter | 同上。別の露出記録でもう一度遮断 |
| 11 | L355 | PreviouslyServedPostsFilter | このセッションですでに配信済みのもの |
| 12 | L356 | MutedKeywordFilter | ミュートしたキーワードに一致するもの |
| 13 | L357 | AuthorSocialgraphFilter | ブロックまたはミュートしたアカウント |
| 14 | L358 | VideoFilter | リクエストが動画除外の場合に動画を遮断 |
| 15 | L359 | TopicIdsFilter | 指定トピック外、または除外トピック内のもの |
| 16 | L360 | NewUserMinEngagementFilter | 新規アカウントの未フォローコンテンツでインタラクション数が不足 |
| 17 | L361 | InventoryHoldoutFilter | ハッシュにより決定的に一定割合を留保(デフォルト 0%) |
| 18 | L418 | VFFilter | 可視性システムが drop と回答したもの |
| 19 | L419 | AncillaryVFFilter | 親/引用/リポスト元が drop されたもの |
| 20 | L420 | DedupConversationFilter | 同じ会話スレッドの別枝 |
照合結果:README が主張する 17+3 個とその順序は、コードと項目ごとに完全一致し、ずれはゼロである。この点について X は美化していない。
⑥ 54 件の可視性ルール
出典は visibility-filtering/rules/registry.rs(912 行)。最初に drop を返すルールを基準とし、それ以降は評価しない。

| 項目 | 数値 | 説明 |
|---|---|---|
| すべての人に適用する base_home_rules() L101–132 | 28 | — |
| 未フォローコンテンツへの追加 oon_drops L138–170 | 26 | — |
| 未フォローコンテンツが通過すべき総数 | 54 | — |
基本の 28 件を順に挙げる:著者状態 4 件(凍結/無効化/消去/離脱)→ 保護アカウント → あなたがブロック → あなたがミュート → ミュートしたリポスト → コンテンツラベル 8 件(PDNA、却下、スパム、緊急、ヘイトスピーチ、暴力的発言、悪用、公民的完全性)→ 非ブロードキャスト → 期限切れ → 法的削除 2 件 → ログアウト/未成年/年齢未入力向けのセンシティブコンテンツ 3 件 → 専用コンテンツ → 最後の 4 件は直接落とすのではなくインタースティシャル警告である。
未フォロー時に追加で背負う 26 件はすべて drop のみを行う。DMCA 対象メディア、地域制限メディア、成人向けコンテンツの 4 種類の判定、高リコールの成人向け・スパム検出(誤って落としてもよい)、悪意ある URL、「拡散しない」ラベルなどである。
⑦ ゼロ重み 6 個の三種類の性質
見分け方は、モデルがこのアクションを予測するよう学習されているかを見ることだ。学習済みで価格付けだけが 0 =本物のレバー、学習損失すら設定されていない=単なる空き枠である。
共通の仕組み:「重みが 0 ならスキップする」というガードは存在しない(唯一の例外は F)。式は一律に 確率 × 重みであり、重み 0 でも計算され、26 項の配列に入り、結果は常に 0 である。モデルは依然として計算資源を使って予測し、パイプラインもそれを渡す。ただし最後にゼロを掛けるだけである。
第 1 類:モデルは学習済みで、価格付けだけが 0 —— 本物のレバー
| パラメータ | 対応する予測ヘッド | オンにした効果 |
|---|---|---|
| ProfileClickWeight | CLIENT_TWEET_CLICK_PROFILE = 30 | 「著者のアイコンをクリック」が採点され始める。すでに positive_sum(:112)に入り、上げると分子と分母を同時に拡大する |
| DwellWeight | CLIENT_TWEET_RECAP_DWELLED = 11 | 「滞在したか」が採点され始める。現状は「秒数には値を付けるが、滞在の有無には値を付けない」 |
| QuotedVqvWeight | CLIENT_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-479 と xrecsys.py:566-585。ギャラリーレイアウトには tweedie、それ以外には mae を使う)。
| パラメータ | 連続ヘッド | 実態 |
|---|---|---|
| ContClickDwellTimeWeight | CLICK_DWELL_TIME = 2 | 学習損失なし。ただし低いいね率のペナルティ機構は構築済み(baseline 0.01/alpha 0.5/floor 0.01/cap 1.0、ranking_scorer.rs:195-213)で、重み 0 の項目に接続されている |
| ContActiveSecs5mResidualNormWeight | ACTIVE_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 |

⑧ BRIDGE_PROBABILITY 専論
repo 全体で大文字小文字を問わず grep -rin bridg を行うと、ヒットは 25 件である。まず同名のノイズを三種類除外する。
| ヒット | 位置 | 実際には何か |
|---|---|---|
| AdActionInfo.bridgeType AdActionInfo.isTwclidBridged | recsys.proto:1306-1307 | 広告コンバージョンのアトリビューション。twclid=Twitter Click ID。推薦アルゴリズムの bridging とは無関係 |
| _bridge_ads_s3_env() | retrieval_dataset.py:33,50 | S3 環境変数を設定 |
| smyte_bridge | botmaker/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 bridge = 0 ヒット。ランキング式にはその項がなく、ゼロを掛ける場所すら残されていない。
二本の配線は、どちらも入力で、どちらもオフ
| 経路 | スイッチ(デフォルト) | 何をするか |
|---|---|---|
| A:主モデル | concat_history_bridge_prob = False recsys_model.py:534 | ca[:, :, 0] を取得 → 0~1 に clip → 著者埋め込みベクトルの後ろに接続(:976-978)→ 射影行列を通す |
| B:特徴量前処理 | enable_bridge_prob = False recsys_feature_prep.py:166 | cont_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 経路はいずれもインデックス 0を読むが、列挙では 0 は INVALID、bridge は 4 となっている。
| 証拠 | 内容 |
|---|---|
| 一 | 列挙値 2、3、4 が配列インデックスとして使われることは一度もない。phoenix 全体で [:, :, 2]/[:, :, 3]/[:, :, 4] はそれぞれ 0 ヒットで、読み出されるのは [:, :, 0] と [:, :, 1] だけである |
| 二 | 2 スロットだけの連続配列が存在する(parquet_recsys.py:845 のデフォルト num_continuous_actions: int = 2、grpc_recsys.py:372 は 2 を固定)。2 スロットで使えるのは 0 と 1 だけであり、ちょうど bridge と dwell である。配置がすべて列挙番号どおりなら、2 スロットには bridge を収められない |
| 三 | 二つの Rust プロデューサー(crates/common/xai-recsys/src/util.rs:725-727、crates/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_len | 1,022 | 一度に直近 1,022 個の行動を見る |
| candidate_seq_len | 64 | 一バッチで 64 件の候補をスコアリング |
| num_layers | 8 | 8 層の Transformer |
| emb_size | 2,560 | モデル幅 |
| query_heads / kv_heads | 20 / 4 | グループ化クエリ注意機構(GQA) |
| total_samples | 1×10^11 | 1,000 億件の学習サンプル |
| user_vocab_size | 100,000,000 | 1 億ユーザーのハッシュバケット |
| item_vocab_size | 100,000,000 | 1 億投稿のハッシュバケット |
| author_vocab_size | 30,000,000 | 3,000 万著者 |
| ip_vocab_size | 10,000,000 | 1,000 万 IP |
| emb_learning_rate | 0.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 件の Scala | Twitter 時代のコミュニティクラスタ検索(未フォローソース) |
| vm-ranker/ | 10 件の Rust | 最終再ランキング。中核は dpp.rs(決定的点過程で多様化を行う) |
| grox/ | 165 件の Python | LLM(Grok)でコンテンツ安全性をラベル付けするオフラインフロー |
| botmaker/ | 492+73 件 | ルールエンジンと 73 件の安全ルール(Java/Scala の遺産) |
⑩ 公開されていない部分と三つの欠陥
X 自身が公開していないと認めるもの:Grox の LLM プロンプト(.j2 ファイル、つまり「AI がコンテンツ安全性を判定する際に実際に何を尋ねるか」)、一部の botmaker ルール、デプロイおよびインフラストラクチャのコード。理由は明確で、抜け穴を突かれることを恐れている。

欠陥一:一つのフィルターファイルが宣言されず、実質的に死んだコード
home-mixer/filters/ には filter ファイルが 27 個あるが、mod.rs は 26 個しか宣言していない。宣言されていないのは popular_topics_author_dedup_filter.rs であり、ディスク上にはあるがコンパイル時には取り込まれない。
欠陥二:mod.rs の第 8 行は空行で、アルファベット順の切れ目にある
宣言は厳密にアルファベット順であり、drop_duplicates_filter と following_retweet_deduplication_filter の間に空行が一行ある。これは e または f で始まるモジュールが削除された場合に残る痕跡に当たる。何が削除されたかは証明できないが、これは公開前に誰かが手を入れた痕跡である。
欠陥三:6 個のフィルターは存在するが、このパイプラインでは使われない
ad_adjacent_served、following_retweet_deduplication、invalid_conversation_module、push_to_home_dedup、result_size、self_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-533、config.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 ×3 | Phoenix モデルの棚卸し、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_UNKNOWN | 0 |
| 「param! ブロックは合計 37 個」 | 実際は 183 |
| 「history_seq_len: 50」 | 実際は 1,022 |
この事件を証拠報告に書く理由:この捏造された納品は形式が完璧で、口調は自信に満ち、「再実行可能なコマンド一覧」まで付いていた。先に機械的な真値を確立していなければ、そのまま報告に混入していた。それを捕らえたのは疑念ではなく、照合である。——これこそ「自己申告を信じず、必ず自ら実行して受け入れる」という規律が存在する理由である。

私自身が犯した三つの誤り(あわせて記録)
- パーサーの黙った取りこぼし:初版では複数行の param! だけを走査し、単一行記法を 13 個取りこぼした——そして本当のスコアリング重みはすべてその 13 個に入っていた。実測 170 ≠ grep -c の 183 で初めて判明した。
- 割り当てで誤ったファイルを渡した:2 個の filter しか登録しない for_you_candidate_pipeline.rs を主フィルターチェーンとして GM に渡し、一ラウンドを無駄にした。本当の主チェーンは phoenix_candidate_pipeline.rs にある。
- 検証スクリプト自体に bug があった:初版の verify-all.sh は grep -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.
出典アンカー
- xai-org/x-algorithm 原始碼儲存庫 · https://github.com/xai-org/x-algorithm · 在 IDAEO 的其他引用
- x-algorithm README(官方說明) · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/README.md · 在 IDAEO 的其他引用
- 權重參數定義 param.rs · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/home-mixer/params/param.rs · 在 IDAEO 的其他引用
- 評分器 ranking_scorer.rs · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/home-mixer/scorers/ranking_scorer.rs · 在 IDAEO 的其他引用
- 常數定義 config.rs · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/home-mixer/params/config.rs · 在 IDAEO 的其他引用
- 候選管線與過濾器註冊 · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/home-mixer/candidate_pipeline/phoenix_candidate_pipeline.rs · 在 IDAEO 的其他引用
- 可見度規則註冊表 · https://github.com/xai-org/x-algorithm/blob/a389166f6cf5da70a286b568c87695d4dcdce3a1/visibility-filtering/rules/registry.rs · 在 IDAEO 的其他引用
この記事を引用
TK Lin・《Xアルゴリズム解析の証拠総覧》・IDAEO 知識庫・2026-08-14・https://km.idaeo.ai/post/ai/x-algorithm-evidence