## 로컬 STT?

로컬 STT는 외부 서비스 대신 저의 컴퓨터에서 돌리는 음성 인식입니다. 통화 녹음을 글로 옮기는 :term[전사]{key="transcription"}를 CLOVA Speech 대신 Mac Studio의 로컬 STT로 해보려고, 2026년 10월 26일과 27일 이틀 동안 시험했습니다.

결론부터 말하면, 할 일과 일정은 로컬로 바꿔도 처음부터 거의 그대로 뽑혔고, 약속은 :term[화자 분리]{key="speaker-diarization"}를 바로잡은 뒤에 CLOVA Speech에 가까워졌습니다.

그러나 사람 이름은 엔진을 바꿔도 CLOVA Speech만큼 따라오지 못했습니다. 엔진을 Qwen3-ASR로 정하고 이름을 잇는 규칙과 비교 기준까지 고친 뒤, 제가 직접 들어 확인한 기준에서는 차이가 0.8%p로 줄었습니다. 다만 통화 25건으로는 10%p 안팎의 차이도 가를 수 없고, 다른 기준과 긴 통화에서는 아직 낮았습니다.

이 글은 통화 전사를 로컬로 옮기며 무엇이 그대로였고 무엇을 더 고쳐야 했는지 공유하려고 썼습니다. 다 읽으면 한국어 통화에 어떤 로컬 엔진이 나았는지, 로컬 STT와의 차이를 무엇에 대고 재야 하는지, 같은 이름 힌트가 엔진에 따라 어떻게 다르게 작용하는지 알게 됩니다.

CLOVA Speech를 바꾸려는 이유는 세 가지입니다.

- **비용**: 음성 인식과 화자 분리를 합쳐 1분에 28원이고, 통화가 늘면 같이 늘어납니다.
- **외부 전송**: 통화 녹음을 외부 서비스로 보내는 단계를 줄이고 싶었습니다. 전사록은 로컬로 바꿔도 지금처럼 Codex가 읽습니다.
- **외부 의존**: 통화 처리가 외부 서비스의 사정에 기대는 부분을 줄이고 싶었습니다.

## 지금의 통화 처리

이로하는 제가 Hermes Agent로 만든 개인 비서입니다. 자세한 구조는 [Hermes Agent로 나만의 개인 비서 만들기](/261021-hermes-agent-personal-assistant)에 썼고, 여기서는 통화를 처리하는 부분만 요약했습니다.

1. 휴대폰이 통화를 녹음하면, 제가 만든 업로드 앱이 녹음 파일을 Google Drive에 올립니다.
2. 제가 만든 프로그램 call-ingest가 녹음을 내려받아 [CLOVA Speech](https://api.ncloud-docs.com/docs/ai-application-service-clovaspeech)로 전사합니다. 누가 언제 말했는지 나누는 화자 분리도 CLOVA Speech가 해줍니다.
3. 이로하가 전사록을 읽고 두 갈래로 정리합니다.

- **바로 처리하는 갈래**: 통화를 요약하고 사실·약속·일정을 뽑아, 할 일과 일정을 등록한 뒤 Slack으로 알립니다.
- **맥락으로 쌓는 갈래**: 통화에 나온 사람·회사·프로젝트를 맥락 기록의 기존 항목에 이어두고, 약속과 사실을 쌓습니다.

맥락 기록에는 사람·회사마다 그동안 전사록에 적힌 여러 철자가 별칭으로 쌓여 있습니다. 이로하가 이름을 잇기 쉽도록, 맥락 기록을 관리하는 프로그램이 전사록에 별칭이 철자 그대로 나오는 기존 사람·회사를 15개까지 골라 후보로 같이 넘깁니다.

시험에서 바꾼 것은 2단계의 전사입니다. 로컬에서는 음성 인식 모델이 말을 받아 적고, 화자 분리는 [pyannote](https://huggingface.co/pyannote/speaker-diarization-community-1)가 따로 맡습니다. 받아 적은 단어마다 시각이 있어야 화자 분리 결과와 맞춰 누가 무슨 말을 했는지가 나옵니다.

![통화 녹음이 업로드 앱과 call-ingest, CLOVA Speech를 거쳐 이로하에게 가고, 바로 처리하는 갈래와 맥락으로 쌓는 갈래로 나뉘는 흐름도](https://nabichan.me/content-assets/261028/call-flow.4449f736.svg "굵은 테두리는 이로하, 회색 상자는 제가 만든 프로그램입니다. 점선 안의 CLOVA Speech 자리를 로컬 STT로 바꿔 시험했습니다")

## 비교 방법

### 성공 기준과 기준선

전사록은 이로하가 읽는 중간 결과라서, 성공 기준은 이로하가 뽑은 결과가 CLOVA Speech 때와 비슷한지로 정했습니다.

- **바로 처리하는 갈래**: 등록할 할 일·일정의 수와 내용. 실제로 등록하지 않고 "등록했을 항목"만 답하게 했고, 내용은 AI가 두 결과를 나란히 읽고 비교했습니다.
- **맥락으로 쌓는 갈래**: 이어둔 사람·회사·프로젝트 이름과 약속의 수. 약속은 통화에서 누군가 하겠다고 한 일이고, 그중 제가 하겠다고 한 것은 "저의 약속"으로 따로 셌습니다.

같은 전사록을 줘도 이로하가 쓰는 언어 모델(LLM)의 결과는 매번 조금씩 달라서, 같은 CLOVA Speech 전사록으로 추출을 한 번 더 돌려 기준선으로 삼았습니다. 아래 표의 "CLOVA 재실행"이 이 기준선입니다. 한 번 더 돌린 값이라 흔들림의 폭을 대략 보여줄 뿐입니다.

### 이름 연결의 정답

사람·회사 이름은 정답을 따로 만들어 채점했습니다.

1. CLOVA Speech 전사록으로 추출을 세 번 돌리고, 두 실행이 함께 뽑은 사람·회사를 정답으로 삼았습니다. 이 글에서는 이 정답을 "CLOVA Speech 합의"라고 불렀습니다.
2. 보완을 모두 마친 로컬 설정(이 글에서는 "마지막 로컬 설정")과 CLOVA Speech 쪽이 엇갈린 이름은, 이름이 나온 대목을 몇 초씩 잘라 세 계열의 엔진(Qwen3-ASR, Whisper, Cohere Transcribe)으로 힌트 없이 다시 받아 적었습니다. 둘 이상이 같은 철자로 적으면 실제로 말한 것으로 봤습니다(투표).
3. 투표로 가르지 못한 이름은 제가 직접 듣고 정했습니다.

직접 들을 때는 이름이 나온 6초와 앞뒤 16초, 두 쪽이 받아 적은 글, 투표 결과를 한 화면에 두었습니다. 듣는 사람은 저 한 명이었고, 화면에는 어느 쪽 글인지도 함께 표시돼 있었습니다.

CLOVA Speech 합의로 잴 때는 점수를 매기는 실행이 정답 만들기에 끼지 않도록, CLOVA Speech의 각 실행을 나머지 두 실행이 함께 뽑은 이름으로 채점했습니다. 로컬은 CLOVA Speech의 세 실행에서 두 개씩 고르는 세 쌍으로 정답을 각각 만들어 채점하고 평균했습니다.

고친 정답은 CLOVA Speech 세 실행 중 둘 이상이 뽑은 이름에서 시작해, 엇갈린 이름만 투표와 직접 듣기로 고친 것입니다. CLOVA Speech 실행은 자기가 낀 정답으로 채점됐고, 마지막 로컬 설정이 아닌 다른 설정을 이 정답으로 재면 불리합니다.

- **재현율**: 정답에 든 사람·회사 중 그 실행이 뽑은 비율
- **정답 밖 추가**: 정답을 만든 CLOVA Speech 실행 어느 쪽에도 없는 이름(프로젝트 포함)을 25건에서 더 뽑은 수

CLOVA Speech 합의는 맥락 기록에 이미 있던 사람·회사만 세고, 투표와 직접 듣기로 고친 정답에는 새로 나온 사람까지 넣었습니다. 프로젝트는 실행마다 이름을 다르게 지어서 재현율에서 뺐습니다.

이 기준은 시험하면서 세 번 바꿨습니다. 처음에는 CLOVA Speech 추출 한 번을 정답으로 썼고, 합의와 투표, 직접 듣기를 차례로 더했습니다.

기준을 바꾼 것은 모두 로컬이 못 미친 결과를 본 뒤였고, 바꾼 이유는 CLOVA Speech 추출도 실행마다 흔들리고 틀린 이름을 정답에 넣을 수 있다는 것이었습니다. 뒤의 "보완 뒤 결과"에는 합의 정답부터 직접 듣고 고친 정답까지 네 가지 값을 나란히 적었습니다.

### 전사 지표

전사록 자체는 세 가지로 비교했습니다.

- **다른 글자**: 공백과 문장부호를 빼고 CLOVA Speech 전사록과 다른 글자 수(편집 거리)를 CLOVA Speech의 글자 수로 나눈 값. 숫자와 영문의 표기 차이도 그대로 셌습니다.
- **반복**: 같은 말을 되풀이한 구간의 수
- **이름 적중**: CLOVA Speech 전사록으로 이로하가 이은 사람·회사 이름이 로컬 전사록에 철자 그대로 나온 수

통화마다 센 이름은 25건을 합쳐 77개이고, 맞힌 이름만 셌고, 없는 이름을 잘못 끼워 넣은 것은 세지 않았습니다. CLOVA Speech 전사록도 이 77개 중 33개만 철자 그대로 담고 있었습니다. 나머지는 이로하가 문맥과 후보를 보고 이은 것입니다.

### 표본과 환경

표본은 15분 이상 통화 10건(총 221분)과 15분 미만 통화 15건입니다. 2025년 7월 11일부터 2026년 10월 26일까지 쌓인 통화 5,387건 중 96%가 15분 미만이고 36%가 1분 미만입니다. 긴 통화는 무작위로 골랐고(평균 22분, 가장 긴 것 42.7분), 짧은 통화는 길이 구간 다섯 개에서 3건씩 따로 뽑았습니다.

25건은 작은 표본입니다. 뒤에 나오는 기준값과 규칙도 이 25건을 보고 골라 같은 25건으로 쟀고, 아래 숫자는 모두 이 25건에서 잰 값입니다.

시험은 운영 기록을 읽기만 하고 운영과 따로 돌렸습니다. 맥락 기록은 복사본에서 시험한 통화가 남긴 흔적을 지우고 썼습니다.

- **기기**: Mac Studio(Apple M1 Max, 메모리 64GB), macOS 27.0.1
- **음성 인식 서버**: [oMLX](https://github.com/jundot/omlx) 0.7.2(내장 [mlx-audio](https://github.com/Blaizzy/mlx-audio) 0.4.3)
- **화자 분리**: pyannote.audio 4.0.7, speaker-diarization-community-1
- **추출**: Hermes Agent v0.22.2, 운영과 같은 Codex

## 모델별 결과

### Whisper large-v3와 turbo

먼저 oMLX에서 OpenAI의 Whisper large-v3와 large-v3-turbo로 긴 통화 10건을 전사했습니다. turbo는 large-v3에서 글을 만들어 내는 부분(디코더)을 32층에서 4층으로 줄인 모델입니다. 이 표는 정답을 고치기 전, CLOVA Speech 추출 한 번과 비교한 값입니다.

|  | large-v3 | turbo | CLOVA 재실행 |
|---|---|---|---|
| 전사 시간(오디오 221분) | 26.1분 | 11.7분 | - |
| 이름 겹침 비율 | 0.59 | 0.66 | 0.93 |
| 약속(CLOVA Speech 44) | 31 | 37 | 43 |
| 등록할 항목(CLOVA Speech 45) | 45 | 43 | 50 |

괄호 안은 CLOVA Speech 전사록으로 시험 환경에서 뽑은 값이고, CLOVA 재실행 열과의 차이가 흔들림의 크기입니다. 이름 겹침 비율은 그 CLOVA Speech 결과와 각 열의 결과에 나온 사람·회사·프로젝트 이름 중 양쪽에 다 있는 이름의 비율이고, 1이면 같습니다. 전사 시간은 다른 시험 작업과 겹치지 않게 차례로 잰 값입니다.

바로 처리하는 갈래는 **거의 대등**했습니다. 이로하가 전사록 전문을 읽고 할 일을 뽑아서, 이름이 조금 틀려도 할 일과 일정은 살아남았습니다. turbo는 large-v3보다 두 배 넘게 빨랐고 대부분의 지표에서 CLOVA Speech에 더 가까워서, 이후 시험은 turbo로 했습니다.

문제는 **맥락으로 쌓는 갈래**였습니다. 화자가 너무 잘게 나뉘어 약속이 빠졌고, turbo가 사람 이름을 받침 하나 다르게 받아 적어 이로하가 없던 사람을 새로 만들거나 철자가 비슷한 다른 사람에게 잘못 이었습니다. 화자 문제는 뒤에 소개할 작은 화자 흡수로 바로잡았고, 이름 문제는 엔진을 바꿔보았습니다.

### 엔진별 전사

[STT 모델 실제 성능 비교: 한국어 회의 녹음 35분, 7개 모델 테스트](https://blog.dongjun.win/posts/korean-stt-7-model-comparison/)는 같은 turbo 모델이라도 WhisperKit이 faster-whisper보다 용어를 더 잘 알아들었다고 보고합니다. Qwen3-ASR과 Cohere Transcribe는 [2026 한국어 유튜브 STT(ASR) 모델 및 런타임 분석](https://wikidocs.net/343561)에서 알게 됐습니다.

- **WhisperKit**: Argmax가 만든 Apple 기기용 Whisper 실행기입니다. 같은 turbo 모델을 Apple 칩의 AI 전용 장치인 Neural Engine에서 돌려서, GPU를 쓰는 다른 작업과 다투지 않습니다. whisperkit-cli 1.1.1을 썼습니다.
- **Qwen3-ASR 1.7B**: Qwen 팀이 2026년 1월에 공개한 음성 인식 모델입니다. 언어 모델 Qwen3에 음성 인코더를 붙인 구조이고, 한국어를 포함해 30개 언어를 받아 적고, Apache 2.0 라이선스입니다.
- **Cohere Transcribe 03-2026**: Cohere가 2026년 3월에 공개한 2B(매개변수 약 20억 개) 모델입니다. 한국어를 포함해 14개 언어를 지원하고, Apache 2.0 라이선스입니다.

oMLX에서 Qwen3-ASR과 Cohere Transcribe는 단어마다 시각을 돌려주지 않습니다. 통화를 통째로 전사하는 방식과 함께, 화자 분리로 나눈 구간마다 따로 전사해 그 구간의 화자를 붙이는 방식도 시험했습니다.

이 절에서는 화자 없이 전사록만 비교했고, 엔진들을 동시에 돌려서 속도는 비교하지 않았습니다.

표의 이름 힌트는 통화에 나올 법한 이름을 엔진에 미리 알려주는 방법입니다. 통화 상대와, 지난 기록에서 그 상대와 함께 나온 사람, 자주 나온 사람의 이름을 프롬프트로 넣었고, 이 글에서는 이 목록을 "통화 상대 기준 힌트"라고 불렀습니다.

| 전사 | 다른 글자(긴/짧은 통화) | 반복 | 이름 적중 |
|---|---|---|---|
| CLOVA Speech | - | 2 | 33 |
| Whisper turbo(oMLX) | 17.2% / 18.5% | 20 | 21 |
| Whisper turbo(WhisperKit) | 17.4% / 16.2% | 6 | 23 |
| WhisperKit, 이름 힌트 | 33.1% / 30.4% | 11 | 27 |
| Qwen3-ASR, 화자 구간마다 | 18.3% / 18.1% | 1 | 22 |
| Qwen3-ASR, 통째 | 15.9% / 15.6% | 3 | 22 |
| Qwen3-ASR, 통째, 이름 힌트 | 16.6% / 15.7% | 2 | 28 |
| Cohere, 화자 구간마다 | 28.9% / 26.7% | 36 | 24 |

다른 글자 비율은 정확도가 아닙니다. Qwen3-ASR은 "네", "응" 같은 맞장구를 CLOVA Speech보다 더 받아 적어서 이 비율이 오르기도 합니다. 마지막 로컬 설정도 CLOVA Speech에 없는 글을 더 적은 몫(6.6%p)이 빠뜨린 몫(2.2%p)보다 컸습니다.

WhisperKit은 oMLX와 같은 turbo 모델인데 반복이 20개에서 6개로 줄었지만, 이름 적중은 거의 같았습니다. WhisperKit에 힌트를 주면 이름 적중은 27개로 올랐지만 말을 빠뜨려 다른 글자 비율이 두 배 가까이 됐습니다.

Qwen3-ASR을 통째로 돌리며 이름 힌트를 주면 반복이 늘지 않고 이름 적중이 28개로 올랐습니다. 힌트 목록이 전사록에 그대로 적힌 일은 25건 중 1건에서 한 번 있었고, 이름 적중은 그 부분을 빼고 셌습니다.

Cohere Transcribe는 반복이 36개로 가장 불안정했고, 통째 전사는 뒤에 소개할 메모리 문제로 끝내지 못했습니다.

### 엔진마다 다른 이름 힌트 효과

같은 힌트의 부작용이 엔진에 따라 크게 달랐고, 두 모델은 프롬프트를 받는 자리부터 다릅니다.

oMLX가 쓰는 mlx-audio의 Whisper는 오디오를 30초 창으로 나눠 차례로 받아 적고, 창마다 앞에서 받아 적은 글을 프롬프트로 같이 넣습니다(`condition_on_previous_text`). 이름 힌트(`initial_prompt`)는 그 글의 맨 앞에 한 번 들어가고, 프롬프트에는 끝부분 223토큰(모델이 글을 나누는 단위)까지만 들어갑니다. 앞에서 받아 적은 글이 쌓일수록 힌트가 밀려나므로, 20분짜리 통화에서는 앞부분 몇 창에만 힌트가 닿습니다.

통화를 28초 이하 조각으로 잘라 조각마다 힌트를 붙였지만, 결과는 오히려 나빠졌습니다.

- 짧은 조각에 힌트가 붙자 같은 말을 되풀이하는 루프에 빠졌습니다. 한 통화에서는 같은 영어 음절이 218번 반복됐습니다.
- CLOVA Speech 전사록에 없는 이름이 13번 끼어들었습니다. 힌트 없이는 3번이었습니다.
- 긴 통화 10건에서 고른 이름 329개 중 실제로 통화에 나온 이름은 20개뿐이었습니다.

Whisper는 프롬프트 자리에 앞 구간의 전사문이 들어가도록 학습한 모델입니다([Whisper 논문](https://arxiv.org/abs/2212.04356)). oMLX와 WhisperKit 모두 힌트를 주면 같은 말을 되풀이하거나 말을 빠뜨렸습니다.

Qwen3-ASR은 시스템 프롬프트에 넣은 문맥을 배경 지식으로 쓰도록 학습한 모델입니다([Qwen3-ASR 기술 보고서](https://arxiv.org/abs/2601.21337)). 통째로 전사할 때는 힌트로 이름 적중이 오르고 부작용이 거의 없었습니다.

짧은 구간마다 힌트를 붙였을 때는 이름 77개 중 68개를 맞혀 CLOVA Speech의 두 배가 넘었습니다. 다시 확인해보니 긴 통화의 화자 구간 3,015개 중 340개(11%)에 힌트 이름이 3개 이상 들어 있어, 힌트 목록을 그대로 받아 적은 것이었습니다.

짧은 구간에서는 두 엔진 모두 힌트에 흔들렸고, 통째나 4분 조각처럼 문맥이 긴 전사에서는 Qwen3-ASR만 이득을 봤습니다. Cohere Transcribe에는 힌트를 주지 않았습니다.

## Qwen3-ASR로 정한 이유

전사 결과를 보고 엔진을 Qwen3-ASR로 정했습니다.

- **이름**: 통째로 전사할 때 이름 힌트로 이름 적중이 오르면서 부작용이 적었던 엔진은 Qwen3-ASR이었습니다. Whisper는 같은 힌트로 말을 되풀이하거나 빠뜨렸습니다.
- **안정성**: 반복이 적었고, Cohere Transcribe처럼 크게 흔들리지 않았습니다.

이때까지 추출로 견준 값은 CLOVA Speech 추출 한 번을 정답으로 쓴 것이라, Whisper turbo와의 차이가 재실행의 흔들림 안이었습니다. 엔진 사이의 추출 차이는 뒤의 후보 보강 그림에 다시 적었습니다.

WhisperKit은 Neural Engine에서 돌아 GPU를 다투지 않는다는 장점이 있었지만, 이름 적중이 Whisper turbo와 거의 같았습니다. Qwen3-ASR은 대신 단어 시각이 없어서 정렬 모델과 4분 조각 나누기가 더 붙고, 정렬 모델이 메모리 3.7GB를 더 씁니다.

이름 연결은 엔진과 상관없이 후보 규칙도 보강해야 했습니다. 아래에는 Whisper 시험 때 만든 작은 화자 흡수와, 해보고 버린 시도도 같이 적었습니다.

## 보완 내역

### 작은 화자 흡수

화자 분리 단계의 문제라 Whisper turbo로 시험할 때 찾았고, Qwen3-ASR 설정에도 그대로 썼습니다.

pyannote는 2~3명이 한 통화를 4~6명으로 나눴습니다. 한 사람의 말이 여러 화자로 흩어지자, 이로하는 누가 저인지 가리지 못해 약속을 빼거나 상대의 약속으로 넣었습니다. 약속이 사라진 통화는 모두 화자가 잘게 나뉜 통화였습니다.

처음에는 화자를 최대 4명으로 제한해봤지만, 말한 시간이 4~5%뿐인 조각 화자는 그대로 남았습니다. 녹음은 모두 한 채널(모노)이라 채널로 화자를 가를 수 없었고, 운영에서는 통화마다 화자 수를 미리 알 수 없어 화자 수를 정해주지 않았습니다.

상한 대신 말한 시간이 통화 전체의 10% 미만인 화자를 시간상 가장 가까운 주 화자에게 넘기는 작은 화자 흡수를 썼습니다. Whisper turbo로 잰 긴 통화 10건 중 7건은 CLOVA Speech와 화자 수가 같아졌고, 2건은 CLOVA Speech가 3명으로 나눈 통화에서 말이 적은 셋째 화자까지 흡수돼 2명이 됐습니다. 남은 1건은 흡수한 뒤에도 5명이었습니다.

약속도 42건(그중 저의 약속 27건)으로 CLOVA Speech의 44건(25건)에 가까워졌습니다. 흡수 전에는 37건(17건)이었습니다.

다만 실제로 짧게만 말한 셋째 사람도 주 화자에 합쳐져서, 그 사람의 말은 주 화자의 말이 됩니다. 저의 맞장구나 짧은 대답이 따로 작은 화자로 나뉘면, 시간상 가까운 상대에게 넘어갑니다.

작은 화자 흡수를 더한 turbo로 15분 미만 통화 15건도 비교했습니다. 등록할 항목과 약속은 짧은 통화에서도 CLOVA Speech 수준이었고, Whisper가 무음 구간에 없는 말을 지어내는 문제는 1분 미만 통화에서도 보이지 않았습니다.

### 정렬 모델로 화자 붙이기

단어마다 시각이 없으면 화자를 붙일 수 없어서, 받아 적은 글과 오디오를 맞대어 단어마다 시각을 찾는 :term[강제 정렬]{key="forced-alignment"}을 더했습니다. 정렬에는 같은 팀의 [Qwen3-ForcedAligner](https://huggingface.co/Qwen/Qwen3-ForcedAligner-0.6B)를 썼습니다.

1. 통화를 무음 지점에서 4분 이하 조각으로 나눕니다. 정렬 모델이 한 번에 처리하는 길이보다 짧게 맞춘 것입니다.
2. 조각마다 통화 상대 기준 힌트를 붙여 Qwen3-ASR로 전사하고, 새어 나온 힌트 문구는 지웁니다.
3. 같은 조각을 Qwen3-ForcedAligner로 정렬해 단어마다 시각을 받습니다.
4. 작은 화자 흡수를 거쳐 단어마다 화자를 붙입니다.

4분 조각에서는 힌트 문구가 25건 모두 한 번도 새어 나오지 않았고, 이름 적중은 29개였습니다(힌트 없이 24개, CLOVA Speech 33개). 이 설정에서 등록할 항목은 75건으로 CLOVA Speech(74건)와 재실행(82건) 사이였고, 약속은 74건으로 CLOVA Speech(79건)보다 적었습니다.

### 이름 힌트 고르기

통화 상대 기준 힌트는 통화 내용과 상관없는 이름이 대부분이었습니다. 10월 27일에는 힌트 없이 한 번 전사한 뒤, 맥락 기록의 이름·별칭 중 그 전사록에 나왔거나 철자가 비슷하게 나온 것만 골라 힌트로 넣고 다시 전사했습니다.

힌트 이름은 25건 합계 809개에서 191개로 줄었고, 이름 적중은 32개로 CLOVA Speech(33개)와 거의 같아졌습니다. 그러나 아래 후보 보강 그림과 표에서 보듯 이로하가 이은 이름은 통화 상대 기준 힌트보다 적었고(77%·80% 대 83%·83%), 정답 밖 추가는 많았습니다(17~20개 대 12개). 힌트는 통화 상대 기준으로 정했습니다.

처음에는 힌트에 철자가 비슷한 이름이 같이 들어 있어서 엉뚱한 이름으로 받아 적는다고 봤습니다. 그러나 비슷한 이름을 힌트에서 빼도 나아지지 않았고, 다시 보니 힌트 없는 전사도 같은 오인식을 냈습니다. 대조군을 같이 보지 않아서 원인을 잘못 짚은 것입니다.

다만 새로 나온 사람의 이름이 힌트에 있던 기존 사람의 이름으로 바뀌어 잘못 이어진 사례는 따로 있었습니다.

### 이름을 잇는 후보 보강

Whisper 시험 때 먼저 맥락으로 쌓는 갈래가 넘기는 후보에, 철자가 자모 하나만 다른 기존 이름도 더했습니다(이 글에서는 "비슷한 철자 후보"). 틀린 연결과 가짜 사람은 줄었지만, 맞는 연결은 늘지 않았습니다. 받침과 초성이 함께 바뀌어 자모가 셋 이상 다른 오인식이 많았기 때문입니다.

CLOVA Speech 합의보다 좁게, CLOVA Speech 세 실행이 모두 뽑은 64개를 기준으로 로컬이 놓친 10개를 하나씩 봤습니다.

- **후보에 오르지 못함(6개)**: 영문 약어를 한글 읽기로 받아 적은 것 2개, 소리를 다르게 들은 것 3개, 전사록이 길어 이로하가 한 번에 읽는 8,000자 밖으로 밀린 것 1개
- **후보였는데 뽑지 않음(4개)**: 모두 한 통화에서 나왔습니다. 그 사람을 가리키는 말을 틀리게 받아 적은 통화였습니다.

후보를 고르는 별칭은 CLOVA Speech 전사록에서 쌓였으니, 지금의 후보 규칙은 CLOVA Speech의 표기에 유리합니다. 전사는 그대로 두고 후보만 세 가지 보강했습니다.

1. 영문 약어 별칭은 한글 읽기도 찾습니다. 25건 중 2건에만 붙었고, 둘 다 놓쳤던 바로 그 이름이었습니다.
2. 통화 상대와 자주 함께 나온 사람·회사 5개를, 전사록에 이름이 없어도 후보로 더합니다. 전사가 이름을 못 알아보게 적어도 이로하가 문맥으로 잇게 하려는 것입니다.
3. 더하는 후보의 상한을 15개에서 25개로 늘립니다. 긴 통화는 10건 중 8건에서 철자로 걸린 후보만으로 상한이 차서, 2번 후보가 거의 다 잘렸습니다.

보강 셋은 엔진과 상관없는 규칙이지만, Whisper turbo에는 붙여보지 않았습니다.

아래 그림과 표는 CLOVA Speech 합의로 잰 값입니다. 로컬 설정은 모두 작은 화자 흡수와 비슷한 철자 후보를 썼고, "+"로 시작하는 줄은 바로 윗줄에 보강을 하나씩 더한 것입니다. CLOVA Speech는 운영 규칙 그대로이고, 이름 힌트(키워드 부스팅)도 운영처럼 주지 않았습니다.

![설정마다 CLOVA Speech 합의로 잰 재현율을 점으로 찍고, CLOVA Speech 세 실행의 범위 94~98%를 회색 띠로 나타낸 그림](https://nabichan.me/content-assets/261028/candidates.0ca4d7c4.svg "보강을 하나씩 더할 때마다 83%에서 89%로 올라 회색 띠에 다가갑니다")

:::details
숫자로 보기: 설정별 재현율과 정답 밖 추가

| 설정 | 재현율 | 정답 밖 추가 |
|---|---|---|
| CLOVA Speech 1~3회 | 94~98% | 2~7 |
| Whisper turbo | 77% | 14 |
| Qwen3-ASR, 통화 상대 기준 힌트 | 83% / 83% | 12 |
| + 약어 읽기 | 84% / 84% | 13 |
| + 함께 나온 사람·회사 5개 | 87% | 13 |
| + 후보 상한 25개 | 89% / 89% | 12~13 |
| Qwen3-ASR, 힌트 없음, 약어 읽기 | 81% | 12 |
| Qwen3-ASR, 1차 전사 기준 힌트 | 77% / 80% | 17~20 |
:::

두 번 돌린 설정은 두 값을 함께 적었습니다. 두 번 돌린 네 설정 중 셋은 두 번 같은 재현율을 냈고, 1차 전사 기준 힌트는 3%p 흔들렸습니다.

힌트 없는 Qwen3-ASR(81%, 약어 읽기 포함)과 통화 상대 기준 힌트에 약어 읽기를 더한 설정(84%)을 견주면 힌트가 더한 몫은 3%p쯤이고, Whisper turbo(77%)와의 나머지 차이가 엔진과 정렬 쪽 몫입니다.

보강 셋은 재현율을 83%에서 89%로 올리는 동안 정답 밖 추가를 거의 늘리지 않았습니다. 약속은 마지막 로컬 설정에서 73~77건으로, CLOVA Speech(76~79건)와 비슷했습니다.

같은 통화의 다른 전사를 후보 찾기에 보태보기도 했지만, 두 번 돌리니 93%와 84%로 크게 흔들려서 이 방법은 버렸습니다. 이 설정에서는 이로하가 읽는 전사록에 이름이 틀린 철자로 적혀 있으면, 후보에 있어도 잇지 못했습니다.

마지막 로컬 설정에서 남은 차이는 세 실행이 모두 뽑은 64개 중 6개와, 합의 정답에는 들어가지 않는 새로 나온 사람의 철자 4개입니다. 6개 중 3개는 잘못 듣거나 전사록이 잘려 후보에 오르지 못했고, 3개는 한 통화에 몰려 있었습니다.

## 보완 뒤 결과

### 엇갈린 이름 가르기

CLOVA Speech 합의만으로는 CLOVA Speech가 틀린 경우가 점수에 잡히지 않습니다. 마지막 로컬 설정과 CLOVA Speech 쪽이 엇갈린 사람·회사 이름은 27개였습니다. 로컬만 뽑은 것이 11개, CLOVA Speech만 뽑은 것이 16개입니다.

- 로컬만 뽑은 11개 중 5개는 세 계열이 모두 그 이름을 적었습니다. 실제로 말했는데 CLOVA Speech 쪽이 빠뜨리거나 다른 이름으로 적은 것입니다.
- CLOVA Speech만 뽑은 16개 중 8개는 다른 계열도 들었습니다. 로컬이 정말로 놓친 것입니다.
- 나머지 13개(CLOVA Speech만 뽑은 8개, 로컬만 뽑은 5개)는 다른 계열이 그 이름을 적지 않았습니다. 이름이 틀려서인지 엔진들이 그 이름을 못 알아들어서인지는 투표로 가를 수 없었습니다.

투표 엔진에는 로컬 쪽 엔진인 Qwen3-ASR도 들어 있습니다. Qwen3-ASR을 빼고 나머지 두 계열이 모두 적은 것만 세어도 로컬만 뽑은 이름의 판정은 그대로였고, CLOVA Speech만 뽑은 이름 중 말한 것은 8개에서 4개로 줄었습니다. Qwen3-ASR이 로컬 쪽에 유리하게 작용하지는 않았습니다.

로컬만 뽑은 나머지 1개는 이름이 나온 자리를 찾지 못해 뺐습니다. 투표로 못 가른 13개는 같은 순간에 나온 두 쌍을 합쳐 11문항으로 만들어 직접 들었고, 5분 남짓 걸렸습니다.

CLOVA Speech가 맞은 것이 3문항, 로컬이 맞은 것이 4문항이었습니다. 2문항은 둘 다 틀렸고, 2문항은 발음이 뭉개져 알 수 없어 채점에서 뺐습니다.

### 기준별 결과

마지막 로컬 설정과 CLOVA Speech를 네 기준으로 나란히 놓았습니다.

![네 정답 기준마다 마지막 로컬 설정의 재현율을 점으로, CLOVA Speech 세 실행의 범위를 막대로 나타낸 그림](https://nabichan.me/content-assets/261028/criteria.27a3f989.svg "로컬 점이 CLOVA Speech 막대 안에 든 것은 아래 두 기준뿐입니다")

:::details
숫자로 보기: 기준별 재현율

| 정답 | 마지막 로컬 설정 | CLOVA Speech 1~3회 |
|---|---|---|
| CLOVA Speech 합의(기존 사람·회사) | 89% | 94~98% |
| 투표로 고침, 못 가른 CLOVA Speech만 뽑은 8개 포함 | 80% | 86~92% |
| 투표로 고침, 못 가른 CLOVA Speech만 뽑은 8개 제외 | 88% | 87~91% |
| 직접 듣고 고침 | 84% | 82~87% |
:::

앞에서 밝힌 대로 고친 정답에는 새로 나온 사람까지 들어 있어서, 첫 줄과 바로 견줄 수는 없습니다. 직접 듣고 고친 정답은 사람·회사 83개라 이름 하나가 약 1.2%p입니다.

마지막 로컬 설정이 CLOVA Speech의 범위 안에 든 것은 네 기준 중 두 기준에서였습니다. 통화 25건에서 통화를 무작위로 다시 뽑아 차이를 4,000번 계산한 95% 구간으로 보면, 합의 정답에서는 로컬이 7.6%p 낮았고 구간의 위쪽 끝도 0보다 조금 아래였습니다(−16.1~−0.0%p). 직접 듣고 고친 정답에서는 0.8%p 낮았지만 구간이 0을 사이에 두고 넓게 걸쳐서(−10.4~+7.7%p), 25건으로는 같다고도 다르다고도 할 수 없습니다.

로컬이 약한 곳은 긴 통화였습니다. 직접 듣고 고친 정답으로 긴 통화 10건은 로컬 81%, CLOVA Speech 84~88%였고, 짧은 통화 15건은 로컬 88%, CLOVA Speech 78~90%였습니다. 합의 정답으로 봐도 로컬은 긴 통화 82%, 짧은 통화 97%였습니다.

고친 정답 밖에서 더 뽑은 이름은 실행마다 CLOVA Speech가 3~7개, 로컬이 3~4개였습니다. 그중 직접 듣고 틀렸다고 확인한 것은 CLOVA Speech 쪽이 새 사람으로 잘못 만든 2~3개, 로컬 쪽이 기존 사람에 잘못 이은 1개였습니다. 앞에서 밝힌 대로 CLOVA Speech 쪽 값은 자기가 낀 정답으로 채점한 값입니다.

## 처리 시간과 메모리

마지막 로컬 설정은 이 Mac Studio에서 오디오 길이의 8~9배 속도로 처리했습니다(긴 통화 9.0배, 짧은 통화 8.0배). 전사와 정렬, 화자 분리를 합친 값이고, 22분짜리 통화 한 건이 2분 30초 안팎에 끝나는 속도입니다.

| 통화 길이(3건 평균) | 전사·정렬 | 화자 분리 |
|---|---|---|
| 1분 미만(평균 20초) | 2.1초 | 0.8초 |
| 1~2분(평균 85초) | 7.4초 | 2.5초 |
| 2~5분 | 12.5초 | 4.2초 |
| 10~15분 | 75초 | 24초 |

전사·정렬 시간의 80% 안팎은 Qwen3-ASR 전사였고, 말이 촘촘한 조각일수록 느렸습니다. 아주 짧은 통화는 고정 지연 때문에 배속이 낮아서, 20초짜리 통화는 3초쯤 걸렸습니다.

화자 분리 서비스는 띄운 뒤 첫 요청에 6.3초가 걸렸고, 그다음부터는 0.7초였습니다. 하루 평균 41분인 저의 통화량이면 처리에 하루 5분 안팎이 듭니다.

이 설정만 올리면 메모리는 oMLX 5~6GB, 정렬 서비스 3.7GB, 화자 분리 서비스 2GB 안팎으로 합쳐 11~12GB쯤입니다. 프로세스 메모리(RSS) 기준입니다.

전사와 정렬은 oMLX를 다른 전사 작업과 같이 쓰지 않을 때 쟀고, 화자 분리 시간은 앞선 시험에서 한 번 잰 값입니다. CLOVA Speech 경로는 전사가 끝난 시각이 기록에 남지 않아서, 처리 시간은 로컬만 적었습니다.

시험 중에 여러 작업을 한꺼번에 걸었을 때는 운영으로 옮기기 전에 걸릴 문제도 드러났습니다.

- **메모리**: 전사 작업 셋(Qwen3-ASR 통째·구간 전사, Cohere 통째 전사)을 oMLX에 한꺼번에 걸자, oMLX 하나가 GPU 메모리 57GB를 잡고 디스크를 메모리처럼 쓰는 스왑이 20GB를 넘었습니다. 작업을 멈춘 뒤에도 37GB가 붙잡혀 있었고, 모델을 내리자 7GB로 줄었습니다.
- **대기열**: oMLX는 전사 요청을 한 번에 하나씩 처리해서, 몇 분짜리 통째 요청 뒤에 짧은 구간 요청이 30분 넘게 줄을 섰습니다.
- **재시작**: oMLX를 재시작하거나 새 버전으로 올리면 그사이의 요청이 실패해서, 실패한 요청을 다시 보내는 장치가 필요합니다.

## 결정과 남은 과제

엔진은 Qwen3-ASR로 정했지만, 통화 전사를 CLOVA Speech에서 바로 바꾸지는 않았습니다. 바꾸려면 전사 엔진과 함께 정렬 모델과 작은 화자 흡수, 이름을 잇는 후보 규칙 네 가지를 운영에 넣어야 하는데 아직 넣지 않았습니다. 후보 규칙 넷은 비슷한 철자 후보, 약어 읽기, 함께 나온 사람·회사 5개, 후보 상한 25개입니다.

처음에 든 세 가지 이유로 보면 이렇습니다.

- **비용**: 1분에 28원 대신, 늘 켜 둔 Mac Studio에서 하루 5분 안팎을 씁니다.
- **외부 전송**: 녹음은 CLOVA Speech로 가지 않지만, 전사록은 지금처럼 Codex가 읽습니다.
- **외부 의존**: CLOVA Speech 대신 oMLX와 Mac Studio 한 대에 기대게 됩니다.

남은 과제는 넷입니다.

- 긴 통화에서 남는 이름 차이
- 비교 조건의 차이: CLOVA Speech에는 이름 힌트와 후보 보강을 주지 않았습니다.
- 정답에 남은 CLOVA Speech 쪽 편향과, 규칙을 만든 25건으로 잰 점
- 메모리, 대기열, 실패한 요청을 다시 보내는 장치 같은 운영 문제

시험 방법에서 얻은 것도 있습니다.

- LLM 추출은 같은 입력에도 흔들려서, 정답은 여러 실행이 함께 뽑은 것으로 잡고 같은 설정도 두 번씩 재야 했습니다.
- 비교 기준을 비교 대상의 한쪽에서 가져오면, 다른 쪽이 나아져도 점수에 잡히지 않았습니다.
- 엇갈린 이름만 골라 들으니 가르는 데 5분 남짓이면 됐습니다.
- 숫자가 지나치게 좋을 때는 힌트가 새어 나온 것부터, 원인을 찾았다 싶을 때는 대조군부터 봐야 했습니다.

:::see-also
- [paper] [Qwen3-ASR Technical Report](https://arxiv.org/abs/2601.21337)
- [repo] [Qwen3-ASR](https://github.com/QwenLM/Qwen3-ASR)
- [docs] [Qwen3-ForcedAligner-0.6B](https://huggingface.co/Qwen/Qwen3-ForcedAligner-0.6B)
- [docs] [Cohere Transcribe 03-2026](https://huggingface.co/CohereLabs/cohere-transcribe-03-2026)
- [docs] [pyannote speaker-diarization-community-1](https://huggingface.co/pyannote/speaker-diarization-community-1)
- [repo] [Argmax OSS Swift(WhisperKit)](https://github.com/argmaxinc/argmax-oss-swift)
- [paper] [Robust Speech Recognition via Large-Scale Weak Supervision](https://arxiv.org/abs/2212.04356)
- [repo] [openai/whisper](https://github.com/openai/whisper)
- [article] [Whisper large-v3-turbo 공개](https://github.com/openai/whisper/discussions/2363)
- [repo] [oMLX](https://github.com/jundot/omlx)
- [repo] [mlx-audio](https://github.com/Blaizzy/mlx-audio)
- [docs] [CLOVA Speech API](https://api.ncloud-docs.com/docs/ai-application-service-clovaspeech)
- [article] [STT 모델 실제 성능 비교: 한국어 회의 녹음 35분, 7개 모델 테스트](https://blog.dongjun.win/posts/korean-stt-7-model-comparison/)
- [article] [2026 한국어 유튜브 STT(ASR) 모델 및 런타임 분석](https://wikidocs.net/343561)
:::