# LLM入出力データ仕様（渡したデータ／返ってきたデータ）

最終更新: 2026-06-19
対象出力: `local_batch_api/output_full/`（SQLite `ramen_soup_evaluations.sqlite3` ＋ `training_dataset.csv` / `.jsonl`）
LLM: BytePlus ModelArk / モデル `seed-2-0-pro-260328`

このドキュメントは「各動画について、LLMに**何を渡し**、**何が返ってきたか**」を、実データの代表例とともにまとめたものである。

---

## 0. 全体像

```
1動画
 └─ 前処理(可読性レイヤー: density_resolver) … 多フレーム抽出→湯気除外→スープ抽出→最良フレーム選定＋濃度確定
      ├─ ① 不可読 かつ 湯気多(steam_mean≥0.8) → LLMに送らず「判定不可」レコードを生成（課金なし）
      └─ ② それ以外 → LLM送信（画像1枚＋固定プロンプト＋濃度参考値）→ JSON評価が返る
```

- **総件数 520件**（動画＝ユニーク520）
  - **LLM送信（課金あり）: 384件**
  - **送信前スキップ（課金なし・readability_layer判定）: 135件**
  - （※ assessable=true 367 / 判定不可 153 は別軸の集計。送信されても撮影不備で判定不可になる場合があるため、送信数とは一致しない）

---

## 1. LLMに渡したデータ（リクエスト）

送信1件は `user` メッセージ1本で、**content は2ブロック構成**。

| ブロック | 種別 | 内容 |
|---|---|---|
| ① | `input_image` | best frame 画像1枚（data URL = base64埋め込み JPEG） |
| ② | `input_text` | 固定の評価方針プロンプト（約4,400文字）＋ 濃度の参考入力 ＋ 撮影品質ヒント |

リクエストJSONの形（画像は省略表記）:

```json
{
  "model": "seed-2-0-pro-260328",
  "input": [
    {
      "role": "user",
      "content": [
        { "type": "input_image", "image_url": "data:image/jpeg;base64,..." },
        { "type": "input_text",  "text": "あなたはラーメン豚山のスープ状態を…（評価方針 約4,400字）…\n\n【別入力】スープ濃度(計測/人手ラベル): 13\n【撮影品質】スープ表面の視認性が低めです。…" }
      ]
    }
  ]
}
```

### ① 送信画像（best frame）について
- 動画そのものではなく、前処理が選んだ**スープ表面が最も読める1フレーム**。
- 実体は `local_batch_api/prepared_best_frames/<ID>_best.jpg`。
- 選定根拠のメトリクスはDB(`frame_metrics_json`)に保存（**LLMには画像のみ送り、数値は渡さない**）。例（193.MOV）:

```json
{
  "frame_no": 59, "video_fps": 29.98, "selection_score": 2.35,
  "sharpness": 78.46, "motion_blur_score": 0.368, "blowout_ratio": 0.0033,
  "contrast": 0.233, "steam_haze_score": 0.0108, "extraction_strategy": "ffmpeg_middle",
  "density_resolution": {
    "human": "13", "value": "13", "source": "provided", "readable": true,
    "note": "CSV提供値 / 可読性: スープ可視16枚/占有率0.96/抽出信頼度平均0.83",
    "frames_used": 16, "steam_mean": 0.6217
  }
}
```

### ② テキスト（プロンプト）について
- **固定の評価方針**「ラーメン豚山 スープ画像評価プロンプト方針」全文。全文は `local_batch_api/output_full/_prompt_full.txt` に書き出し済み。要点:
  - 出力は**JSONのみ**（Markdown・前置き・補足禁止）。
  - 味・香り・塩分・Brixは画像から推定しない（Brixは別入力扱い）。
  - 静止画なので時間変化（対流・泡や湯気の動き）は判断しない。
  - 撮影不備とスープ品質低評価を分離。不備が強ければ `assessable=false` / `retake_recommended=true`。
  - **固定評価項目**: `photo_quality` / `visual_density` / `water_level` / `oil_emulsification` / `boiling_heat_state`。
  - `visual_density` は液体本体（泡・湯気・脂膜・具材表面を除く）の透明感・白濁度で `10/11/12/13/14/判定不可` を判定。11/12境界ルールを固定明文化。
- **濃度の参考入力**（末尾に注入）:
  - `【別入力】スープ濃度(計測/人手ラベル): 13`
  - 濃度はLLMに当てさせず、**CSV人手ラベル or 濃度AIで確定した値を参考として渡す**（リークではなく設計意図。返答reasonに「計測ラベル値を適用」と反映）。
- **撮影品質ヒント**（可読性が低い場合のみ付記）:
  - `【撮影品質】スープ表面の視認性が低めです。確信が持てない項目は confidence を下げ、必要に応じて retake_recommended=true としてください。`

### 出力JSONスキーマ（プロンプトで指定している返答形式）
```json
{
  "assessable": true,
  "retake_recommended": false,
  "image_condition_score": 0,
  "visual_density":      { "value": "10/11/12/13/14/判定不可", "reason": "...", "confidence": "高/中/低" },
  "water_level":         { "state": "水位良好/水位不足/判定不可", "reason": "...", "confidence": "..." },
  "oil_emulsification":  { "state": "油感が強い/乳化が進んでいる/乳化しすぎ/油感と乳化の状態が良い/判定不可", "reason": "...", "confidence": "..." },
  "boiling_heat_state":  "良い/強すぎる/弱い/判定不能",
  "photo_quality":       "良い/湯気が多い/白飛び/暗い/角度不良/判定不能",
  "deduction_reasons":   ["..."],
  "craftsperson_check_points": ["..."],
  "summary": "短い評価理由"
}
```

### トークン使用量（参考・1件あたり実測例: 807.MOV）
| 区分 | トークン |
|---|---|
| input | 4,902 |
| output | 915（うち reasoning 425） |
| total | 5,817 |

---

## 2. 返ってきたデータ（レスポンス）

API生レスポンス(`response_json`)は ModelArk 形式で、`output` 配列に **reasoning（思考要約）** と **message（本文）** が入る。本文 `output_text` がプロンプト指定のJSON文字列。これをパースしたものが `assessment_json`。

### 例A：LLM送信して評価成立（193.MOV / assessable=true）
パース済み `assessment_json`:
```json
{
  "assessable": true,
  "retake_recommended": false,
  "image_condition_score": 9,
  "visual_density":     { "value": "13", "reason": "液体本体がクリーミーな白ベージュで透明感が非常に弱く…別途入力された計測ラベル値13を適用する。", "confidence": "高" },
  "water_level":        { "state": "水位良好", "reason": "液面が鍋内側高さの6割以上であり、鍋縁までの空きが適切で…", "confidence": "高" },
  "oil_emulsification": { "state": "乳化が進んでいる", "reason": "明確に分離した油膜は確認されず、液体全体が均一に白濁し…", "confidence": "高" },
  "boiling_heat_state": "良い",
  "photo_quality": "良い",
  "deduction_reasons": [],
  "craftsperson_check_points": ["現在の沸騰状態・水位を維持してください"],
  "summary": "撮影品質良好、濃度13、水位良好、乳化状態適切、沸騰状態も良好なスープです。"
}
```

### 例B：送信前スキップ（191.MOV / readability_layer）
LLMには送らず、可読性レイヤーが直接「判定不可」レコードを生成。`request_json` は本レコードでは生成されるが、不可読のため評価項目は判定不可で埋まる。
```json
{
  "assessable": false,
  "retake_recommended": true,
  "photo_quality": "不良",
  "visual_density":     { "value": "判定不可", "confidence": "低" },
  "water_level":        { "state": "判定不可" },
  "oil_emulsification": { "state": "判定不可" },
  "boiling_heat_state": "判定不可",
  "image_condition_score": 0,
  "soup_density_input": "11",
  "summary": "可読性レイヤーによりスープ表面が確認できずLLM評価を見送り（判定不可）。",
  "skip_reason": "不可読かつ湯気多 steam_mean=1.00>=0.8",
  "evaluation_source": "readability_layer"
}
```
> 例Bには `skip_reason` / `evaluation_source=readability_layer` が付く点が、例Aとの見分け方。

---

## 3. DB（evaluations テーブル）に保存される列

| 列 | 内容 |
|---|---|
| `input_video_path` / `video_sha256` | 入力動画と内容ハッシュ |
| `csv_path` / `csv_row_index` | 人手ラベルCSVの出所 |
| `metadata_json` | CSV由来メタ（撮影日時等） |
| `frame_path` / `frame_metrics_json` | 送信フレームと選定メトリクス（§1①） |
| `request_json` | **LLMに渡したデータ**（画像data URL＋プロンプト） |
| `response_json` | **LLMの生レスポンス**（reasoning＋本文＋usage） |
| `assessment_json` | レスポンス本文JSONをパースした最終評価（§2） |
| `error` | 通信エラー等（成功は `none`） |

`training_dataset.csv` / `.jsonl` は、上記から動画単位でデダプした**最終学習データ520行**（古い失敗行は含まない）。

---

## 4. 補足・注意

- **濃度の扱い**: LLMは濃度を「当てる」のではなく、確定値を参考に総合評価する。濃度の正解は CSV提供値(`source=provided`) または 濃度AI推定(`source=model`、high信頼のみ採用)。
- **送信前スキップ135件**: 不可読×湯気多のみ。湯気「量」単独を閾値にせず、判定軸はスープ可読性（RF-DETR抽出信頼度）。
- **再現性**: プロンプト方針を変えた場合はデータ統一のため `output_full` を作り直し全件再送が安全（引き継ぎ資料 §7）。
- 関連: [引き継ぎ資料.md](引き継ぎ資料.md) / プロンプト全文 `local_batch_api/output_full/_prompt_full.txt`
</content>
</invoke>
