推薦システム構築ツールRecotemを 2.0 として全面的に書き直しました。これまでの Django + Vue + Celery + PostgreSQL + Redis という7サービス構成の Web アプリケーションから、YAML レシピを書いて recotem train / recotem serve を実行するだけのシングルパッケージ Python CLI/ライブラリに刷新しています。
なぜ書き直したのか
旧 Recotem は Web UI を中心に据えていたため、利用者は管理画面から学習ジョブを登録し、結果を確認し、モデルを配信する仕組みでした。便利な一方で、「ちょっと推薦モデルを動かしたい」「既存のデータパイプラインに組み込みたい」というケースでは複雑な構成です。PostgreSQL / Redis / Channels / Daphne / nginx といったサービスを立ち上げる必要があり、コンテナイメージも複数に分かれていました。
Recotem 2.0 では発想を逆転させ、「Web UI をやめて、設定ファイル(YAML レシピ)で完結させる」方針にしました。1 つの YAML が 1 つのモデル、1 つの /predict/{name} エンドポイントに対応します。インストールは pip install recotem の一発、配布物も単一の Docker イメージです。
レシピ駆動の利用フロー
1. レシピを書く
データソース、アルゴリズム、チューニング設定を 1 つの YAML にまとめます。
name: purchase-recommender
data:
source: csv
path: s3://my-bucket/purchases.csv
user_column: user_id
item_column: item_id
training:
algorithms: [IALS, BPR, RP3beta]
trials: 30
timeout_per_trial: 300
2. 学習
recotem train recipe.yaml
irspack + Optuna でアルゴリズム選択とハイパーパラメータ探索を行い、HMAC 署名付きのバイナリアーティファクトを出力します。アルゴリズム別の試行回数や試行ごとのタイムアウトを指定でき、Optuna のストレージは in-memory / SQLite / PostgreSQL から選択して並列・再開可能なチューニングが実行できます。
3. サービング
recotem serve --recipes ./recipes/
ディレクトリ内のレシピを監視し、アーティファクトが更新されたら無停止でホットスワップします。FastAPI ベースで、/predict/{name} エンドポイントが自動的に生やされる仕組みです。
廃止したコンポーネント
シンプル化のために、旧バージョンで使っていた以下のスタックは全て削除しました。
- バックエンド: Django, Django REST Framework, Django Channels, Daphne, Celery
- データストア: PostgreSQL, Redis
- フロントエンド: Vue, Vite, PrimeVue, Tailwind
- インフラ: nginx プロキシ, 推論専用サブサービス
代わりに recotem パッケージひとつで完結します。配布は PyPI と単一 Docker イメージ、Helm チャートはサービング専用に学習用 CronJob オプション付きという構成です。
データソースのプラグイン化
データソースは recotem.datasources の entry point で拡張可能なプラグイン方式に変更しました。標準で以下に対応しています。
- CSV / Parquet: ローカルファイルシステム, オブジェクトストア (
s3://,gs://,az://,abfs://,abfss://), HTTPS - BigQuery: GA4 のBQ エクスポート構成を想定したパターン
HTTPS データソースを使う場合、sha256 による整合性ピンが必須で、RECOTEM_MAX_DOWNLOAD_BYTES(デフォルト 256 MiB)でダウンロードサイズの上限を設定できます。リダイレクト時のスキーム変更拒否や、プライベート IP への接続拒否(SSRF対策)も組み込まれており、安心して外部 URL を指定できます。
セキュリティ設計
CLI/ライブラリ化に伴い、アーティファクトの取り回しが運用上の中心になるため、署名と検証を強化しています。
- HMAC 署名付きアーティファクト: マジックバイト + バージョン + リザーブ + kid + ヘッダ JSON + ペイロードという構造。複数 kid を持つ
KeyRingでゼロダウンタイムの鍵ローテーションが可能 - FQCN allow-list: 復元時に読み込めるクラスを手動で列挙し、
numpy.*/scipy.sparse.*の限定的なモジュールプレフィックス許可と高リスクサブモジュール拒否を組み合わせる二重防御 - API キー認証: X-API-Key ヘッダで認証。digest は
hashlib.scrypt+ ドメイン分離ソルトrecotem.api-key.v1で保存 - TrustedHost / CORS デフォルト拒否: 余計なホスト/オリジンからのアクセスを排除
/healthと/health/detailsの分離: 匿名アクセス可能なヘルスチェックは件数のみ、kidやbest_classといったメタデータは認証付きの/health/detailsのみで開示- OpenAPI のフェイルセキュア:
/docsと/openapi.jsonはRECOTEM_ENVがdevelopment/dev/testのときのみ有効 - 構造化ログのリダクション: structlog のチェーン先頭にリダクションプロセッサを置き、シークレットや認証情報の混入を防止
--insecure-no-auth や --dev-allow-unsigned といった開発用フラグは RECOTEM_ENV でガードされ、本番環境では使えない設計です。
オブザーバビリティ
/metrics エンドポイントは RECOTEM_METRICS_ENABLED=true のオプトイン方式で、12 種類の Prometheus メトリクスを出します。
- 推論系:
recotem_predict_total,recotem_predict_latency_seconds - モデル状態:
recotem_model_loaded,recotem_active_recipes - 障害系:
recotem_artifact_load_failures_total,recotem_artifact_stat_failures_total,recotem_watcher_unhandled_errors_total,recotem_metadata_lookup_errors_total,recotem_recipe_rescan_errors_total,recotem_recipes_dir_scan_failures_total - 運用系:
recotem_swap_total,recotem_bigquery_storage_fallback_total
構造化ログには train_done / train_error / tuning_aborted といった正規化済みイベントを出力し、run_id / exit_code / artifact / best_class / best_score / trials / trained_at / kid などのフィールドを統一的に持たせています。
ホットスワップとフェイルセーフ
サービング側では、アーティファクトファイルの更新を検知して無停止で読み替えます。ここに以下の堅牢性が組み込まれています。
- Read-once プロトコル: stat → read の TOCTOU を回避
- Stale-on-swap-fail: ホットスワップ時の復元失敗で 503 にせず、直前のモデルを継続提供
- Lenient startup: 壊れた YAML があっても起動を中断せず、
/health/detailsにloaded=false/last_load_error=...で開示 X-Recotem-Metadata-Degraded: 1ヘッダ: メタデータ参照に失敗した推論レスポンスを明示的にマーク- OOM のフォールスルー: ファイル読み込みや学習パイプラインでの OOM は意図的にラップせず伝播
チュートリアル: 1 コマンドで動かす
examples/tutorial-purchase-log/ を新設し、HTTPS で取得できる小さな公開 CSV(sha256 ピン済み)を使ったゼロセットアップのクイックスタートを用意しました。compose.yaml の tutorial フローに組み込まれており、docker compose で学習からサービングまで通せます。
テスト
ユニット / 統合 / fuzz の 3 階層で 1,420 ケースをカバーしました。
- hypothesis によるアーティファクトローダーのバイトミューテーション fuzz
- レシピローダーの YAML ミューテーション fuzz
pytest-httpserverを使った HTTPS CSV ソースの統合テスト- 実ファイルウォッチャーでの並列ホットスワップテスト
- 起動時の壊れた YAML 耐性テスト
CI では pytest / ruff lint / ruff format / シークレットログ検査 / Docker ビルド / e2e (train → serve → predict) / CodeQL / Trivy を回しています。
まとめ
Recotem 2.0 は、Web UI ベースの推薦システム管理ツールから、YAML レシピ駆動の CLI/ライブラリへと舵を切りました。pip install recotem でインストールして、recotem train recipe.yaml と recotem serve --recipes ./recipes/ を打つだけでモデル学習から API 配信まで完結します。Docker, Kubernetes (Helm), オブジェクトストア, BigQuery, Prometheus といったクラウドネイティブな環境とそのまま接続でき、HMAC 署名・scrypt API キー・SSRF 対策など運用に必要なセキュリティも揃っています。
- PR #85: Recotem 2.0: clean rewrite as recipe-driven CLI/library
- リポジトリ: codelibs/recotem