# TheRoad Scene Lab — 파이프라인 단계별 상세

> 각 단계의 모델, 입출력, 병렬 처리, 재시도 전략을 코드 기반으로 정리

---

## 전체 흐름

```mermaid
flowchart TD
    subgraph "Phase 1: 요소 추출"
        T0["Turn 0: 스타일 분석<br/>Gemini Pro"]
        T1["Turn 1: 요소 목록<br/>Gemini Pro"]
        T15["Turn 1.5: 리뷰+필터링<br/>GPT-5.5"]
        T17["Turn 1.7: 상세 정보<br/>GPT-5.5"]
        T2["Turn 2+: T2I 프롬프트<br/>Gemini Pro × N 병렬"]
        T0 --> T1 --> T15 --> T17 --> T2
    end

    subgraph "Phase 2: 씬 분석"
        S1["정규식 세그먼테이션"]
        S2["긴 씬 분할<br/>Gemini Lite"]
        S3["씬 연관 추출<br/>Gemini Pro"]
        S4["아웃룩 추출<br/>Gemini Pro"]
        S5["씬 상세 분석<br/>Gemini Pro × N 병렬"]
        S1 --> S2 --> S3 --> S4 --> S5
    end

    subgraph "Phase 3A: 참조 이미지"
        R1["T2I 생성<br/>Gemini Image × 15 병렬"]
        R2["GPT LVM 검증<br/>GPT-5.5"]
        R3["재생성 + 비교<br/>Gemini Image + GPT-5.5"]
        R4["인물+아웃룩 복합<br/>Gemini Image × 15 병렬"]
        R1 --> R2 --> R3 --> R4
    end

    subgraph "Phase 3B: 씬 이미지"
        I1["프롬프트 번역<br/>Gemini Pro"]
        I2["T2I 생성 × 2변형<br/>Gemini Image"]
        I3["GPT 앵글 추천 + 선택<br/>GPT-5.5"]
        I4["fal.ai 앵글 적용<br/>qwen-image-edit"]
        I5["GPT 최종 선택 (N+1)<br/>GPT-5.5"]
        I1 --> I2 --> I3 --> I4 --> I5
    end

    T2 --> S1
    S5 --> R1
    R4 --> I1
```

---

## Phase 1: 요소 추출

### Turn 0 — 세계관 + 시각 스타일
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` |
| 함수 | `entity_extractor_v2.extract_entities_multiturn()` |
| 입력 | 시나리오 전문 + 이전 에피소드 요소 (이름+설명+t2i_prompt) |
| 출력 | style_result: era, region, genre, episode_summary |
| 스키마 | `turn0_style_schema.json` |
| 병렬 | 순차 |

### Turn 1 — 요소 목록 추출
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` |
| 입력 | 시나리오 전문 + prior_block |
| 출력 | characters/locations/props 이름+등장횟수 |
| 스키마 | `TURN1_SCHEMA` |

### Turn 1.5 — GPT 리뷰 + 필터링
| 항목 | 값 |
|------|-----|
| 모델 | **`gpt-5.5`** (OpenAI Responses API) |
| 함수 | `_gpt_review_entity_list()` |
| 입력 | Turn 1 결과 (이름+등장횟수) |
| 출력 | importance_map + 필터링된 리스트 |
| 필터 | importance="none" AND appearances<2 → 제거 |
| LLMCallLog | ✅ 기록 |

### Turn 1.7 — 요소 상세 일괄 추출
| 항목 | 값 |
|------|-----|
| 모델 | **`gpt-5.5`** (OpenAIClient) |
| 입력 | 시나리오 전문 + 확정 요소 목록 |
| 출력 | 요소별 description + visual_traits |
| 재시도 | 누락 요소 1회 재시도 |

### Turn 2+ — 요소별 T2I 프롬프트 생성
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` (독립 클라이언트) |
| 함수 | `_extract_single_entity()` |
| 입력 | 요소 이름/타입 + GPT 상세 + 스타일 |
| 출력 | t2i_prompt per entity |
| 병렬 | **10 workers**, 2초 스태거 |
| 재시도 | 3회, 2×N초 대기 |

---

## Phase 2: 씬 분석

### Step 1 — 정규식 세그먼테이션
| 항목 | 값 |
|------|-----|
| 모델 | 없음 (정규식) |
| 함수 | `_segment_by_heading()` |
| 입력 | 시나리오 전문 |
| 출력 | ~37개 기본 씬 세그먼트 |
| 패턴 | `^\s*(INT\|EXT\|INT/EXT\|EXT/INT)\.` |

### Step 2 — 긴 씬 분할
| 항목 | 값 |
|------|-----|
| 모델 | **`gemini-3.1-flash-lite-preview`** |
| 함수 | `_split_long_scenes()` |
| 입력 | 600자 이상 씬 |
| 출력 | 논리적 2분할 |
| 재시도 | 3회 |

### Step 3 — 씬 시각적 연관 추출
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` |
| 함수 | `extract_scene_dependencies()` |
| 입력 | segments (헤딩 + 텍스트 600자) |
| 출력 | {scene_index: {prev_ref, next_ref, reason}} |
| 기타 | 같은 장소의 가장 가까운 이전/이후 씬 매핑 |

### Step 4 — 아웃룩 추출
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` (settings.gemini_text_model) |
| 함수 | `extract_all_outlooks()` |
| 입력 | segments + 캐릭터 목록 + 시나리오 전문 |
| 출력 | outlooks[] + scene_assignments[] |
| 재시도 | 3회 |

### Step 5 — 씬 상세 분석 (병렬)
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` (독립 클라이언트) |
| 함수 | `_process_scene()` |
| 입력 | 씬 텍스트 + 요소 목록 + 아웃룩 매핑 + 연관 씬(헤딩만) |
| 출력 | beat_title, scene_type, t2i_variations×2, visible_entities |
| 병렬 | **10 workers**, 2초 스태거 |
| 재시도 | 3회, 5×(N+1)초 대기 |
| 기타 | 체크포인트 지원 |

---

## Phase 3A: 참조 이미지 생성

### 엔티티 참조 이미지
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-flash-image-preview` (T2I) |
| 검증 | **`gpt-5.5`** (LVM Vision) |
| 함수 | `generate_and_validate_reference()` |
| 병렬 | 배치별 **15 workers** |
| 기타 | 의존성 위상 정렬, severity=severe 시 재생성+비교 |

### Character+Outlook 복합 참조
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-flash-image-preview` |
| 함수 | `_gen_composite()` |
| 입력 | 캐릭터 얼굴 참조 + 아웃룩 설명 |
| 프롬프트 | `ref_image_prompts/character_outlook_ref.md` |
| 병렬 | **15 workers** |

---

## Phase 3B: 씬 이미지 생성

### B1 — 프롬프트 번역
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-pro-preview` (GeminiTextClient 기본) |
| 함수 | `_build_final_scene_prompt()` |
| 입력 | 한국어 T2I (마커 포함) + 참조 이미지 라벨 |
| 출력 | 영어 T2I (마커→번호 치환, 참조 지시 포함) |
| 기타 | `translate_prompt.md` 템플릿 |

### B2 — T2I 생성 (2변형)
| 항목 | 값 |
|------|-----|
| 모델 | `gemini-3.1-flash-image-preview` |
| 함수 | `generate_and_validate_scene()` |
| 입력 | 영어 T2I + 참조 이미지 + 이전 연관 씬 이미지 |
| 출력 | PNG 이미지 |
| 병렬 | 씬 내부 **3 workers** (변형별) |
| 기타 | ModerationError 시 PromptSanitizer 4회 재시도 |

### B3 — GPT 앵글 추천 + 이미지 선택
| 항목 | 값 |
|------|-----|
| 모델 | **`gpt-5.5`** (Vision, 다중 이미지) |
| 함수 | `_select_and_recommend_angle()` |
| 입력 | N개 이미지 + beat_title + T2I |
| 출력 | {best_for_angle, horizontal_angle (0-360), vertical_angle (-30~90), zoom (0-10)} |
| 제약 | horizontal≥20 OR abs(vertical)≥20 (최소 20°) |
| 재시도 | 429 시 3회 (3/6/9초) |

### B4 — fal.ai 앵글 적용
| 항목 | 값 |
|------|-----|
| 모델 | **`fal-ai/qwen-image-edit-2511-multiple-angles`** |
| 함수 | `_apply_fal_angle()` |
| 입력 | 선택 이미지 + horizontal/vertical/zoom |
| 출력 | 앵글 편집된 PNG |
| 타임아웃 | 120초 |
| 조건 | `settings.fal_key` 설정 시에만 |

### B5 — GPT 최종 선택 (N+1개)
| 항목 | 값 |
|------|-----|
| 모델 | **`gpt-5.5`** (Vision, 다중 이미지) |
| 함수 | `_select_final_best()` |
| 입력 | N개 T2I + 1개 fal.ai = N+1개 이미지 |
| 출력 | 선택 인덱스 → `is_primary=1` |
| Fallback | 실패 시 첫 번째 이미지 |

---

## 모델 요약

| 모델 | 용도 | 호출 위치 |
|------|------|----------|
| `gemini-3.1-pro-preview` | 텍스트 분석, 프롬프트 번역 | Turn 0/1/2+, 씬 분석, 아웃룩, 연관씬 |
| `gemini-3.1-flash-lite-preview` | 긴 씬 분할 | Step 2 only |
| `gemini-3.1-flash-image-preview` | T2I/I2I 이미지 생성 | 참조/씬 이미지 |
| `gpt-5.5` | Vision 판단 + 텍스트 리뷰 | 리뷰, 검증, 선택, 앵글추천 |
| `fal-ai/qwen-image-edit-2511` | 앵글 편집 | B4 (조건부) |

## 병렬 처리

| 단계 | 최대 workers | 스태거 |
|------|-------------|--------|
| 요소 T2I | 10 | 2초 |
| 씬 상세 | 10 | 2초 |
| 참조 이미지 | 15 | - |
| 씬 이미지 | 15 | - |
| 씬 내 변형 | 3 | - |

## 씬당 최종 이미지 수

| 구성 | 수량 |
|------|------|
| T2I 변형 | 2개 (설정 가능: `scene_variation_count`) |
| fal.ai 앵글 | 1개 (fal_key 설정 시) |
| **총** | **3개/씬** |
