# 05. Prompts 및 LLM 감사

> 원본 코드/프롬프트/기존 문서를 수정하지 않고 생성한 read-only 감사 문서입니다.

## Prompt source 구조

```mermaid
flowchart TB
    DBPrompt[(prompt_template DB)] --> Loader[prompt_loader.load_prompt/load_schema]
    FilePrompt[prompts/_base/<module>/<version>] --> Loader
    DirectFile[direct hard-coded file readers] --> ActiveCode[active modules/services]
    Loader --> ActiveCode
    PromptAPI[/api/v1/prompts] --> DBPrompt
    AdminUI[PromptManager UI] --> PromptAPI
```

의도는 DB prompt가 있으면 DB를 우선 사용하고, 없으면 `prompts/_base` 파일로 fallback하는 구조다.

하지만 실제로는 세 가지 source가 함께 존재한다.

| Source | 사용 경로 | 리스크 |
|---|---|---|
| DB `prompt_template` | `prompt_loader` DB-first, prompt admin API | version ordering/coverage drift |
| `prompts/_base` file fallback | 대부분 active step | stem별 fallback으로 version pack mix 가능 |
| direct file read | sanitizer, validator, scene image translation 등 | DB activation/audit 우회 |

## 현재 DB prompt module 현황

현재 DB `prompt_template`에는 114 rows가 있다.

| module | total | active |
|---|---:|---:|
| `entity_extraction` | 10 | 2 |
| `entity_extractor_v2` | 11 | 0 |
| `i2i_editor` | 4 | 4 |
| `image_validation` | 3 | 3 |
| `lvm_prompts` | 15 | 9 |
| `outlook_extractor` | 6 | 2 |
| `outlook_merger` | 2 | 2 |
| `pdf_validation` | 1 | 1 |
| `prompt_sanitizer` | 2 | 2 |
| `prototype_prompts` | 8 | 8 |
| `reference_image` | 3 | 3 |
| `ref_image_prompts` | 4 | 0 |
| `scene_dependency` | 2 | 2 |
| `scene_extractor_v2` | 28 | 5 |
| `scene_generator` | 4 | 4 |
| `scene_image` | 1 | 1 |
| `scene_stills` | 2 | 2 |
| `t2i_composer` | 2 | 2 |
| `t2i_visual_converter` | 3 | 1 |
| `variation_recommender` | 3 | 1 |

중요한 점은 최신 active pipeline의 주요 module 일부가 DB에 없거나 inactive라서 파일 fallback이 실제 경로라는 점이다. 예를 들어 `scene_detail`, `shot_validator`, `background_master_plan`, `background_prompt`, `floor_plan_prompt` 같은 최신 계열은 파일 prompt 의존도가 크다.

## File prompt store 현황

| 항목 | 값 |
|---|---:|
| `prompts/_base` modules | 57 |
| versioned prompt/schema/user files | 236 |

Active로 보이는 주요 prompt pack:

| 계열 | module |
|---|---|
| scene/shot | `scene_summary`, `beat_extract`, `shot_extract`, `shot_validator`, `shot_selection`, `scene_director`, `shot_director`, `scene_camera_flow`, `shot_staging`, `scene_detail`, `scene_consistency`, `shot_dependency_t2i` |
| entity | `entity_character_list`, `entity_all`, `entity_extract_v4`, `entity_relation`, `entity_filter`, `entity_extractor_v2`, `entity_detail`, `entity_t2i` |
| outlook | `outlook_extractor` phase prompts |
| background | `background_classify`, `background_master_plan`, `floor_plan_prompt`, `background_prompt` |
| image/reference | `ref_image_prompts`, `lvm_prompts`, `prompt_sanitizer`, `scene_image`, `character_state_variant` |
| guide | `prototype_prompts` world guide |

Retired/on-demand/stale 후보:

| module | 판단 |
|---|---|
| `scene_cinematography` | deprecated/disabled step lineage |
| `shot_cinematography` | disabled |
| `scene_dependency` | old dependency |
| `outlook_extraction` | old outlook path |
| `scene_verify` | disabled/deprecated |
| `background_planner` | Phase 5 replaced by Phase 7 |
| `background_chain_planning` | chain experiment lineage |
| `background_chain_render` | chain experiment lineage |
| `location_floor_plan` | old floor-plan mode |
| `text_cleanup` | active text cleanup uses inline prompt |
| `shot_dependency` | code-based scoring path, LLM refinement is `shot_dependency_t2i` |
| `entity_review_v4` | class exists but current registry path 아님 |

## Prompt pack atomicity 문제

현재 file fallback은 "module 전체 version pack"을 고정하지 않고, stem별로 최신 파일을 찾는다.

```mermaid
flowchart LR
    LoadSystem[load system] --> V11[v11 system.md]
    LoadUser[load user] --> V11User[v11 user.md]
    LoadSchema[load schema] --> V10Schema[v10 schema.json]
    V11 --> Call[one LLM call]
    V11User --> Call
    V10Schema --> Call
```

확인된 예:

| module | mix |
|---|---|
| `shot_extract` | v11 `system/user` + v10 schema |
| `shot_director` | v2 prompt + v1 schema |
| `entity_extractor_v2` | v5 detail prompt + v4 system/schema |

이것이 항상 버그라는 뜻은 아니다. 그러나 의도된 호환인지 강제되지 않으면 prompt/schema contract drift가 발생한다.

## DB prompt version ordering 문제

DB prompt query는 문자열 기준 `ORDER BY version DESC` 또는 `MAX(version)` 패턴을 사용한다.

문제:

| version 예 | lexical 결과 |
|---|---|
| `9.2026...` vs `10.2026...` | 문자열 정렬에서 `9...`가 더 클 수 있음 |
| `2.*` vs `11.*` | numeric latest와 lexical latest 불일치 가능 |

파일 fallback은 `_version_sort_key`로 numeric parsing을 시도하지만, DB query는 그렇지 않다.

## Direct file reader 문제

일부 active code는 `prompt_loader`를 통하지 않는다.

| 위치 | 방식 | 리스크 |
|---|---|---|
| `prompt_service.py` scene image translation | newest dir 직접 탐색 후 `translate_prompt.md` read | DB prompt activation 우회, partial newest dir이면 crash |
| `prompt_sanitizer.py` | hard-coded version path | DB audit/activation 우회 |
| `image_validator.py` | hard-coded version path | prompt update UI와 분리 |
| `pdf_validator.py` | hard-coded version path | prompt update UI와 분리 |
| legacy generators | `prototype_prompts/v5` hard-code | registry와 drift |

## Model routing source 중복

```mermaid
flowchart TB
    Manifest[STEP_MANIFEST default_model/provider] --> UI[step metadata / display]
    PipelineSteps[llm_client.PIPELINE_STEPS] --> Runtime[_resolve_model runtime]
    ProjectConfig[project LLM config] --> Runtime
    Settings[core/config.py settings] --> Runtime
    HardCoded[image steps hard-coded gpt-image-2] --> ImageRuntime[image runtime]
```

문제는 runtime text LLM routing이 주로 `llm_client.PIPELINE_STEPS`를 보고, manifest의 `default_model/provider`와 항상 같지 않다는 점이다.

확인된 drift:

| 위치 | drift |
|---|---|
| README | env sample `OPENAI_MODEL=gpt-5.4` |
| `core/config.py` | default `openai_model = gpt-5.5` |
| `STEP_MANIFEST` | 일부 raw model명 또는 alias |
| `llm_client.PIPELINE_STEPS` | 실제 `_resolve_model()` 기본 map |
| image render steps | manifest는 `gpt`, 실제 호출은 `gpt-image-2` |
| prompt translation trace | 실행 model과 trace model label 불일치 가능 |

## Structured output 검증 문제

현재 LLM client는 provider strict JSON schema를 사용하지만, `json.loads()` 이후 local schema validation이 충분하지 않다.

```mermaid
flowchart LR
    Schema[JSON schema] --> Provider[provider strict mode]
    Provider --> Text[model response]
    Text --> JSON[json.loads]
    JSON --> Downstream[downstream code]
    JSON -. missing local validation .-> Risk[shape drift risk]
```

리스크:

| 리스크 | 설명 |
|---|---|
| provider별 strict 해석 차이 | OpenAI/Gemini/LiteLLM path가 완전히 같지 않을 수 있음 |
| schema 누락 | legacy schema 중 root `additionalProperties` 누락 사례 존재 |
| fallback path | fallback response가 schema를 느슨하게 통과할 수 있음 |
| downstream late failure | prompt contract failure가 나중의 Python key error/list mismatch로 나타남 |

## Image provider 구분

| 이미지 종류 | 주 provider/config |
|---|---|
| reference / scene image | `settings.gemini_image_model` 계열 |
| floor plan / background render | 실제 코드에서 `gpt-image-2` 호출 |
| chain experiment scripts | 별도 OpenAI image chain logic |
| validation/LVM | `lvm_prompts` + LLM/image validation modules |

문제는 UI/project LLM override가 image provider까지 동일하게 제어하는 것처럼 보일 수 있지만, 실제 image step은 별도 hard-coded/model config를 따른다는 점이다.

## 권장 개선 방향

| 우선순위 | 개선 |
|---|---|
| P1 | prompt pack atomic loading 도입: module/version을 먼저 고정하고 stem은 같은 version에서만 로드 |
| P1 | DB prompt version 정렬을 numeric-aware로 변경 |
| P1 | prompt loader provenance를 checkpoint/LLM log에 기록 |
| P1 | direct file reader를 `prompt_loader` 계약으로 흡수하거나 예외 목록 문서화 |
| P1 | manifest default_model과 runtime `_resolve_model` source 통합 |
| P1 | image provider config와 text LLM config를 UI/API에서 분리 표시 |
| P2 | local JSON schema validation을 provider response 후 항상 수행 |
| P2 | retired prompt pack을 archive 또는 inactive index로 분리 |
