WIP: define Luke Scribe v0.1 design
[gstack-context] Decisions: Validate the transcription core and benchmark gate before API, queue, and realtime work; preserve five expansion contracts. Remaining: Build the benchmark dataset and implement detect/transcribe/bench. Skill: /office-hours [/gstack-context]
This commit is contained in:
@@ -0,0 +1,866 @@
|
|||||||
|
# Luke Scribe v0.1 설계
|
||||||
|
|
||||||
|
- 작성일: 2026-08-10
|
||||||
|
- 저장소: `lukehemmin/luke_scribe`
|
||||||
|
- 브랜치: `main`
|
||||||
|
- 상태: **v0.1 범위 승인**
|
||||||
|
- 모드: Builder / Research
|
||||||
|
- 상위 문서:
|
||||||
|
- `.omc/specs/deep-interview-luke-scribe-stt-api.md`
|
||||||
|
- `.omc/plans/consensus-luke-scribe-stt-api.md`
|
||||||
|
|
||||||
|
## 1. 결정 요약
|
||||||
|
|
||||||
|
Luke Scribe의 최종 방향은 파일 전사와 실시간 전사를 제공하는 self-hosted STT API다.
|
||||||
|
하지만 v0.1에서는 플랫폼 전체를 만들지 않는다. 먼저 아래 질문에 측정값으로 답한다.
|
||||||
|
|
||||||
|
> 한국어와 영문 기술용어가 섞인 음성을 우리 하드웨어에서 어느 모델과 설정으로 전사해야 하는가?
|
||||||
|
|
||||||
|
v0.1은 `WAV/MP3 -> TranscriptResult JSON` 전사 코어와 모델 벤치마크만 완성한다.
|
||||||
|
REST API, Redis 큐, WebSocket, 화자 분리, LLM 후처리는 이후 버전으로 미룬다.
|
||||||
|
|
||||||
|
단, v0.1을 폐기형 프로토타입으로 만들지는 않는다. 다음 다섯 가지 계약은 이후 버전에서도 유지한다.
|
||||||
|
|
||||||
|
1. 정규화된 오디오 입력 계약
|
||||||
|
2. 전사 엔진 인터페이스
|
||||||
|
3. `TranscriptResult` 결과 스키마
|
||||||
|
4. 장치 탐지 및 실행 프로파일
|
||||||
|
5. 단일 전사를 조율하는 `TranscriptionService`
|
||||||
|
|
||||||
|
이 경계 덕분에 v0.2의 FastAPI와 v0.3 이후의 큐·실시간 처리는 전사 코어를 수정하지 않고 호출 방식만 추가할 수 있다.
|
||||||
|
|
||||||
|
## 2. 제품 정의
|
||||||
|
|
||||||
|
> **Luke Scribe는 한국어와 영문 기술용어가 섞인 음성을 외부 STT API에 보내지 않고 통제된 실행 환경에서 전사하는 엔진이자 API다.**
|
||||||
|
|
||||||
|
### 첫 사용자
|
||||||
|
|
||||||
|
Luke Scribe를 호출할 Luke의 내부 서비스와 개발 도구다.
|
||||||
|
|
||||||
|
### 첫 번째 성공 장면
|
||||||
|
|
||||||
|
개발자가 실제 회의 또는 기술 설명이 담긴 WAV/MP3 파일을 CLI에 전달한다. Luke Scribe는 다음 정보를 포함한 JSON을 생성한다.
|
||||||
|
|
||||||
|
- 전체 전사문
|
||||||
|
- 구간별 시작·종료 시각과 텍스트
|
||||||
|
- 감지 언어
|
||||||
|
- 실제 사용한 모델, 장치, 정밀도
|
||||||
|
- 전사 시간과 RTF(real-time factor)
|
||||||
|
- 경고 및 강등 내역
|
||||||
|
|
||||||
|
결과에서 `API`, `vLLM`, `FastAPI`, `Kubernetes`, `GPU` 같은 핵심 기술용어가 원문 표기로 보존되어야 한다.
|
||||||
|
|
||||||
|
## 3. v0.1 범위
|
||||||
|
|
||||||
|
### 포함
|
||||||
|
|
||||||
|
- Python 3.11+ 프로젝트 구조와 CLI
|
||||||
|
- `faster-whisper` 기반 전사 엔진
|
||||||
|
- `large-v3-turbo`와 `large-v3` 비교
|
||||||
|
- CPU 및 CUDA 장치 탐지
|
||||||
|
- 실행 장치와 `compute_type` 자동 선택 및 명시적 override
|
||||||
|
- WAV/MP3 입력
|
||||||
|
- ffmpeg 기반 16 kHz mono 오디오 정규화
|
||||||
|
- 전체 텍스트와 세그먼트 타임스탬프를 포함한 JSON 출력
|
||||||
|
- 한국어·영문 기술용어 혼용 벤치마크
|
||||||
|
- WER, K-CER, entity 보존율, 처리 시간, RTF, VRAM/RSS 측정
|
||||||
|
- 임시 파생 오디오 삭제
|
||||||
|
- 단위·통합·스모크 테스트
|
||||||
|
|
||||||
|
### 제외
|
||||||
|
|
||||||
|
- FastAPI와 HTTP 엔드포인트
|
||||||
|
- Redis/RQ와 비동기 Job 큐
|
||||||
|
- WebSocket 실시간 전사
|
||||||
|
- 화자 분리
|
||||||
|
- SRT/VTT 출력
|
||||||
|
- glossary/rules/LLM 후처리
|
||||||
|
- Docker Compose 운영 구성
|
||||||
|
- Colab 터널
|
||||||
|
- 4시간/2GB 운영 보장
|
||||||
|
- 다중 GPU 및 다중 워커
|
||||||
|
|
||||||
|
제외 항목은 포기한 기능이 아니다. 모델과 결과 계약이 검증된 뒤 순서대로 연결한다.
|
||||||
|
|
||||||
|
### 런타임 기준선
|
||||||
|
|
||||||
|
구현 시작 시 `pyproject.toml`과 lockfile에서 정확한 버전을 고정한다. 문서 수준의 지원 범위는 다음과 같다.
|
||||||
|
|
||||||
|
| 항목 | v0.1 기준 |
|
||||||
|
|---|---|
|
||||||
|
| Python | 3.11 이상, 구현 시 선택한 minor 버전 고정 |
|
||||||
|
| faster-whisper | 구현 시 검증한 단일 버전 고정 |
|
||||||
|
| CTranslate2 | faster-whisper 및 CUDA와 함께 검증한 버전 고정 |
|
||||||
|
| ffmpeg / ffprobe | 시스템 실행 파일 필수, 시작 전 존재 및 버전 확인 |
|
||||||
|
| 모델 | `large-v3-turbo`, `large-v3` |
|
||||||
|
| 모델 획득 | Hugging Face에서 최초 1회 다운로드 후 로컬 캐시 |
|
||||||
|
| 네트워크 없음 | 캐시된 모델만 사용; 없으면 `model_unavailable_offline`로 실패 |
|
||||||
|
| 디스크 | 두 모델 캐시와 임시 WAV를 저장할 여유 공간을 preflight에서 검사 |
|
||||||
|
| 필수 GPU | **Colab T4 16GB를 v0.1 기준 GPU로 우선 확정** |
|
||||||
|
| 필수 CPU | x86_64, RAM 16GB 이상을 qualification 기준으로 사용 |
|
||||||
|
|
||||||
|
GTX 1050, L4, A100, H100은 추가 관측 대상이지 v0.1 완료를 막는 필수 환경이 아니다. Colab T4를 사용할 수 없게 되면 구현 전에 동급 이상의 NVIDIA GPU 한 종을 명시적으로 대체 지정한다.
|
||||||
|
|
||||||
|
Colab T4는 공개·합성·익명화한 benchmark 자료의 GPU qualification에만 사용한다. 실제 내부 음성은 자체 통제 GPU에서만 처리하며 Colab으로 업로드하지 않는다.
|
||||||
|
|
||||||
|
### v0.1 지원 입력 범위
|
||||||
|
|
||||||
|
- 컨테이너: WAV, MP3
|
||||||
|
- 오디오 코덱: ffmpeg가 PCM WAV 또는 MP3로 식별한 입력
|
||||||
|
- 검증 방식: 파일 확장자가 아니라 ffprobe 결과 사용
|
||||||
|
- 검증 보장 범위: 파일당 60분 이하, 1GB 이하
|
||||||
|
- 손상·부분 다운로드 파일: `audio_probe_failed`
|
||||||
|
- ffprobe/ffmpeg timeout: 기본 60초, CLI 옵션으로만 상향 가능
|
||||||
|
- 더 긴 파일: 명시적으로 `unsupported_input_envelope` 반환
|
||||||
|
|
||||||
|
## 4. 핵심 전제
|
||||||
|
|
||||||
|
1. 현재 가장 큰 불확실성은 API 구조가 아니라 실제 KO+EN 혼용 전사 품질이다.
|
||||||
|
2. `large-v3-turbo`를 기본 후보로 두되, 벤치마크를 통과하지 못하면 `large-v3`를 기본으로 채택한다.
|
||||||
|
3. v0.1은 한 프로세스에서 한 파일을 처리한다. 동시성은 아직 제품 가치 검증에 필요하지 않다.
|
||||||
|
4. 모든 상위 계층은 동일한 `TranscriptionService`와 `TranscriptResult`를 사용한다.
|
||||||
|
5. 약한 하드웨어에서 조용히 품질을 낮추지 않는다. 실제 선택된 모델과 정밀도를 결과에 기록한다.
|
||||||
|
6. 외부 STT API로 음성이나 전사문을 전송하지 않는다.
|
||||||
|
|
||||||
|
## 5. 실행 흐름
|
||||||
|
|
||||||
|
```text
|
||||||
|
WAV / MP3
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Audio Ingest
|
||||||
|
- 형식/크기 검사
|
||||||
|
- duration probe
|
||||||
|
- ffmpeg -> 16 kHz mono WAV
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Device Resolver
|
||||||
|
- CPU/CUDA 탐지
|
||||||
|
- compute_type 결정
|
||||||
|
- 사용자 override 검증
|
||||||
|
|
|
||||||
|
v
|
||||||
|
Transcription Engine
|
||||||
|
- faster-whisper
|
||||||
|
- turbo 또는 large-v3
|
||||||
|
- VAD / language / hotwords 옵션
|
||||||
|
|
|
||||||
|
v
|
||||||
|
TranscriptResult
|
||||||
|
- text / segments
|
||||||
|
- model/device/precision
|
||||||
|
- timings/warnings
|
||||||
|
|
|
||||||
|
+-----------------> JSON 파일
|
||||||
|
|
|
||||||
|
+-----------------> Benchmark evaluator
|
||||||
|
- K-CER
|
||||||
|
- entity retention
|
||||||
|
- RTF
|
||||||
|
- VRAM/RSS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 확장 가능한 경계
|
||||||
|
|
||||||
|
확장성을 위해 모든 기능을 미리 추상화하지 않는다. 아래 다섯 경계만 안정적인 계약으로 만든다.
|
||||||
|
|
||||||
|
### 6.1 `AudioIngestor`
|
||||||
|
|
||||||
|
책임:
|
||||||
|
|
||||||
|
- 입력 파일 검증
|
||||||
|
- ffprobe로 메타데이터 확인
|
||||||
|
- ffmpeg로 정규화
|
||||||
|
- 임시파일 수명 관리
|
||||||
|
|
||||||
|
v0.1에서는 로컬 파일만 받는다. 이후 HTTP 업로드와 영상 파일은 이 계층 앞에 입력 어댑터로 추가한다.
|
||||||
|
|
||||||
|
`NormalizedAudio` 계약:
|
||||||
|
|
||||||
|
| 필드 | 형식 | 필수 | 규칙 |
|
||||||
|
|---|---|---:|---|
|
||||||
|
| `path` | path | 예 | Luke Scribe가 소유한 임시 정규화 파일 |
|
||||||
|
| `duration_sec` | float | 예 | `> 0`, ffprobe 측정값 |
|
||||||
|
| `sample_rate` | int | 예 | 항상 `16000` |
|
||||||
|
| `channels` | int | 예 | 항상 `1` |
|
||||||
|
| `sample_format` | enum | 예 | v0.1은 `s16`만 |
|
||||||
|
| `source_codec` | string | 예 | ffprobe가 보고한 원본 코덱 |
|
||||||
|
| `source_size_bytes` | int | 예 | `> 0` |
|
||||||
|
|
||||||
|
### 6.2 `TranscriptionEngine`
|
||||||
|
|
||||||
|
개념적 인터페이스:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class TranscriptionEngine(Protocol):
|
||||||
|
def transcribe(
|
||||||
|
self,
|
||||||
|
audio: NormalizedAudio,
|
||||||
|
options: TranscriptionOptions,
|
||||||
|
) -> TranscriptResult: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
v0.1 구현은 `FasterWhisperEngine` 하나만 둔다. 멀티 엔진 플러그인 시스템은 만들지 않는다.
|
||||||
|
|
||||||
|
`TranscriptionOptions` 계약:
|
||||||
|
|
||||||
|
| 필드 | 형식 | 기본값 | 제약 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `model` | enum | benchmark 전 `large-v3-turbo` | `large-v3-turbo` 또는 `large-v3` |
|
||||||
|
| `language` | string/null | `ko` | `null`이면 자동 감지 |
|
||||||
|
| `device` | string | `auto` | `auto`, `cpu`, `cuda`, `cuda:N` |
|
||||||
|
| `compute_type` | string/null | `null` | `null`이면 resolver가 결정 |
|
||||||
|
| `vad` | bool | `true` | faster-whisper VAD 사용 여부 |
|
||||||
|
| `hotwords` | list[string] | `[]` | 빈 문자열 제거, 중복 제거 |
|
||||||
|
|
||||||
|
### 6.3 `TranscriptResult`
|
||||||
|
|
||||||
|
CLI, 향후 REST API, 큐 워커, WebSocket 최종 결과가 공유하는 정규 형태다. 전송 형식과 내부 모델을 분리해 향후 필드를 추가해도 기존 소비자가 깨지지 않게 한다.
|
||||||
|
|
||||||
|
### 6.4 `DeviceProfile`
|
||||||
|
|
||||||
|
장치 탐지 결과와 실행 결정을 분리한다.
|
||||||
|
|
||||||
|
- 탐지값: CPU, GPU 이름, compute capability, 총/가용 VRAM
|
||||||
|
- 결정값: device, compute type, model
|
||||||
|
- 출처: `auto` 또는 사용자 override
|
||||||
|
- 경고: CPU fallback, 정밀도 변경, 모델 미지원
|
||||||
|
|
||||||
|
v0.1에서는 한 장치와 한 실행만 선택한다. 워커 수 계산은 v0.2 이후에 추가한다.
|
||||||
|
|
||||||
|
장치 resolver는 다음 순서로만 결정한다.
|
||||||
|
|
||||||
|
| 요청 | 탐지 조건 | 결과 |
|
||||||
|
|---|---|---|
|
||||||
|
| `device=cpu` | CPU 사용 가능 | CPU + `int8` |
|
||||||
|
| `device=cuda[:N]` | 지정 GPU 존재, 모델 적재 가능 | 지정 GPU + 요청/자동 compute type |
|
||||||
|
| `device=cuda[:N]` | GPU 없음·인덱스 오류·적재 불가 | fallback 없이 실패 |
|
||||||
|
| `device=auto` | CUDA GPU에서 모델 적재 가능 | CUDA 사용; cc>=7.0이면 `float16` 우선 |
|
||||||
|
| `device=auto` | CUDA는 있으나 float16 부적합 | 지원되는 `int8_float16` 또는 `int8` |
|
||||||
|
| `device=auto` | GPU 없음 또는 모델 적재 불가 | CPU `int8`, warning 기록 |
|
||||||
|
| 명시 `compute_type` | 런타임이 지원하지 않음 | fallback 없이 실패 |
|
||||||
|
|
||||||
|
자동 모델 추천값은 benchmark 전에는 `unresolved`다. `detect`는 하드웨어상 적재 가능한 모델 목록만 보여준다. benchmark가 승인한 결정 파일이 있으면 그때부터 `recommended_model`을 출력한다.
|
||||||
|
|
||||||
|
`DeviceProfile` 필수 필드:
|
||||||
|
|
||||||
|
- `requested_device`, `selected_device`, `selection_source`
|
||||||
|
- `device_name`, `compute_capability`
|
||||||
|
- `vram_total_mb`, `vram_free_mb`
|
||||||
|
- `requested_compute_type`, `selected_compute_type`
|
||||||
|
- `loadable_models`, `recommended_model`
|
||||||
|
- `warnings`
|
||||||
|
|
||||||
|
### 6.5 `TranscriptionService`
|
||||||
|
|
||||||
|
`TranscriptionService`는 안정적인 애플리케이션 경계다. 입력 수명 관리, 장치 선택, 엔진 호출, timing 수집, 결과 조립을 담당한다. CLI는 이 서비스만 호출한다. 이후 API와 큐 워커도 동일한 서비스를 호출한다.
|
||||||
|
|
||||||
|
```python
|
||||||
|
class TranscriptionService:
|
||||||
|
def transcribe_file(
|
||||||
|
self,
|
||||||
|
source: Path,
|
||||||
|
options: TranscriptionOptions,
|
||||||
|
) -> TranscriptResult: ...
|
||||||
|
```
|
||||||
|
|
||||||
|
서비스 내부의 클래스 구성과 orchestration 순서는 변경할 수 있지만, 위 호출 의미와 `TranscriptResult` 계약은 v1 동안 유지한다.
|
||||||
|
|
||||||
|
## 7. 권장 프로젝트 구조
|
||||||
|
|
||||||
|
```text
|
||||||
|
luke_scribe/
|
||||||
|
├── docs/
|
||||||
|
│ └── luke-scribe-v0.1-design.md
|
||||||
|
├── benchmarks/
|
||||||
|
│ ├── README.md
|
||||||
|
│ ├── manifest.yaml
|
||||||
|
│ └── references/
|
||||||
|
├── src/luke_scribe/
|
||||||
|
│ ├── __init__.py
|
||||||
|
│ ├── cli.py
|
||||||
|
│ ├── config.py
|
||||||
|
│ ├── audio/
|
||||||
|
│ │ ├── ingest.py
|
||||||
|
│ │ └── models.py
|
||||||
|
│ ├── devices/
|
||||||
|
│ │ ├── detect.py
|
||||||
|
│ │ └── profile.py
|
||||||
|
│ ├── engine/
|
||||||
|
│ │ ├── base.py
|
||||||
|
│ │ └── faster_whisper.py
|
||||||
|
│ ├── pipeline/
|
||||||
|
│ │ └── transcribe.py
|
||||||
|
│ ├── results/
|
||||||
|
│ │ ├── models.py
|
||||||
|
│ │ └── json_writer.py
|
||||||
|
│ └── benchmark/
|
||||||
|
│ ├── evaluator.py
|
||||||
|
│ ├── metrics.py
|
||||||
|
│ └── report.py
|
||||||
|
├── tests/
|
||||||
|
│ ├── unit/
|
||||||
|
│ ├── integration/
|
||||||
|
│ └── fixtures/
|
||||||
|
├── pyproject.toml
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
`api/`, `jobqueue/`, `realtime/`, `postprocess/` 디렉터리는 실제 기능을 구현하는 버전에서 추가한다. 빈 디렉터리나 가짜 인터페이스를 미리 만들지 않는다.
|
||||||
|
|
||||||
|
## 8. CLI 계약
|
||||||
|
|
||||||
|
### 장치 탐지
|
||||||
|
|
||||||
|
```bash
|
||||||
|
luke-scribe detect
|
||||||
|
```
|
||||||
|
|
||||||
|
출력 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"requested_device": "auto",
|
||||||
|
"selected_device": "cuda:0",
|
||||||
|
"selection_source": "auto",
|
||||||
|
"device_name": "NVIDIA T4",
|
||||||
|
"compute_capability": "7.5",
|
||||||
|
"vram_total_mb": 15360,
|
||||||
|
"vram_free_mb": 14820,
|
||||||
|
"requested_compute_type": null,
|
||||||
|
"selected_compute_type": "float16",
|
||||||
|
"loadable_models": ["large-v3-turbo", "large-v3"],
|
||||||
|
"recommended_model": null,
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`recommended_model=null`은 benchmark 결정 파일이 아직 없다는 뜻이다.
|
||||||
|
|
||||||
|
### 단일 파일 전사
|
||||||
|
|
||||||
|
```bash
|
||||||
|
luke-scribe transcribe samples/meeting.mp3 \
|
||||||
|
--language ko \
|
||||||
|
--model large-v3-turbo \
|
||||||
|
--device auto \
|
||||||
|
--output result.json
|
||||||
|
```
|
||||||
|
|
||||||
|
필수 동작:
|
||||||
|
|
||||||
|
- 성공 시 exit code `0`
|
||||||
|
- 입력 오류 시 exit code `2`
|
||||||
|
- 모델/장치 초기화 실패 시 exit code `3`
|
||||||
|
- 전사 실패 시 exit code `4`
|
||||||
|
- 오류는 stderr, 결과 JSON은 지정 파일 또는 stdout으로 출력
|
||||||
|
- override를 적용할 수 없으면 조용히 변경하지 않고 명확하게 실패
|
||||||
|
|
||||||
|
### 벤치마크
|
||||||
|
|
||||||
|
```bash
|
||||||
|
luke-scribe bench benchmarks/manifest.yaml \
|
||||||
|
--models large-v3-turbo,large-v3 \
|
||||||
|
--output benchmark-report.json
|
||||||
|
```
|
||||||
|
|
||||||
|
벤치마크는 같은 정규화 오디오와 같은 옵션을 사용해 모델만 바꿔 비교한다.
|
||||||
|
|
||||||
|
### 전체 옵션 계약
|
||||||
|
|
||||||
|
| 명령 | 옵션 | 기본값 | 규칙 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `detect` | `--json` | `true` | v0.1은 JSON 출력만 보장 |
|
||||||
|
| `transcribe` | `--language` | `ko` | `auto`이면 `null`로 전달 |
|
||||||
|
| `transcribe` | `--model` | 결정 파일 또는 turbo | 지원 enum만 허용 |
|
||||||
|
| `transcribe` | `--device` | `auto` | `cpu`, `cuda`, `cuda:N` |
|
||||||
|
| `transcribe` | `--compute-type` | `auto` | 명시값은 fallback 금지 |
|
||||||
|
| `transcribe` | `--vad/--no-vad` | `--vad` | 상호 배타적 |
|
||||||
|
| `transcribe` | `--hotword` | 없음 | 반복 가능 |
|
||||||
|
| `transcribe` | `--output` | stdout | `-`도 stdout 의미 |
|
||||||
|
| `transcribe` | `--force` | `false` | 기존 출력 파일 overwrite 허용 |
|
||||||
|
| `transcribe` | `--log-level` | `INFO` | 로그는 stderr |
|
||||||
|
| `bench` | `--models` | 두 모델 모두 | 한 모델만 지정 가능 |
|
||||||
|
| `bench` | `--device` | `auto` | 한 run 안에서는 고정 |
|
||||||
|
| `bench` | `--compute-type` | `auto` | 모델 간 동일 정책 적용 |
|
||||||
|
| `bench` | `--hotword-set` | `none,domain` | 반복 가능; 정의된 실험군만 허용 |
|
||||||
|
| `bench` | `--repeats` | `3` | warm-up 이후 측정 반복 수 |
|
||||||
|
| `bench` | `--output` | 필수 | JSON report 경로 |
|
||||||
|
|
||||||
|
기존 파일이 있고 `--force`가 없으면 모델을 로드하기 전에 실패한다. stdout에 성공 JSON을 쓰는 동안에는 progress bar를 출력하지 않는다.
|
||||||
|
|
||||||
|
`bench --models`에 한 모델만 지정하면 report만 생성한다. 모델 결정에는 두 모델의 동일 실행 환경 비교가 필요하므로 단일 모델 run은 decision artifact를 생성하거나 기존 artifact를 변경할 수 없다.
|
||||||
|
|
||||||
|
## 9. 결과 스키마 v1
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"status": "completed",
|
||||||
|
"source": {
|
||||||
|
"name": "meeting.mp3",
|
||||||
|
"codec": "mp3",
|
||||||
|
"size_bytes": 804231
|
||||||
|
},
|
||||||
|
"normalized_audio": {
|
||||||
|
"duration_sec": 42.8,
|
||||||
|
"audio_format": "pcm_s16le",
|
||||||
|
"sample_rate": 16000,
|
||||||
|
"channels": 1
|
||||||
|
},
|
||||||
|
"execution": {
|
||||||
|
"model": "large-v3-turbo",
|
||||||
|
"device": "cuda:0",
|
||||||
|
"compute_type": "float16",
|
||||||
|
"language_requested": "ko",
|
||||||
|
"language_detected": null,
|
||||||
|
"language_detection_confidence": null
|
||||||
|
},
|
||||||
|
"timings": {
|
||||||
|
"model_load_sec": 4.21,
|
||||||
|
"transcription_sec": 6.32,
|
||||||
|
"rtf": 0.148
|
||||||
|
},
|
||||||
|
"text": "오늘 API 서버에서 vLLM을 사용해 보겠습니다.",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"index": 0,
|
||||||
|
"start": 0.52,
|
||||||
|
"end": 4.84,
|
||||||
|
"text": "오늘 API 서버에서 vLLM을 사용해 보겠습니다.",
|
||||||
|
"avg_logprob": -0.21,
|
||||||
|
"no_speech_prob": 0.01
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 스키마 규칙
|
||||||
|
|
||||||
|
- `schema_version`은 필수다.
|
||||||
|
- 시간은 초 단위 실수다.
|
||||||
|
- `text`는 세그먼트를 읽기 순서대로 합친 최종 문자열이다.
|
||||||
|
- 모델·장치·정밀도는 요청값이 아니라 실제 적용값을 기록한다.
|
||||||
|
- 확장 필드는 추가할 수 있지만 기존 필드의 의미와 자료형은 v1 안에서 바꾸지 않는다.
|
||||||
|
- 실패 결과는 동일한 envelope에서 `status="failed"`, `error.code`, `error.message`를 제공한다.
|
||||||
|
- `language_requested`가 언어를 강제하면 `language_detected`와 confidence는 `null`이다.
|
||||||
|
- 자동 감지일 때만 감지 언어와 confidence를 기록한다.
|
||||||
|
- 세그먼트는 `index` 오름차순이며 서로 역행할 수 없다.
|
||||||
|
- 초 단위 시간은 JSON number로 기록하고 최소 millisecond 정밀도를 보존한다.
|
||||||
|
- `text`는 각 세그먼트의 trim된 텍스트를 단일 공백으로 연결한 값이다.
|
||||||
|
- 소비자는 알 수 없는 추가 필드를 무시해야 한다.
|
||||||
|
|
||||||
|
구현 시 이 계약을 `docs/schemas/transcript-result-v1.schema.json`으로 옮기고 fixture를 JSON Schema validator로 검증한다.
|
||||||
|
|
||||||
|
실패 envelope 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"status": "failed",
|
||||||
|
"source": {"name": "broken.mp3"},
|
||||||
|
"error": {
|
||||||
|
"code": "audio_probe_failed",
|
||||||
|
"message": "입력 오디오를 해석할 수 없습니다.",
|
||||||
|
"retryable": false
|
||||||
|
},
|
||||||
|
"warnings": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. 벤치마크 설계
|
||||||
|
|
||||||
|
### 데이터셋 구성
|
||||||
|
|
||||||
|
v0.1 판단용 데이터셋은 최소 다음 조건을 충족한다.
|
||||||
|
|
||||||
|
- 총 30개 이상 클립
|
||||||
|
- 총 음성 길이 60분 이상
|
||||||
|
- 실제 사용 환경에서 수집한 한국어 중심 음성
|
||||||
|
- 조용한 녹음, 생활 소음, 마이크 거리 차이를 모두 포함
|
||||||
|
- 단독 화자와 두 명 이상 대화 포함
|
||||||
|
- 영문 기술용어 entity 최소 100회 등장
|
||||||
|
- 각 클립에 사람이 검수한 정답 전사문과 entity 목록 제공
|
||||||
|
|
||||||
|
개인정보가 있는 원본은 저장소에 커밋하지 않는다. `manifest.yaml`에는 로컬 경로 또는 익명화된 fixture만 기록한다.
|
||||||
|
|
||||||
|
`manifest.yaml` 필수 필드:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dataset_version: "1.0"
|
||||||
|
hotword_sets:
|
||||||
|
none: []
|
||||||
|
domain: [API, vLLM, FastAPI, Kubernetes, LLM, GPU]
|
||||||
|
clips:
|
||||||
|
- id: ko-en-001
|
||||||
|
audio_path: /secure/local/path/ko-en-001.wav
|
||||||
|
reference_path: references/ko-en-001.txt
|
||||||
|
language: ko
|
||||||
|
entities:
|
||||||
|
- canonical: API
|
||||||
|
surface: API
|
||||||
|
start_char: 3
|
||||||
|
end_char: 6
|
||||||
|
- canonical: vLLM
|
||||||
|
surface: vLLM
|
||||||
|
start_char: 12
|
||||||
|
end_char: 16
|
||||||
|
tags: [clean, single-speaker]
|
||||||
|
```
|
||||||
|
|
||||||
|
entity annotation의 `start_char`/`end_char`는 UTF-8 byte가 아니라 reference Unicode code point index다. 같은 entity가 두 번 나오면 occurrence를 두 개 기록한다.
|
||||||
|
|
||||||
|
`domain` hotword set은 평가 전 고정한 공통 용어집이다. 클립별 정답 entity를 실행 시 주입하지 않는다. 이렇게 해야 benchmark 정답을 모델 입력으로 누출하지 않는다.
|
||||||
|
|
||||||
|
hotword set은 `benchmarks/hotwords/domain-v1.json`처럼 별도 artifact로 저장한다. artifact에는 `version`, 정렬된 `terms`, `sha256`을 포함한다. manifest와 model decision은 이름뿐 아니라 artifact 경로·버전·hash를 참조한다.
|
||||||
|
|
||||||
|
### 필수 지표
|
||||||
|
|
||||||
|
| 지표 | 의미 | v0.1 사용 목적 |
|
||||||
|
|---|---|---|
|
||||||
|
| WER | 전체 단어 오류율 | 일반적인 전사 품질 비교 |
|
||||||
|
| K-CER | 한국어 정규화 후 문자 오류율 | 한국어 띄어쓰기 차이 영향 완화 |
|
||||||
|
| Entity 보존율 | 기술용어가 원형대로 남은 비율 | 핵심 제품 품질 판정 |
|
||||||
|
| RTF | 처리 시간 / 오디오 길이 | 장치별 처리 속도 판정 |
|
||||||
|
| Peak VRAM/RSS | 최대 메모리 사용량 | 배포 가능 장치 판정 |
|
||||||
|
| Failure rate | 실패 클립 / 전체 클립 | 안정성 판정 |
|
||||||
|
|
||||||
|
### 지표 계산 규칙
|
||||||
|
|
||||||
|
K-CER용 정규화는 다음 순서로 고정한다.
|
||||||
|
|
||||||
|
1. Unicode NFKC 정규화
|
||||||
|
2. 영문은 lowercase로 변환하되 entity 평가는 원래 대소문자를 유지
|
||||||
|
3. 문장부호 제거
|
||||||
|
4. 모든 whitespace 제거
|
||||||
|
5. 숫자는 아라비아 숫자로 통일하는 별도 mapping file 적용
|
||||||
|
6. Unicode code point 단위로 Levenshtein distance 계산
|
||||||
|
|
||||||
|
K-CER는 전체 reference 문자 수를 분모로 substitution + deletion + insertion을 합산한 corpus-level micro average다. WER는 일반 비교를 위한 보조 지표로 원래 whitespace token을 사용한다. 클립별 지표는 별도로 보존하되 모델 선택에는 corpus aggregate K-CER를 사용한다.
|
||||||
|
|
||||||
|
entity 보존은 manifest의 occurrence annotation 수를 분모로 한다. K-CER 계산에서 생성한 reference-hypothesis 문자 alignment를 이용해 각 reference entity span을 hypothesis 위치에 투영한다. 투영된 구간의 앞뒤 8 Unicode code point window 안에 canonical 표기가 정확히 존재할 때만 성공이다. 하나의 hypothesis occurrence는 한 annotation에만 매칭한다. 따라서 다른 문장에 우연히 생성된 entity가 실제 누락을 상쇄하지 못한다.
|
||||||
|
|
||||||
|
### 벤치마크 실행 규칙
|
||||||
|
|
||||||
|
1. 모델·장치·compute type 조합마다 별도 프로세스를 사용한다.
|
||||||
|
2. 측정 전에 비평가용 warm-up 클립 하나를 1회 처리한다.
|
||||||
|
3. 각 평가 클립을 기본 3회 실행한다.
|
||||||
|
4. 품질 지표는 아래 deterministic decode 설정의 첫 결과를 사용한다.
|
||||||
|
5. 시간과 메모리는 3회 min, median, max를 보고한다.
|
||||||
|
6. `model_load_sec`는 별도 측정하고 RTF에서는 제외한다.
|
||||||
|
7. RTF는 순수 transcription wall time / audio duration으로 계산한다.
|
||||||
|
8. RSS는 `psutil`로 해당 benchmark process의 RSS를 100ms 간격 sampling한다.
|
||||||
|
9. VRAM은 NVML의 해당 PID 사용량을 100ms 간격 sampling한다.
|
||||||
|
10. 환경 정보에는 OS, Python, ffmpeg, faster-whisper, CTranslate2, CUDA, cuDNN, GPU driver 버전을 기록한다.
|
||||||
|
|
||||||
|
고정 decode 설정:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
beam_size: 5
|
||||||
|
temperature: 0.0
|
||||||
|
condition_on_previous_text: true
|
||||||
|
vad_filter: true
|
||||||
|
vad_parameters:
|
||||||
|
min_silence_duration_ms: 500
|
||||||
|
speech_pad_ms: 200
|
||||||
|
word_timestamps: false
|
||||||
|
without_timestamps: false
|
||||||
|
```
|
||||||
|
|
||||||
|
`language`, `model`, `device`, `compute_type`, `hotword_artifact`는 실험 변수로 report `run_config`에 기록한다. 그 밖의 faster-whisper decode 기본값도 실제 해석값을 모두 report에 직렬화한다.
|
||||||
|
|
||||||
|
benchmark report 필수 top-level 필드:
|
||||||
|
|
||||||
|
- `report_version`, `dataset_version`, `generated_at`
|
||||||
|
- `environment`
|
||||||
|
- `run_config`
|
||||||
|
- `models[]`
|
||||||
|
- 모델·hotword variant별 `wer`, `k_cer`, `entity_retention`, `failure_rate`
|
||||||
|
- 모델·hotword variant별 `rtf_min`, `rtf_median`, `rtf_max`
|
||||||
|
- 모델·hotword variant별 `peak_rss_min_mb`, `peak_rss_median_mb`, `peak_rss_max_mb`
|
||||||
|
- 모델·hotword variant별 `peak_vram_min_mb`, `peak_vram_median_mb`, `peak_vram_max_mb`
|
||||||
|
- `decision.status`, `decision.default_model`, `decision.reasons[]`
|
||||||
|
|
||||||
|
### 모델 선택 게이트
|
||||||
|
|
||||||
|
`large-v3`를 정확도 기준선으로 사용한다. 먼저 각 모델이 절대 품질 기준을 통과하는지 검사한다.
|
||||||
|
|
||||||
|
절대 품질 기준:
|
||||||
|
|
||||||
|
1. entity 보존율 `>= 95%`
|
||||||
|
2. K-CER `<= 15%`
|
||||||
|
3. 실패율 `0%`
|
||||||
|
|
||||||
|
어느 모델도 절대 기준을 통과하지 못하면 `decision.status="no_acceptable_model"`로 끝낸다. 이 결과도 유효한 v0.1 연구 결론이지만, 제품 기본 모델 승인과 v0.2 진입은 차단한다.
|
||||||
|
|
||||||
|
각 모델은 먼저 자신의 최종 배포 variant를 정한다. `none`이 절대 기준을 통과하면 `none`을 선택한다. `none`은 실패하고 `domain`이 통과하면 version/hash가 고정된 `domain`을 선택한다. 둘 다 실패하면 해당 모델은 실패다. 이후 모델 비교에는 각 모델의 최종 배포 variant 지표만 사용한다. 따라서 평가 구성과 실제 기본 실행 구성이 달라지지 않는다.
|
||||||
|
|
||||||
|
완전한 모델 결정표:
|
||||||
|
|
||||||
|
| turbo | large-v3 | 결정 |
|
||||||
|
|---|---|---|
|
||||||
|
| 실패 | 실패 | `no_acceptable_model` |
|
||||||
|
| 통과 | 실패 | turbo 기본 |
|
||||||
|
| 실패 | 통과 | large-v3 기본 |
|
||||||
|
| 통과 | 통과 | 아래 상대 비교 적용 |
|
||||||
|
|
||||||
|
두 모델이 모두 절대 기준을 통과하면, `large-v3-turbo`가 아래 조건을 모두 만족할 때 turbo를 기본 모델로 선택한다.
|
||||||
|
|
||||||
|
1. entity 보존율 `>= 95%`
|
||||||
|
2. turbo의 K-CER가 large-v3보다 상대적으로 15% 넘게 나쁘지 않음
|
||||||
|
3. 실패율 `0%`
|
||||||
|
4. 대상 GPU에서 turbo의 median RTF가 large-v3보다 최소 10% 작음
|
||||||
|
|
||||||
|
하나라도 실패하면 `large-v3`를 기본 모델로 선택한다. 두 모델은 benchmark와 명시적 CLI override를 위해 계속 지원한다. 실시간 기본 모델은 WebSocket 프로토타입 단계에서 별도로 판단한다.
|
||||||
|
|
||||||
|
hotwords 적용 전·후 결과는 독립 run으로 기록한다. 결정 artifact에는 선택된 hotword set도 함께 저장한다.
|
||||||
|
|
||||||
|
### 모델 결정 artifact
|
||||||
|
|
||||||
|
`bench`는 report와 별도로 `benchmarks/decisions/default-model-v1.json`을 atomic write한다.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"decision_version": "1.0",
|
||||||
|
"status": "approved",
|
||||||
|
"default_model": "large-v3-turbo",
|
||||||
|
"default_hotword_set": "none",
|
||||||
|
"hotword_artifact": null,
|
||||||
|
"dataset_version": "1.0",
|
||||||
|
"report_sha256": "...",
|
||||||
|
"model_variants": {
|
||||||
|
"large-v3-turbo": {"hotword_set": "none", "hotword_artifact": null},
|
||||||
|
"large-v3": {"hotword_set": "domain", "hotword_artifact": "benchmarks/hotwords/domain-v1.json", "hotword_sha256": "..."}
|
||||||
|
},
|
||||||
|
"decided_at": "2026-08-10T00:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`status`는 `approved` 또는 `no_acceptable_model`이다. artifact의 schema, dataset version, report hash 검증이 실패하면 무시하지 않고 `invalid_model_decision`으로 실패한다.
|
||||||
|
|
||||||
|
`transcribe`의 모델 선택 순서:
|
||||||
|
|
||||||
|
1. 명시적 `--model`이 있으면 해당 모델 사용
|
||||||
|
2. 유효한 `approved` artifact가 있으면 default model/hotword set 사용
|
||||||
|
3. artifact가 없으면 연구 bootstrap으로 turbo를 사용하고 `model_decision_missing` warning 기록
|
||||||
|
4. `no_acceptable_model`이면 명시적 `--model` 없는 실행은 `model_decision_blocked`로 실패
|
||||||
|
|
||||||
|
hotword 선택은 모델 선택과 독립적으로 다음 우선순위를 따른다.
|
||||||
|
|
||||||
|
1. 하나 이상의 명시적 `--hotword`가 있으면 그 목록이 전부를 override
|
||||||
|
2. 그렇지 않고 decision artifact에 선택 모델의 `model_variants`가 있으면 해당 hotword artifact 사용
|
||||||
|
3. artifact가 없거나 해당 모델 기록이 없으면 hotwords 비활성화
|
||||||
|
|
||||||
|
명시적 `--model`로 기본 모델을 바꿔도 유효한 decision artifact 안에 해당 모델 variant가 있으면 그 모델의 검증된 hotword 설정을 사용한다.
|
||||||
|
|
||||||
|
## 11. 오류 및 안전 계약
|
||||||
|
|
||||||
|
| 코드 | 상황 | 동작 |
|
||||||
|
|---|---|---|
|
||||||
|
| `invalid_input` | 파일 없음, 지원하지 않는 형식 | 모델을 로드하기 전에 실패 |
|
||||||
|
| `audio_probe_failed` | ffprobe가 입력을 해석하지 못함 | 원인과 stderr 요약 반환 |
|
||||||
|
| `unsupported_input_envelope` | 60분 또는 1GB 지원 범위 초과 | 처리 시작 전에 실패 |
|
||||||
|
| `model_download_failed` | 모델 다운로드 실패 | 재시도 가능 여부 표시 |
|
||||||
|
| `model_unavailable_offline` | 네트워크와 캐시 모델 모두 없음 | 필요한 모델과 캐시 위치 표시 |
|
||||||
|
| `model_load_failed` | 모델 또는 CT2 초기화 실패 | 장치·정밀도·런타임 정보 포함 |
|
||||||
|
| `device_unavailable` | 요청한 CUDA 장치 없음 | 자동 CPU 변경 없이 실패 |
|
||||||
|
| `out_of_memory` | 모델 적재 또는 추론 OOM | 실패 시점과 실제 설정 기록 |
|
||||||
|
| `transcription_failed` | 추론 중 예외 | 원본 예외를 감싼 안정적 오류 코드 반환 |
|
||||||
|
|
||||||
|
자동 모드에서만 명시적인 fallback을 허용한다. fallback이 발생하면 `warnings`와 로그에 반드시 남긴다. 사용자가 `--device cuda:0`처럼 강제한 값은 자동으로 바꾸지 않는다.
|
||||||
|
|
||||||
|
fallback 및 retry 규칙:
|
||||||
|
|
||||||
|
| 실패 시점 | `auto` | 명시 override |
|
||||||
|
|---|---|---|
|
||||||
|
| GPU preflight 부적합 | CPU int8로 1회 전환 | 즉시 실패 |
|
||||||
|
| 모델 load OOM | float16 -> int8_float16 -> CPU int8, 최대 2회 전환 | 즉시 실패 |
|
||||||
|
| 모델 download 실패 | 네트워크 오류일 때만 1회 재시도 | 동일 |
|
||||||
|
| 추론 중 OOM | 전체 파일 자동 재실행 없이 실패 | 즉시 실패 |
|
||||||
|
| ffmpeg/probe 실패 | 재시도 없이 실패 | 동일 |
|
||||||
|
|
||||||
|
추론 중 OOM에서 자동 CPU 재실행을 하지 않는 이유는 긴 파일을 사용자 모르게 처음부터 다시 처리하지 않기 위해서다.
|
||||||
|
|
||||||
|
임시 디렉터리는 성공·실패·중단 모든 경로에서 정리한다. 원본 파일은 읽기 전용으로 취급하며 삭제하거나 수정하지 않는다.
|
||||||
|
|
||||||
|
정리 보장은 정상 종료와 catch 가능한 signal(`SIGINT`, `SIGTERM`)까지다. `SIGKILL`, 전원 장애, 호스트 crash에서는 즉시 삭제를 보장할 수 없다. 각 run은 고유 prefix가 있는 temp directory를 사용하고, 시작 시 TTL이 지난 Luke Scribe orphan directory를 정리한다.
|
||||||
|
|
||||||
|
CLI 실패 출력 규칙:
|
||||||
|
|
||||||
|
- `--output`이 지정되었고 입력 검증 이후 실패하면 해당 경로에 실패 envelope를 atomic write한다.
|
||||||
|
- stdout 모드에서는 실패 envelope를 stdout에 한 줄 JSON으로 출력한다.
|
||||||
|
- stderr에는 사람이 읽는 한 줄 요약과 로그만 출력한다.
|
||||||
|
- 출력 파일 자체를 쓸 수 없으면 JSON 생성을 시도하지 않고 exit code `5`로 종료한다.
|
||||||
|
- 부분 JSON 파일은 남기지 않는다. 임시 파일에 쓴 뒤 rename한다.
|
||||||
|
|
||||||
|
`TranscriptionService`는 실패 결과를 반환하지 않고 `LukeScribeError`의 typed subclass를 발생시킨다. CLI 어댑터가 이를 exit code와 실패 envelope로 변환한다.
|
||||||
|
|
||||||
|
| 오류 범주 | 실패 JSON | stderr | exit code |
|
||||||
|
|---|---:|---:|---:|
|
||||||
|
| CLI 문법·알 수 없는 옵션 | 아니요 | usage + 오류 | 2 |
|
||||||
|
| `invalid_input`, probe, 지원 범위 | 예 | 한 줄 요약 | 2 |
|
||||||
|
| 모델·장치·decision 오류 | 예 | 한 줄 요약 | 3 |
|
||||||
|
| 추론·OOM 오류 | 예 | 한 줄 요약 | 4 |
|
||||||
|
| 결과 파일 write 오류 | 아니요 | 한 줄 요약 | 5 |
|
||||||
|
|
||||||
|
## 12. 테스트 전략
|
||||||
|
|
||||||
|
### 단위 테스트
|
||||||
|
|
||||||
|
- 장치 탐지 결과에서 compute type 결정
|
||||||
|
- 사용자 override 검증
|
||||||
|
- ffprobe 결과 파싱
|
||||||
|
- ffmpeg 명령 구성
|
||||||
|
- 임시파일 정리
|
||||||
|
- 결과 스키마 직렬화와 역직렬화
|
||||||
|
- WER/K-CER/entity 보존율 계산
|
||||||
|
- 모델 선택 게이트의 경계값
|
||||||
|
- transcript JSON Schema validation
|
||||||
|
- manifest와 report schema validation
|
||||||
|
- orphan temp directory TTL 판정
|
||||||
|
|
||||||
|
### 통합 테스트
|
||||||
|
|
||||||
|
- 짧은 WAV 파일을 CPU로 전사
|
||||||
|
- MP3를 정규화한 뒤 전사
|
||||||
|
- 잘못된 파일과 손상 파일 거부
|
||||||
|
- 존재하지 않는 CUDA 장치 강제 시 명확한 실패
|
||||||
|
- 전사 성공·실패 후 임시파일 부재 확인
|
||||||
|
- 같은 fixture에 대해 두 모델의 benchmark report 생성
|
||||||
|
- 출력 경로가 이미 존재할 때 `--force` 계약 확인
|
||||||
|
- 실패 envelope의 stdout/file 동작 확인
|
||||||
|
|
||||||
|
### 실제 하드웨어 스모크 테스트
|
||||||
|
|
||||||
|
- x86_64/RAM 16GB CPU 환경 1종
|
||||||
|
- Colab T4 16GB 또는 구현 전에 지정한 동급 이상 NVIDIA GPU 1종
|
||||||
|
- 각 환경에서 `detect`, `transcribe`, `bench` 실행
|
||||||
|
- 모델, compute type, RTF, peak memory를 결과물로 보관
|
||||||
|
|
||||||
|
GPU가 없는 CI에서는 모델과 ffmpeg 호출을 대체한 계약 테스트까지만 수행한다. CPU의 대형 모델 qualification에는 별도 timeout과 최대 RSS를 기록하며, 너무 느린 실행은 실패가 아니라 `unsupported_for_production` 경고로 분류할 수 있다. 실제 GPU 모델 스모크 테스트는 Colab T4 job 또는 수동 검증으로 수행한다.
|
||||||
|
|
||||||
|
## 13. 완료 기준
|
||||||
|
|
||||||
|
v0.1은 다음 항목을 모두 만족해야 완료다.
|
||||||
|
|
||||||
|
- [ ] `detect`가 CPU/GPU 및 권장 실행값을 JSON으로 출력한다.
|
||||||
|
- [ ] `transcribe`가 WAV와 MP3를 처리해 schema v1 JSON을 생성한다.
|
||||||
|
- [ ] CPU와 실제 NVIDIA GPU 각각 한 환경에서 전사에 성공한다.
|
||||||
|
- [ ] benchmark dataset이 30개·60분·entity 100회 기준을 충족한다.
|
||||||
|
- [ ] turbo와 large-v3 비교 보고서가 생성된다.
|
||||||
|
- [ ] WER, K-CER, entity 보존율, failure rate, RTF, VRAM/RSS가 보고서에 포함된다.
|
||||||
|
- [ ] 정규화·warm-up·3회 반복·min/median/max 규칙으로 같은 데이터의 결과를 재현할 수 있다.
|
||||||
|
- [ ] 모델 선택 게이트가 `approved` 기본 모델 또는 `no_acceptable_model` 결정을 생성한다.
|
||||||
|
- [ ] 어느 모델도 절대 품질 기준을 통과하지 못하면 v0.2 진입이 차단된다.
|
||||||
|
- [ ] 성공·실패 경로에서 임시 파생 오디오가 남지 않는다.
|
||||||
|
- [ ] catch 가능한 중단과 시작 시 orphan cleanup이 검증된다.
|
||||||
|
- [ ] 단위·통합 테스트가 통과한다.
|
||||||
|
- [ ] transcript result, benchmark manifest, benchmark report의 machine-verifiable schema가 존재한다.
|
||||||
|
- [ ] README에 설치, 모델 다운로드, 3개 CLI 사용법, 알려진 제한을 문서화한다.
|
||||||
|
|
||||||
|
완료의 핵심 산출물은 코드 양이 아니라 다음 세 가지다.
|
||||||
|
|
||||||
|
1. 재현 가능한 전사 명령
|
||||||
|
2. 안정적인 결과 스키마
|
||||||
|
3. 모델 선택을 뒷받침하는 벤치마크 보고서
|
||||||
|
|
||||||
|
## 14. 확장 로드맵
|
||||||
|
|
||||||
|
### v0.2: Batch API
|
||||||
|
|
||||||
|
- FastAPI
|
||||||
|
- `POST /v1/transcriptions`
|
||||||
|
- `GET /v1/transcriptions/{id}`
|
||||||
|
- API Key 인증
|
||||||
|
- in-process Job 실행
|
||||||
|
- 결과 보관과 원본/파생 오디오 삭제
|
||||||
|
- Docker GPU/CPU 이미지
|
||||||
|
|
||||||
|
전사 API는 v0.1의 `TranscriptionService`를 호출하고 `TranscriptResult`를 그대로 응답 모델로 변환한다.
|
||||||
|
|
||||||
|
### v0.3: Durable Queue
|
||||||
|
|
||||||
|
- Redis + RQ no-fork worker
|
||||||
|
- `queued -> processing -> completed|failed|cancelled` 상태
|
||||||
|
- 진행률과 queue position
|
||||||
|
- 협조적 취소
|
||||||
|
- 재시작 내성
|
||||||
|
- 공유 입력/결과 스토어
|
||||||
|
|
||||||
|
Job은 `TranscriptResult`를 감싸지만 결과 자체의 스키마는 변경하지 않는다.
|
||||||
|
|
||||||
|
### v0.4: Realtime
|
||||||
|
|
||||||
|
- WebSocket init frame과 API Key 인증
|
||||||
|
- PCM16/Opus 입력 협상
|
||||||
|
- VAD와 rolling buffer
|
||||||
|
- partial/final 결과
|
||||||
|
- LocalAgreement 기반 확정 규칙
|
||||||
|
- 세션 수 제한과 backpressure
|
||||||
|
- 30분 메모리 평탄성 테스트
|
||||||
|
|
||||||
|
실시간의 `final` 세그먼트는 v0.1의 세그먼트 모델을 재사용한다. `partial`은 별도의 변경 가능한 이벤트 타입으로 둔다.
|
||||||
|
|
||||||
|
### v0.5: Output and Post-processing
|
||||||
|
|
||||||
|
- word timestamps
|
||||||
|
- SRT/VTT
|
||||||
|
- glossary와 deterministic rules
|
||||||
|
- confidence flag
|
||||||
|
- 선택적 diarization
|
||||||
|
- 선택적 LLM correction
|
||||||
|
|
||||||
|
### v1.0: Internal Production
|
||||||
|
|
||||||
|
- 운영 대상 하드웨어 프로파일 확정
|
||||||
|
- 관측 지표와 경보
|
||||||
|
- 4시간/2GB 처리 보장
|
||||||
|
- 보관 및 삭제 정책 검증
|
||||||
|
- 부하·장기 실행·복구 테스트
|
||||||
|
- Colab 또는 외부 터널은 실제 필요가 확인된 경우에만 포함
|
||||||
|
|
||||||
|
## 15. 고려한 접근
|
||||||
|
|
||||||
|
### 접근 A: 전사 코어와 벤치마크 우선 — 채택
|
||||||
|
|
||||||
|
- 가장 큰 기술 불확실성을 먼저 제거한다.
|
||||||
|
- 모델 선택을 추측이 아닌 실제 데이터로 결정한다.
|
||||||
|
- 결과·엔진·장치 경계를 유지해 이후 API 확장이 가능하다.
|
||||||
|
- 첫 버전에서 API와 실시간 데모가 없다는 단점이 있다.
|
||||||
|
|
||||||
|
### 접근 B: 파일 API와 준실시간을 함께 구현
|
||||||
|
|
||||||
|
- 최종 제품과 가까운 데모를 빠르게 볼 수 있다.
|
||||||
|
- 모델 품질, VAD, 버퍼, WebSocket 문제가 동시에 얽힌다.
|
||||||
|
- 실패 원인을 분리하기 어렵고 전사 코어 계약이 흔들릴 수 있다.
|
||||||
|
|
||||||
|
### 접근 C: 기존 P1~P5 플랫폼 전체 구현
|
||||||
|
|
||||||
|
- 처음부터 운영 기능을 모두 고려한다.
|
||||||
|
- 사용자와 모델 품질이 검증되기 전에 큐·스토리지·동시성 복잡도가 생긴다.
|
||||||
|
- 구현량이 아니라 검증 순서가 잘못될 가능성이 높다.
|
||||||
|
|
||||||
|
## 16. 열린 질문
|
||||||
|
|
||||||
|
다음 질문은 문서 작성만으로 정하지 않고 v0.1의 데이터 또는 실제 사용 조건으로 닫는다.
|
||||||
|
|
||||||
|
1. 벤치마크 음성은 어떤 실제 업무 환경에서 수집할 것인가?
|
||||||
|
2. Colab T4 외에 첫 번째 추가 GPU 관측 대상은 GTX 1050, L4, 또는 별도 서버 중 무엇인가?
|
||||||
|
3. entity 목록의 초기 도메인은 AI/개발 용어만 포함할 것인가, 다른 전문 분야도 포함할 것인가?
|
||||||
|
4. K-CER 15%라는 절대 기준이 10개 pilot 결과에서도 현실적이고 충분히 엄격한가?
|
||||||
|
5. v0.2 Batch API에서 처음부터 비동기 Job을 제공할지, 동기 호출로 시작할지?
|
||||||
|
|
||||||
|
## 17. 다음 작업
|
||||||
|
|
||||||
|
1. 10개 대표 클립으로 소규모 spike dataset을 먼저 만든다.
|
||||||
|
2. `large-v3-turbo`와 `large-v3`를 동일 조건으로 수동 실행한다.
|
||||||
|
3. 결과 스키마와 지표 계산이 유효한지 확인한다.
|
||||||
|
4. 문제가 없으면 30개·60분 기준 데이터셋으로 확장한다.
|
||||||
|
5. 벤치마크 결과로 기본 모델을 결정한 뒤에만 v0.2 API 설계를 시작한다.
|
||||||
|
|
||||||
|
## 18. 이번 결정에서 확인된 사용자 의도
|
||||||
|
|
||||||
|
- “일단 A 먼저 가도 되긴한데”라는 선택으로 모델 품질을 먼저 검증하는 순서에 동의했다.
|
||||||
|
- “어차피 확장해야 하는 거면”이라는 조건 때문에 v0.1도 확장 경계를 가진 실제 코어로 설계했다.
|
||||||
|
- 따라서 이번 범위는 기능을 많이 넣는 대신, 나중에 API와 실시간 기능이 의존할 계약을 정확히 만드는 데 집중한다.
|
||||||
Reference in New Issue
Block a user