点数が付く前に、18枚のふるいがある
Filterが見ているのは、"あなたの見込み"ではなく"確定した条件"
Xの公開アルゴリズムを読み解くシリーズ、第7回です。
このシリーズは、1件の投稿があなたのおすすめ欄に届くまでの流れを、入口から順に歩いています。投稿が登録される「棚」(第6回のインデックス)、そこから候補を拾い集める3つの係、Thunder・PhoenixそしてSimClusters。ここまでで、数千件規模の候補が机の上に積まれました。
第5回の9ステップでいうと、次は④「ふるいにかける(18個)」実はこの工程、第5回で「18個ある」と数だけお伝えして、中身を一度も解説していませんでした。順番に辿ると言っておきながら、ここだけ飛ばすわけにはいきません。
この工程を担当する部品は、コード上どれも「Filter(フィルター)」という名前で並んでいます。数えるとちょうど18枚。候補に点数が付く前に、この18枚が順番に並んでいます(ただし後で見る通り、18枚のうち何枚かはリクエストの条件やスイッチ次第で、毎回は働きません)
先に言葉を1つだけ。おすすめは読者ごとに別々に作られます。この記事では「ある読者ひとりのおすすめを作る1回の処理」を、その読者の「列」と呼びます。あなたの投稿は読者の数だけたくさんの列に同時に並んでいて、これから見る通り、ふるいの結果は列ごとに違います。
結論を先に言います。
- 点数の前に18枚、点数の後にさらに3枚のふるいがある
- 判定は白黒。点数のような加減点ではなく、通るか落ちるか
- この段では反応の予測を計算しない。もう確定していること(見た、ブロックした、48時間過ぎた)だけで判定する
- そしてここにも、「用意されているが動いていない」装置が2つ眠っている
順番に見ていきます。
ふるいの正体。点数の前に「資格」を審査する
工程の並びを思い出してください。候補を集める→情報を付ける→ふるいにかける→AIが予測する→点数にする。
ふるいがAIの予測より前に置かれていることには、意味が読み取れます。AIの予測は、この工場でいちばん高くつく作業です。どうせ落とす候補にその予測を使うのは無駄なので、白黒はっきり判定できるものを先に、安いコストで落としてしまう。そういう配置に見えます(配置はコードの事実、理由の説明はここでは解釈です)
そしてもう1つ。ふるいの判定は白黒です。通るか、落ちるか。点数のように「低いけど残る」がありません。どんなに良い投稿でも、資格がなければこの時点で読者の列から消えます。
ただし、落ちることは投稿への罰ではありません。あくまで「その列の、その1回の候補から外れる」だけです。読者Aの列で落ちた投稿が、読者Bの列では普通に通っている。それがこの工程の日常です。
18枚の一覧(すべて点数が付く前)
コード上の並び順に、働きでグループ分けして全部並べます。
※グループ名はコードに書かれているものではなく、読みやすさのためにこちらで整理した区分です。番号と内容はコードのままです。
事務整理(3枚)
1. 重複の削除 — 複数の係が同じ投稿を持ってきたら、1つにまとめる
2. データ不良 — 投稿の本体データが読み込めなかった候補を外す
7. リポストの重複 — 同じ元投稿のリポストが複数あれば、先に並んだ1件だけ残す(元投稿とリポストが両方来た場合も同じ)
資格チェック(5枚)
3. 48時間 — 投稿から48時間を過ぎた候補は落ちる(後述)
4. 自分の投稿 — 自分のおすすめに自分の投稿は出ない
5. フォロー外のリポスト・リプライ — フォロー外の読者の列では落ちる(後述)
6. フォロー外のNSFW — SimClusters経由で来た、フォロー外のNSFW作者の投稿を落とす
8. サブスク限定 — 購読していない読者の列では落ちる
「もう見た」の3段構え(3枚)
9. 見た投稿(見た記録) — リクエストと一緒に届く閲覧記録と照合(後述)
10. 見た投稿(サーバの予備記録) — サーバ側の表示記録でもう一度確認
11. 出した投稿 — 過去にその読者の画面へ届けた投稿を外す
読者の意思(2枚)
12. ミュートキーワード — 設定した語を含む投稿を落とす(後述)
13. ブロック・ミュート関係 — 6つの条件で判定(後述)
法対応(1枚)
14. ブラジル2026年選挙 — 選挙裁判所に報告されたアカウントの投稿を落とす(後述)
条件付き・眠っているもの(4枚)
15. 動画除外 — アプリが「動画抜き」を指定したリクエストのときだけ働く
16. トピック指定 — トピックを指定したリクエストのときだけ働く
17. 新規アカウントの保護 — 既定オフ(後述)
18. 実験用ホールドアウト — 既定オフ(後述)
この4枚だけ性格が違います。15と16は「リクエストがそう指定したときだけ働く」門、17と18は「スイッチが既定でオフ」の門。壊れているわけではなく、出番が限られているだけです。
番号はコード上の並び順です。ここから、読者のみなさんに関係が深いものを深掘りします。
候補としての賞味期限は、48時間
ふるいの3枚目は、投稿から48時間を過ぎた候補を落とします。上限はコードに定数で書かれています。48時間です。
つまり、おすすめ候補としての資格には期限があります。どれだけ良い投稿でも、2日を過ぎたらこの列では戦えません。月曜の朝に投稿したものは、水曜の朝には候補資格が切れる計算です。「伸びるのは最初の1〜2日」という経験則に、構造的な根拠が1つ見つかった形です(おすすめ経路に限った話で、伸び方のすべてを説明するものではありません)
第6回を読んだ方は、引っかかるかもしれません。棚には「常緑動画・5年」なんてものがあったはずでは? その通りで、棚そのものは最長5年分の投稿を保持しています。でもFor Youのこの列では、48時間の門がすべての候補に掛かります。5年物の棚は別の用途と共用、と読むのが自然ですが、コードに説明はないので、ここは観察に留めます。
もう1つ、この門には抜け道というより「時計の仕組み」があります。判定に使われるのは候補自身の時刻です。つまり、3日前の投稿そのものは門を通れませんが、それを今日リポストしたなら、リポストは今日生まれた候補として時計が新しい。引用も同じで、引用は新しい投稿なので新しい時計を持ちます(正確に言えば、古い投稿への今日のリプライも新しい時計を持ちます。ただしリプライはフォロー外では次の門で落ちるので、外へ広がる形にはなりません)
「古い投稿そのものをもう一度候補の列に戻す」という意味では、リポストと引用が主な包装です。「古い名作が誰かの引用で再燃する」現象には、こういう構造の裏付けがあります。
フォロー外の読者へ運ばれるのは、単独投稿と引用だけ
ふるいの5枚目は、こう判定します。その候補がフォロー外の読者の列に並んでいて、リポストまたはリプライなら、落とす。
つまり、こういうことです。
- あなたのリポストは、フォロワーにしか届かない
- あなたのリプライも、(リプライ単体としては)フォロー外のおすすめに出ない
- フォロー外へ運ばれる形は、単独投稿と引用の2つ
ここで気をつけたいのは、「フォロー外」が誰から見た話かです。この門が見ているのは、その候補を書いた人を読者がフォローしているかどうか。リプライで言えば、判定されるのは元投稿の主ではなく、リプライを書いた人です。
つまり、あなたのリプライが並べるのは「あなたをフォローしている人の列」あなたをフォローしていない人の列には、リプライという形では並べません。「リプライで露出を稼ぐ」がフォロー外の開拓にならないのは、この門のせいです。
なお、このふるいはもう1つ、スレッドの文脈情報が取得できなかったリプライも落とします(表示に必要な親投稿が読めないため、と読めます)
では、リポストに意味はないのか。「リレー」の話
「リポストはフォロワーにしか届かない」と聞くと、リポストという行為に価値が無いように聞こえます。そうではありません。この門が制限しているのは「1回ぶんの距離」だけです。
あなたの投稿を、フォロワーのAさんがリポストしたとします。Aさんのフォロワーから見ると、そのリポストは「自分がフォローしている人のリポスト」、つまりフォロー内の候補です。門は通ります。あなたをフォローしていない読者の列に、あなたの投稿がAさん経由で並ぶわけです。
整理すると、フォロワーの外への広がり方は2種類あります。
- 単独投稿と引用: 候補集めの係(Phoenix・SimClusters)が、フォロー外の読者の列へ直接運ぶ
- リポスト: 1ホップずつの数珠つなぎ。あなた→フォロワー→そのフォロワー、と人の手でリレーされて外へ伸びる
「リポストされること」の価値は、このリレーの起点が増えることにあります。リポストという形をアルゴリズムがフォロー外へ運ばないからこそ、運び手は人間です。
誤解しないでほしいのは、これは「形」の話であって「投稿の中身」の話ではないことです。あなたの元投稿そのものは、PhoenixやSimClustersがフォロー外の読者へ直接運びます(第2回・第4回)運ばれないのは「リポストという包装」のほうで、中身は別の便で飛んでいきます。
1つ補足しておくと、この門はリポストが「どの係から来たか」を見ていません。見ているのは、その読者にとってフォロー外かどうかだけです。実際、Phoenixもリポストの候補を返せる作りになっています。それでも結果は同じで、その読者にとってフォロー外なら、どの係が運んできたリポストでもここで落ちます。リポストという包装のままフォロー外へ抜ける道は、どこにもありません。
実はこれ、リプライも同じ構造です。誰かがあなたの投稿にリプライを付けたなら、そのリプライはリプライを書いた人のフォロワーの列に候補として残れます。あなたをフォローしていない人の候補に、あなたとの会話がその人経由で入る。そういうことが起こり得ます。
制約もリポストと同じです。元投稿を既に見ている読者の列では、既読の照合(リプライは親投稿まで遡ります)で落ちます。さらにその会話から残れるのは、後で見る通り最高点の1件だけ。そして候補に残ることと画面に出ることは、やはり別の話です。
それでも、リポストもリプライも「その形を作った人のフォロワーまで」は候補になれる。フォロー外へ一足飛びに運ばれないだけで、人を介した1ホップは生きています。
ただし、注意が2つ。
1つ。同じ元投稿は、1人の読者の列に1件しか並びません(ふるい7枚目の重複除去)100人にリポストされても、ある読者の画面に出るのはそのうち先に並んだ1件だけです。
2つ。リポストを受け取った読者が元投稿を既に見ていた場合、「もう見た」の照合で落ちます。次の章で見る通り、照合はリポスト元まで遡るからです。リレーが効くのは、まだ見ていない読者に対してだけです。
セルフリポストは、フォロワー向けの「再放送」にしかならない
自分の投稿を自分でリポストする、いわゆるセルフリポスト。これをこの構造に通してみると、働ける範囲がはっきり見えます。
まず入口です。セルフリポストは、フォロー内の候補集め係(第1回のThunder)が拾う候補になり得ます。リポストは「新しい投稿」として扱われるので、いまタイムラインを読み込んでいるフォロワーの列に、新鮮な候補として入りやすい。さらに言うと、48時間の門(3枚目)は候補自身の時刻で判定するので、元投稿が48時間を過ぎていても、リポスト自体の時計は新しく、この門も通れると読めます。
ただし、そこから先の門がこう働きます。
- フォロー外の読者の列: リポストなので、5枚目の門を通れません。フォロー外の開拓には一切働きません
- 元投稿を既に見たフォロワーの列: 「もう見た」の3段構えがリポスト元まで遡って照合するので、落ちます
- まだ見ていないフォロワーの列: 門は通れます。ただし、候補に入ることと画面に出ることは別の話。そこから先は点数と選抜(次回以降)の勝負です
残る働き場所は、「まだ元投稿を見ていない、いま読み込んでいるフォロワー」だけです。つまりセルフリポストは、活動時間帯の違うフォロワーへの再放送としては構造上きちんと機能します。
ただし、外へは出ません。ここで言う「出ない」は、セルフリポストという包装の話です。前の章で書いた通り、元投稿そのものはPhoenixやSimClustersがフォロー外へ運ぶ道を別に持っています。公開コードから確認できるのは「リポストの包装はフォロー外の門を通れない」までで、セルフリポストという行為が他に何の影響も持たないと断定できるわけではありません。
「もう見た」は、3段構えで排除される
9枚目・10枚目・11枚目は、全部同じ目的です。その読者に一度届いた投稿を、重ねて防ぐ。
- 見た記録: 「見た投稿」のIDの一覧と、それを省メモリの形式(ブルームフィルタ)にまとめたものが、おすすめのリクエストと一緒に届く
- サーバの予備記録: サーバ側にも表示の記録があり、端末の記録が欠けても拾える
- 出した記録: 「画面へ届けた」投稿のリストとも照合する(既定では有効ですが、リクエストの種類によっては働かない設定もあります)
3つ目には注釈が要ります。これは「読者が見た」ではなく「システムが画面へ出した」記録です。出したことの記録であって、読者が実際にその投稿を目にしたことの証明ではありません。
1つ目の「見た記録」に使われるブルームフィルタは、記録を極端に小さく持てる代わりに、まれに「見ていない投稿を見た扱いにする」方向の間違いを許す仕組みです(逆に「見たのに見ていない扱い」になることは起きません)同じものを二度出す失敗のほうを、取りこぼしより嫌う設計だと読めます。
しかも、照合は投稿単体ではありません。リポストはリポスト元まで、リプライはリプライ先の親投稿まで遡って照合します。元投稿を見たことがあれば、それのリポストも落ちます。
面白いのは、引用は遡らないことです。照合の対象はリポスト元とリプライ先だけで、引用元は含まれません。引用は元投稿の再登場ではなく「別の新しい投稿」として扱われる。フォロー外の門を通れるのが単独投稿と引用だった話と、ここは筋が通っています。
見た記録・表示の記録・出した記録。このどれかに引っかかれば落ちます。同じ投稿を同じ読者へ二度出す失敗を、何重にも防ぐ構造です(ただし今見た通り、誰かがそれを引用した場合は、別の投稿として通ります)
さらに言えば、排除は3段どころではありません。候補を集める段階でも、Thunderへの取り出し依頼に「見た投稿」のリストが渡されていて、拾う前の時点で弾かれています(渡されるのはこのリストだけで、ブルームフィルタや表示記録は後のふるいの担当です)ふるいの3枚は、そこを通り抜けてきたものへの念押しです。
ブロック・ミュートは、点数の前に落ちている
13枚目は、読者と投稿者の関係を6つの条件で見ます。1つでも当てはまれば落ちる。
- 読者が投稿者をミュートしている
- 読者が投稿者をブロックしている
- 投稿者が読者をブロックしている
- 引用元の作者が読者をブロックしている
- 読者が引用元の作者をブロックしている
- 読者がリポスト元の作者をブロックしている
注目してほしいのは、投稿者と読者の関係については方向を問わないことです。読者があなたをブロックしていれば当然落ちますが、逆に、あなたが読者をブロックしている場合も、その読者の列であなたの投稿は落ちます(3つ目の条件)どちらから切った関係でも、門は閉まります。
細かい話をすると、引用元の作者についても両方向を見ますが、リポスト元の作者については「読者がブロックしている」側だけを見ています。
引用とリポストの「元」まで見るのも特徴です。あなたが誰かの投稿を引用したら、その誰かをブロックしている読者の列では、あなたの引用ごと落ちます。
そして、ここが今回いちばんお伝えしたいことです。
前回までの流れで、点数の重み表に「ブロック: −31.2」「ミュート: −58.8」というマイナスがあることに触れてきました。あれは「この読者はこの投稿を見たらミュートしそうか」という予測に掛かる数字です。整理すると、こうなります。
済んだ事実は門で落とし、まだ見ぬ反応は点数で予測する。
実際にブロック・ミュートした読者の列では、あなたの投稿はふるいの段階で物理的に消えています。点数のマイナスが働く相手は、「まだ関係は切れていないが、切りそうな気配のある読者」だけ。FilterとRankingScorer(次回)は、時間の向きが逆なんです。過去を見る係と、未来を見積もる係。
もう少し正確に言うと、Filterが見ているのは「過去の出来事」だけではありません。48時間経ったか、動画か、購読者か。「いま確定していること」も含みます。18枚に共通するのは、この段で反応の確率を計算しないことです。読者がこの投稿にどう反応しそうかという見積もりは、次の工程まで出てきません。ここで使われるのは、時刻・関係・設定・そして既に付いているラベルだけです。
「ラベル」と書いたのは、6枚目(フォロー外のNSFW)が、投稿者に付いた成人向け判定の印を使っているからです。その印がどう付いたかまで遡れば機械の判断が入っているかもしれませんが、この工程にとっては「もう決まっている属性」でしかありません。読み取るだけで、計算はしない。それがこの段の性格です。
ミュートキーワードは、本文を分解して照合する
12枚目、ミュートキーワード。実装を見ると、単純な「文字列を含むか」ではありません。投稿の本文を一度トークン(語のまとまり)に分解してから、設定された語の並びと照合しています。
ここでは「ミュートキーワードはおすすめ候補の段階で効いている」「照合は思ったより丁寧」の2点だけ持ち帰ってください。
法律が、コードに直書きされている
14枚目は毛色が違います。ブラジルの2026年選挙に関して、選挙裁判所に報告されたアカウントのID一覧がソースコードに直接書かれていて、該当アカウントの投稿・リポスト・引用・スレッドの親投稿までを落とします。
面白いのは2点。
1つ。読者がそのアカウントをフォローしていれば、落としません。「おすすめとして押し付けない」ことが目的で、見たい人から隠すものではない、という線引きです。
2つ。コードのコメントに、リストの整備過程がそのまま残っています。「このアカウントは実在が確認できなかった」「この議員はコード執筆時点でアカウントを消していた」法対応の実務が公開リポジトリに透けて見えるのは、コードが公開されているからこそです。
眠っている装置が、また2つ
17枚目は「新規アカウントの保護」です。登録から30分未満(または長期離脱からの復帰直後30分)の読者には、フォロー外の低評価投稿を見せない。そういう設計ですが、スイッチは既定でオフ。しかも落とす基準の閾値も既定0なので、既定値のままオンにしても、落ちる候補はほとんど出ない計算です。
18枚目は「実験用ホールドアウト」投稿と読者の組み合わせから機械的に一定割合を間引く実験装置ですが、これも既定オフ、間引き率もすべて0%です。
第2回の「新規ユーザー向けモデル」、次回扱う「新規閲覧者へのフォロー外×0.00001」と同じ、用意されているが動いていないパターン。この配管の多さは、もう本シリーズの風物詩です。
点数の後にも、3枚待っている
18枚を通った候補は、AIの予測(⑤)と点数付け(⑥)へ進みます。ただ、ふるいはそこで終わりではありません。点数が付き、上位が選ばれた後に、もう3枚あります。
1. 表示可否の判定 — 表示してよいかの判定で「落とす」となった投稿を外す。第5回の9ステップで⑨「安全チェック」と呼んだ工程の実体は、主にここです(安全性以外の理由も含みます)
2. 巻き添えの除外 — スレッドの親投稿・引用元・リポスト元が1で落ちた場合に、それにぶら下がる候補も一緒に落とす
3. 会話の重複 — 同じ会話(スレッド)にぶら下がる候補が複数あれば、いちばん点数の高い1件だけを残す
3つ目は覚えておく価値があります。
同じ会話からその1回のおすすめに残れるのは、1件だけ。
スレッドで会話がどれだけ盛り上がっても、その会話がある読者のおすすめ欄に出られる枠は1つで、スレッド内の全投稿がスコアで争います。自分のスレッドに連投した場合も、読者の列に並ぶのは最高得点の1件です。
だからどうなる
1. 勝負は48時間
候補としての資格が切れます。同じ投稿そのものがおすすめ候補として戦えるのは、投稿から48時間まで。それを過ぎた露出は、おすすめ以外の経路(検索・プロフィール・固定表示など)の仕事です。あるいは、リポストや引用のように新しく作られた候補を介して「新しい時計」をもらうか(おすすめ経路に限った話で、伸び方のすべてを説明するものではありません)
2. 自分の力でフォロー外に出せる形は、2つ
アルゴリズムがフォロー外の列へ直接運ぶのは、単独投稿と引用だけです。フォロワーの外へ届けたい内容は、単独投稿にするか、引用で自分の言葉を足す形にする。自力で選べるのはこの2択です。
リポストとリプライは、フォロー外の列には並べません。ただし「その形を作った人のフォロワー」までは届くので、他の人がリポストやリプライをしてくれれば、そこから1ホップずつ外へ伸びます。自力ではなく、人を介する経路です。
セルフリポストについては、包装がフォロー外の門を通れない以上、フォロワーより外へは出ません(元投稿そのものが別経路で運ばれるかどうかは、また別の話です)
3. 同じ読者への二度目を、何重にも防いでいる
見た記録・表示記録・出した記録に加えて、候補を拾う時点でも除外が掛かります。同じ投稿がもう一度同じ読者へ届くことは、まずありません(再登場するとしたら、誰かの引用という別の投稿の形です)出す前に磨く理由が、ここにもあります。
4. ブロック・ミュートした相手には、点数の前に届かなくなっている
ブロック・ミュートされた読者の列では、点数以前に消えています。逆に言えば、点数の世界(次回)で戦っているのは、まだ関係が切れていない読者だけです。
5. ふるいの結果も、読者ごとに違う
18枚の多くは「その読者が見たか」「その読者がブロックしているか」「その読者が購読しているか」で判定します。点数だけでなく、門の開き方も読者ごと。あなたの投稿は、読者Aの列では通り、読者Bの列では落ちています。
まとめ
- 点数の前に18枚、点数の後に3枚のふるい。判定は白黒で、その1回の中に敗者復活はない
- 候補の賞味期限は48時間。判定は候補自身の時刻なので、リポストや引用は新しい時計を持つ
- フォロー外の列で残れるのは単独投稿と引用だけ。リポストとリプライの包装は「それを作った人のフォロワー」までで、外へは人のリレーで1ホップずつ伸びる。セルフリポストはフォロワー向けの再放送
- 「もう見た」は3段構えで排除。リポスト元・リプライ先まで遡って照合(引用は遡らない)
- 投稿者とのブロックは両方向を見て門で落ちる(ミュートや引用元・リポスト元には個別の条件)点数のマイナスが見るのは「まだ切れていない読者の気配」
- 同じ会話からその1回のおすすめに残れるのは1件だけ
- 眠っている装置が2つ(新規保護・実験用間引き)
Filterが見ているのは、確定した条件です。見た、切れた、過ぎた、もう付いている印。反応の予測を計算することなく、既に決まっていることだけで候補を削り、残った候補が次の工程へ進みます。
そこで待っているのが、"あなたの見込み"に重みを掛け算する係、RankingScorerです。次回は、いよいよ点数の付け方を扱います。あの有名な「いいね0.5点、通報−234点」の表の、正しい読み方です。
補足: 検証範囲について
この記事はコミット45b48ba(8月27日)を基準に、各ふるいの実装ファイルを個別に読んで書いています。「18枚」はこのコミットのFor You候補パイプラインに並ぶ数で、更新で増減し得ます。既定値・定数はすべて公開コードのもので、本番で同じ設定が使われているとまでは断定できません。
参照(クリックすると該当箇所が開きます)
48時間の定数(MAX_POST_AGE):
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/params/config.rs#L36
フォロー外のリポスト・リプライ除外:
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/filters/oon_retweet_reply_filter.rs
ブロック・ミュート関係の6条件:
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/filters/author_socialgraph_filter.rs
「もう見た」の3段構え:
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/filters/previously_seen_posts_filter.rs
ブラジル2026年選挙フィルタ:
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/filters/brazil_2026_election_filter.rs
会話の重複(スレッドから1件):
https://github.com/xai-org/x-algorithm/blob/45b48ba/home-mixer/filters/dedup_conversation_filter.rs
前回まで:
第1回(Thunder): https://x.com/FFBuncho/status/2089576142814728301
第2回(Phoenix): https://x.com/FFBuncho/status/2090012510409863499
第3回(セマンティックID): https://x.com/FFBuncho/status/2090276308844708093
第4回(SimClusters): https://x.com/FFBuncho/status/2091314098948686194
第5回(9ステップ): https://x.com/FFBuncho/status/2091712865015304550
第6回(インデックス): https://x.com/FFBuncho/status/2092442718610022666
この記事のもとになったX記事
Xで公開した第7回の記事を、サイト用に読みやすく再構成したものです。
ふくふく文鳥