Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

なぜ Promovolve なのか

デジタル広告は、いまの姿になる必然などありませんでした。雑誌の時代は、すでに別のモデルを証明していたのです。掲載誌に合うように選ばれた広告。そのページを大切に思う人の手で配置された広告。そして「そこにあるのが自然だから」という理由で読者に受け入れられ——しばしば楽しまれさえした広告。旅行雑誌には航空会社の広告が載り、料理雑誌には包丁の広告が載りました。誰も追跡されてはいませんでした。

プログラマティックの時代——ページが読み込まれる数ミリ秒の間に、機械が広告を売買しオークションを走らせる時代——は、それをユーザープロファイルへの入札と引き換えに手放しました。その結果はおなじみのものです。同意バナー、広告ブロッカー、不正、クリックベイトの掲載面、そして誰のためでもなく互いを出し抜き合う入札最適化ツールの軍拡競争。パブリッシャーの取り分は仲介業者の連鎖を通るたびに削られ、広告主は自分なら決して選ばなかったページに広告を出し、読者は一足の靴にインターネット中を追いかけ回されます。

Promovolve は、現代のインフラで雑誌モデルを再建する試みです。その約束を、優先順位の順に並べます。

人ではなく、ページをターゲットにする。 LLM が、読者がいままさに見ているページを読み、コンテンツカテゴリーに分類します。キャンペーンはカテゴリーをターゲットにします。Cookie もユーザープロファイルも端末フィンガープリントもなく、同意を求めるべきものが何もありません——システムは読者が誰なのかを決して知らないのです。

読者に舵を握らせる。 Promovolve の広告は小さな雑誌です。ページの中では折りたたまれて座っており、タップされたときだけ全画面の誌面に展開されます。読者は、あとで見返したい広告の角を折ることができます——ドッグイヤーです。そのしおりは読者自身のブラウザの中に住んでいます。しおりを付けた広告との再会は、広告主にとって無料であり、学習システムからは見えません。覚えていてもらえた広告は贈り物であって、課金イベントではないのです。

正直さを最善の入札にする。 オークションはセカンドプライスかつ品質調整型です。入札額を安く偽っても得はなく、読者が実際に反応するクリエイティブは、単に高く払うだけのクリエイティブに勝ちます。Promovolve がキャンペーン側の入札最適化ツールを一切用意しないのは、この仕組みには最適化する余地が残されていないからです。

コントロールをパブリッシャーに渡す。 すべてのクリエイティブは、そのサイトで配信される前に、パブリッシャーの承認キューを通ります。フロア価格はパブリッシャーに代わって、コンテンツカテゴリーごとに、実際に配信された収益を測定して最適化されます——思惑を持った取引所によってではなく。

手の内を見せる。 このプラットフォームはオープンソースであり、この本は実際の動作を説明します——意図的に単純にした部分も、試して捨てたアイデアも含めて。本文がクラス名を挙げるなら、そのクラスは実在します。仕組みが削除されたなら、本はそう書きます。

次の章は、この商売そのもの——パブリッシャー、広告主、インプレッション、オークション——を、広告を売り買いした経験のない読者のためにゼロから定義します。経験があるなら読み飛ばしてください。その後の章は、まず1つのページと1人の読者の目を通して物語を一度手短に語り、それから各メカニズムを分解していきます。広告フォーマット、クリエイティブ生成パイプライン、分類、オークション、承認、配信時の選択、価格決定、ペーシング、フロア、そしてすべての土台にあるクラスター。最終章は、この設計を従来のアドテクと突き合わせ、違いを一つずつ検分します。

すべての広告の裏にある取引

広告を買ったことも売ったこともないなら、この章はあなたのための章です。この業界が使う言葉をここで一通り定義します。本書の残りの部分は、これらの言葉を断りなく使うからです。キャンペーンを運用したことがある、サイトを収益化したことがある——そんな方は先へ進んでください。ここに驚きはありません。

悩みがかみ合う、2人の他人

パブリッシャーは、読者が訪れる場所——旅行ブログ、レシピサイト、地域ニュースのページ——を持っている人です。読者にページを届けるにもお金がかかり、記事を書くにもお金がかかりますが、読者はお金を払いません。パブリッシャーが持っているのは、注目です。

広告主は逆の悩みを抱えています。ピラティススタジオ、オンラインショップ、航空会社——売るものはあるのに、誰にも見てもらえない。広告主に足りないのは、注目です。

広告とは、この2人の間の取引です。パブリッシャーはページの上に長方形の場所——スロット(広告枠)——を確保して貸し出します。広告主はそこにメッセージを載せます。この本に書かれていることはすべて、この取引を公正に、速く、双方にとって割に合うものにするための機械仕掛けです。

取引の単位

1人の人が1つの広告を1回見ること。それがインプレッションです。これがこの商売の原子です。数えられるのも、値段が付くのも、支払われるのも、この単位です。

インプレッション1回の価値は1円の何分の一かでしかないので、価格は1,000回単位で示されます。これが CPM(cost per mille = 1,000回あたりの費用)です。「CPM 5ドル」は、1,000回の表示に5ドル、という意味です。本書で広告主が「5ドルで入札する」と言うとき、入札されているのはこの数字です。

広告そのもの——画像、文言、レイアウト、読者が実際に目にするその制作物——はクリエイティブと呼ばれます。名詞としては少し奇妙ですが、これが業界の標準語であり、本書でも頻繁に登場します。1人の広告主が複数のクリエイティブを走らせて、読者がどれを好むか確かめることもあります。

キャンペーンは、広告主の継続的な発注書です。これらのクリエイティブを、この日予算で、この CPM 入札額で、この種のコンテンツに向けて——という指定の束です。スロットを奪い合うのは、このキャンペーンたちです。

なぜオークションなのか

どの瞬間にも、同じスロットを複数のキャンペーンが欲しがっています。誰が枠を取り、いくら払うのか——それを1日に何百万回も、人間の交渉なしに決めなければなりません。ほぼすべての場所での答えが——ここでも——オークションです。資格のあるすべてのキャンペーンが値段を言い、機械が勝者を選びます。

オークションには2つの流儀があり、その違いは本書が1章を割くほど重要です。**ファーストプライス(第一価格)オークションでは、勝者は自分の入札額をそのまま払います。これは正直者に罰を与えます。自分にとっての本当の上限額で入札すると、競争が弱いときには必ず払い過ぎになるからです。だからファーストプライスの入札者は、勝てる最低額を推測するソフトウェアを雇います。この慣行をビッドシェーディング(入札額の値引き)と呼びます。**セカンドプライス(第二価格)**オークションでは、勝者は勝つのに必要だった額——次点の入札額のわずか上——を払います。高く入札しても損をしなくなるので、安全な戦略は単純に「そのインプレッションが自分にとって持つ価値」をそのまま入札することになります。どちらの流儀でも、勝者が実際に払う価格をクリアリング価格(約定価格)**と呼びます。

一方のパブリッシャーは、フロア(最低価格)を設定できます。これを下回る価格ではスロットを売らない、という線です。フロアは薄い競争から身を守ります。入札者が1人しかいなければ、「次点のわずか上」はほとんどタダ同然を意味してしまうからです。ただし落とし穴があります。フロアを高くしすぎると入札者が全員逃げ、スロットは何も稼がなくなります。売れ残ったスロットは*アンフィル(未充足)*と呼ばれ、実際に広告が配信されたスロットの割合がパブリッシャーのフィル率です。フロアをいくらにするかは正真正銘のジレンマであり、本書はその答えを勘ではなく測定で出すことに1章を割いています。

お金の流れ

広告主の日予算は、キャンペーンが1日に使ってよい上限です。そして予算をうまく使うとは、朝だけでなく1日全体に行き渡らせることです(この問題をペーシングと呼びます)。インプレッションごとのクリアリング価格が予算から差し引かれ、プラットフォームがわずかな割合をマージンとして受け取り、残りがパブリッシャーの収益になります。インプレッションの流れを「支払うべきお金」と「受け取るお金」に変える日次の帳簿仕事が**精算(セトルメント)**です。

語彙はこれで全部です。2人の当事者、スロット、インプレッション、1,000回あたりの価格、オークション、フロア、予算、そして分配。本書の残りは、Promovolve がこの古い取引をどう違うやり方で回すか、の話です——まずは、1つのページと1人の読者と1つの広告の物語から。

1つのページ、1人の読者、1つの広告

この本に書かれていることはすべて、この章が描く数秒の中で起きます。まず一度、全体の形をつかむために読んでください。残りの章が各ステップを説明します。

パブリッシャーが登録する

ユキは小さな旅行サイトを運営しています。彼女はサイトを Promovolve に登録し、オペレーターがサイト申請を承認し、彼女はドメインを自分が管理していることを証明します——/.well-known/promovolve.txt に置くトークンファイル、あるいはホスティングがファイルを置かせてくれないなら DNS TXT レコードで。この瞬間から、彼女のサイト ID で広告をリクエストできるのは検証済みホスト上のページだけになります。彼女の埋め込みコードを別のドメインにコピーした者には 403 が返ります。

彼女はテンプレートに script タグを1つ入れます。スロットは寸法を持ったただの div です。それ以外に設定するものはありません——広告サーバーの管理画面も、ラインアイテム(従来型広告サーバーの、手作業で交渉される配信契約)も、サイズの交渉もなしです。

広告主が登録する

ケンタはピラティススタジオのオーナーです。デザインチームも制作会社もありません——あるのはランディングページだけです。彼は Promovolve に URL と日予算と CPM 入札額——広告が1,000回表示されるごとに支払ってよい価格——を渡します。プラットフォームのパイプラインが彼のランディングページを本物のブラウザで読み、画像と文言と配色を抽出し、LLM が文言を3ページの雑誌ナラティブに書き直し、レンダリングして、結果を彼に見せます。彼はキャンペーンを出したいカテゴリー——フィットネスウェルネス——を選び、開始します。

新しい記事が最初の読者と出会う

ユキは朝6時に温泉町の記事を公開します。6時14分、最初の読者が到着します。ページの広告タグが広告を要求します——しかしサーバーはこの URL を見たことがありません。サーバーは空のスロットとヒントを返します。テキストを送って。タグは読者自身のブラウザの中で記事の本文を抽出し、送り上げます。LLM がそれを分類し——旅行アジア旅行——結果が保存されます。この1人目の読者は広告を見ませんでしたが、以降のすべての読者は見ることになります。

分類は48時間有効です。その後もまだ記事にトラフィックがあれば、次の訪問がテキストを再送し、時計はリセットされます。誰にも読まれないページは、そのままシステムから抜け落ちていきます。

オークションが走る——次の誰かが来る前に

カテゴリーが手に入ると、サイトのオークショニアが 旅行 とその近傍カテゴリーに登録されたすべてのキャンペーンから入札を集めます。ケンタのキャンペーンは旅行キャンペーンではないので今回は見送りです。航空会社と旅行かばんブランドが入札します。オークショニアは勝者を1人選びません。資格のある全クリエイティブを並べ替え——各キャンペーンの一番手が先頭に来るように——プール全体をページのそばにキャッシュします。本当の選択はあとで、インプレッションごとに行われます。5分ごとに、そしてキャンペーンに変化があるたびに、オークションは静かに再実行されます。

読者に広告が届く

2人目の読者がページを読み込みます。広告タグはページ上の全スロットを列挙した1つのリクエストを送ります。サーバーはホストを確認し、分類が新鮮であることを確認し、ローカルレプリカから候補プールを引き出し、そしてペーシングコントローラーに「このインプレッションはそもそも使うべきか」を判断させます——キャンペーンの予算は朝だけでなく、1日全体もたなければならないのです。

それからサンプリングします。各候補のクリックとドッグイヤーの履歴は一対のベータ分布です。各候補がもっともらしいエンゲージメント率を1つ引き、入札額の減衰関数を掛け、最も高い引きがスロットを取ります——1ページにつき1キャンペーンまで。勝者の予算が確保され、価格は勝者自身の入札額ではなく次点によって決まり、レスポンスにはクリエイティブとトラッキング URL が載ります。

読者が答えを返す

広告は雑誌の表紙ほどの大きさで、ページの中に折りたたまれて座っています。読者がタップすると、スワイプでめくれる誌面に展開されます——表紙、物語、行動喚起。この展開こそが、このフォーマットにおけるクリックです——読者が雑誌を開くことを選んだのです。あとでこの広告に戻りたければ、読者は角を折ります——ドッグイヤー、フォールドイベント、システムが持つ最強の品質シグナルです——そして読者自身のブラウザがそれを記憶します。次にそのサイトのどのページでもその広告主に出会ったとき、しおりを付けたクリエイティブが配信されます。無料で、オークションなしで、学習もなしで。読者が選んだ瞬間に課金することも最適化することも、システムは拒みます。

逆に読者が広告を無視したなら、それもデータです。1時間以内にサンプリング分布は動き、誰にも開かれないクリエイティブは、読者が実際に開くクリエイティブにインプレッションの取り分を譲っていきます。

1日が終わる

UTC の真夜中、消化額のカウンターがロールオーバーし、ペーシングコントローラーは今日の出来を記録して明日の強気さを調整し、サイトの学習済みトラフィック形状が今日のリズムを吸収します——実際に起きたことへ20%だけ寄せるのです。精算が1日の帳簿を書きます。総消化額、プラットフォームマージン、パブリッシャー収益。広告主×キャンペーン×サイトごとに、冪等な行が1行ずつ。

誰も追跡されませんでした。クリック以外、読者に関する何ものもブラウザの外に出ませんでした。そしてこの物語のすべてのメカニズムが、この本の1章ずつになっています。

マガジン形式とドッグイヤー

Promovolve が配信する広告は、ただ1種類です。展開できる雑誌。このフォーマットは好みの問題ではありません——システムの設計の半分が、この形式に依存しています。

折りたたみと展開

ページの中では、クリエイティブ——広告の制作物そのもの、読者が目にするもの——は折りたたまれています。画像と見出しと広告主タグからなる表紙が、パブリッシャーの用意した長方形に収まっている状態です。タップされると全画面のオーバーレイに展開され、読者は小さな雑誌のようにスワイプでめくります。表紙、物語のページ、そして行動喚起のページ。閉じれば、ページは元のままです。

3ページのナラティブは設計として固定されています。これが LLM コピーライターに安定した劇構造(つかみ、中身、お願い)を与え、デザイナーに安定したレイアウトターゲットを与え、読者に一貫したジェスチャー語彙を与えます。レンダラーは自己完結したスクリプトで、クリエイティブを Shadow DOM の中にマウントします。パブリッシャーの CSS は漏れ込めず、広告も漏れ出せません。

イベントの語彙はフォーマットに従います。雑誌を開くことがクリックです——このフォーマットではクリックとは展開のことであり、読者がページを去るのではなく注意を割くことを選んだ瞬間です。誌面のどのページをタップしてもランディングページに遷移します(CTA イベント)。そしてフォールドは後述のドッグイヤーのジェスチャー——広告のしおり付け——であり、すべての中で最も強いシグナルです。配信時の選択はクリックとフォールドの両方から学び、フォールドは2倍の重みを持ちます。

フォールドは広告主に1円も課しません。システムのどこにもフォールド課金は存在しません——読者が自発的に付けたしおりは、課金イベントではないのです。

固定サイズではなく、流動的に

クリエイティブはレイアウトであって、ビットマップではありません。同じクリエイティブが、横長のリーダーボード帯にも、箱形のレクタングルにも、縦長のハーフページ枠にも——広告枠が伝統的にとってきた形たちに——レンダリングされます。レンダラーはマウント時にスロットの寸法を読み、リフローします。コンテナクエリであって、サーバー側のサイズ別亜種ではありません。パブリッシャーは自分のデザインに合うスロット形状を自由に用意でき、広告主はサイズの行列ではなく1つのクリエイティブを維持します。デザインスタッフのいないピラティススタジオがそもそも参加できるのは、これのおかげです。

ドッグイヤー

折りたたまれたクリエイティブの角は、それ自体を折り込めます——ドッグイヤー、読者が紙の本ですでに使っているあのジェスチャーです。ドッグイヤーされた広告はしおりです。

  • 読者のブラウザに住む。 ピンは IndexedDB にスロット単位で保存され、広告リクエストのたびにサーバーに提示されます。サーバーはフォールド状態をステートレスなトークンに署名します。誰が何をしおりしたかは保存しません。そもそも誰なのかを知らないからです。
  • スロットを勝ち取る。 配信時、有効なピンはオークションもスコアリングもペーシングも予算もすべて素通りします。しおりされたクリエイティブが、ただ配信されます。ピンされたスロットが存在しないページでは、ピンされた広告主はサイト全体で通常オークションから除外されます——読者の保存した広告を通常のインプレッションとして燃やしてはならず、同じ広告主の別クリエイティブで読者を追い回してもならないのです。
  • 無料で、学習されない。 ピンによる再会は専用の dogeared_* カウンターで数えられ、消化額からも、CTR 学習からも、あらゆる最適化ループからも除外されます。しおりは読者の選択です。システムがそれを収益化したり学習した瞬間、それは選択でなくなります。
  • 自己修復する。 広告主がサイトを離れたり、キャンペーンが終了したり、クリエイティブが取り消されたりすると、配信レスポンスがクライアントにどのピンが失効したかを伝え、クライアントは IndexedDB から削除します。一時的な参照失敗でピンが消されることはありません——「このクリエイティブはもう存在しない」という確認だけが削除を許します。

この仕組みには、システムを運用する人を驚かせる帰結が1つあります。自分のサイトでテスト中に広告をドッグイヤーすると、その広告主はあなたの通常ローテーションから消えます。システムは設計どおりに動いています——あなたのブラウザがそう頼んだのです。

ランディングページからクリエイティブへ

小さな広告主の多くが持っている「デザインされた制作物」は、ただ1つ。ランディングページです。Promovolve はそれを広告の原典として扱います——キャンペーンの入力は URL であって、バナー素材の zip ではありません。

パイプライン

  1. 抽出。 Playwright ブラウザ(crawler ロールを持つクラスターノード上で動く LPWorker プール)がランディングページを読み込み、原材料を抽出します。見出しの候補、本文、画像、そしてブランドキット——支配的な背景色、文字色、そして塗られた面積の大きい順に並んだ最大6色のパレット。ブランドキットはレンダリングされた DOM から測定されるのであって、モデルが推測するのではありません。広告はランディングページの実際の見た目を受け継ぎます。

  2. 書き直し。 LLM が抽出された文言を3ページの雑誌ナラティブ——表紙のつかみ、物語、行動喚起——に書き直します。ただし固い制約の下で。主張はランディングページ自身のテキストに根拠がなければならず、逐語性が重要な詳細(価格、電話番号)は言い換えられずそのまま持ち越されます。

  3. レンダリングと検証。 クリエイティブはヘッドレスにレンダリングされ、オブジェクトストレージ(Cloudflare R2、CDN 経由で配信)にアップロードされます。次に LLM が2つの問いに照らして検証します。この広告は宣言されたコンテンツカテゴリーに本当に合っているか。そしてブランドセーフか——アダルト、暴力、ヘイトを含まないか。チェックは著述されたテキストがあればそれを読み、ない場合だけレンダリング画像に頼ります。判定はクリエイティブの検証・安全性ステータスを更新し、ターゲットカテゴリーを自動導出します。

色はコードであって、モデルの出力ではない

文字色を LLM が選ぶことは決してありません。決定的なパイプラインが選びます。輝度が「明るい地に濃い文字」か「暗い地に明るい文字」かを決め、すべての組み合わせが WCAG AA コントラスト比(4.5:1)に照らして検査され、コントラストを満たさないブランドキット色は準拠フォールバックに置き換えられます。プロンプトには許可された16進値がハードコードされているので、モデルは読めない色を発明できません。クリエイティブの見た目がおかしいなら、バグはコントラストパイプラインの中——ブレークポイントを置ける場所——にあり、モデルの機嫌の中にはありません。

デザイナー

広告主は、生成されたクリエイティブをブラウザ内のデザイナーで手直しできます。その編集モデルが誠実になるまでには何度かの反復が必要で、その不変条件は「クリエイティブとは何か」を定義するものなので、明記する価値があります。

  • 画像はページにつき1枚、展開ビューが定義する。 全画面誌面の1ページ目に表示される画像が、あらゆる場所でそのページの画像そのものです——折りたたみ表紙もすべてのスロットサイズもそこから導出されます。サイズごとの画像固定はありません。展開ビューで画像を差し替えれば、どこでも、必ず、差し替わります。
  • テキストと色は、デフォルトで全サイズ同期。 各テキストフィールドはサイズごとに「全サイズで同期」設定を持ちます。同期を外せば編集はそのスロットサイズに留まり、再び同期を入れれば現在のテキストが共有値として採用されます。見出し・本文・ページ背景の色はさらに3ページ間でも同期し、それぞれに独立したトグルがあります(ページ背景はデフォルトで同期)。
  • 削除の出所は1つ。 展開ビューからフィールドを削除すると、それを載せているすべてのサイズから消えます——そして確認ダイアログは「すべてから消えます」と言い張るのではなく、対象のサイズを正確に数え上げます。

折りたたみビューと展開ビューは1つのレイアウト文書の2つの投影です。だから広告主が何をしようと、表紙が中身の雑誌と別の商品を宣伝することはできないのです。

ページに広告が届くまで

Promovolve はコンテンツをターゲットにします。だからページが広告を載せられるようになる前に、システムはそのページが何についてのページかを知らなければなりません。面白いのはいつそれが起きるかという判断です。クロールのスケジュールではなく、最初の読者が現れたときなのです。

オンデマンド、読者起動

パブリッシャーのサイトを歩き回るクローラーは存在しません。(初期には存在しました。毎晩の予算を誰も訪れないページの再読に費やしていたので、削除されました。crawler というクラスターロールは、ランディングページ解析ワーカーのホストとしてのみ生き残っています。)代わりに、こう動きます。

  1. 広告タグが、サーバーがまだ分類したことのない URL の広告をリクエストします。レスポンスには空のスロットと、ゼロの鮮度トークンが載ります——テキストを送って
  2. タグはページの可読テキストをブラウザ内で抽出し(8,000文字が上限)、/v1/classify-page に送信します。応答は即座に 202 Accepted。分類が配信をブロックすることはありません。
  3. サイトのエンティティがテキストを LLM(Gemini、現在は gemini-2.5-flash)で IAB コンテンツタクソノミー 3.0 のカテゴリーに分類します——上位3件、信頼度スコア付き。キャンペーンは同じ分類体系に需要——買いたいカテゴリー——を登録するので、マッチングは祖先展開付きの直接的なカテゴリー参照になります(野球のページはスポーツをターゲットにするキャンペーンにマッチ)。中間のマッピング層はありません。

分類のきっかけになったページに広告は出ません。以降のすべての読者には出ます。ページは読者がその存在を証明したまさにそのときに分類され、LLM のコストはトラフィック量ではなく新規 URL の数で抑えられます。サイトエンティティのシングルフライトガードが、話題の記事に殺到する最初の訪問者たちを1回の分類呼び出しにまとめるのです。

公開日ではなく、鮮度

分類はサイトの分類鮮度ウィンドウ——デフォルト48時間——のあいだ有効です。これは分類に対する TTL であって、記事の公開日のチェックではありません。システムのどこも公開日を読みません。

すべての配信レスポンスには、このループを駆動するトークン reclassifyInMs が載ります。これが正のあいだ、広告タグは何も送りません。切れたら、次の訪問がテキストを再送し、新しい分類が新しいウィンドウを開きます。その帰結は——

  • エバーグリーンなコンテンツは配信され続ける。 読者が生きている3年前の記事は、永遠に48時間ごとに再分類されます。
  • 死んだページは失効する。 トラフィックの止まったページはすべてのキャッシュから抜け落ちます——オークショニアは5分ごとにウィンドウを過ぎた分類を刈り取ります。状態量は、読者が実際に訪れるものの量で抑えられます。
  • 内容の変化は捕捉される。 編集された記事は、1ウィンドウ以内に読み直されます。

再起動と障害を生き延びるために

分類はサイトの永続エンティティに保存され、3つの経路でオークション層に再生されます。エンティティの復旧時。新しいオークショニアが名乗りを上げたとき(再起動したオークショニアは空のページキャッシュから始まるので、教え直しが必要です)。そして5分ごとの更新ティック。再生は冪等——同じか古いタイムスタンプは無視される——ので、経路が重なっても無害です。だからクラスターの再起動は自己治癒します。数瞬のうちにオークショニアはすべての新鮮なページを学び直し、オークションを再実行します。

LLM 呼び出し自体はサーキットブレーカー(5連続失敗で30秒オープン)と、API ティアに合わせたトークンバケットのレートリミッターに包まれています。分類の失敗はシングルフライトのスロットを解放するだけで、次の読者が再試行します。配信経路がモデルを待つことは決してありません。

周期オークション

リアルタイムビディング——従来型アドテクのモデル——はインプレッションごとにオークションを走らせ、それぞれにページ読み込みが許す数ミリ秒を与えます。Promovolve はこれを逆転させます。オークションはインプレッションに先立って走り——ページが分類されたとき、周期ティックのとき、そして世界が変わったとき——結果はキャッシュされます。配信はキャッシュを読むだけです。誰も待っていないからこそ、オークションはじっくり考える余裕を持てるのです。

オークションが生み出すもの

サイトの AuctioneerEntity は、ページとスロットごとに、勝者ではなく候補プールを組み立てます。

  1. 需要の照会。 ページのカテゴリー(祖先展開込み)を CategoryBidderEntity アクター——どのキャンペーンがどのカテゴリーに入札しているかの台帳——に対して解決します。CPM のしきい値が競争力のある入札だけを残し、各カテゴリーは CPM 上位およそ50キャンペーンまでしか出せません。
  2. 入札の収集。 資格のある各キャンペーンのエンティティに、クリエイティブと現在の入札額を尋ねます。キャンペーンはここで自分のフィルターを適用します——広告主が掲載先を制限していれば、サイト許可リストなど。
  3. 並べ替え、上限なし。 候補はクリエイティブ単位で重複排除され、CPM 順に並び(同点はパブリッシャー承認済みが勝ち)、各キャンペーンの最良クリエイティブが先頭に来るよう並べ替えられます。資格のあるキャンペーンのプール全体が保持されます——並べ替え時の足切りはありません。配信時の選択は学ぶために敗者を必要とします。敗者を捨てるオークションは、探索を音もなく無効化してしまうのです。
  4. キャッシュ。 プールは ServeIndex——クラスターの章で説明する、複製されたローカル読み取り可能なキャッシュ——に TTL 付きで書き込まれます。

パブリッシャーがまだ承認していないクリエイティブを持つキャンペーンも入札はします。それこそがクリエイティブが承認キューに届く仕組みだからです。しかし承認待ちの需要は配信できず、フロア最適化からも見えません——未承認の入札が市場に何かを教えてはならないのです。

オークションが走るとき

  • 分類時 — ページの最初のオークションは、最初の分類の直後に続きます。
  • タイマーで — すべてのサイトが新鮮なページを周期的に再評価します(本番は5分間隔。コードのデフォルトは30分)。タイマーは保険であり、仕事の大半は次のイベント駆動経路がこなします。
  • イベントで、デバウンス付き — キャンペーンの作成・一時停止・ターゲット変更。クリエイティブの承認・拒否・フラグ。入札額の変更。フロアの移動。それぞれが、影響を受けるページの再評価を1秒のデバウンスでトリガーします。
  • 起動時 — 再起動したオークショニアはサイトエンティティから分類を教え直され、即座に再オークションを蹴ります。クラスター再起動はタイマーを待たずに収束します。

予算切れは削除ではない

キャンペーンが日予算を使い切っても、その ServeIndex エントリーは削除されません——削除すればエントリーに付随するパブリッシャー承認ステータスまで捨てることになり、真夜中のたびに全クリエイティブを承認キューにやり直させてしまいます。代わりにエントリーの TTL は日付のロールオーバーを越えて更新され、配信経路が予算超過キャンペーンを選ばないだけです。ロールオーバーで予算がリセットされれば、クリエイティブは即座に復帰します。

同じ原則が、システム内のすべての退場判断を貫いています。削除イベントは意図的かつ限定的に(広告主の凍結。キャンペーンのサイト退出は読者のピンを道連れにする)。一時的な状態は、削除ではなく標識を立てる。過去の配信バグの大半は、まさにこの規則への違反にさかのぼれました。

パブリッシャーの関門

雑誌の編集者は、自分の文章の隣にどの広告を載せるかを自分で決めます。Promovolve はその権限をパブリッシャーの手に残します。サイトのパブリッシャーが承認するまで、クリエイティブがそのサイトで配信されることはありません。そしてパブリッシャーはいつでも気を変えられます。

クリエイティブがキューに届くまで

提出フォームはありません。キャンペーンがあるサイトのオークションで勝つと、その未承認クリエイティブは自動的にそのサイトの承認キューに並びます——オークションこそが提出なのです。パブリッシャーのダッシュボードは、承認待ちの各クリエイティブを読者が見るのとまったく同じ姿で表示します。折りたたまれた表紙と、展開された雑誌の全体を、ライブでレンダリングして。キューは新しい候補が届くたびにサーバー送信イベント(SSE)で更新されます。

承認はクリエイティブ単位・サイト単位です。あるサイトでの承認は、ほかのどのサイトについても何も言いません。

パブリッシャーの動詞

  • 承認(Approve) — クリエイティブは配信してよい。即座にライブのプールに入ります。再オークションは不要です。
  • 拒否/フラグ(Reject / Flag) — クリエイティブはこのサイトへの入札からブロックされます。ブロックは削除可能なフィルター(クラスター全体に複製されるカッコウフィルター)への所属エントリーなので、入札時に、安く、すべてのオークションで強制されます。フラグを外せばエントリーは消え、クリエイティブは再び競争できます。ブロックは設計として可逆です——「フラグ済み」は外されるまでの意味であって、永遠ではありません。
  • 取り消し(Revoke) — 承認の最も強い取り消しです。クリエイティブは配信を止め、承認待ちに戻ります。入札は続けます(それがキューに再入場する方法です)が、再承認されるまで配信はできません。それを指している読者のドッグイヤーは失効として報告され、読者のブラウザから掃除されます。
  • ドメインのブロック — パブリッシャーはランディングページのドメイン単位でもブロックできます。そこにリンクするすべてのクリエイティブが、どの広告主のものであれ、除去されます。

一度得た信頼は

すでに吟味した広告主のクリエイティブを1枚ずつ審査し続けるのは雑務です。そこでパブリッシャーはサイトごとに自動承認にオプトインできます。デフォルトはオフです。その形の理由を述べます。

  • 手動承認は信頼の表明です——1枚の画像への信頼ではなく、その背後のキャンペーンと、リンク先のブランドへの。だから手動承認のたびに、サイトに2つのトラストアンカーが記録されます。キャンペーンと、ランディングページの登録可能ドメイン(shop.acme.comwww.acme.com は同じブランド。共有ホスティングの2つのテナントは違います)。トグルがオンなら、どちらかのアンカーに一致する新しいクリエイティブはキューを飛ばして即配信されます。自動承認済みの印が付くので、この機能が自分の代わりに何をしたか、パブリッシャーはいつでも監査できます。
  • 信頼は消費されるだけで、連鎖しません。 自動承認は自分のアンカーを作りません——さもなくば1回の承認が共有ドメイン経由で広告主のポートフォリオ全体に雪だるま式に広がりかねません。信頼を広げられるのは、人間が Approve を押したときだけです。
  • 拒否は、推定が外れた証拠です。 自動承認は「過去の承認が未来の承認を予測する」という賭けです。信頼済みキャンペーンやドメインのクリエイティブを拒否・フラグ・取り消しすることは、その賭けの逆の決着であり、アンカーを引き上げます。兄弟クリエイティブは手動キューに戻ります。アンカーはダッシュボードから個別にも外せます。
  • デフォルトオフは、意図的です。 承認済みの需要こそがフロア価格を教えるので、自動的に承認されるものを広げることはサイトの経済を変えます。その選択はパブリッシャーのものであって、デフォルトのものではありません。同じ理由で遡及付与もありません。機能が存在する前の承認はアンカーを作りません——それらと釣り合うはずだった拒否が、記録されていないからです。

トグルをオフにすると、信頼リストは消えず自動承認だけが止まります。オンに戻せば復活します。

本番障害で学んだライフサイクル規則

キャンペーンを一時停止することと、その承認を削除することは別の行為です。これを混同した結果、ある定期デプロイでパブリッシャーの承認キューが吹き飛んだことがあります。確定した規則はこうです。

  • 広告主による明示的な一時停止は、そのサイトでのキャンペーンの承認を取り消します——停止は退場です。再開時、すべてのクリエイティブは承認待ちからやり直します。これはプロダクトとしての判断です。3月にキャンペーンを承認したパブリッシャーが、7月にそれが音もなく再開していたと知るべきではありません。(トラストアンカーはパブリッシャー自身の状態なので停止を生き延びます——自動承認にオプトインしたサイトでは、再開は次のオークションで再承認されます。そこではパブリッシャーがまさにそれを望むと言っているからです。)
  • それ以外はすべて承認を保持します——予算切れ、デプロイ中のカテゴリー再登録の揺れ、エンティティ再起動、再検証。どれも広告主の退場ではないので、どれもパブリッシャーの決定に触れてはなりません。

承認状態は——このシステムの読者向け・パブリッシャー向けのすべてと同じく——保存側に倒します。人間が明示的に選ばない限り、ステータスは標識であって、削除ではありません。

配信の瞬間:トンプソン・サンプリング

オークションは候補のプールをキャッシュしました。いま読者がここにいて、システムは選ばなければなりません——速く、インプレッションごとに、ページ上の全スロットを一度に。ここが Promovolve が学ぶ場所です。

リクエストはページにつき1回

広告タグは全スロットを列挙した1つのバッチリクエストを送ります。サイトの AdServer エンティティは順に確認します。リクエスト元ホストがサイトの検証済みホストであること(違えば 403)。ページの分類が鮮度ウィンドウ内であること(外れていれば 204)。読者のドッグイヤーが最優先で尊重されること(有効なピンはスロットを取り、以下のすべてを飛ばします)。そしてペーシングゲート——予算ペーシング参照——が、このインプレッションをそもそも使うべきかを決めます。

残ったものが、候補プールからの選択です。

平均ではなく、サンプリングでスコアを付ける

各クリエイティブは、インプレッション・クリック・フォールドの直近60分間の窓を分単位のバケットで持ち歩いています。そこから2つのベータ分布を作ります(下の CTR はクリック率——このクリエイティブを見た読者のうち、開いた人の割合です)。

sampledCTR  ~ Beta(clicks + 1,  impressions − clicks + 1)
sampledFold ~ Beta(folds + 1,   impressions − folds  + 1)

リクエストのたび、すべての候補が自分の分布から1つ引きます——平均値は使いません。スコアは:

engagement = sampledCTR + 2.0 × sampledFold + newcomerBonus
score      = engagement × CPM^α

このサンプリングこそが仕掛けのすべてです(これがトンプソン・サンプリングです)。1,000インプレッションを持つクリエイティブの引きは真の率の周りに固く集まり、9インプレッションしかないクリエイティブの引きは荒れます。だから不確かなクリエイティブはときどき高く引いて勝ちます——探索です——それも、自分の不確かさに正確に比例して。探索スケジュールも、イプシロンも、フェーズもありません。信頼度そのものが実験を配分し、データが積もれば引きは狭まり、最良のクリエイティブがただ最も多く勝つようになります。

ここでのクリックとは雑誌を開くこと——このフォーマットではクリックとは展開です。フォールドはドッグイヤーで、2倍の重みを持ちます。広告をしおりする読者は、ただ開く読者より強いシグナルだからです。

指数 α はパブリッシャーの唯一の調整つまみです(bidWeight)。デフォルトは0.5で、10ドルの入札は1ドルの入札に10倍ではなく約3.2倍で勝ります——価格は効きますが、読者が反応するクリエイティブは、無視される金持ちクリエイティブに勝てるのです。発見を重視するパブリッシャーは0.3に、収益なら0.7に。

コールドスタートも、同じ式の中に

コールドスタートとは、ランキング問題の気まずい初日のことです。履歴のない広告をどうスコアすればいいのか?新しいクリエイティブは2つの助けを得ますが、どちらもスコアとして表現されます——別のコールドスタート用コードパスも、ラウンドロビンも、強制配信もありません。

  • インプレッションがゼロのとき: CTR の引きは、そのクリエイティブのカテゴリー親和度スコア(オークション時にページとどれだけ合っていたか)±0.15の一様ノイズに置き換えられ、フォールドの引きは Beta(1, 3) の事前分布から来ます。コイン投げではなく、関連性に裏打ちされた推測です。
  • 最初の50インプレッション: +0.5から始まり線形にゼロへ減衰する加算の新参ボーナス——自信満々の古参に対する保証付きの助走であり、クリエイティブが実データを持つ頃には消えています。

選択は、どのライフサイクル段階でも単一の argmax——最高スコアを取る、それだけ——のままです。

ページを埋める

スロットは大きい順に貪欲に割り当てられ、それぞれが残る候補の中で最高のサンプルスコアを取ります——ただし2つの固い規則の下で。クリエイティブは1ページに最大1回、そしてキャンペーンも1ページに最大1回。1つの広告主に染まったページは読者にもパブリッシャーにも広告主にも悪い。この制約は、ごまかしの効かない割り当て時に強制されます。

勝者の予算はレスポンス送信前に確保され、インプレッションは選択の時点でサーバー側に記録されます——課金は、トラッキングピクセルが読者のブラウザを生き延びるかどうかに依存しません。クリック(展開)、フォールド(ドッグイヤー)、CTA イベントはあとからトラッキングエンドポイント経由で届き、ベータ分布の窓を更新します。 ドッグイヤーによる再会はこのどれも更新しません。専用カウンターに住み、学習からも消化額からもレポーティングの主要指標からも除外されています。

勝者が支払う価格

Promovolve の心臓部はセカンドプライスです。勝者は自分が提示した額ではなく、勝つのに必要だった額を払います。しかし勝敗を決めるスコアは engagement × CPM^α であって裸の入札額ではないので、クリアリング価格も品質調整されなければなりません。

配分はサンプルで、価格は平均で

選択は意図的にサンプリングされた(ノイズの乗った)値を使います。価格決定はそうであってはなりません。ランダムな引きに依存する価格は、同一のインプレッションに違う値段を付けてしまいます。だからシステムはサンプルで配分し、事後平均で価格を付けます。勝者が決まったあと、次点のスコアを平均エンゲージメント率から計算し直し、勝者は「それでも勝てていた最小の CPM」を払います。

clearingCPM = (runnerUpScore / winnerEngagement)^(1/α)

これがスロットのフロアと勝者自身の入札額の間にクランプされます。直感で言えば、スコア式を逆算して「君のエンゲージメント率なら、次善の候補に勝てる一番安い入札額はいくら?」と尋ねているのです。

ここから2つの性質が転がり出てきます。

  • 品質は割引になる。 読者が反応するクリエイティブは、順位を保つのに必要な CPM が低くて済みます。つまり同額で入札する凡庸なクリエイティブより安く払うのです。広告主は、広告を良くすることで自分の価格を良くできます。
  • 入札は正直でいられる。 必要以上に入札額を上げても価格は上がりません(価格は次点が決めます)。下げれば負けるリスクだけが増えます。計算すべきビッドシェーディング戦略が存在しない(シェーディング:払い過ぎを避けるために本当の価値より安く入札すること——ファーストプライスオークションの日々の宿題)からこそ、Promovolve はキャンペーン側の入札最適化ツールを用意しません——この仕組みには、それがやることが残されていないのです。

次点は勝者と同じコンテンツカテゴリーから取られます。だから価格は「この種のページ」への実際の競争を反映し、偶然のカテゴリー横断比較にはなりません。

エッジケース

  • 探索はたいていフロア価格になる。 履歴ゼロの勝者にも同じ平均式で値段が付きます。使われるのはコールド事前分布のエンゲージメント——カテゴリー親和度、フォールド事前分布、新参ボーナス。ボーナスがエンゲージメントを水増しするので、逆算された価格はたいていフロアまでクランプされます。強い同カテゴリー次点と向き合うコールドな勝者は、フロアより上で約定することもあります。
  • 次点なしなら、フロア。 単独の候補はフロアを払います。(一社入札の市場でフロアが崩れ落ちるのを防ぐのはフロア最適化——フロア最適化参照——で、フロアを単独入札者の入札額に釘付けにします。)
  • ピンによる再会は無料。 ドッグイヤーされたクリエイティブが、しおりを付けた読者に配信されるとき、約定価格はゼロです。読者の記憶は在庫ではありません。

消化額はクリアリング価格で記録される

予算の確保も、ペーシングも、帳簿も、入札額ではなく約定した価格を使います。薄い競争に8ドルで入札したキャンペーンの実際の支払いは1,000回あたり2.10ドルかもしれません——予算はその分だけ長持ちし、広告主のレポートには実際に払った価格が載ります。すべての消化イベントは、バッファリングされ重複排除された at-least-once の記録を通って、マイクロドル(100万分の1ドル——整数演算なので帳簿に丸め誤差が積もりません)建ての複式簿記の台帳に流れ込みます。精算は総額をプラットフォームマージン(ベーシスポイント——1万分の1、つまり0.01%——単位の料率で、日付き改定が可能)とパブリッシャー収益に分割します。広告主×キャンペーン×サイト×日ごとに、冪等な行が1行ずつ。

予算ペーシング

朝9時までに使い切られた日予算は、広告主の役に立ちません——夜の観客はキャンペーンを一度も見ず、朝のインプレッションは慌てて買われたものです。ペーシングの仕事は消化を1日に沿わせることであり、その設計原理は機械学習屋のものではなく制御工学者のものです。観測によって自己調整する、単純なフィードバックループ。

ゲート

ペーシングは配信時、選択の手前で確率的なゲートとして働きます。リクエストごとに各キャンペーンはある確率でスロットルされ、スロットルされたキャンペーンはそのインプレッションを見送ります。確率は消化率に対する PI コントローラー——サーモスタットのアルゴリズム:今日の誤差への比例と、その累積履歴への比例で補正する——から来ます。

error      = smoothed(actualSpend / expectedSpend) − 1
throttle   = clamp(Kp·error + Ki·integral, 0, 0.99)

使い過ぎはスロットルを押し上げ、使い足りなければ下がります。コントローラーは意図的に非対称です——超過側の誤差は乗数(基準2倍)で増幅されます。使い過ぎは取り返しがつかず、使い足りない分はあとで追い上げられるからです。

本番の挙動が宿っているのはその先の細部で、それぞれに存在理由があります。

  • 自己調整。 超過乗数は、直近の消化率のローリング窓から自分自身を調整します(1.5倍〜5倍)。慢性的な使い過ぎはコントローラーを強気にし、うまく歩けた日は緩め、学ばれた値は日をまたいで持ち越されます。ゲインは固定、適用は自己調整。
  • 振動の減衰。 消化率がシーソーを始めたら、収まるまで平滑化が強まります——うろつくコントローラーは、遅れるコントローラーより始末が悪いのです。
  • アンチワインドアップ。 積分項はクランプされ減衰します。午前中いっぱいの使い足りなさが、午後のスロットルなし消化を「貯金」することはできません。
  • 猶予期間。 新しい1日(あるいは始まったばかりのキャンペーン)には、穏やかなスロットルの短い窓が与えられます——最低時間最低リクエスト数の両方です。トラフィックの少ないサイトでは10リクエスト来るのに1時間かかることもあるからです。リクエスト数の条件がなかった頃、小さなサイトは何の証拠もなしにスロットルされていました。
  • 日またぎの学習。 今日予算を早々に使い切ったキャンペーンは、明日を強化された乗数のヒント付きで始めます。

期待消化は、時計ではなくトラフィックに従う

expectedSpend が繊細な側の半分です。線形ペーシング(1日の X% の時点で予算の X%)は朝のピークを絞りすぎ、深夜には達成不可能な目標を課します。Promovolve は代わりにサイトごとのトラフィック形状を学びます。24個の時間別ボリュームを、平日と週末で別々に、広告リクエストそのものの到着時刻から。

学習は意図的に保守的です。生まれたてのサイトは初日に高速な日中推定器で形状をブートストラップします。それ以降は——恒久的に——形状は UTC 真夜中のロールオーバーでのみ変わり、今日観測された分布が保存済み形状に 20/80 でブレンドされます。再起動は永続化された形状(両方の曜日タイプ)を復元して同じ体制に戻ります。スナップショットは毎時、ロールオーバー時、シャットダウン時に書かれます。似た日が10日あれば形状は収束し、異常な1日ではほとんど凹みません。

形状は学習専用です。形状を設定する API もダッシュボード項目も、意図的に存在しません。手書きの形状は測定ではなく直感を符号化したものであり、間違った形状は無い方がましだからです——フラットな形状は品よく劣化してちょうど線形ペーシングになりますが、間違った形状は本物のピークを絞ります。パブリッシャーは学ばれた形状を見ることはできます(サイトの観測ページにチャートがあり、サイト統計にエクスポートされます)が、編集はできません。

形状はペーシングに二度仕えます。その累積曲線は PI コントローラーが追う期待消化率になり、時間ごとの相対ボリュームはインプレッションレート目標をスケールします。時間の境界をまたいで補間されるので、レートは段差ではなくスロープで変わります。

ここに無いもの

インプレッション単位の入札調整はありません。勝率予測モデルもありません。ボラティリティでスケジュールされたゲイン表もありません(設計はされましたが、本番が必要としたのは自己調整コントローラーだけだったので、削除されました)。ペーシングは紙ナプキンの上で推論できるフィードバックループです——だからこそ、プラットフォームが配信するすべてのインプレッションの門番を任せられるのです。

フロア最適化

フロア価格は、注目がその価値より安く売られることからパブリッシャーを守ります——低すぎれば、薄い競争の下でセカンドプライスの約定はゼロに向かってすり減り、高すぎればフィル率——実際に広告が配信されるスロットの割合——が崩壊します。パブリッシャーがこの数字を勘で当てる必要はないはずです。だから Promovolve は測ります。

学習ではなく、掃引

FloorSweepOptimizer は、意図して強化学習ではありません。初期には RL のアプローチ(離散化したフロア水準の上の価値推定)が作られ、評価され、捨てられました。ペーシングとトンプソン・サンプリングがすでにあらゆるフロア変更に適応してしまう環境では、信用割当は絶望的で、エージェントが学ぶのはほとんどノイズでした。取って代わったのは、統制された測定です。

  1. 掃引(スイープ)。 もっともらしい範囲(下限は観測された拒否入札、上限は観測された最良入札)に候補フロアを生成します。各候補を一定のオークションティック数だけ保持し、配信された収益——ペーシング後・選択後の実際の稼ぎであって、理論上の約定価格ではない——を測ります。収益だけが正直な目的関数です。その上流にあるものは何であれ、フロアが相互作用する当のシステムたちに攪乱され得ます。
  2. 搾取(エクスプロイト)。 argmax——最も稼いだフロア——を取り、より長い搾取期間だけ保持します。許容差内の同点は高い方のフロアに解決されます。同じ収益をより少ないインプレッションで挙げており、支配的入札者の価値が上振れしたときにも頑健だからです。オプティマイザーが最低フロアにフェイルオープンするのは証拠が欠けているときだけで、最低インプレッション数のガードが、低トラフィックでまぐれ当たりした候補が一握りのデータ点で勝つのを防ぎます。
  3. 繰り返し。 市場は流れます。サイクルは絶え間なく再走します。

カテゴリー別フロア

サイト全体で1つのフロアには独占の病理があります。裕福なカテゴリーが1つ(たとえば金融の需要が12ドル)あると、そのフロアが他のすべてのカテゴリーの需要を丸ごと締め出すのです。だからフロアはコンテンツカテゴリーごとに走り、各カテゴリーが独立に掃引します。データの足りないカテゴリーはサイトレベルのフロアにフォールバックします。この設計の動機になった病理例——単独の高額入札者がサイトフロアを他の全員より上に押し上げる——は、いまでは1つのカテゴリーだけを12ドルに値付けし、旅行は変わらず3ドルで約定します。

掃引そのものより重要なガードレールが2つあります。

  • フロアを教えるのは承認済みの需要だけ。 承認待ちのクリエイティブは入札します(承認キューに届くために)が、オプティマイザーからは見えません——さもなくば、未承認の、拒否されるかもしれないキャンペーンが、自分より長生きするフロアを吊り上げられてしまいます。
  • 単独入札者はフロアを自分の入札額に釘付けにする。 承認済み入札者が1人なら第二価格は存在せず、掃引するものがありません。フロアは入札額にスナップします(入札者が去れば即座に元へ——両方向とも本番で検証済みです)。そして承認済み需要がゼロなら、フロアは即座に最低値まで畳まれます。値付けの相手がいないフロアは、純粋なフィル率の損害でしかありません。

正直な注意書きを1つ。完全に均質な市場——全入札者が同じ CPM——では、共通入札額を下回るすべてのフロアが同一の収益を挙げ、オプティマイザーはその台地の上のどこかに落ち着きます。それは正しい振る舞いであり、このコンポーネントが何であるかを思い出させてくれます。知能ではなく、止まることのない、よく設計された実験なのです。

クラスター

ここまでのすべて——分類、オークション、選択、ペーシング、フロア——は、サイトごとの、状態を持つ仕事です。アーキテクチャの問いは、その状態がどこに住むか。Promovolve の答えは、アクターの中です。現実世界の1つの物につき1つ、それぞれが自分の状態の唯一の書き手として。

エンティティ

システムは Apache Pekko のクラスターシャーディングの上で動きます。各エンティティは ID で宛先指定されるアクターで、常にちょうど1つのノード上に生きています。ノードの参加・離脱に応じてシャードコーディネーターが配置と移動を行います。

エンティティ1つあたり所有するもの
SiteEntityサイト検証、スロット、分類
AdServerサイト配信、ペーシング、クリエイティブ統計、フロア
AuctioneerEntityサイトオークション、ページキャッシュ、承認キューへの供給
CampaignEntityキャンペーン入札、クリエイティブ、予算
AdvertiserEntity広告主キャンペーン、承認、日予算
CategoryBidderEntityカテゴリー×シャード需要の台帳

サイトの配信経路の全体——ペーシング状態、トンプソンの窓、フロアの掃引——は、メッセージを1つずつ処理する単一のアクターです。これは偶然ではなく設計上の立場です。ロックなし、キャッシュ一貫性プロトコルなし、read-modify-write の競合なし。競合する相手となる並行性が、そもそも存在しないからです。引き換えはサイトあたりのスループット上限——1アクターのメールボックス——であり、この立場が成り立つのは、1日数百万インプレッションのサイトを単一アクターが余裕で吸収できるからです。いつか1アクターの容量を超えるサイトが現れたら、それは設計がサイト内部のシャーディングで応えるべきスケーリング問題であって、状態をステートレスにすることで応える問題ではありません。

ノードはロールを担います——api(HTTP)、entity(シャーディングされたアクター)、singleton(クラスター全域のディレクトリ)、そして crawler。この歴史的な名前にもかかわらず、いまはランディングページ解析ワーカーのホストとして存在します。ブロッキングする仕事(Playwright、LLM 呼び出し、JDBC)は専用ディスパッチャーで走るので、負荷を受けたエンティティが配信経路を飢えさせることはできません。

ServeIndex:読みは複製、書きは単一の書き手

配信の読み取りはネットワークを渡ってはなりません。候補プールは ServeIndex——Pekko Distributed Data(CRDT)の上に築かれたもの——に住みます。全ノードが完全なレプリカを持つので、配信時の参照はローカルなマップ読み取りです。エントリーは site|slot のハッシュで32個の名前付きマップに分散されます——複製はマップ単位で働くので、巨大な1枚のマップでは変更のたびに全体が再ゴシップされてしまうのです。

一貫性は意図的に非対称です。書き込みはローカルで確認(WriteLocal)して外へゴシップします——オークション結果が他ノードに見えるのが数十ミリ秒遅れても無害です。例外はキー全体の削除で、これはリトライ付きの過半数書き込みを使います。ゴシップ競争に負けた削除は消したはずの候補を蘇らせ、(より悪いことに)削除のトゥームストーンは、分岐したレプリカが破壊的に食い違い得る唯一の場所だからです。速い経路は結果整合で、破壊的な経路は確実性に対価を払います。

レプリカは永続です——DData は指定キーをローカルの LMDB ストアに永続化します——ので、再起動したノードは世界を再ゴシップする代わりにディスクからキャッシュを回復します。起動時の分類再生(サイトエンティティが新しいオークショニアを冪等に教え直す)と合わせて、クラスター全体の再起動はおよそ1分で自己治癒します。スロットが束の間空で配信され、やがて勝手に埋まっていく「再起動後の暗転窓」です。

痛い目を見て学んだ規則

ノード間メッセージはマーカートレイトによって Jackson-CBOR でシリアライズされ、この規律が厳格なのは失敗が無音だからです。マーカーを欠いた応答ペイロードは、ネットワーク境界でただ消えます。裸のタプルをはじめ未登録の形は、ノード間プロトコルから追放されています。永続状態はフィールドのエイリアスで進化させ、永続化済みフィールドの改名は決して行いません。そして Future が必要とする状態は、必ずメッセージとしてアクターに送り返されなければなりません——アクターの状態に対して Future をインラインで完了させることこそ、このアーキテクチャ全体が防ぐために存在する並行性バグなのです。

コアの周りのプラットフォーム——ダッシュボード、承認キュー、課金、メンバー管理——は、サーバーサイドテンプレートをレンダリングする別の Go サービスです(SPA フレームワークなし)。コアとは HTTP で、プロジェクションと台帳のためには Postgres と話します。認証はパスキーのみ。しかし、この本に書かれたすべての配信判断は、アクターの中で起きています。

アドテクの常識に逆らって

Promovolve は、従来型プログラマティック広告からほぼすべての軸で逸れています。この章は正直な採点表です——従来型スタックの方が上手にやることも含めて。

違い

インプレッション毎オークション → 周期オークション。 RTB はすべてのインプレッションに約100ミリ秒の締め切り付きの専用オークションを与えます。その結果、全参加者がすべてを事前計算し、どのみちキャッシュで答えることを強いられます。Promovolve はオークションをホットパスから丸ごと外しました。分類時と変更イベント時に走り、配信はローカルキャッシュの読み取りとベータ分布の引き1回です。引き換えに、候補プールは数分古くなり得ます。1秒デバウンスのイベント駆動再オークションが、大事なところで古さの上限を締めています。

ユーザーターゲティング → コンテンツターゲティング。 Cookie なし、プロファイルなし、同意装置なし。同意すべき対象が存在しないからです。引き換えは本物です。リターゲティングなし、サイト横断のフリークエンシー管理付きブランドキャンペーンなし、オーディエンスセグメントなし。Promovolve の賭けはこうです。ページの文脈——キーワード照合ではなく LLM が読む——は、プライバシーコストなしで関連性の大半を回収する。そしてドッグイヤーは、アドテクが広告主に与えるリターゲティングの主導権を、読者に与えるのです。

最高額落札 → サンプリングされた品質スコア。 伝統的な取引所は、勝者が良い広告だったかどうかを決して知りません。Promovolve の選択は学習システムです。エンゲージメントの事後分布はインプレッションのたびに鋭くなり、新しいクリエイティブは不確かさに比例した探索を受け取り、式(engagement × CPM^α)は、よく作られた広告が資金の潤沢な広告に勝つことを許します。

入札ランドスケープ → 最適化する対象がない。 DSP——デマンドサイドプラットフォーム、広告主が取引所を相手に戦うために雇う入札ソフトウェア——の存在理由の大部分はビッドシェーディングです。品質調整セカンドプライスはその動機を取り除きます。あなたの価格は次点が決め、あなた自身のエンゲージメント率で割り引かれる。Promovolve は入札最適化ツールを出荷しませんが、その不在はロードマップの穴ではなく、仕組みの機能です。

固定 IAB サイズ → 流動的なクリエイティブ。 1つのレイアウトがどんなスロットにもリフローします。小さな広告主はランディングページの URL 1つから1つのクリエイティブを作り、パイプライン(ブラウザ抽出 → LLM コピーライティング → コントラスト検査付き決定的スタイリング → 視覚モデル検証)が、彼らにはいないデザインチームの代わりを務めます。

取引所側の収益ツール → すべてをパブリッシャー側に。 承認キュー、ドメインブロック、カテゴリー別の測定されたフロア——コントロールはパブリッシャーの手元にあり、フロア最適化の目的関数はパブリッシャーの配信された収益です。モデル化されたものではなく、測定されたもの。

従来型スタックが今も上手にやること

正直さは、もう片方の列を要求します。プログラマティック・アドテクは Promovolve にない規模と流動性を届けます。開かれた取引所プロトコルを通る何千ものバイヤーの需要。どこのどんな在庫にも付くレムナントフィル(売れ残りスロットの最後の買い手)。そしてサイト横断のキャンペーンツール——リーチ、フリークエンシーキャップ(1人が同じ広告を見る回数の制限)、ブランドリフト測定——は、コンテンツターゲティングの単一プラットフォームには構造的に提供できません。RTB のインプレッション毎オークションはいまのこの読者に値段を付けます——それはパフォーマンス広告主にとって本物のお金の価値があります。Promovolve は意図的にこのページだけに値段を付けるのです。

Promovolve は、取引所のゲームで取引所に勝とうとはしていません。これは別の取引です。自分の手の中に実際にあるコントロールと共に、キュレーションされた、読者を尊重する収益化を望むパブリッシャーのための。そして——特に小さな——広告主、自分のランディングページが雑誌広告になり、クオンツのチームがなくても正直に参加できるオークションで値付けされることを望む広告主のための。

この本に書かれたシステムは、読み通せるほど小さく、監査できるほど正直で、その両方ができるようにオープンソースです。

用語集

この業界の——そしてこの本の——語彙を1か所に。Promovolve 固有の意味には ⊛ を付けています。

インプレッション (impression) — 1人の人が1つの広告を1回見ること。この商売の原子。数えられ、値付けされ、支払われる単位。

インベントリ(在庫)(inventory) — パブリッシャー側の供給。パブリッシャーが売りに出すスロット(注目)のこと。

オークション(第一価格/第二価格)(first-price / second-price auction) — 「入札額をそのまま払う」対「勝つのに必要だった額(次点のわずか上)を払う」。セカンドプライスは正直な入札を安全な戦略にする。

キャンペーン (campaign) — 広告主の継続的な発注書。クリエイティブ+日予算+ CPM 入札額+ターゲットのコンテンツカテゴリー。

クリアリング価格(約定価格)(clearing price) — 入札額ではなく、勝者が実際に払う価格。Promovolve では品質調整セカンドプライスで、フロアと勝者の入札額の間にクランプされる。

クリエイティブ (creative) — 広告そのもの。画像、文言、レイアウト——読者が目にする制作物。1つのキャンペーンが複数を走らせることもある。Promovolve ではクリエイティブは固定サイズの画像ではなく流動的なレイアウト。

CPM — cost per mille。インプレッション1,000回の価格。すべての入札と価格がこの単位で示される。

CTA — call to action(行動喚起)。「さあどうぞ」のページやボタン。⊛ Promovolve のイベント語彙では、展開された雑誌から広告主のランディングページへのタップスルーが CTA イベント。

CTR — click-through rate(クリック率)。クリエイティブを見た読者のうち、クリックした人の割合。⊛ ここでの「クリック」は雑誌を開くこと——展開。

精算(セトルメント)(settlement) — 1日分のインプレッションを、支払うべきお金(広告主)、受け取るお金(パブリッシャー)、マージン(プラットフォーム)に変える帳簿仕事。

demand(需要) — 買い手側。どのキャンペーンがどのページをいくらで欲しがっているか。「需要を登録する」=キャンペーンが入札するカテゴリーを宣言すること。

ドッグイヤー(フォールド)(dog-ear / fold) ⊛ — 読者が広告の角を折って自分のブラウザにしおりを付けるジェスチャー。広告主には無料、学習からは不可視、システムが持つ最強の品質シグナル。

入札額の値引き(ビッドシェーディング)(bid shading) — ファーストプライスオークションで払い過ぎを避けるために、本当の価値より安く入札すること。これを行うためだけのソフトウェア産業が存在する。セカンドプライスはこれを無意味にする。

パブリッシャー (publisher) — 読者とコストを抱える側。インプレッションを売る。本書では主に小さなコンテンツサイト。

フィル率 (fill rate) — 実際に広告が配信されたスロットの割合。埋まらなかったスロットは何も稼がない。

フロア(最低価格)(floor) — パブリッシャーが受け入れるスロットの最低価格。低すぎれば薄い競争でタダ同然の約定を招き、高すぎればフィルが死ぬ。⊛ Promovolve はパブリッシャーに勘を求める代わりに、コンテンツカテゴリーごとに測定する。

ペーシング (pacing) — 日予算を朝9時までに使い切らず、1日全体に行き渡らせること。

マージン (margin) — プラットフォームが総消化額から受け取る割合。残りがパブリッシャー収益。

ラインアイテム (line item) — 従来型広告サーバーにおける、手作業で設定される配信契約(期間、サイズ、ターゲティング、価格)。Promovolve には存在しない。

ランディングページ (landing page) — 広告をタップした読者が着地する、広告主自身のページ。⊛ Promovolve では広告の生成でもある。

RTB(リアルタイムビディング)(real-time bidding) — 従来型アドテクのモデル。ページ読み込みの約100ミリ秒の間に、インプレッションごとにオークションを走らせる。⊛ Promovolve はこれを、結果をキャッシュする周期オークションで置き換える。

スロット(広告枠)(slot) — パブリッシャーが貸し出すページ上の長方形。Promovolve では寸法を持った div

広告主 (advertiser) — 売るものはあるが注目のない側。インプレッションを買う。本書では主にランディングページを持つ小さな事業者。