# ラーメンスープ評価AI — VPS設置手順（グレード算出システム）

作成日: 2026-07-14 / 対象OS: **Ubuntu 22.04 LTS（Linux）** / GPU無しCPU運用を前提

このパッケージ1つで「動画 → グレード算出」までを推論できます。学習機能は含みません（推論専用）。

---

> **初めて設置する場合は [INSTALL.md](INSTALL.md)（導入マニュアル）の順番どおりに進めてください。**
> 本書はAPI仕様・公開方式・トラブル一覧などの詳細リファレンスです。

## 0. このパッケージの中身

```
VPSデプロイパッケージ_20260714/
├─ INSTALL.md               … ★導入マニュアル（最初に読む・手順の入口）
├─ README_deploy.md   ← 本書
├─ README_requirements.md   … 環境に必要なものリスト（全網羅）
├─ check_env.py             … ★環境診断（python3 check_env.py で✓/✗と対処法を表示）
├─ requirements.txt         … Python依存一式（torch/rfdetr等を含む）
├─ setup.sh                 … 初回セットアップ（ffmpeg導入・venv作成・pip install・最後に自動診断）
├─ run.sh                   … 起動スクリプト
├─ ramen-grade.service      … 常駐化用 systemd ユニット雛形
├─ app/                     … 推論システム本体（APIサーバー・end-to-endパイプライン）
│   ├─ api_server.py        … ★本番用: キュー処理付きAPIサーバー（複数人対応・Web UI同梱）
│   ├─ batch_test.py        … フォルダ一括テスト（README_batch_test.md 参照）
│   ├─ app.py               … 旧・単独UI（1人利用向け。通常は api_server.py を使う）
│   ├─ pipeline_full.py  scoring_engine.py  infer.py
│   ├─ models/*.pkl         … グレード算出モデル（線形回帰）＋metadata.json
│   └─ samples/
├─ test_videos/             … 一括テスト用の動画置き場（batch_test.py の --input 先）
├─ results/                 … 一括テストの結果出力先（--out 省略時）
├─ prompt/
│   └─ soup_eval_prompt_policy.md … LLM評価プロンプト定義（必須・削除不可）
├─ local_batch_api/         … 上流バッチ処理（フレーム抽出・濃度解決・LLM評価）
│   ├─ config.test10.yaml   … VPS用設定（ffmpegはPATH参照に変更済み）
│   ├─ density_resolver.py  … 上流モデルのパスを環境変数対応にパッチ済み
│   └─ …
├─ common/                  … 共通モジュール
├─ upstream_suite/          … ★上流の画像AI（ソース＋学習済み重み・約195MB）
│   └─ 03_soup-density-ai/  … RF-DETR(スープ抽出)/濃度分類/湯気分類 のsrc・models
│       └─ … 04_steam-classification/…/steam_classifier_best.pt
└─ secrets/
    └─ byteplus.key.example … ここにAPIキーを置く（後述）
```

> **注**: `upstream_suite/` は元は別リポジトリ（`ramen-soup-ai-suite`）にあったものを、
> 単独で動くよう本パッケージへ同梱しています。開発機ではパスがハードコードされていましたが、
> 同梱の `density_resolver.py` は環境変数 `RAMEN_SUITE_DIR` を見るよう修正済みで、
> 既定で同梱の `upstream_suite/` を使います。

---

## 1. 処理の流れ（何が動くか）

```
動画アップロード ＋ 濃度(任意) ＋ 時間帯
  └ ① ffmpeg でフレーム抽出
  └ ② 上流 画像AI（PyTorch, ここが計算の主役）
        湯気分類(EfficientNet-B0) → スープ抽出(RF-DETR) → 濃度推定
        ゲート①: スープ表面が読めない → 「要確認」で停止（LLMに送らない）
  └ ③ LLM画像評価（BytePlus API を外部呼び出し）
        ゲート②: 4軸が判定不可 → 「要確認」で停止
  └ ④ グレード算出（線形回帰）→ スコア／グレードを表示
```

- 計算負荷の実体は **②のPyTorchモデル**。③はAPIなのでサーバー計算はほぼ無し、④は激軽。
- **GPUが無ければ自動でCPU動作**します（`device: auto`）。1動画あたり概ね数十秒〜2分。

---

## 2. 必要スペック（確認）

| 項目 | 最低 | 推奨 | 現行サーバー(6core/8GB/400GB)の判定 |
|---|---|---|---|
| CPU | 2コア | 4コア | ✅ 6コアで余裕 |
| メモリ | 4GB | 8GB＋swap | ⚠️ 8GBで可。**必ずswapを4〜8GB確保**（下記4.5） |
| ディスク | 30GB | 40GB | ✅ 400GBで十分（800GB不要） |
| GPU | 不要 | 8GB VRAM | ⚠️ 無くても動作（速度のみ制約） |

同時に複数リクエストを捌く運用ならメモリ16GB推奨。1件ずつの逐次処理なら8GB＋swapで問題ありません。

---

## 3. 事前に用意するもの

1. **Ubuntu 22.04 のVPS**（SSHログインできる状態）
2. **BytePlus(ARK) APIキー** … ③のLLM画像評価に必須。外向きHTTPS通信を許可
3. sudo 権限（ffmpeg等の導入に使用）

---

## 4. 設置手順

### 4.1 パッケージをVPSへ転送
ローカル(Windows)から。フォルダ名は英数字に変えると扱いやすいです（例 `ramen-grade`）。

```bash
# 例: scp で転送（ローカルのGit Bash / WSL / Mac から）
scp -r "VPSデプロイパッケージ_20260714" ユーザー名@サーバーIP:/home/ユーザー名/ramen-grade
```
※ 195MB程度あります。回線が細い場合は zip 圧縮してから転送してください。

### 4.2 セットアップスクリプト実行
```bash
ssh ユーザー名@サーバーIP
cd ~/ramen-grade
bash setup.sh
```
これで ffmpeg 導入 → `.venv` 作成 → PyTorch(CPU版) → 残り依存(rfdetr等) が入り、
最後に環境診断（check_env.py）が自動実行されます。
（初回は rfdetr/torch のダウンロードで数分〜十数分かかります）

**環境の状態はいつでも次で診断できます**（不足があると ✗ と対処法が表示されます）:
```bash
python3 check_env.py
```

### 4.3 APIキーを設定（どちらか一方）
```bash
# A) キーファイルを置く
nano secrets/byteplus.key      # キー文字列を1行だけ貼り付けて保存
# B) もしくは環境変数（run.sh / systemd 側で設定）
export ARK_API_KEY=xxxxxxxx
```

### 4.4 動作テスト（軽い順に確認）
```bash
source .venv/bin/activate
export PYTHONIOENCODING=utf-8 PYTHONUTF8=1
export RAMEN_SUITE_DIR="$(pwd)/upstream_suite"

# ④後段のみ（上流・APIを使わずグレード算出だけ確認）
cd app && python infer.py --demo

# end-to-end（動画1本で上流→LLM→グレードまで通す。要APIキー＆動画ファイル）
python pipeline_full.py --video /path/to/123.MOV --brix 12 --slot 開店前
```
`infer.py --demo` が scored/要確認 を表示すれば、Python環境とグレード算出モデルはOK。
`pipeline_full.py` が通れば上流(RF-DETR)とAPIまで含めて全て正常です。

### 4.5 【重要】swap を確保（メモリ8GB対策）
```bash
sudo fallocate -l 8G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab   # 再起動後も有効化
free -h   # Swap行が出ればOK
```

### 4.6 起動
```bash
cd ~/ramen-grade
bash run.sh        # APIサーバーを 127.0.0.1:8000 で起動
```

---

## 5. API仕様（複数人からの動画に順番対応）

`api_server.py` は**受付キュー方式**です。動画を受け取ると即座に `job_id` を返し、
推論は1本のワーカーが1件ずつ順番に処理します（メモリ8GB環境で安全に動かすため同時実行しない）。

### 認証
環境変数 `RAMEN_API_TOKEN` を設定して起動すると、全 `/api/*` に `X-API-Key: <トークン>` ヘッダが必要になります。
未設定なら認証なし（SSHトンネル・nginx Basic認証で守る前提のときのみ）。

### エンドポイント

| メソッド | パス | 説明 |
|---|---|---|
| POST | `/api/jobs?name=<ファイル名>&slot=<時間帯>&brix=<濃度・省略可>` | 動画をリクエストボディ(バイナリ)で送信。**即時に** `job_id` を返す(202) |
| GET | `/api/jobs/<job_id>` | ジョブの状態と結果。`status`: queued → processing → done / error |
| GET | `/api/jobs` | 直近ジョブ一覧（結果本文なし） |
| GET | `/api/health` | 稼働確認（認証不要）。キュー滞留数・モデル読込状態 |
| GET | `/` | ブラウザ用UI（動画アップロード→順番待ち表示→結果表示） |

### 使用例（curl）
```bash
TOKEN=xxxxxxxx   # RAMEN_API_TOKEN と同じ値

# 1) 動画を投げる → job_id が即返る
curl -X POST "http://127.0.0.1:8000/api/jobs?name=123.MOV&slot=開店前&brix=12" \
     -H "X-API-Key: $TOKEN" --data-binary @123.MOV
# => {"job_id":"j0001_...","status":"queued","queue_position":0,...}

# 2) 数秒ごとに結果を確認（done になるまで）
curl -H "X-API-Key: $TOKEN" http://127.0.0.1:8000/api/jobs/j0001_...
# => {"status":"done","result":{"status":"scored","total_score":88.3,"total_grade":"A",...}}
```

### 結果JSONの読み方（`result` フィールド）
- `status: "scored"` … 採点成功。`total_score`/`total_grade`（総合）、`soup_score`/`soup_grade`（スープ）
- `status: "要確認"` … AIが判定不能（湯気過多・スープ面不可視・LLM判定不可）。`reason` に理由。点数は付かない
- 動画は処理後に自動削除。処理履歴は `app/_jobs/history.jsonl` に1行1件で残る

### 制限値（api_server.py 冒頭の定数で変更可）
- 動画1本の上限: 500MB（`MAX_UPLOAD_BYTES`）
- 処理時間目安: GPU数十秒 / CPU数十秒〜2分（1件あたり。初回のみ＋モデル読込1〜2分）

---

## 6. 外部からアクセスする（公開方法）

APIサーバーには `RAMEN_API_TOKEN` による認証がありますが、トークンだけで全世界公開するのは推奨しません。
次のいずれか（トークン認証との併用）にしてください。

### 方式A（推奨）: nginx リバースプロキシ＋Basic認証＋常駐化
1. アプリは `127.0.0.1:8000` のまま（run.sh 既定 / systemd）で常駐化 → 手順6
2. nginx を入れて 80/443 → 127.0.0.1:8000 へ転送し、Basic認証を付ける
```bash
sudo apt-get install -y nginx apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd ラーメン管理者   # パスワード設定
```
`/etc/nginx/sites-available/ramen` を作成:
```nginx
server {
    listen 80;
    server_name _;                 # ドメインがあればここに
    client_max_body_size 500M;     # 動画アップロード上限（api_server側の500MBに合わせる）
    location / {
        auth_basic "Ramen Grade AI";
        auth_basic_user_file /etc/nginx/.htpasswd;
        proxy_pass http://127.0.0.1:8000;
        proxy_read_timeout 300s;   # 上流推論が長いのでタイムアウト延長
        proxy_set_header Host $host;
    }
}
```
```bash
sudo ln -s /etc/nginx/sites-available/ramen /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```
（本番はドメイン＋ `certbot` でHTTPS化を推奨）

### 方式B（社内利用など最小構成）: SSHトンネル
公開せず、使う人が手元から:
```bash
ssh -L 8000:127.0.0.1:8000 ユーザー名@サーバーIP
# ブラウザで http://127.0.0.1:8000
```

### 方式C（非推奨・簡易）: 直接公開
どうしても直接公開する場合のみ。ファイアウォールで接続元IPを絞ること。
```bash
HOST=0.0.0.0 PORT=8000 bash run.sh
sudo ufw allow from 許可するIP to any port 8000
```

---

## 7. 常駐化（サーバー再起動後も自動起動）

```bash
# ramen-grade.service の <DEPLOY_DIR>=/home/ユーザー名/ramen-grade, <USER>=ユーザー名 に置換
nano ramen-grade.service
sudo cp ramen-grade.service /etc/systemd/system/ramen-grade.service
sudo systemctl daemon-reload
sudo systemctl enable --now ramen-grade
sudo systemctl status ramen-grade      # active (running) を確認
journalctl -u ramen-grade -f           # ログ監視
```

---

## 8. GPUインスタンスで動かす場合（任意・高速化）

1. NVIDIAドライバ＋CUDAを導入
2. `requirements.txt` の注記に従い CUDA版 torch を入れる:
   ```bash
   pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121
   ```
3. `config.test10.yaml` の `density.device` は `auto` のままでOK（自動でcuda使用）
4. VRAMは8GBあれば十分（RF-DETR+分類器で数GB）

---

## 9. トラブルシューティング

| 症状 | 原因 / 対処 |
|---|---|
| `rfdetr is not installed` | `pip install rfdetr` が失敗。`setup.sh` を再実行、pycocotoolsのビルドに `build-essential` が要る場合あり: `sudo apt-get install -y build-essential` |
| `ffmpeg failed` / `ffprobe not found` | `sudo apt-get install -y ffmpeg`。`which ffmpeg` で確認 |
| `API key not found` | `secrets/byteplus.key` を作成、または `ARK_API_KEY` を設定 |
| 上流モデルが見つからない | `RAMEN_SUITE_DIR` が `upstream_suite` を指しているか確認（run.sh で自動設定） |
| 推論中にプロセスが落ちる(Killed) | メモリ不足(OOM)。手順4.5のswapを設定。同時処理を避け1件ずつに |
| `libGL.so.1` エラー | `sudo apt-get install -y libgl1 libglib2.0-0`（headless版cv2でも稀に必要） |
| アップロードが途中で切れる | nginxの `client_max_body_size` と `proxy_read_timeout` を上げる |

---

## 10. モデル更新のとき

再学習した場合、開発システムから次を上書きコピーします。
- グレード算出モデル: `app/models/*.pkl` と `metadata.json`
- 上流モデル: `upstream_suite/…/models/` 配下の `.pth` / `.pt`

特徴量・ゲート定義は開発システムと一致させてください（不一致だと精度が崩れます）。

---

## 参考精度（学習時 5-fold OOF / 採点対象346件）
- 総合(total): グレード一致 88.7% / ±15点以内 91.6% / MAE 8.35
- スープ(soup): グレード一致 87.6% / ±15点以内 90.5% / MAE 8.04
