fbe13dddcc
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>
402 lines
31 KiB
Markdown
402 lines
31 KiB
Markdown
# Deep Interview Spec: luke_scribe — 로컬 STT 전사 API 시스템
|
|
|
|
> 내부용(비공개) 음성/영상 → 텍스트 전사 API. 로컬 모델 실행, GPU/CPU 자동·수동 선택,
|
|
> 실시간(WebSocket) + 배치(파일/영상), 작업 큐·진행률, 혼용어 대응, 후처리, Colab 자동 노출.
|
|
|
|
## Metadata
|
|
- Interview ID: `di-luke-scribe-stt-20260602`
|
|
- Rounds: 3 (스코어링) + 추가 아이디어 1 + 열린 결정 확정 1
|
|
- Final Ambiguity Score: **~10%** (threshold 20%; 열린 결정 6건 확정 후)
|
|
- Type: **greenfield** (빈 저장소 `luke_scribe`)
|
|
- Generated: 2026-06-02
|
|
- Threshold: 0.2 / Threshold Source: `default`
|
|
- Initial Context Summarized: no
|
|
- Status: **PASSED · 결정 확정 완료 · CCG 외부리뷰 반영(v2.2)**
|
|
|
|
## Clarity Breakdown
|
|
| Dimension | Score | Weight | Weighted |
|
|
|-----------|-------|--------|----------|
|
|
| Goal Clarity | 0.94 | 0.40 | 0.376 |
|
|
| Constraint Clarity | 0.90 | 0.30 | 0.270 |
|
|
| Success Criteria | 0.86 | 0.30 | 0.258 |
|
|
| **Total Clarity** | | | **0.904** |
|
|
| **Ambiguity** | | | **0.096 (~10%)** |
|
|
|
|
---
|
|
|
|
## Topology (확정 컴포넌트)
|
|
|
|
| # | Component | Status | 설명 | 커버리지 |
|
|
|---|-----------|--------|------|----------|
|
|
| 1 | **Ingestion API** | active | 실시간 스트림(WebSocket) + 파일/영상 업로드 수집 | AC-1, AC-7, AC-9 |
|
|
| 2 | **Transcription Engine** | active | 로컬 STT(faster-whisper), **하이브리드: 실시간=turbo / 배치=large-v3** | AC-4 |
|
|
| 3 | **Realtime Pipeline** | active | VAD·청크·부분/최종 결과 스트리밍 | AC-8 |
|
|
| 4 | **Output / Results** | active | 요청별 출력옵션(txt/ts/word/diarize/SRT/VTT), 결과 보관(7일) | AC-9, AC-11 |
|
|
| 5 | **Job Queue / Concurrency** (1급) | active | Job 추상화, **Redis 영속 큐**, 워커풀, 우선순위 레인, queue_position·진행률 | AC-5, AC-6 |
|
|
| 6 | **Device Manager** (횡단) | active | GPU/CPU 자동감지 → 정밀도·워커수·동시성 자동 산정, 강제 플래그 | AC-2, AC-3 |
|
|
| 7 | **Post-processing** | active | glossary/rules + (옵션)LLM 보정(백엔드 설정화) + confidence 플래그 | AC-10 |
|
|
| 8 | **Connectivity / Tunnel** | active | Colab 등 공인 IP 없는 환경 자동 외부 노출(cloudflared 등) | AC-13 |
|
|
|
|
---
|
|
|
|
## Goal
|
|
|
|
**내부 서비스가 호출하는 비공개 API로, 실시간 음성·녹음 파일·mp3·mp4(및 기타 영상)를 입력받아 로컬에서 실행되는 STT 모델로 텍스트로 전사한다.** 실시간 입력은 준실시간(3~5초 내 부분 결과)으로 전사한다. 모델은 감지된 하드웨어(GPU/CPU)에 맞춰 정밀도·동시성을 자동 결정하되 `auto | cpu | cuda` 강제 선택도 가능하다. 다수 작업을 동시/대기열로 처리하고, 호출자는 대기열 위치와 진행률을 조회할 수 있다. 한국어 중심이되 한·영 혼용 기술용어(예: "API", "vLLM")를 음차로 망가뜨리지 않고 정확히 전사한다. **정확도가 중요한 배치는 large-v3, 저지연이 중요한 실시간은 turbo**로 분리한다(하이브리드).
|
|
|
|
---
|
|
|
|
## Constraints (제약)
|
|
|
|
- **로컬 실행(STT).** 외부 STT API(구글/AWS 등) 의존 금지 — Whisper를 우리 하드웨어에서 직접 실행. 모델 가중치는 **미존재 시 HuggingFace에서 자동 다운로드(인터넷 OK), 손상 시 재다운로드**. *에어갭/오프라인 강제는 요구사항 아님.*
|
|
- **하드웨어 폭이 매우 넓음:** 개발=GTX 1050(Pascal, 2~4GB), 테스트=Colab/T4/L4/A100/H100. → 고정 수치 설정 불가, **자동 산정 필수**.
|
|
- **정밀도 자동 선택:** compute capability ≥ 7.0 → fp16, Pascal(6.x) → int8, VRAM 부족 → CPU 폴백, CPU → int8.
|
|
- **동시성/워커 수는 감지된 VRAM·코어로 자동 산정**(오버라이드만 허용).
|
|
- **모델 하이브리드:** 실시간=turbo, 배치=large-v3 (둘 다 설치, `model` 오버라이드 가능).
|
|
- **언어:** 한국어 우선 + 자동 감지, 한·영 혼용(code-switching) 정확도가 하드 요구.
|
|
- **실시간 전송:** WebSocket. 목표 지연 3~5초(관대) → 정확도 우선 청킹 가능.
|
|
- **인증:** API Key 헤더(내부용).
|
|
- **큐:** **Redis 영속 큐(RQ, no-fork)** — 재시작 내성·다중 워커. **Colab/개발은 in-process 폴백(Redis 불필요)**.
|
|
- **보관:** 결과/메타만 **7일** 보관(설정화·자동삭제), **업로드 원본 오디오는 처리 후 즉시 삭제**.
|
|
- **파일 상한:** 모든 입력 **비동기 Job 기본**, 절대 상한 **4시간 / 2GB**(초과 `413`, 설정화).
|
|
- **배포 이원화:** CLI(셸 스크립트)=개발·테스트·Colab / Docker(FastAPI/Python)=프로덕션(내부).
|
|
- **CPU 폴백은 항상 지원.**
|
|
|
|
## Non-Goals (명시적 비범위)
|
|
|
|
- 외부 공개(public) API·과금·멀티테넌시 SaaS 기능.
|
|
- 자체 STT 모델 학습/파인튜닝(기성 Whisper 계열 사용).
|
|
- 번역(translation) — 1차 범위 외.
|
|
- 프런트엔드 UI(API/CLI만 제공).
|
|
- 영구 원본 오디오 아카이빙(원본은 삭제가 기본).
|
|
|
|
---
|
|
|
|
## Acceptance Criteria (검증 가능 기준)
|
|
|
|
- [ ] **AC-1** 동일 시스템으로 파일(오디오/영상)·실시간(WebSocket) 입력을 모두 전사한다.
|
|
- [ ] **AC-2** `device=auto`가 GTX 1050에서 int8/CPU로, T4/L4/A100/H100에서 fp16로 자동 동작하고, `cpu`/`cuda[:n]` 강제 플래그가 동작한다.
|
|
- [ ] **AC-3** 정밀도·워커 수가 감지된 VRAM/compute capability로 자동 산정되며 `--workers`/`--compute-type` 오버라이드가 가능하다.
|
|
- [ ] **AC-4** 혼용어 검증: *"그 API 서빙할 때 vLLM 쓰면 성능 대박이야"* 입력 시 "API", "vLLM"이 영문 그대로(핫워드 적용 시) 전사된다. 배치 경로(large-v3)에서 정확도가 더 높음을 확인한다.
|
|
- [ ] **AC-5** 동시 다중 작업을 받아 Redis 큐에 적재/동시 처리하며, 작업 중에도 신규 입력을 계속 수신한다.
|
|
- [ ] **AC-6** 호출자가 `queue_position`(앞 N건)과 `progress`(처리된 길이/전체, %)를 조회할 수 있다.
|
|
- [ ] **AC-7** 장시간/대용량 파일이 VAD 세그먼트로 분할되어 진행률을 제공하고 메모리 사용이 일정하다. 4h/2GB 초과는 `413`.
|
|
- [ ] **AC-8** 실시간 부분 결과가 3~5초 내 스트리밍되고 최종 결과로 안정화된다(turbo 경로).
|
|
- [ ] **AC-9** 영상 파일이 ffmpeg로 오디오 추출 후 전사되고, 출력 옵션(timestamps/word/diarize/formats)이 요청별로 동작한다.
|
|
- [ ] **AC-10** 후처리: glossary/rules가 동작하고, LLM 보정(백엔드 `local`/`external` 설정화, 기본 off·신뢰도 게이팅)과 저신뢰 구간 플래그가 동작한다.
|
|
- [ ] **AC-11** API Key 인증이 적용되고, 전사 완료 후 원본 오디오가 삭제되며 결과만 7일 보관된다.
|
|
- [ ] **AC-12** CLI(`serve`/`transcribe`/`bench`/`detect`)와 Docker(GPU/CPU 이미지 + Redis)로 각각 실행된다.
|
|
- [ ] **AC-13** Colab에서 `--tunnel cloudflare`로 공개 URL이 자동 발급되어 외부에서 호출된다.
|
|
|
|
---
|
|
|
|
## Architecture (상세 설계)
|
|
|
|
### 시스템 개요도
|
|
|
|
```
|
|
┌───────────────────────────────────────────────┐
|
|
내부 호출자 │ luke_scribe API │
|
|
(서비스/CLI) │ │
|
|
│ │ ┌──────────────┐ ┌────────────────────┐ │
|
|
REST ├──── 파일/영상 ─▶│ │ Ingestion API│────▶│ Job Queue (Redis) │ │
|
|
(HTTP)│ │ │ (FastAPI) │ │ - priority lanes │ │
|
|
│ │ │ - upload │ │ (realtime/batch) │ │
|
|
WS ├── 실시간 오디오▶│ │ - WS stream │ │ - queue_position │ │
|
|
│ │ │ - auth(API │ │ - progress │ │
|
|
│ │ │ Key) │ │ - durable/재시작내성 │ │
|
|
│ │ └──────┬───────┘ └─────────┬──────────┘ │
|
|
│ │ │ ffmpeg(영상→오디오) │ dispatch │
|
|
│ │ ▼ ▼ │
|
|
│ │ ┌──────────────┐ ┌────────────────────┐ │
|
|
│ │ │ Realtime │ │ Worker Pool │ │
|
|
│ │ │ Pipeline │ │ (N = 자동산정) │ │
|
|
│ │ │ VAD→chunk→ │◀───▶│ ┌───────────────┐ │ │
|
|
│ │ │ partial/final│ │ │ Engine │ │ │
|
|
│ │ │ (turbo) │ │ │ faster-whisper│ │ │
|
|
│ │ └──────┬───────┘ │ │ rt=turbo │ │ │
|
|
│ │ │ │ │ batch=large-v3│ │ │
|
|
│ │ ▼ │ └──────┬────────┘ │ │
|
|
│ │ ┌──────────────┐ │ │ │ │
|
|
│ │ │Post-processing│◀───┤ │ uses │ │
|
|
│ │ │glossary/rules │ └─────────┼───────────┘ │
|
|
│ │ │+LLM(opt,plug) │ │ │
|
|
│ │ │+conf flag │ ┌─────────▼──────────┐ │
|
|
│ │ └──────┬───────┘ │ Device Manager │ │
|
|
│ │ ▼ │ GPU/CPU 감지 → │ │
|
|
│◀── 결과/진행률 ─┤ ┌──────────────┐ │ fp16·int8·CPU / │ │
|
|
│ (txt/srt/ │ │Output/Results│ │ worker수·동시성 │ │
|
|
│ vtt/json) │ │store(7일,결과)│ └────────────────────┘ │
|
|
│ │ └──────────────┘ │
|
|
│ │ Connectivity/Tunnel (Colab→cloudflared 자동) │
|
|
│ └───────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 1) Ingestion API (입력/수집)
|
|
|
|
**REST (배치/파일):**
|
|
| Method | Path | 설명 |
|
|
|--------|------|------|
|
|
| `POST` | `/v1/jobs` | multipart: `file`(오디오/영상) + `options`(JSON). → `{job_id, status:"queued", queue_position}` |
|
|
| `GET` | `/v1/jobs/{id}` | 상태 조회: `queued`(queue_position, jobs_ahead) / `processing`(progress %, processed_sec/total_sec, eta) / `completed` / `failed`(error) |
|
|
| `GET` | `/v1/jobs/{id}/result?format=txt\|srt\|vtt\|json` | 결과 조회(포맷 변환) |
|
|
| `DELETE` | `/v1/jobs/{id}` | 작업 취소 |
|
|
| `GET` | `/v1/jobs` | 작업 목록/필터 |
|
|
|
|
**WebSocket (실시간):** `WS /v1/stream`
|
|
- 1) **init 프레임(첫 메시지=인증):** `{type:"init", api_key, audio:{codec,sample_rate,channels}, options:{language:"ko", ...}}` — 2초 내 유효 init 없으면 close. *브라우저는 WS 핸드셰이크에 헤더를 못 넣으므로 인증은 이 첫 메시지로.*
|
|
- 2) 클라이언트 → 오디오 청크(PCM16/opus 등) 연속 전송
|
|
- 3) 서버 → `{type:"partial", text, t0,t1}`(가설) / `{type:"final", segment, start, end, words[]}`(확정) / `{type:"status", ...}`
|
|
|
|
**Admin/관측:** `GET /health`, `GET /v1/system`(device 프로파일·워커수·큐 깊이), `GET /v1/models`.
|
|
|
|
**인증:** REST = `X-API-Key` 헤더. **WS = 첫 `init` 메시지의 `api_key`**(헤더 못 쓰는 브라우저 대응; 키가 URL/로그에 안 남음). 키별 스코프/사용량 확장 여지.
|
|
|
|
**요청 옵션 스키마(`options`):**
|
|
```jsonc
|
|
{
|
|
"language": "ko", // 기본 ko(한국어 우선). "auto"|"en"|"ja"... 요청별 override
|
|
"model": null, // null=경로별 기본(rt=turbo, batch=large-v3). 오버라이드 가능
|
|
"device": "auto", // "auto" | "cpu" | "cuda" | "cuda:0"
|
|
"compute_type": null, // null=자동. "float16"|"int8"|"int8_float16"
|
|
"timestamps": true, // 세그먼트 타임스탬프
|
|
"word_timestamps": false, // 단어 단위
|
|
"diarize": false, // 화자 분리(pyannote, opt, HF 토큰)
|
|
"formats": ["json"], // ["txt","srt","vtt","json"]
|
|
"hotwords": [], // (선택) 반복되는 고정 도메인 용어만 1회 등록. 미예측 — 비우면 ko+모델+후처리로 대응
|
|
"glossary_id": null, // 저장된 도메인 사전 참조
|
|
"vad": true, // 무음 제거
|
|
"post_correction": { // 단계 제어
|
|
"mode": "rules", // "none"|"glossary"|"rules"|"llm"
|
|
"backend": "local", // llm 모드 시: "local"|"openai"|"external"
|
|
"corrector_model": null // 백엔드별 모델/엔드포인트
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2) Transcription Engine (전사 엔진)
|
|
|
|
- **런타임:** **faster-whisper (CTranslate2)** — openai-whisper 대비 ~4배 빠르고 메모리 적음, GPU/CPU·fp16/int8 지원, Silero VAD 내장, 배치 추론 지원.
|
|
- **모델 전략(확정): 하이브리드**
|
|
- **실시간 경로 = large-v3-turbo** (저지연; 디코더 4층 경량화).
|
|
- **배치 경로 = large-v3** (혼용어/다국어 정확도 우위).
|
|
- 두 모델 모두 설치, 경로별 기본값 적용. `model` 옵션/환경변수로 런타임 오버라이드.
|
|
- **혼용어 대응(핵심):**
|
|
1. `hotwords`/`initial_prompt`에 도메인 용어 주입 → 기술용어 음차화 방지.
|
|
2. 저장 가능한 **Glossary**(도메인 사전) → `glossary_id`로 재사용.
|
|
3. (옵션) 후처리 LLM 보정으로 잔여 오류 교정.
|
|
- **제외:** distil-whisper, NVIDIA Parakeet/Canary(영어 중심 → 한국어 혼용 부적합).
|
|
|
|
> ✅ **결정 근거:** 다국어/혼용어 정확도는 **large-v3가 turbo보다 우위**(turbo는 일부 언어 정확도 하락). 따라서 정확도 중요한 배치는 large-v3, 저지연 중요한 실시간은 turbo로 분리(하이브리드 확정). 추후 도메인 샘플 WER 벤치로 실시간 경로의 v3 승격 여부 재평가 가능.
|
|
|
|
### 3) Realtime Pipeline (실시간)
|
|
|
|
- WebSocket 오디오 프레임 → 링버퍼 → **Silero VAD**로 발화 구간 검출 → 청크 구성 → 전사 → 부분/최종 방출.
|
|
- **안정화 정책:** LocalAgreement(연속 가설 일치분 확정) 또는 AlignAtt(2025 SOTA). 지연이 3~5초로 관대하므로 **큰 청크 + LocalAgreement-2**로 정확도 우선. 실시간 경로 기본 모델은 **turbo**.
|
|
- **참고 구현:** [WhisperLive](https://github.com/collabora/WhisperLive)(faster-whisper 백엔드, WS, VAD), [WhisperLiveKit](https://github.com/QuentinFuxa/WhisperLiveKit)(AlignAtt), [whisper_streaming](https://github.com/ufal/whisper_streaming)(→ SimulStreaming 대체 추세). 정책 채택/재구현 모두 가능.
|
|
|
|
### 4) Output / Results (출력·보관)
|
|
|
|
- 텍스트 기본 + 요청 옵션별 타임스탬프/단어/화자/자막.
|
|
- 포맷 변환: `json`(원천) → `txt`/`srt`/`vtt`/structured-`json`.
|
|
- **보관(확정):** 결과/메타만 **7일** 보관(설정화·만료 자동삭제), **원본 오디오는 전사 직후 삭제**.
|
|
- 저장소: 기본 로컬 파일/SQLite, 확장 시 S3/DB 가능.
|
|
|
|
### 5) Job Queue / Concurrency (큐·동시성)
|
|
|
|
- **Job 추상화:** 파일·실시간·영상 모두 Job으로 통일. 작업 중에도 신규 Job 계속 수신.
|
|
- **우선순위 레인:** 실시간 세션=저지연 우선 / 배치=처리량 레인.
|
|
- **워커풀:** 워커 수 = Device Manager 자동 산정. 각 워커가 디바이스 바인딩된 모델 인스턴스 보유.
|
|
- **큐 백엔드(확정):** **Redis + RQ(no-fork)** 영속 큐를 처음부터 사용 → 재시작 내성·다중 워커 프로세스 지원. *Celery 기본 prefork는 CUDA와 충돌하므로 no-fork 워커 사용.* (Colab/개발용 in-process 폴백.)
|
|
- **진행률:** 장시간 파일은 VAD 세그먼트 분할 → `progress = 완료 세그먼트 / 전체`(또는 처리 오디오초/전체초). `queue_position` = 큐 인덱스.
|
|
- **백프레셔:** 최대 큐 길이 초과 시 `429`.
|
|
|
|
### 6) Device Manager (능력 등급 자동판정 — 설계 중심축)
|
|
|
|
**감지:** GPU 유무·device name·compute capability·**VRAM(총/여유)**, **시스템 RAM**, **디스크 여유**(모델 다운로드 공간).
|
|
|
|
**능력 등급(Capability Tier) 자동판정:** 부팅 시 실측으로 *어떤 모델을 어디서 어떻게* 제공할지 결정 — **무음 모델 강등이 아니라, 등급이 "제공 가능 모델"을 정함.**
|
|
|
|
| 등급 | 하드웨어가 감당하는 것 | 동작 |
|
|
|---|---|---|
|
|
| **T0 · CPU** | GPU로 turbo도 무리(또는 GPU 없음) | turbo를 **CPU**로 실행 |
|
|
| **T1 · turbo-GPU** | turbo는 GPU OK, large-v3는 무리 | turbo만 GPU. **large-v3 미제공**(배치도 turbo) |
|
|
| **T2 · 스왑** | large-v3는 되지만 turbo와 **동시 상주**는 무리 | 호출에 따라 모델 **로드/언로드**(한 번에 하나 상주, MRU 유지로 스왑 최소화) |
|
|
| **T3 · 동시상주** | turbo + large-v3 **동시 적재** 가능 | 둘 다 상주 → 실시간(turbo)+배치(large-v3) **동시 처리** |
|
|
| ~~T4 · 다중복제~~ | 모델 여러 벌 병렬 적재 | **제외**(복잡도 과다) |
|
|
|
|
- **정밀도:** cc≥7.0 → fp16/int8_float16, Pascal(6.x, 예 1050) → int8, CPU → int8. (부팅 VRAM 실측으로 확정.)
|
|
- **모델 적재 가능성은 부팅 실측으로 판정**(정적 상수 비의존): turbo 미적재→CPU(T0), large-v3 미적재→미제공(T1), 동시 미적재→스왑(T2), 동시 적재 가능→동시상주(T3).
|
|
- **디스크 가드:** turbo ~1.6GB / large-v3 ~3GB 다운로드 전 여유 공간 점검, 부족 시 명확 오류.
|
|
- **투명성:** `/v1/system`·`/v1/models`로 현재 **등급·제공 가능 모델**, 결과엔 **`model_used`/`compute_type_used`** 항상 표시 → 몰래 강등 없음.
|
|
- **오버라이드:** `--device auto|cpu|cuda:N`, `--compute-type`, `--model`, `--workers`.
|
|
- **CLI `detect`**로 등급·제공모델·권장설정 출력. **1050**: 보통 T1(turbo-int8)/T0. **T4~H100**: T3 하이브리드 풀가동.
|
|
|
|
### 7) Post-processing (전사 오류 후처리)
|
|
|
|
순차 파이프라인(요청별 `post_correction.mode`로 단계 제어):
|
|
1. **Glossary/Hotwords (선택):** 반복되는 고정 도메인 용어를 1회 등록해 디코드 바이어스. *매 전사 예측이 아님* — 안 쓰면 ko 앵커+모델+규칙으로 대응.
|
|
2. **Rule/Dictionary 정규화(deterministic):** 알려진 오인식 → 표준 용어 치환, 정규식, 약어 대소문자 보정.
|
|
3. **LLM 보정(확정: 백엔드 설정화, 기본 off·confidence-gated):** 저신뢰/고WER 구간만 교정(Judge-Editor: 고신뢰 스팬 유지, 불확실 스팬만 재작성). **백엔드 플러그형** — `local`(소형 LLM, 오프라인·프라이버시) 또는 `openai`/`external`(OpenAI 호환 엔드포인트), `corrector_model` 설정 가능. 기본 비활성(약 HW 보호·과교정 방지).
|
|
4. **Confidence 플래깅:** 세그먼트별 신뢰도 부여, 저신뢰 구간 표시 → 선택적 휴먼 리뷰.
|
|
|
|
> ⚠️ **리서치 근거:** LLM 후처리는 **입력 WER이 높을 때(>10%)** WER을 크게 낮추지만, 이미 정확한 전사에는 **paraphrastic drift(과교정)** 위험 → 신뢰도 게이팅 필수. 도메인 고유명사/기술용어 손상이 핵심 위험(R-WER/EWER로 측정).
|
|
> 🔒 **프라이버시:** `external`/`openai` 백엔드는 전사 텍스트를 외부로 전송하므로 내부 전용 정책과 상충 가능 → **기본은 `local`**, 외부 백엔드는 명시적 opt-in.
|
|
|
|
### 8) Connectivity / Tunnel (Colab 자동 외부 노출)
|
|
|
|
- **환경 자동 감지**(Colab/Kaggle/dev) → 옵션 시 터널 기동.
|
|
- **기본: cloudflared Quick Tunnel** — `https://<random>.trycloudflare.com`, **계정/도메인 불필요**, 임시 URL, 제로 설정. (`--tunnel cloudflare`)
|
|
- **대안: ngrok** — authtoken 필요, 무료는 재시작 시 URL 변경, 요청 인스펙션 제공. (`--tunnel ngrok --ngrok-token ...`)
|
|
- **안정 도메인:** named Cloudflare Tunnel(CF 계정+도메인 필요).
|
|
- **프로덕션/실IP:** `--tunnel none`, host IP 바인딩.
|
|
- 기동 시 공개 URL + API Key 출력.
|
|
|
|
### 배포 (Deployment) — 코드는 하나, 실행 프로파일 둘
|
|
|
|
| | **Colab / 개발** | **프로덕션(내부)** |
|
|
|---|---|---|
|
|
| 실행 | **순수 Python / `run.sh`로 python 직접** (Docker ❌) | Docker + `docker-compose` |
|
|
| 큐 | **in-proc(Redis 불필요)** | Redis + 별도 worker |
|
|
| 저장 | 로컬 디스크 1개(공유 이슈 없음) | **공유 볼륨/오브젝트 스토어**(api↔worker) |
|
|
| 가중치 | 받아서 캐시(예: Drive) | 받아서 캐시(미존재 시 다운로드) |
|
|
| 외부노출 | **cloudflared 바이너리** 실행 | 실제 IP |
|
|
|
|
- **CLI (`run.sh` + `cli.py`)**: `serve`(API+옵션 터널) / `transcribe <file>` / `bench`(모델·등급 벤치) / `detect`(등급·프로파일). **Colab은 Docker 불가 → 반드시 이 경로.**
|
|
- **Docker(프로덕션)**: GPU 이미지(`nvidia/cuda`) + CPU(slim). compose: API + **Redis** + worker(+옵션 LLM). **입력 원본·파생 wav·결과는 공유 스토어**(컨테이너 경계에서 안 깨지게).
|
|
- **모델 프로비저닝(공통):** 시작 시 존재 확인 → 없으면 다운로드 → 손상(로드 실패/체크섬) 시 재다운로드 → 준비 전엔 `status:"loading"`.
|
|
- **설정:** env+`.env`/yaml — model(rt/batch), device, workers, api_keys+scopes, retention_days, tunnel, redis_url(프로덕션), corrector+allowlist.
|
|
|
|
### 기술 스택 (제안)
|
|
|
|
Python 3.11+, **FastAPI** + uvicorn, **faster-whisper(CTranslate2)** (turbo + large-v3), **ffmpeg**(영상→16kHz mono), **Silero VAD**(faster-whisper 내장), **Redis + RQ(no-fork)**(영속 큐), pydantic v2, **LLM 보정 백엔드**(local: llama.cpp/transformers · external: OpenAI 호환 client), (옵션) **pyannote.audio**(diarization, HF 토큰), **cloudflared**/pyngrok(터널), loguru/structlog(로깅), prometheus-client(메트릭, 옵션).
|
|
|
|
---
|
|
|
|
## Assumptions Exposed & Resolved (가정 노출·해소)
|
|
|
|
| Assumption | Challenge | Resolution |
|
|
|------------|-----------|------------|
|
|
| "한 번에 한 작업이면 충분" | 동시/대기 다작업·중간 추가 입력은? | 큐를 1급 승격, Redis 영속, 우선순위 레인 + queue_position/progress |
|
|
| "엔진은 정하면 끝" | 혼용어 vs 속도 충돌 | **하이브리드**(실시간 turbo / 배치 large-v3) + 핫워드/후처리 |
|
|
| "동시성 수치를 정해야 함" | 1050~H100 폭이 너무 큼 | Device Manager가 VRAM/CC로 정밀도·워커수 자동 산정 |
|
|
| "출력 형식을 시스템이 고정" | 호출마다 다를 수 있음 | 요청 `options`로 출력 옵션 전달(요청별) |
|
|
| "실시간은 최저 지연 필수" | 3~5초도 허용 | 큰 청크 + 안정화 정책으로 정확도 우선 |
|
|
| "Colab도 IP로 호출" | Colab은 공인 IP 없음 | cloudflared Quick Tunnel 자동 노출 |
|
|
| "전사 결과만 보면 됨" | 오인식/오타 교정은? | 후처리 파이프라인(glossary→rules→LLM(opt, 백엔드 설정화)→flag) |
|
|
| "후처리는 로컬만" | 외부 LLM 허용? | local/external 백엔드 설정화, external은 프라이버시 opt-in |
|
|
|
|
---
|
|
|
|
## Ontology (Key Entities)
|
|
|
|
| Entity | Type | Fields | Relationships |
|
|
|--------|------|--------|---------------|
|
|
| Job | core | id, type(file/stream/video), status, queue_position, progress, options, created_at | has many Segment, produces TranscriptResult |
|
|
| AudioInput | core | source(stream/file/video), codec, duration, size | belongs to Job |
|
|
| Engine | core | runtime(faster-whisper), model(turbo/large-v3), compute_type | used by Worker |
|
|
| Device | core | kind(gpu/cpu), name, vram_total/free, compute_capability | profiled by DeviceManager |
|
|
| DeviceProfile | supporting | precision, max_workers, fallback | derived from Device |
|
|
| Worker | core | id, device, model_instance, busy | consumes Queue(Redis), runs Engine |
|
|
| Queue | core | lane(realtime/batch), depth, backend(redis) | holds Job |
|
|
| Segment | supporting | index, start, end, text, words[], confidence | belongs to Job |
|
|
| TranscriptResult | core | text, segments[], formats, language | belongs to Job |
|
|
| RequestOptions | supporting | language, model, device, formats, hotwords, post_correction | configures Job |
|
|
| Glossary | supporting | id, terms[] | applied to Engine/Post-processing |
|
|
| PostProcessor | supporting | mode, backend(local/external), corrector_model, stages | transforms TranscriptResult |
|
|
| Session(realtime) | core | ws_conn, buffer, options | produces partial/final Segment |
|
|
| ApiKey | supporting | key, scopes, usage | authorizes Job |
|
|
| RetentionPolicy | supporting | result_ttl=7d, delete_source=true | governs Output |
|
|
| Tunnel | supporting | provider(cloudflare/ngrok/none), public_url | exposes API |
|
|
|
|
## Ontology Convergence
|
|
|
|
| Round | Entity Count | New | Changed | Stable | Stability Ratio |
|
|
|-------|-------------|-----|---------|--------|----------------|
|
|
| 1 | 10 | 10 | - | - | N/A |
|
|
| 2 | 13 | 3 | 0 | 10 | ~77% |
|
|
| 3 | 14 | 1 | 0 | 13 | ~92% |
|
|
| 추가반영 | 16 | 2 (PostProcessor, Tunnel) | 0 | 14 | ~88% |
|
|
| 결정확정 | 16 | 0 | 1 (Queue→Redis) | 16 | ~100% (수렴) |
|
|
|
|
---
|
|
|
|
## 확정된 결정 (Resolved Decisions)
|
|
|
|
| # | 결정 | 확정 내용 |
|
|
|---|------|-----------|
|
|
| 1 | 모델 전략 | **하이브리드** — 실시간=turbo(저지연), 배치=large-v3(혼용어 정확도). 모델 설정 오버라이드 가능. |
|
|
| 2 | 큐 영속성 | **Redis 영속 큐(RQ, no-fork)** 처음부터 — 재시작 내성·다중 워커. Colab/개발 in-process 폴백. |
|
|
| 3 | LLM 후처리 | 포함, **백엔드 설정화**(local 소형 LLM / OpenAI 호환 external), 기본 off·confidence-gated. external은 프라이버시 opt-in. |
|
|
| 4 | 결과 보관 | **7일**(설정화·자동삭제). 원본 오디오는 전사 직후 삭제. |
|
|
| 5 | 파일 상한 | 모든 입력 **비동기 Job 기본**, 절대 상한 **4시간 / 2GB**(초과 `413`, 설정화). |
|
|
| 6 | 화자 분리 | **옵션 포함**(pyannote, HF 토큰), 기본 off, 요청 시 `diarize=true`. |
|
|
|
|
---
|
|
|
|
## CCG 외부 리뷰 반영 (v2.2)
|
|
|
|
외부 advisor(Codex/Gemini) 리뷰 + 사용자 확정으로 갱신:
|
|
- **모델 프로비저닝:** 인터넷 다운로드 OK(에어갭 아님). 미존재→다운로드, 손상→재다운로드, 로딩 중 `status:"loading"`. (구 "오프라인 동작" 문구 폐기.)
|
|
- **능력 등급 자동판정(§6):** GPU VRAM·RAM·디스크로 **T0(CPU)~T3(turbo+large-v3 동시상주)** 자동 결정, T4(다중복제) 제외. 결과에 **`model_used`** 항상 표시(무음 강등 없음).
|
|
- **기본 언어 `ko`** + 요청별 override. **hotwords는 선택**(매 전사 예측 아님).
|
|
- **WS 인증 = 첫 `init` 메시지**(api_key+오디오포맷+옵션).
|
|
- **배포 2프로파일:** Colab/개발=순수 Python·in-proc·바이너리 cloudflared / 프로덕션=Docker+Redis+공유 스토어.
|
|
- (미채택·추후 검토: webhook 콜백, Idempotency-Key, 목록 페이지네이션, 만료 결과 `410`.)
|
|
|
|
---
|
|
|
|
## 구현 로드맵 (제안 Phase)
|
|
|
|
| Phase | 범위 | 산출 |
|
|
|-------|------|------|
|
|
| **P1 Core** | faster-whisper 통합(turbo+large-v3), Device Manager 자동감지, CLI `transcribe`/`detect`, 파일 동기 전사, ffmpeg 영상 추출 | 단발 전사 동작(1050/CPU 포함) |
|
|
| **P2 API+Queue** | FastAPI, **Redis 영속 큐**·워커풀, 상태/진행률 API, API Key, 결과 보관(7일)·원본 삭제, Docker(GPU/CPU/Redis) | 비동기 배치 API |
|
|
| **P3 Realtime** | WebSocket 스트리밍, VAD 청크, LocalAgreement 부분/최종(turbo) | 실시간 전사 |
|
|
| **P4 Output+Post** | timestamps/word/SRT/VTT, glossary/rules 후처리, confidence flags | 풍부한 출력 + 1차 후처리 |
|
|
| **P5 Advanced** | LLM 후처리 백엔드(local/external, 옵션), diarization(pyannote, 옵션), Colab cloudflared 자동, 메트릭/모니터링, `bench` | 운영·고급 기능 |
|
|
|
|
## 리스크
|
|
|
|
- **약 GPU(1050) 실시간 한계:** turbo도 Pascal/2GB에선 버거움 → int8/CPU 폴백, 실시간은 사실상 T4+ 권장(문서화).
|
|
- **turbo 혼용어 정확도:** 핫워드/후처리로 보완하되 도메인 벤치로 검증, 필요 시 실시간도 v3 승격.
|
|
- **LLM 후처리 과교정:** 신뢰도 게이팅 필수.
|
|
- **외부 LLM 후처리 프라이버시:** `external`/`openai` 백엔드 사용 시 전사 텍스트 외부 전송 → 내부 전용 정책 검토 필요(기본 local).
|
|
- **Redis 의존성:** 영속 큐가 Redis에 의존 → 단일 장애점. HA/단발용 in-process 폴백으로 완화.
|
|
- **GPU 메모리 동시성:** 워커당 VRAM 추정 오차 → 보수적 산정 + OOM 재시도/강등.
|
|
- **Quick Tunnel 임시 URL:** trycloudflare는 비영구 → 안정 필요 시 named tunnel/ngrok.
|
|
|
|
---
|
|
|
|
## Interview Transcript (요약)
|
|
|
|
<details>
|
|
<summary>Q&A (3 라운드 + 추가 아이디어 + 결정 확정)</summary>
|
|
|
|
**Round 0 — Topology:** 4개 컴포넌트 + 큐/동시성·중간 추가입력·대기열 위치·진행률·대용량 처리 요구 → 큐 1급 승격.
|
|
|
|
**Round 1 (100%→~40%)**
|
|
- 엔진: 혼용어 대응 + 속도 중요, 후보 large-v3, 추천 요청.
|
|
- 언어: 한국어+자동감지, 혼용어("vLLM 쓰면 성능 대박") 정확 전사 필수.
|
|
- 실시간 전송: **WebSocket**.
|
|
|
|
**Round 2 (~40%→~26%)**
|
|
- 엔진: turbo 단일(속도) → 이후 하이브리드로 확정.
|
|
- 하드웨어: GTX 1050·Colab·T4/L4/A100/H100 → **자동 감지·자동 용량 산정**.
|
|
- 동시성: **하드웨어 기반 자동 조절**.
|
|
- 출력: **요청별 옵션**.
|
|
|
|
**Round 3 (~26%→~14%, PASSED)**
|
|
- 배포: **CLI + Docker/FastAPI**. 인증: **API Key**. 보관: **결과만·원본 삭제**. 실시간 지연: **3~5초 관대**.
|
|
|
|
**추가 아이디어 (반영)**
|
|
- 전사 오류 **후처리** → Post-processing(glossary→rules→LLM(opt)→flag).
|
|
- Colab 등 공인 IP 부재 환경 **자동 외부 노출** → cloudflared Quick Tunnel 기본.
|
|
|
|
**열린 결정 확정 (~14%→~10%)**
|
|
- 모델=하이브리드, 큐=Redis 영속, LLM 후처리=백엔드 설정화(local/external), 보관 7일, 상한 4h/2GB, 화자분리 옵션(off).
|
|
|
|
</details>
|
|
|
|
---
|
|
*Generated by deep-interview · threshold 20% (default) · ambiguity ~10% (열린 결정 6건 확정) · PASSED*
|