Greenfield setup for luke_scribe (local STT transcription API). No source
code yet; this captures the completed design phase so teammates can ramp
through oh-my-claudecode.
Includes:
- .omc/plans/consensus-luke-scribe-stt-api.md — consensus impl plan v2.2
- .omc/specs/deep-interview-luke-scribe-stt-api.md — deep-interview spec
- .omc/artifacts/ask/{codex,gemini}-*.md — external review (CCG)
- .omc/project-memory.json — omc project memory
- opencode.json, .claude/settings.json — shared tooling config
- .gitignore — excludes ephemeral omc state/session logs and local settings
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
27 KiB
Consensus Implementation Plan: luke_scribe — 로컬 STT 전사 API
- Status:
pending approval(consensus v2.2 — v2.1 합의 + CCG 외부리뷰(Codex/Gemini) 반영; §3.6 능력등급·§3.10 프로비저닝/WS/공유스토어/Colab) - Mode:
--consensus --direct --deliberate - Source spec:
.omc/specs/deep-interview-luke-scribe-stt-api.md(ambiguity ~10%, PASSED) - Project: greenfield
/root/luke_scribe - Generated: 2026-06-02 · Revised: 2026-06-02 (v2)
v2 changelog는 문서 맨 끝 §13 참조. v1 대비 P0 3건(워커 실행모델, VRAM 측정/단계역전, 취소·임시파일·보관경합), P1 4건, P2 3건, 모호 AC 4건을 반영.
1. Requirements Summary
내부용(비공개) 로컬 STT 전사 API. 단일 Job 추상화로 배치(파일/영상) 와 실시간(WebSocket) 입력 처리. faster-whisper(CTranslate2) 런타임, 하이브리드 모델(실시간=turbo, 배치=large-v3) — 단 P1 bench 게이트로 검증. Device Manager 가 GPU/CPU를 감지하고 부팅 시 VRAM 실측으로 정밀도·워커수를 산정(1050~H100), auto|cpu|cuda 강제 가능. Redis(RQ) 영속 큐(배치) + 전용 실시간 핸들러(WS), queue_position·progress 보고. 요청별 출력옵션(txt/ts/word/diarize/SRT/VTT). 후처리(glossary→rules→LLM(local/external, 기본 off·신뢰도 게이팅)→confidence). API Key 인증(+스코프), 결과 7일 보관·파생 오디오 포함 즉시 삭제, 4h/2GB → 413, 큐 만재 → 429. 배포: CLI(dev/Colab)+Docker(prod)+Colab cloudflared. diarization 옵션(pyannote).
2. RALPLAN-DR Summary
Principles
- Hardware-adaptive, fail-explicit — 1050~H100 자동 감지·정밀도/동시성 산정. 적재 불가 시 모델/정밀도 강등 → CPU로 우아하게 내려가되, 강등이 불가능하면 명확한 오류로 거부(조용한 OOM·무한 강등 금지). "never fail"이 아니라 "never fail silently".
- One Job abstraction, two execution lanes — 모든 입력을 Job 수명주기(queued→processing→completed/failed/cancelled)로 통일하되, 배치=RQ 워커 / 실시간=장수명 WS 핸들러로 실행 레인을 분리(WS는 enqueue-once가 아니므로).
- Accuracy/latency 분리(검증 기반) — batch=large-v3, realtime=turbo. 단 하이브리드 채택은 P1 bench의 측정 델타로 게이트(불충분하면 단일 모델로 단순화).
- Privacy-first, enforced — 원본+파생 오디오 즉시 삭제(모든 종료 경로
finally), 결과 7d TTL, 외부 LLM egress는 allowlist+opt-in+감사로그 없이는 금지. - Dev/prod parity — 동일 코어, CLI(dev/Colab)/Docker(prod)는 설정 차이. 큐는 prod=RQ / dev=in-proc 폴백이되 동일 Job 인터페이스 뒤에 두어 의미 동등성 유지.
Decision Drivers (top 3)
- 혼용어(KO+EN) 정확도 — hotwords + 모델 선택 + 후처리.
- 하드웨어 이식성 + 자동 스케일(실측 기반).
- 동시성 + 가시성(queue_position/progress).
Viable Options
D1. 큐/동시성 백엔드
- (A) Redis + RQ
SimpleWorker(no-fork) + 장수명 모델 보유 프로세스 ✅ (채택, 사용자 Redis 확정 + fork 문제 회피) — Pros: 영속·재시작 내성, 모델 1회 적재 후 재사용, CUDA-fork 충돌 회피. Cons: SimpleWorker는 작업 중 하트비트 없음 → progress emit로 보완 필요. - (B) Redis + Celery — Pros: 라우팅/우선순위/재시도 성숙. Cons: GPU 단일박스엔 과함. Invalidation: RQ+SimpleWorker로 충분.
- (C) in-process asyncio + GPU 세마포어 — dev/단발 폴백 + P2 컨틴전시(RQ/CUDA가 막히면 동일 Job 인터페이스로 폴백).
⚠️ 근거: RQ 기본 워커는 작업당
os.fork()하며, 부모에서 초기화된 CUDA 컨텍스트는 fork된 자식에서 재사용 불가(pytorch#40403). 따라서 fork 금지(SimpleWorker/장수명) 가 필수. RQ progress는 내장이 없어job.save_meta()수동 호출 필요(RQ docs).
D2. 실시간 스트리밍 구현
- (A) faster-whisper 위 커스텀 LocalAgreement-2 ✅ (채택) — Pros: 큐/디바이스/후처리 통합 제어. Cons: 안정화 정확성 직접 구현(난이도 과소평가 금지 → §3.7c 계약 명시). 지연 3~5초 관대는 지연만 완화하지 정확성은 아님.
- (B) WhisperLive 백엔드 vendoring — 검증된 WS+VAD를 backend로 감싸기. Invalidation: 통합·하이브리드 제어가 우선이나 청킹/안정화 휴리스틱은 차용.
- (C) WhisperLiveKit(AlignAtt/SimulStreaming) — 2025 SOTA. Invalidation: 3~5초 목표엔 과투자(P5 옵션).
D3. 추론 백엔드 추상화
- (A) faster-whisper 단일 엔진 + compute_type 자동 ✅ — Pros: GPU/CPU/int8/fp16 단일 경로. Cons: 타 엔진 미지원(범위상 불필요).
- (B) 멀티 엔진 플러그인 — Invalidation: 조기 추상화 → 얇은 인터페이스(
engine/base.py)만 두고 구현 1종.
3. Target Project Structure
luke_scribe/
├── pyproject.toml (uv; extras: gpu, diarize, llm) ├── run.sh ├── .env.example
├── docker/{Dockerfile.gpu, Dockerfile.cpu, docker-compose.yml}
└── src/luke_scribe/
├── config.py # pydantic-settings (model rt/batch, device, precision, redis, retention, api_keys+scopes, tunnel, corrector+allowlist)
├── cli.py # typer: serve | transcribe | bench | detect
├── api/{app.py, deps.py, schemas.py, routes/{jobs.py, stream.py, admin.py}}
├── devices/{manager.py, profile.py, vram_probe.py}
├── engine/{base.py, faster_whisper_engine.py, model_registry.py}
├── audio/{ingest.py, vad.py}
├── pipeline/{batch.py, realtime.py}
├── jobqueue/{broker.py, jobs.py, worker.py, inproc.py, cancel.py}
├── postprocess/{pipeline.py, glossary.py, rules.py, llm.py, confidence.py}
├── diarization/pyannote_diarizer.py
├── results/{store.py, formats.py, retention.py}
├── connectivity/tunnel.py
└── observability/{logging.py, metrics.py}
3.5 Worker Execution Model & GPU Concurrency (P0-1 해소)
- 배치 레인: RQ
SimpleWorker(또는 장수명 커스텀 워커). 워커 부팅 시WhisperModel을 1회 적재해 프로세스 수명 동안 보유(재적재·fork 금지). GPU당 워커 프로세스 1개 기본, 워커 내 GPU 접근은 단일 스레드(동시 decode 금지). 동시성 = device-bound 워커 프로세스 수(인터프로세스),--workers오버라이드. - 실시간 레인: WS 세션은 enqueue-once가 아니므로 RQ에 넣지 않고 API 프로세스 내 장수명 turbo 핸들러(asyncio + 단일 GPU 락)가 처리. 세션→Job 매핑은 상태 추적용으로만.
- 하트비트/heartbeat 공백 보완: SimpleWorker는 작업 중 하트비트가 없으므로 §3.7d의 throttled progress emit가 사실상 하트비트 역할(장시간 작업이 "멈춤"으로 오인되지 않게).
- 컨틴전시/Colab: GPU·RQ가 막히거나 Colab(Docker 불가) 이면 D1-C in-proc 큐(Redis 불필요) 로, 동일 Job 인터페이스 유지(§3.10d).
3.6 VRAM Sizing & Capability Tier (P0-2 해소 + CCG ③: 능력 등급 자동판정)
-
부팅 시 실측(
devices/vram_probe.py): GPU VRAM(총/여유)·RAM·디스크 여유 감지 + 대상 모델을 1회 로드해allocated실측(헤드룸 ×1.3). 정적 상수 비의존. -
보수 기본 상수(측정 전 폴백): large-v3 fp16 ≈10GB·int8 ≈3.5GB, turbo fp16 ≈4GB·int8 ≈1.8GB.
-
능력 등급(자동 — 무음 모델강등이 아니라 "제공 가능 모델"을 등급이 결정):
등급 조건(실측) 제공 T0 CPU GPU로 turbo도 무리/GPU 없음 turbo@CPU T1 turbo-GPU turbo는 GPU OK, large-v3 무리 turbo@GPU (large-v3 미제공 → 배치도 turbo) T2 스왑 large-v3 OK, turbo와 동시상주 불가 호출별 load/unload(MRU 상주, 스왑 최소화) T3 동시상주 turbo+large-v3 동시 적재 가능 둘 다 상주 → rt(turbo)+batch(large-v3) 동시 T4모델 다중복제 제외 -
정밀도: cc≥7.0&free≥12GB→
float16; cc≥7.0&free<12GB→int8_float16; Pascal(6.x)→int8; CPU→int8. -
워커수:
workers = max(1, floor((free_VRAM − reserve) / measured_per_worker))(§3.9b의 reserve = 헤드룸 + 실시간 모델 footprint). -
디스크 가드: 다운로드 전 여유 공간 확인, 부족 시 명확 오류.
-
투명성:
/v1/system·/v1/models로 등급·제공모델, 결과에model_used/compute_type_used항상. -
OOM 처리: 강등 시도 ≤2회(fp16→int8→CPU) 후 실패(재큐 1회). 1050: T1(turbo-int8)/T0. T4~H100: T3.
3.7 Job Lifecycle: Cancellation, Temp Files, Retention (P0-3 해소)
- (a) 협조적 취소:
DELETE /v1/jobs/{id}→ Redis에 cancel 플래그. 워커는 faster-whisper 세그먼트 제너레이터를 소비하며 세그먼트 경계마다 플래그 확인(유일한 선점 지점). 현재 세그먼트 연산은 완료 후 중단 → 상태cancelled. (긴급 hard-kill은 워커 프로세스 종료 옵션으로만.) - (b) 임시파일 수명: ffmpeg 파생 wav는 추적 tempdir에 생성, 모든 종료 경로(success/fail/cancel/OOM)의
finally에서 삭제. 업로드 원본도 전사 시작 시점 이후 보유하다 종료 시 삭제. - (c) 실시간 LocalAgreement 계약: 설정 명시 —
redecode_window(예: 마지막 15s 오디오),confirmed_prefix절단 규칙,retained_left_context(예: 5s), VAD 무음 경계에서 확정. 확정 세그먼트 방출 후 버퍼 절단(메모리 평탄). 단위 테스트로 버퍼 절단 불변식 검증. - (d) progress 발행: 워커가 제너레이터를 소비하며
processed_sec/total_sec계산 → throttledjob.save_meta()(N 세그먼트마다 또는 ≥1s).total_sec는 ingest 시 duration probe로 확보.queue_position= 레인 큐 인덱스. - (e) 보관 sweeper 경합:
results/retention.py는 터미널 상태(completed/failed/cancelled) Job만 7d TTL 청소. {queued, processing} 보유 결과·임시물은 건드리지 않음.
3.8 Security Boundary (P1-7 해소)
- API Key + 스코프:
X-API-Key검증 +ApiKey.scopes강제(예:transcribe,admin). 키 회전/폐기 설정. - 외부 egress 통제: LLM
external/openai백엔드는 config allowlist 엔드포인트에만 송신 가능(SSRF 방지), 기본 off + 명시 opt-in + 전송 1건당 감사 로그(key id, endpoint, job id). 옵션으로 전송 전 PII 마스킹.
3.9 Shared-GPU Accounting & Realtime Concurrency (v2.1 — 합의 잔여조건)
- (a) 이벤트 루프 비블로킹: 실시간 turbo decode는 동기 CTranslate2 호출이므로
await loop.run_in_executor(single_thread_executor, decode)로 오프로딩(단일 GPU 락 직렬성 유지). P3 착수 시 CT2의 GIL 해제 여부 검증 — 미해제면 실시간 레인을 별도 디코드 프로세스로 분리. (NEW-1a) - (b) 공유 GPU VRAM 회계:
reserve = base_headroom + (realtime_enabled ? measured_realtime_vram : 0). 실시간 모델이 동일 GPU 상주 시 배치 워커수 공식(§3.6)이 그 footprint를 반드시 포함 → 단일 GPU + 실시간 동시 활성에서 oversubscribe 금지. (NEW-1b) - (c) 실시간 동시 세션: turbo 인스턴스 1개를 전 세션이 직렬 공유(단일 GPU 락). 최대 동시 WS 세션 상한(설정) + 초과 시 거부/대기, 실시간 레인 대기시간 메트릭. AC-8(≤5s)은 "≤N 세션 한도 내" 보장으로 명시. (R3)
- (d) 실효 compute_type 로깅:
vram_probe//v1/system이 요청 vs 실효 compute_type 보고, 불일치(T4int8_float16무음 강등 등) 경고. AC-2/3 계약에 포함. (NEW-2/R2) - (e) RQ job_timeout: enqueue 시
job_timeout ≥ 4h(+마진)(duration probe 기반). RQ 기본 180s로는 장시간 작업이 3분에 강제 종료되어 AC-7 위반 → 반드시 상향. (R1/NEW-3) - (f) Phase Exit 구속력: 각 Phase Exit는 hard gate(미충족 시 다음 Phase 착수 금지). 단 옵션 기능(diarization/LLM/tunnel) 미완은 "문서화된 제한"으로 soft 허용. (R4)
3.10 CCG 리뷰 반영 — 프로비저닝·WS·공유스토어·실행 프로파일 (v2.2)
- (a) 모델 프로비저닝(오프라인 강제 폐기): 시작 시 모델 존재 확인 → 없으면 HF 다운로드(인터넷 OK) → 체크섬/로드 실패 시 캐시 purge 후 1회 재다운로드 → 준비 전 API는
503/status:"loading". ("로컬 실행"=외부 STT API 미사용이지 에어갭 아님 — 스펙 "오프라인" 문구 폐기.) - (b) WS init-frame 인증: 연결 직후 첫 메시지
{type:"init", api_key, audio:{codec,sample_rate,channels}, options}, 2초 내 미수신 시 close. 헤더 못 쓰는 브라우저 대응 + 키가 URL/로그에 안 남음. 코덱/샘플레이트도 이 프레임에서 협상(Gemini WS 지적 흡수). - (c) 공유 스토어(#1, 프로덕션 Docker 한정): api↔worker가 별도 컨테이너 → 입력 원본·파생 wav·결과를 공유 볼륨/오브젝트 스토어, Job 메타는 Redis. per-container 로컬 SQLite 금지. Colab/단일프로세스는 디스크 1개라 무관.
- (d) 실행 프로파일 2종(코드 동일): Colab/개발=순수 Python(
run.sh/python -m ...)·in-proc 큐(Redis 불필요)·로컬 디스크·cloudflared 바이너리. 프로덕션=Docker+Redis+worker+공유 스토어. (Colab은 Docker 불가가 하드 제약.) - (e) 언어/hotwords: 기본
language="ko"(요청별 override). hotwords는 선택(반복 고정용어 1회 등록; 매 전사 예측 아님) — 기본 경로는 ko 앵커+모델+rules 후처리, P1 bench로 hotwords 불필요 여부 실측. - (미채택·추후: webhook, Idempotency-Key, 페이지네이션, 만료
410.)
4. Implementation Steps (by phase, with file refs & exit criteria)
P1 — Core + 측정 게이트
- 스캐폴딩 —
pyproject.toml(extras),config.py,run.sh. - Device Manager + VRAM probe —
devices/{manager.py, vram_probe.py, profile.py}: 감지 + §3.6 실측·정밀도·워커수·Model-Fit 분기. AC-2/3. - Engine + registry —
engine/faster_whisper_engine.py(transcribe: hotwords/initial_prompt/word_ts/vad),model_registry.py(rt=turbo/batch=large-v3, 오버라이드). AC-4. - Audio ingest(스트리밍) —
audio/ingest.py: ffmpeg를 파일로 파이프(전체 배열 인메모리 금지), duration/size probe, 4h/2GB→413.audio/vad.py: Silero VAD. AC-7/9. - CLI
detect/transcribe/bench—bench를 P1로 전진: 도메인 KO+EN 클립으로 turbo vs large-v3 R-WER + entity 보존율 + 속도 + 실측 VRAM 측정 → 하이브리드 게이트 판정. AC-4/12.
- Exit: CPU와 실 GPU 1종에서 단일 파일 전사 성공;
detect가 measured VRAM·정밀도·워커수 출력;bench가 모델 델타 리포트 산출.
P2 — API + Queue
- FastAPI + 인증 —
api/{app.py, deps.py, schemas.py}: API Key+스코프(§3.8). AC-11. - RQ 워커(SimpleWorker) —
jobqueue/{broker.py, jobs.py, worker.py, cancel.py, inproc.py}: §3.5 실행모델, §3.7d progress, §3.7a 취소, §3.6 OOM 강등. AC-5/6. - Jobs 라우트 —
api/routes/jobs.py:POST /v1/jobs(만재→429),GET(queue_position/progress),result?format=,DELETE(취소),GET /v1/jobs. AC-1/6. - Results + retention —
results/{store.py, formats.py, retention.py}: §3.7b/e, 원본·파생 삭제, 7d TTL 터미널만. AC-11. - Docker —
Dockerfile.{gpu,cpu}+ compose(api+redis+worker). CT2/CUDA/cuDNN 트리플 핀(예: CUDA12+cuDNN9; 구형/Colab은 CT2 다운그레이드 경로 문서화).detect가 런타임 CUDA 버전 노출. AC-12. - Admin —
/health,/v1/system(profile/워커/큐깊이/가용 VRAM),/v1/models.
- Exit: 다건 enqueue→progress→result; 강제 OOM 시 강등·실패 경로 시연; 취소가
cancelled로 종료 + 임시파일 삭제 확인.
P3 — Realtime
- 실시간 —
pipeline/realtime.py(§3.7c 계약: redecode_window/prefix/left-context/VAD),api/routes/stream.py(WS, partial/final/status, 백프레셔). AC-8.
- Exit: 부분결과 ≤5s + 최종 안정화; 30분 세션 메모리 평탄(±15% 이내, 단조증가 없음).
P4 — Output + Post-processing
- 출력 옵션 — timestamps/word/SRT/VTT 요청별. AC-9.
- 후처리(glossary/rules/flag) —
postprocess/{pipeline.py, glossary.py, rules.py, confidence.py}. AC-10.
- Exit: glossary on/off diff로 entity 보존 향상 측정; 저신뢰 플래그 부착.
P5 — Advanced
- LLM 보정(옵션) —
postprocess/llm.py: local/external 백엔드, §3.8 egress 통제, confidence-gated, 기본 off. AC-10. - Diarization(옵션) —
diarization/pyannote_diarizer.py(HF 토큰). - Colab 터널 —
connectivity/tunnel.py: API lifespan에 종속 supervise(같이 start/stop), URL 회전 시 재출력, 임시성 명시. AC-13. - 관측/벤치 확장 —
observability/{logging,metrics}.py(큐깊이·워커가동·RTF·OOM 카운트),bench확장.
- Exit: external egress allowlist+감사로그 동작; Colab
serve --tunnel cloudflare외부 200.
5. Acceptance Criteria (수치화)
스펙 AC-1~13 상속 + 모호 항목 절대 기준화:
- AC-2/3:
detect가 measured VRAM 기반 정밀도/워커수 산정 + 능력 등급(T0~T3) 자동판정·/v1/models제공모델·model_used표시(§3.6); 1050→int8/CPU(large-v3 GPU 불가), T4→int8_float16, A100/H100→fp16. - AC-4(혼용어, 절대): 도메인 entity 용어(vLLM·API·FastAPI·Kubernetes·LLM·GPU 등) verbatim 보존율 ≥ 95%(hotwords on, domain set), 그리고 도메인 R-WER ≤ {P1 bench 기준선}. 보조로 batch(v3) ≤ realtime(turbo).
- AC-5/6: 다건 동시 → 각
GET이queue_position(앞 N건)·progress %(processed_sec/total_sec); 만재429. - AC-7(메모리, 절대): 2h 배치 + 30분 WS에서 워밍업 후 peak RSS 변동 ±15% 이내, 단조 증가 없음; OOM 없음; 4h/2GB→
413. - AC-8: 부분결과 ≤5s(동시 ≤N 세션 한도 내), 최종 안정화; 다중 세션 부하에서도 REST/배치 응답성 유지(이벤트 루프 비블로킹, §3.9a).
- AC-11: 전사 후 원본+파생 wav 부재; 결과 7d 후 만료; 터미널 Job만 청소.
6. Risks and Mitigations
| Risk | Mitigation |
|---|---|
| CUDA fork 실패 | SimpleWorker/장수명·model-load-once, fork 금지(§3.5) |
| OOM(동시성) | 측정 VRAM(§3.6) 기반 워커수, semaphore, 강등 최대 2회 후 실패 |
| turbo 혼용어 부족 | P1 bench 게이트(§4-5), 하이브리드/hotwords/후처리, 모델 스왑 |
| 실시간 떨림/누수 | LocalAgreement 계약(§3.7c), 버퍼 절단 불변식 테스트 |
| 취소 no-op / 임시파일 누출 | 협조적 취소 + finally 삭제(§3.7a/b) |
| 보관 경합 | 터미널 상태만 sweeper(§3.7e) |
| 외부 LLM PII 유출 | allowlist+opt-in+감사로그+마스킹(§3.8) |
| CT2/cuDNN 버전 불일치 | Dockerfile 트리플 핀 + 다운그레이드 경로(P2-10) |
| Redis SPOF | in-proc 폴백(D1-C), 헬스체크 |
7. Pre-mortem (deliberate — 3 시나리오, 위험표와 비중복)
- "P2 통합에서 워커가 fork-CUDA로 즉시 죽었다." 원인: RQ 기본 fork. 예방: §3.5 SimpleWorker 강제 + P2 Exit에 "실 GPU 워커 기동" 게이트, P1에서 워커 기동 스파이크 선검증.
- "데모에서 'vLLM'→'브이엘엘엠'으로 신뢰 상실." 원인: 모델/hotwords 미검증. 예방: P1 bench 게이트(entity 보존율 ≥95% 기준), 기본 hotwords 사전 동봉, 미달 시 v3 채택 자동 판정.
- "30분 WS 세션에서 메모리 누수로 컨테이너 OOM-kill." 원인: 링버퍼/가설 미절단. 예방: §3.7c 절단 계약 + 버퍼 불변식 단위테스트 + P3 Exit의 ±15% 메모리 게이트.
8. Expanded Test Plan (deliberate, 수치 게이트 포함)
- Unit: Device 결정(cc/measured-VRAM→compute_type/workers/Model-Fit 분기), OOM 강등 캡(≤2), LocalAgreement 버퍼 절단 불변식, formats(srt/vtt 타임코드), rules 정규화, retention(터미널만), options(413/429).
- Integration: ffmpeg(스트리밍, 인메모리 아님)→engine→result, Redis enqueue→SimpleWorker→progress(save_meta throttle)→status, 취소 플래그→
cancelled+임시파일 삭제, glossary diff(entity 보존율), egress allowlist 차단/허용. - E2E: 파일 Job 수명주기(생성→progress→결과·원본+파생 삭제), 동시 다건 queue_position, WS 세션(부분≤5s), Colab
serve --tunnel cloudflare200, 강제 OOM 강등 경로. - Observability: 메트릭(큐깊이·워커가동률·RTF·OOM 카운트·실시간 레인 대기시간), 구조적 로그(job_id 상관),
/health·/v1/system계약(요청 vs 실효 compute_type 보고 검증), 2h 배치/30분 WS RSS ±15% 평탄 검증, external egress 감사로그 1건/전송, 다중 세션 AC-8(≤5s) + 동시GET /v1/jobs응답성 바운드, >180s 작업이 job_timeout으로 강제종료되지 않음, 공유 GPU(배치+실시간) VRAM 비-oversubscribe Model-Fit 상호작용 테스트.
9. Verification Steps
uv sync && uv run python -m luke_scribe.cli detect→ measured VRAM·정밀도·워커수.... bench --samples samples/ko_en/→ turbo vs v3 R-WER·entity 보존율·VRAM → 하이브리드 판정.... transcribe samples/ko_en.wav→ entity verbatim ≥95% 확인.docker compose up→ 다건POST/GET(progress/위치), 결과 후 원본+파생 부재,DELETE→cancelled.- 강제 작은-VRAM 환경 → 강등(≤2)·실패 경로 시연.
- WS 클라이언트 30분 → 부분≤5s, RSS ±15%.
pytest tests/(unit/integration) + e2e 스모크.- Colab →
serve --tunnel cloudflareURL 외부 200; egress allowlist 차단 테스트.
10. ADR (refined)
- Decision: faster-whisper 단일 엔진 + (게이트된)하이브리드 모델 + Redis(RQ SimpleWorker, no-fork, model-load-once) 배치 + 전용 WS 실시간 핸들러 + 측정 기반 DeviceProfile + CLI/Docker(버전 핀).
- Drivers: 혼용어 정확도, 이식성/자동스케일(실측), 동시성/가시성.
- Alternatives: Celery(과함), in-proc(폴백/컨틴전시), WhisperLive/Kit(참고/P5), 멀티엔진(조기), RQ 기본 fork(CUDA 불가→거부).
- Why chosen: 사용자 확정(하이브리드/Redis/후처리/보관/상한/diarize) 준수 + fork/VRAM/취소 리스크를 명시 메커니즘으로 해소.
- Consequences: SimpleWorker 하트비트 공백→progress로 보완; 실시간 안정화 자체 구현; 1050 배치 large-v3 GPU 불가; Redis 의존; 공유 GPU에서 배치+실시간 VRAM 합산 회계(§3.9b)·실시간 decode 오프로딩(§3.9a) 필요.
- Follow-ups: Redis HA, SimulStreaming(P5), egress 마스킹 고도화. (도메인 bench는 follow-up이 아니라 P1 게이트로 승격.)
11. Open Questions (사용자 확인 권장)
- 실제 배포가 다중 GPU 워커를 필요로 하나, 아니면 단일 T4/Colab 위주? (후자면 워커수 공식 위험 축소.)
- turbo의 KO entity 보존율이 P1 bench에서 ≥95%면 단일 모델로 단순화할 의향이 있는지(VRAM/복잡도 절감).
- 취소는 협조적(세그먼트 경계) 으로 충분한지, 즉시 hard-kill이 필요한지.
12. v2 Changelog (적용 내역 → 리뷰 매핑)
- [P0-1] §3.5 Worker Execution Model 신설 — SimpleWorker/no-fork/model-load-once, 배치·실시간 레인 분리, in-proc 컨틴전시. (Arch P0-1, Critic F1)
- [P0-2] §3.6 VRAM 부팅 실측 + 보수 상수(large-v3 fp16 10GB) + Model-Fit 분기 +
bench를 P1로 전진(단계 역전 수정). (Arch P0-2/P1-4, Critic F2) - [P0-3] §3.7 협조적 취소 + 임시파일
finally삭제 + 보관 sweeper 터미널-한정. (Arch P0-3/P1-6, Critic F3) - [P1] §3.7d progress 메커니즘(save_meta throttle), §3.8 보안 egress allowlist+감사+스코프, 하이브리드 P1 게이트. (Arch P1-4/5/7, Critic F4/F5)
- [P1-신규] §4 단계별 Exit Criteria + in-proc 컨틴전시 명시. (Critic F6)
- [P2] §3.7c LocalAgreement 계약, §4-4 ffmpeg 스트리밍 보장, §4-10/17 cloudflared lifecycle + CT2/CUDA/cuDNN 핀. (Arch P2-8/9/10)
- [AC] AC-4 절대 기준(entity 보존율 ≥95% + R-WER 기준선), AC-7 메모리 ±15% 평탄, 워커당_추정=측정값, OOM 강등 ≤2회,
429/413분리. (Critic 모호성 4건) - [Risk] OOM 위험을 측정 상수 기반으로 재작성(순환성 제거); pre-mortem #1을 위험표와 비중복화하고 예방을 P1/P2 게이트에 배선.
13. v2.1 Changelog (합의 잔여조건 반영)
- [NEW-1a] §3.9a 실시간 decode
run_in_executor오프로딩(+GIL 검증/프로세스 분리 폴백). - [NEW-1b] §3.9b
reserve에 실시간 모델 VRAM 포함 → 공유 GPU oversubscribe 방지. - [R1/NEW-3] §3.9e RQ
job_timeout ≥ 4h. - [NEW-2/R2] §3.9d 요청 vs 실효 compute_type 로깅 + AC-2/3 계약 + 관측 테스트.
- [R3] §3.9c 실시간 최대 동시 세션 상한 + 대기시간 메트릭 + 다중세션 AC-8 테스트.
- [R4] §3.9f Phase Exit 구속력(hard/soft) 명시.
합의 결과 (Consensus Outcome)
- Architect (v2): APPROVE WITH CONDITIONS — P0×3 해소 확인, 잔여 NEW-1(a/b)/2/3.
- Critic (v2): APPROVE WITH CONDITIONS — CRITICAL×3·MAJOR×3 해소 확인, NEW-1 검증 + R1~R4.
- 모든 잔여 조건을 v2.1에 반영 → 합의 도달. iteration 2/5.
14. v2.2 Changelog — CCG 외부 리뷰(Codex/Gemini) 반영
- ②→프로비저닝: "오프라인 강제" 폐기. 다운로드 OK + 손상 재다운로드 +
loading상태(§3.10a). 스펙 제약 문구 수정. - ③→능력 등급: §3.6을 T0(CPU)~T3(동시상주) 자동판정으로 재작성, T4(다중복제) 제외,
model_used항상 표시(무음 모델강등 폐기). - ④→WS init 인증: §3.10b 첫 메시지 인증+코덱 협상. hotwords는 선택으로 강등(§3.10e).
- ⑤→기본
ko: 요청별 override(§3.10e). - #1→공유 스토어: 프로덕션 Docker 한정 공유 볼륨/오브젝트 스토어(§3.10c); Colab 무관.
- Colab: Docker 불가 → 순수 Python·in-proc·바이너리 cloudflared(§3.5, §3.10d).
- 출처:
.omc/artifacts/ask/{codex,gemini}-20260603-095739.md. 미채택(추후): webhook·Idempotency-Key·페이지네이션·410.
Consensus v2.2 — pending approval. 실행(team/ralph/autopilot)은 사용자의 별도 명시 승인이 있어야만 진행됩니다. 승인 전 소스 수정·커밋·실행 스킬 호출 없음.