こんにちは、ソーシャル経済メディア「NewsPicks」の高山です。
この記事は NewsPicks アドベントカレンダー 2023 の15日目の記事です。
昨日は森田さんによる『メディアのミッションによって"良い"ニュース推薦システムって違うのかも! n週連続推薦システム系 論文読んだシリーズ32週目(番外編)メディアモデルと5つの多様性指標群の論文等を読んで思いを馳せた話』でした!
以前にNewsPicksのプッシュ通知でBrazeというサービスを導入した話を書きました。
今回はそのシステムを改修し、通知にレコメンドエンジンを導入した話を書いていきます。
社内ドキュメント一歩手前ぐらい詳しくなってしまったので、もし皆さんが似たようなシステムを設計するときには読んでみてください。
おさらい
まずは前回の記事にも書いた仕組み(旧システムと呼びます)をおさらいしていきます。
- 編成チーム*1が「事前テスト配信」として2〜4つのプッシュ通知内容を合計10%〜20%程度のユーザーに送る
- 10分後ぐらいにダッシュボードで開封率を確認する
- 開封率が高かったプッシュ通知内容を他の全員のユーザーに送る(「全体配信」)
これの実現のためにBrazeというサービスを使っています。
Brazeのキャンペーン機能の中のA/Bテスト機能を使っている話は前回のブログで書きましたのでご覧ください。
実際には
- 2つの内容で事前テスト配信をするキャンペーン(10%のユーザー対象)
- 3つの内容で事前テスト配信をするキャンペーン(15%のユーザー対象)
- 4つの内容で事前テスト配信をするキャンペーン(20%のユーザー対象)
というように、事前テスト配信だけで3つのキャンペーンを作って使い分けていました。事前テスト配信の後の全体配信も、3つに分けていました。
- 2つの内容で事前テスト配信をした後の全体配信(残りの90%のユーザー対象)
- 3つの内容で事前テスト配信をした後の全体配信(残りの85%のユーザー対象)
- 4つの内容で事前テスト配信をした後の全体配信(残りの80%のユーザー対象)
今回やりたいこと
新システムで実現したいことは、事前テスト配信の結果から各ユーザーに個別のプッシュ通知を送ることです。以下のような流れになります。
- 事前テスト配信をする
- 事前テスト配信の開封率のログが溜まるまで10分ほど待つ
- そのログを使い、残りのユーザーがどのプッシュ通知を開封するかを予測する
- 残りのユーザーのそれぞれに、開封予測に応じてプッシュ通知を送る
この開封予測の部分は自前で開発するレコメンドエンジンになります。今日の記事ではこのレコメンドエンジンの中身には踏み込みませんが、おいおい書いていければと思います。
それ以外に、次のようなことを考慮しました。
- BrazeのA/Bテスト機能では、A群/B群/C群/D群の割り当てがキャンペーンを作ったときに決定される
- つまり旧システムでは、長い目で見たときに常にAを送られるユーザーと常にBを送られるユーザーが出てきてしまい、偏りが生まれてしまう
- これを新システムでは解消したい
- つまり旧システムでは、長い目で見たときに常にAを送られるユーザーと常にBを送られるユーザーが出てきてしまい、偏りが生まれてしまう
- Brazeのカスタムアトリビュートを大量にセットする設計方針を採用した(後述)が、カスタムアトリビュートをセットする回数に上限がある
- NewsPicksの料金プランでは、月2.5億回までしかセットできない
- 社内の編成チーム向けのUI/UXはあまり変えない
- うまくいくか自信の持てない段階で編成チームにあまり負担をかけたくない
- 今回担当したメンバーが社内の管理画面を開発するのにあまり慣れていない
- Brazeのキャンペーンは管理画面からポチポチ作る必要があるため、大量に作るのはミスの原因になるのでやりたくない
設計
上記を加味して次のような設計をしました。
夜間バッチ処理
まず、ユーザー全体を、過去の一定期間以内にサービスを利用したかどうかによってアクティブユーザーと非アクティブユーザーに分けます。
非アクティブユーザーにはプッシュ通知の事前テスト配信や開封予測はせずに、単に事前テスト配信で結果が良かったプッシュ通知を一括で送ることにしました。この部分は旧システムと同様になります。
アクティブユーザーにはさらに事前テスト配信対象者と非対象者に分けて、事前テスト配信対象者にはBrazeのカスタムアトリビュートをランダムに割り当ててセットします。これをもとに毎日ランダムにA群/B群/C群/D群の割り当てが決まります。カスタムアトリビュートを付けるのをアクティブユーザーに絞ることでBrazeのAPI呼び出し回数を節約することができました。
このときアクティブユーザーの一覧や、セットするカスタムアトリビュートをS3に出力しておきます。そのログを後でRedshiftに入れることで、詳細な分析ができるようになります。
Brazeでは前日にセットしたカスタムアトリビュートが残っているため、それを削除してから今日の値をセットします。幸いなことに、Brazeで特定のカスタムアトリビュートが付いているユーザー一覧を出力できましたので、それによって実現できました。*2
もし一覧を出力できなければ、昨日にセットしておいたカスタムアトリビュートを自前で持っておかなければいけません。しかし、様々な要因によって自前のリストとBraze側との差分が発生する可能性がありますので、自前のリストが正確であるという保証はありません。もしそういうことをやる必要があったら、この設計は諦めていたでしょう。
事前テスト配信
編成チームのメンバーが2〜4つのプッシュ通知内容を決めて事前テスト配信ボタンを押したときには、BrazeのキャンペーンにAPIリクエストを送るだけです。この部分は旧システムとほぼ同様になります。
違いとしては、後続の処理を実行するために、ここでSQSに通知するようにします。
事前テスト配信後
SQS経由でECSの開封率予測のプロセスが起動します。SQSには事前テスト配信のプッシュ通知内容が2〜4個入っています。
開封率予測プロセスは、まず10分待機して、Redshiftからリアルタイムのアクセスログを取得します。
また、レコメンドエンジンが使うユーザーの特徴量もRedshiftから取得し、レコメンドエンジンで開封予測をします。
この開封予測処理の結果もS3に出力して、後でRedshiftに入れて分析できるようにします。
開封予測結果に従ってBrazeにカスタムアトリビュートを設定します。この時、アクティブユーザーのうち、事前テスト配信対象外のユーザーにカスタムアトリビュートをセットします。(ただし、一部のユーザーにはセットしません。後述)
ここまで終わったらSlackに通知を送り、編成チームのメンバーに次のステップに進んでもらいます。
全体配信
編成チームのメンバーは、ダッシュボードで開封率を見て、結果が良かったプッシュ通知を選んで「全体配信」ボタンを押します。
レコメンドをするのであれば編成チームが1つ選ぶ必要は無いのではないかと思うところですが、敢えてこのようにしました。元々、開封率だけでなく様々な要因に基づいて1つを選んでいたのでそれを踏襲しました。
上で書いた、アクティブユーザーのうち、事前テスト配信対象外のユーザーのうち一部のユーザーには、ここで選んだプッシュ通知を送ります。これにより、レコメンドエンジンを使うプッシュ通知と、編成チームが選ぶプッシュ通知の開封率を比較分析することができます。
おわりに
以上のようにして、「今回やりたいこと」で書いたことをすべて実現できるようなシステムを作ることができました。
書き切れなかったことも山ほどあるのですが、既にだいぶ詳しくなってしまったのでこのぐらいで終わりにしようと思います。 (社内のメンバーは「プッシュ通知パーソナライズの設計」とNotionで検索!)
告知
NewsPicks ではエンジニアを募集中です!ご興味のある方はこちらまで。
https://hrmos.co/pages/uzabase/jobs/NP_Eng004
今回の記事がおもしろいと思ったら NewsPicks アドベントカレンダーの他の記事も見てみてくださいね。 明日はテスト自動化の守護神こと西薗さんが書いてくれます。お楽しみに!