Surface 別 解説
GraphQL enum RecommendationSurface の 6 種類について、Surface ごとに「何を推すのか」「どこで使われているのか」「どう scoring しているのか」「今どこまで実装が進んでいるのか」を 1 ファイルにまとめる。
TIP
Base Scoring + Surface Strategy の 2 層構造と全体設計は 仕様 (spec) を、技術選定の経緯は 技術選定 (technology-choices) を読む。各 Surface ファイルでは「その Surface 固有の話」だけを書き、共通仕様はリンクで参照する (重複させない)。
現状の Surface 一覧
| Surface | doc | Admin Preview | Strategy.Aggregate | Strategy.PreFilter | Strategy.PostRerank | 本番投入 (新 pipeline) |
|---|---|---|---|---|---|---|
| FOR_YOU | for-you.md | ✅ adminForYouScenarios | ✅ Identity | ✅ defaultPolicy (Candidates + 既プレイ除外) | ⚠ no-op | ✅ prd 投入済 (scenariosScreenV2 の「あなたにおすすめ」、MOBL-4295) |
| QUICK_MATCH | quick-match.md | ✅ adminQuickMatchScenarios (複数 userID) | ✅ Least Misery + 履歴シグナル統合 greedy | ✅ 全員未プレイ / 人数一致 / locale / 無料 / 90 分以内 / release only / ジャンル | ✅ MMR + N≥1 ハード制約 (MOBL-4286) | ✅ prd 投入済 (asynq matchmake 経路、SuppressLogging=false) |
| SIMILAR | similar.md | ✅ adminSimilarScenarios (scenarioId + viewerIds[]) | ✅ <p_context, p_candidate> 内積 (ScenarioSimilarityBatch)、未学習時は viewer mean フォールバック | ✅ self-exclusion + 人数フィルタ (±2、1 人用除外) + locale 積集合 (viewer 0 で skip) + 2 人以上プレイ済み除外 | ✅ half-half ミックス (全員未プレイ ⇔ 1 人プレイ済み) | ✅ prd 投入済 (Scenario.similarScenarios / Query.similarScenarios、UZU-4294) |
| EVENT_DETAIL | event-detail.md | ❌ 未 expose | ❌ stub (not implemented yet) | ⚠ 共通ルールのみ | ⚠ no-op | ❌ 未投入 |
| AUTHOR | author.md | ❌ 未 expose | ❌ stub (not implemented yet) | ⚠ 共通ルールのみ | ⚠ no-op | ❌ 未投入 |
| COLLECTION | collection.md | ❌ 未 expose | ❌ stub (not implemented yet) | ⚠ 共通ルールのみ | ⚠ no-op | ❌ 未投入 |
入口の 2 系統
新 pipeline (pipeline.Recommender) からの呼び出し方は 2 通り:
- Registry 経由 (staged Strategy): FOR_YOU / SIMILAR / (将来の EVENT_DETAIL / AUTHOR / COLLECTION)。
SurfaceStrategyinterface の PreFilter → Aggregate → PostRerank を順に呼ぶだけ。backend/internal/usecase/recommendation/surfaces/registry.go - 直接 delegate: QUICK_MATCH 専用。V/F/P 加減算と MMR と N≥1 ハード制約を 1 ステップで同時に効かせる統合 greedy なので、PreFilter/Aggregate/PostRerank には分解できない。
pipeline.RecommenderがQuickMatchRequestを型 assertion してstrategy/quick_matchパッケージに直接 dispatch する (strategy-decisions 2026-04-28)
本番で動いている別系統 (新 pipeline 未経由)
surface 別 doc には載らないが、新 pipeline を通らない旧実装が本番で現役の箇所がある:
SearchScenarios(orderBy: RECOMMENDED)(backend/internal/adapters/graph/scenario_query.resolvers.go) →usecase.RecommendationSortedScenarios()(タグ preference + popularity + 乱数)。Web 検索の「おすすめ順」。Surface には分類されない別系統
Surface 追加時の手順
新しい Surface (例えば EVENT_DETAIL) を実装するときの作業順:
recommendation/types.goに Request 型 (EventDetailRequest) を定義して、SurfaceRequestinterface を満たすrecommendation/surfaces/event_detail.goにEventDetailStrategy(SurfaceStrategy実装) を足すrecommendation/surfaces/registry.goのBuildswitch から該当 surface の stub error を消し、新 Strategy を返すように差し替えるbackend/internal/adapters/graph/schema/recommended_scenarios.gqlにadminEventDetailScenariosquery を足すbackend/internal/adapters/graph/recommended_scenarios.resolvers.goに admin resolver を足す- Admin Preview UI (
javascript/apps/admin/src/app/recommendation/preview/page.tsx) のモード切替に追加する - このディレクトリの該当 doc (
event-detail.md) を実装に合わせて書き直す - 一覧表 のステータス列を更新する