Skip to main content

LLM Runtime and Model Portability

1. 결정 요약

Cinema의 LLM 기능은 SurfAI 서버가 소유한 API 프로젝트와 API 키로 실행한다.

MVP는 OpenAI Responses API를 최초 Provider로 사용하되 Cinema 도메인 코드가 특정 회사나 모델명을 직접 참조하지 않도록 한다. Sequence·Scene·Beat·Shot 분석은 논리적 Model Profile을 선택하고, Provider Adapter가 실제 API 요청으로 변환한다.

2. 과금과 인증 경계

  1. ChatGPT Pro를 포함한 ChatGPT 구독 계정의 사용량은 API 호출에 사용할 수 없다. ChatGPT 제품 구독과 OpenAI API의 인증·과금은 별도다.
  2. OpenAI API 키는 Next.js 클라이언트에 전달하지 않고 NestJS 백엔드 또는 비동기 Worker에서만 사용한다.
  3. 개발·스테이징·운영 환경은 서로 다른 API Project와 키, 사용 한도, 비용 경고를 사용한다.
  4. MVP는 SurfAI 소유 API 키를 사용한다. 사용자 API 키를 받는 BYOK는 암호화 저장, 폐기, 권한, 장애 처리 계약이 마련된 이후 별도 기능으로 검토한다.

3. 도메인과 Provider의 분리

Cinema 도메인은 범용 대화 API가 아니라 필요한 작업 단위의 인터페이스를 가진다.

StructureAnalysisOrchestrator.runGuided(input)
StructureAnalysisOrchestrator.runIntegrated(input)
SequenceProposalGenerator.generate(input)
SceneProposalGenerator.generate(input)
BeatProposalGenerator.generate(input)
ShotProposalGenerator.generate(input)
ShotDesignEditor.suggest(input)
ShotPromptPackageGenerator.generate(input)

각 구현은 공통 LlmGateway에 다음 항목을 전달한다.

profileKey
instructions
input
outputSchema
requestMetadata

LlmGateway는 Model Profile을 해석하고 해당 Provider Adapter를 선택한다. Adapter는 인증, 요청 포맷, reasoning 옵션, 응답 사용량, 오류 코드를 공통 결과로 정규화한다.

Provider별 고유 기능을 억지로 하나의 거대한 공통 인터페이스에 맞추지 않는다. Cinema 도메인에서 실제로 필요한 구조화 생성 계약은 공통화하고, 이미지·파일·도구 호출 등 기능 차이는 Adapter capability로 명시한다.

4. Model Profile

도메인 코드는 실제 모델명 대신 안정적인 논리 키를 사용한다.

cinema.sequence.default:
provider: openai
model: gpt-5.6-luna
reasoningEffort: medium
promptVersion: sequence-v1
schemaVersion: sequence-proposal-v1

현재 활성 흐름은 작업별 cinema.character.default, cinema.sequence.default, cinema.beat.default, cinema.shot.default, cinema.shot-editor.default, cinema.shot-prompt-package.default Profile을 사용하며 사용자가 모델을 선택하지 않는다. Scene은 현재 Sequence Profile을 공유한다. 기본 모델과 작업별 Prompt, Structured Output schema, reasoning 설정은 서버가 관리한다. Context Director Profile은 선택 기능을 실제 운영할 때만 활성 경로에 연결한다.

초기 권장 정책은 다음과 같다.

용도초기 Profile 정책
Sequence·Scene 기본 분석비용 효율형 단일 모델
Character 추출Revision 원문과 기존 Project Character key를 입력으로 사용하는 별도 Structured Output Profile
Beat·Shot 설계작업별 Profile을 유지하되 기본 모델은 Sequence 설정을 상속
Shot AI 편집현재 Shot Proposal과 사용자 명령으로 검토 가능한 전체 수정안 생성
Frame·Motion Prompt승인 Shot에서 독립 Frame Prompt와 Motion Prompt를 함께 생성
Context Director선택 기능을 켠 경우에만 사용하는 Shot별 Structured Output Profile
모델 변경평가 후 CINEMA_SEQUENCE_MODEL, CINEMA_BEAT_MODEL, CINEMA_SHOT_MODEL 교체

실제 모델명은 코드가 아니라 설정 또는 Profile Registry에서 바꾼다. 다만 모델 변경은 문자열 치환으로 끝내지 않고 프롬프트 버전, 출력 스키마, 평가 결과를 함께 검토한다. 운영 일관성이 중요한 Profile은 지원되는 모델 snapshot을 고정하고, alias 업그레이드는 평가 세트를 통과한 뒤 반영한다.

MVP에서는 배포 설정으로 Profile을 관리한다. 런타임 관리자 UI와 DB 기반 Profile 관리는 여러 Provider를 실제 운영하는 시점까지 도입하지 않는다.

5. Structured Output

Sequence, Scene, Beat, Shot 분석 결과를 자유 형식 텍스트로 받은 뒤 문자열 파싱하지 않는다. Provider가 지원하는 JSON Schema 기반 Structured Output을 사용하고, 서버에서 동일 스키마로 다시 검증한다.

LLM response
-> provider-level strict JSON Schema
-> server schema validation
-> domain invariant validation
-> ProposalSet persistence

검증 실패 응답은 정식 Sequence나 Scene을 만들지 않는다. 제한된 횟수의 schema repair 또는 재시도 후 ProposalSet.status = FAILED와 표준화된 오류 원인을 저장한다.

Character와 Shot 사이에는 Asset 선택 권한을 LLM에 주지 않는다. Character 분석은 기존 Registry를 C1, C2와 같은 요청 범위 key로 받고 matchCharacterKey만 반환한다. Shot 분석도 승인된 Character key와 화면 내 PRIMARY | SECONDARY | BACKGROUND 역할만 반환한다. 서버는 key 존재 여부와 중복을 검증해 실제 ID Mapping으로 저장하며, Persona AssetVersion은 CharacterAssetMapping을 통해 별도로 해석한다.

Provider와 무관한 Proposal Schema가 시스템의 기준이다. Adapter는 Provider 응답을 이 스키마로 변환하며, Provider 원본 응답은 디버깅·보안·보존 정책에 따라 별도 제한 저장한다.

6. 비동기 실행 계약

API 요청 안에서 긴 LLM 작업을 완료할 때까지 기다리지 않는다. ScriptRevision과 ProposalSet을 먼저 저장하고 Worker가 실행 결과를 기록한다. 이 구조는 재시도, 비용 추적, 중복 요청 방지, 진행 상태 표시의 기준이 된다.

7. 실행 메타데이터

각 ProposalSet 또는 LLM Run에는 최소한 다음 정보를 남긴다.

provider
model
modelSnapshot?
profileKey
promptVersion
schemaVersion
reasoningEffort?
inputTokens
outputTokens
cachedInputTokens?
latencyMs
status
errorCode?
createdAt
completedAt?

민감한 시나리오 원문을 운영 로그에 그대로 출력하지 않는다. 입력 원문은 ScriptRevision으로 추적하고 실행 레코드에는 Revision ID와 입력 해시를 저장한다.

8. 모델 변경 검증

모델 또는 주요 Prompt를 변경하기 전에 대표 시나리오 평가 세트를 실행한다. 최소 평가 항목은 다음과 같다.

  • Sequence·Scene 경계의 정확성
  • 원문 누락과 중복 여부
  • 제목과 Narrative Purpose의 유효성
  • 동일 입력 반복 시 구조 안정성
  • Schema 준수율과 재시도율
  • 지연 시간과 분석당 비용

모델 교체는 Profile별 평가 결과와 함께 배포하며, 기존 ProposalSet은 당시 실행 메타데이터를 유지한다.

9. 현재 구현

Sequence, Scene, Beat, Shot 분석과 Shot AI 편집·Prompt Package 생성에는 다음 구성요소가 구현되어 있다.

구성요소현재 구현
Model Profile Registry분석 Profile과 cinema.shot-editor.default, cinema.shot-prompt-package.default를 환경 설정의 실제 모델명으로 해석. 사용자 선택 UI 없음
LLM GatewayProfile의 provider에 맞는 Adapter 선택
OpenAI AdapterResponses API와 Zod Structured Output 사용, 사용자 ID는 SHA-256 safety_identifier로 전달
실행 추적provider, model, snapshot, profile, prompt/schema version, token, cache token, latency, response ID, 오류 저장
결과 검증Sequence·Scene·Beat는 startMarker를 부모 원문에서 해석해 전체 범위와 offset을 검증. Shot은 beatNumber를 현재 활성 Beat ID로 해석하고 전 Beat 커버리지·동일 Scene·시간 범위를 검증
오류 격리API 키 미설정 또는 Provider 오류 시 서버 기동은 유지하고 해당 ProposalSet만 FAILED 처리

환경 설정은 다음과 같다.

OPENAI_API_KEY
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_TIMEOUT_MS=300000
CINEMA_SEQUENCE_MODEL=gpt-5.6-luna
CINEMA_SEQUENCE_REASONING_EFFORT=medium
CINEMA_SEQUENCE_ANALYSIS_MAX_CHARS=500000
CINEMA_SCENE_ANALYSIS_MAX_CHARS=200000
CINEMA_BEAT_MODEL=gpt-5.6-luna
CINEMA_BEAT_REASONING_EFFORT=medium
CINEMA_BEAT_ANALYSIS_MAX_CHARS=100000
CINEMA_SHOT_MODEL=gpt-5.6-luna
CINEMA_SHOT_REASONING_EFFORT=medium

OPENAI_API_KEY는 서버 기동에 필수가 아니다. 키가 없는 환경에서도 Project와 Script 편집은 동작하며 실제 Sequence 분석 ProposalSet은 OPENAI_NOT_CONFIGURED 오류로 종료한다.

Context Director는 현재 활성 제작 경로에 연결되지 않으며 Prompt 생성의 선행 조건도 아니다. Shot Prompt Package는 승인 Shot에서 파생되는 편집 가능한 데이터이고, Shot 자체가 제작 사양의 원천 데이터다. LLM 요청 메타데이터는 공통 Cinema LLM Request 이력에 기록한다.

10. 비동기 실행의 현재 한계

현재 구현은 API 트랜잭션에서 ScriptRevision과 ProposalSet을 먼저 저장하고 응답한 뒤 같은 NestJS 프로세스에서 분석 함수를 실행한다. 따라서 HTTP 요청과 긴 Provider 호출은 분리되지만 영속 Job Queue는 아니다.

  • 같은 프로젝트에는 동시에 하나의 Sequence 분석, 같은 Sequence에는 동시에 하나의 Scene 분석, 같은 Scene에는 유형별로 Beat 분석 하나와 Shot 분석 하나만 PROCESSING일 수 있다.
  • 실행 중 프로세스가 종료되면 자동 재개하지 않는다.
  • 서버 기동 시 15분 이상 남은 PROCESSING ProposalSet을 ANALYSIS_INTERRUPTED로 실패 처리한다.
  • 운영 재시도, 다중 Worker lease, dead-letter 처리는 후속 영속 Worker 단계에서 추가한다.