# 중요 설계 결정 사항

> **절대 규칙: LLM에 전달하는 데이터(시나리오 텍스트, 씬 텍스트, description 등)를 코드에서 절대 자르지 마라 ([:400], [:500] 등 금지). 어떤 경우에도 원문 전체를 전달할 것. 이전에 여러 곳에서 [:300]~[:600]으로 자르는 버그가 있었으며 전부 제거함. 앞으로도 절대 추가 금지.**

> **Gemini 3.1 Pro는 긴 문맥 분석에 GPT-5.5보다 우수.** outlook_extraction(119씬) 테스트: GPT-5.5는 44씬 누락(35% 손실), Gemini Pro는 실질 0개 누락. scene_director, outlook_extraction 등 전체 씬을 한번에 분석하는 단계는 반드시 Gemini Pro 사용.

## 1. scene_director — 전체 씬 일괄 분석 (씬별 X)

scene_director는 **반드시 전체 시나리오의 모든 씬을 한번에** LLM에 전달해야 한다.

**이유**: 씬별로 개별 분석하면 앞쪽 씬의 맥락(접속/빙의 상태)을 알 수 없어서
소울라이드/빙의/원격접속 중인 인물을 물리적 존재로 오판함.

**사례**: 씬#3에서 강의원이 동녘 몸에 소울라이드 접속 → 씬#8에서 강의원이 대사를 함 →
씬#8만 보면 강의원이 물리적으로 있는 것처럼 보이지만, 실제로는 동녘의 몸을 빌려 말하는 것.

## 2. 인물 대사/행동 ≠ 물리적 존재

대사를 하거나 행동이 묘사되더라도 물리적 존재를 단정하면 안 된다.
- 빙의/접속 중인 인물이 다른 몸으로 대사하는 경우
- "(인물A(인물B))" 표기 — 괄호 안은 원격 조종자
- 몽타주에서 번갈아 보이는 것은 편집 기법

## 2-1. 회상/꿈 씬은 카메라에 찍히면 포함

scene_director 판단 기준은 "현재 시점에 물리적 존재하는가"가 아니라 **"카메라에 찍혀야 하는가"**이다.

- **회상(플래시백)**: 과거 장면이지만 영상으로 연출되면 → 인물 **포함**
- **꿈/환상**: 시각적으로 연출되는 장면이면 → 인물 **포함**
- **원격접속/빙의**: 접속자의 몸은 다른 곳 → 여전히 **제외**

**사례**: 요괴전 씬 29-30은 회상(플래시백)이지만 백련/명승/길도가 행동·대사하며 씬이 전개됨 → 카메라에 찍혀야 하므로 포함. 기존 v5 프롬프트에서는 "회상=무조건 제외"로 되어있어 3개 씬이 빈 배열로 처리됨 → v6에서 수정.

## 3. visual_world_rules — 자동 추출, 하드코딩 금지

entity_style 단계에서 시나리오 전문을 읽고 시각적 세계관 규칙을 자동 추출.
작품 고유명사나 특정 시나리오에만 해당하는 내용을 프롬프트에 하드코딩하면 안 된다.

## 4. 아웃룩 3단계 분리

1. 얼굴 참조 (1:1) — 인물 정체성
2. 아웃룩 단독 (1:1) — 의상만, 얼굴 없이 (마네킹 스타일)
3. 합성 (16:9) — 얼굴 ref + 아웃룩 ref → 전신

같은 아웃룩을 여러 인물이 공유 가능 (유니폼 등).

## 5. 씬 이미지 참조 — 합성 우선

_resolve_refs_for_prompt에서:
1. composite 합성 이미지 우선 (1장)
2. fallback: 얼굴 + 아웃룩 단독 분리 (2장)
3. _used_ref_ids에 composite_key + char_id + outlook_id 모두 등록 (중복 방지)

## 6. 씬별 요소 필터링

scene_detail에 전체 요소 전달 금지.
scene_director가 PRESENT 판정한 인물 + 씬 텍스트에 등장하는 배경/소품만 전달.

## 7. prompt_used 형식

- 얼굴: 한국어 설명 텍스트
- 아웃룩 단독: `[outfit:{outlook_id}] 설명`
- 합성: `[composite:{char_id}:{outlook_id}] 인물+아웃룩명`
- 레거시: `[outlook_id:{outlook_id}] 설명` (이전 코드 호환)

pipeline_gate에서 두 형식 모두 매칭해야 함.

## 8. scene_director는 반드시 Gemini 3.1 Pro

GPT-5.5는 소울라이드/빙의 상황에서 조종자를 물리적 존재로 오분류.
Gemini Pro는 director_notes 기반으로 정확 판단 (씬3,4에서 김의원 제거 성공).

- step_manifest의 scene_director default_model = `gemini-pro`
- director_notes (visual_world_rules에서 추출한 판단 기준)를 반드시 전달
- visual_world_rules 전체가 아닌 `director_notes` 항목만 전달

## 9. visual_world_rules에 director_notes 포함

visual_world_rules 추출 시 `director_notes` 배열을 함께 추출한다.
- 시나리오 고유의 "물리적 존재 판단 기준"을 LLM이 자동 도출
- 범용 예시가 아닌 작품 설정 기반 판단 원칙
- scene_director에만 전달 (scene_detail 등에는 불필요)

## 10. scene_still sync는 UPSERT (DELETE→INSERT 금지)

sync가 scene_still을 매번 삭제→재생성하면 새 UUID가 발급되어
이미지의 still_id 참조가 깨진다 (still_id = NULL).
still_index 기준으로 기존 ID를 보존하며 UPDATE, 새 씬만 INSERT.

## 11. entities 딕셔너리에 short_id 반드시 포함

image_service.py에서 entities 로드 시 `short_id` 필드 누락하면
entity_lookup → _sid_to_uuid 매핑 실패 → 합성 이미지 매칭 안 됨
→ 인물 참조 이미지 누락 → 엉뚱한 얼굴 생성.

## 12. T2I에서 복합 ID(C08O09) LLM에게 요청 금지

LLM이 복합 ID를 혼동한다 (C08O01을 동녘+검은의전정장으로 잘못 조합).
인물과 아웃룩을 **따로** 받아서 코드에서 조합해야 한다.
scene_detail 프롬프트에 씬별 인물+아웃룩 매핑을 명시적으로 전달.

## 13. 이미지 자동 생성 — 마지막 N개 T2I + 앵글 1개

scene_variation_count = T2I 생성 개수 (기본 2).
프롬프트가 몇 개든 기존 이미지가 몇 개든,
항상 마지막 N개 프롬프트로 T2I 생성 + fal.ai 앵글 1개.

## 14. SRD EP1 분석 점검표

SRD Part 1 에피소드 분석 후 반드시 확인할 항목:

- □ 씬 갯수 — 37 이상 (gemini-flash 세그먼테이션 기준)
- □ 요소 — 인물 13~16, 배경 3~7, 소품 5~10 적정 범위
- □ 아웃룩 — 10~18개, 전 씬 배정, VE에 없는 인물에 배정 안 됨
- □ 프롬프트 — 정확히 N개 (scene_variation_count 기준)
- □ 프롬프트 내 엔티티 — director VE만 사용, VE 위반 0건
- □ 인물 누락 — VE 인물이 프롬프트에 전부 포함
- □ 의원 오분류 — 씬3~8(배틀로얄/헬기/야산)에 강의원/김의원 없는지
- □ still_frame_prompt — VE 위반 없는지
- □ director_notes — 추출됐는지, 소울라이드 규칙 포함
- □ scene_summary — 전체 씬 요약 있는지

## 15. 씬 세그먼테이션 — LLM 파서 정의 + 코드 실행 방식

LLM에 시나리오 텍스트를 직접 분석시켜 씬 헤딩을 찾게 하면 매번 결과가 다름 (31~57씬).
대신 LLM에게 **Pydantic/regex 파서를 정의**하게 하고, 코드에서 실행하면 결정적(deterministic).

**흐름:**
1. LLM(gemini-flash 추천)에게 텍스트 앞부분(~6000자) + "씬 헤딩 regex 만들어라" 요청
2. 받은 regex를 re.compile → 전체 텍스트에 finditer 실행
3. 에러 또는 예상 대비 50% 미만 → 에러 메시지 포함 retry (최대 3회)

**주의:**
- PDF 추출 텍스트는 줄바꿈 없이 붙어있을 수 있음
- re.MULTILINE 사용 금지, 가변 길이 lookbehind 금지
- text_cleanup 제거됨 — PDF 원본 직접 사용

**모델 비교 (미아 시나리오 기준):**
- gpt-mini: 3차까지 실패 (최대 5씬)
- gpt-5.5: 2차(retry)에서 99씬 성공
- gemini-flash: 1차에서 바로 99씬 성공 ← 추천

## 16. 소품 추출 기준

- 3씬 이상 반복 등장
- 캐릭터가 착용/사용/탑승하는 것만
- 부속 소품은 본체에 통합 (아머+헬멧→아머)
- 일반 물건, 배경 시설, UI 화면, 의류, 상황 묘사 제외
- 에피소드당 5~10개 목표

## 17. 씬 세그먼테이션 — 평균 길이 검증 필수

씬 regex가 씬 내부 장소 전환(`- 장소명.`)을 씬 경계로 오인하면,
8~19자짜리 초소형 씬이 대량 생성됨 (문 로스트에서 87개 조각 발생).

**검증 규칙**: 매칭 후 평균 씬 길이가 `scene_split_threshold`(기본 600자)의 50% 미만이면
regex가 잘못된 것으로 판단하고 retry. 에러 메시지에 "씬 번호 패턴으로 분리하라"는 힌트 포함.

**사례**:
- 문 로스트 1부: gemini-flash가 `- ` 패턴으로 87개 분리 (평균 280자) → 실패
- gemini-pro가 `\d+\.` 기반 패턴으로 35개 분리 (평균 698자) → 정확
- `best_matches`는 평균 길이 검증을 통과한 것만 저장 (짧은 잘못된 매칭이 덮어쓰지 않도록)

## 18. 물체(소품) 추출 기준 — "참조 이미지가 필요한가?"

"소품"이 아니라 **"중요 물체"**가 정확한 의도. 추출 판단 핵심:
**"참조 이미지 없이 텍스트만으로 여러 번 생성해도 매번 비슷하게 나와서 구분이 안 되면 → 제외.
매번 다르게 나와서 일관성이 깨지면 → 포함."**

**포함 기준**:
- 2씬 이상 등장 (대사 언급이 아닌 시각적 출현 기준)
- 인물이 직접 들거나, 사용하거나, 탑승하는 것
- 작더라도 클로즈업되거나 인물이 쥐는 장면이 있으면 포함
- 로봇/메카/외골격 등 기계 장치는 의상이 아닌 물체로 포함

**제외 기준**:
- 시각적 출현이 1씬뿐인 것 (대사에서 여러 번 언급돼도)
- 사람이 옷처럼 입는 것 (우주복, 갑옷, 제복 → 아웃룩 영역)
- 배경 장비 (벽면 모니터, TV 등 인물이 주목하지 않는 것)
- 텍스트만으로 생성해도 매번 비슷한 범용 물체 (일반 총, 일반 차량)

**사례**:
- 아르테미스 목걸이: 2씬, 고유 문양, 클로즈업 → 포함 ✓
- 프로스트 캐리어: 2씬, 바꿔치기 트릭 핵심 → 포함 ✓
- ZRBB51 아머 유닛: 로봇 장치, 의상 아님 → 포함 ✓
- 우주복: 옷처럼 입음 → 아웃룩으로 추출 (소품에서 제외) ✓
- 벽면 모니터/TV: 배경 장비 → 제거 ✓

## 19. 요소 추출 모델 배치 — 인물(GPT), 배경/물체(Gemini Pro)

SRD/미아/문로스트 3개 시나리오에서 GPT vs Gemini Pro 비교 테스트 결과:

**인물: GPT 유지**
- GPT가 풀네임 사용 (강주현 vs 주현) — 네이밍 일관성 우수
- Gemini는 단역 추가 발견하지만 네이밍 불안정

**배경: Gemini Pro**
- Gemini가 장소를 세분화 (SRD: 12→16, 미아: 9→16)
- GPT는 통합 (오리엔티스 시설 10씬 → Gemini는 복도/실험실/외곽 분리)

**물체: Gemini Pro**
- GPT는 번호판 같은 잘못된 추출 반복 (4/5회)
- Gemini는 번호판 0/5, 서사 핵심 소품(유리공병, 초음파사진) 발견

**entity_filter/entity_review 제거**
- 추출 프롬프트에서 2씬 기준 적용 + 코드에서 2씬 미만 자동 제거로 충분
- filter가 서사 핵심 소품(캐리어, 유리공병, 다이어리)을 잘못 제거하는 문제 있었음
- entity_extract_v4 코드(라인 47)에서 `scene_appearances >= 2` 하드필터 유지

**물체 T2I 프롬프트 — 장착/부착 물체 크기 참조**
- 사람/다른 물체에 장착하는 물체: 장착 대상을 연필 스케치로 함께 표현
- 독립 물체: 기존대로 단독 배경

## 20. scene_director 결과 검증 스크립트

`backend/scripts/verify_scene_director.py` — scene_director 결과를 Gemini Pro + GPT 5.4 양쪽에 교차 검증.
- 각 씬에 앞 2씬 요약 + 현재 씬 전문 + 전체 엔티티 목록 + 현재 판별 결과 제공
- 두 모델이 독립적으로 누락/오류 지적
- `--scenes 11,29,30` 으로 특정 씬만 검증 가능
- 사용법: `.venv/bin/python scripts/verify_scene_director.py <project_id> <episode_id> [--scenes 11,29,30]`

## 21. scene_detail — 캐릭터별 전체 아웃룩 전달

scene_detail에서 T2I 프롬프트 생성 시, outlook_extraction phase2의 씬별 매핑(1개 고정)이 아니라 **해당 캐릭터의 모든 아웃룩**을 LLM에 전달하고 씬 텍스트에 맞는 것을 선택하게 한다.

- 씬 도중 의상이 바뀌는 경우(예: 남작 검은망토→피의갑옷) variation별로 다른 조합 선택 가능
- phase2 매핑 오류에 영향 받지 않음
- 컨텍스트 부담 최소 (씬 1개 + 아웃룩 전체 ~750자 추가)

**사례**: 요괴전 씬 38 — 기존: `C01O02(피의갑옷)`만 사용 → 변경: `C01O01(검은망토)` + `C01O02(피의갑옷)` 모두 가능, LLM이 전반부/후반부 구분

## 22. scene_director 후 1씬 엔티티 자동 제거

scene_director 완료 후 `present_entity_ids` 기준으로 1씬 이하 출현 엔티티를 자동 제거한다.
- entity_extract의 `scene_appearances`가 아닌 scene_director의 실제 물리적 존재 기준
- DB(EntityCanon, EntityEpisodeLink, CharacterOutlook) 연관 데이터 먼저 정리 후 삭제
- 0씬 출현(entity_extract에는 있지만 scene_director에 안 나온) 엔티티도 제거
