공부/인공지능

음성 비서가 느린 진짜 병목은 LLM 첫 토큰이었습니다 — 헤르메스 데스크톱에 로컬 음성 스택 붙이기

TechToast 2026. 10. 17. 10:13
반응형

헤르메스 데스크톱에 음성 대화를 붙였습니다. 제 목소리를 받아 적고, LLM이 생각하고, 다시 목소리로 답해 주는 구성입니다. ChatGPT 음성 모드처럼 바로 대답이 나올 줄 알았는데, 실제로는 답이 한 박자 늦었습니다. 말을 끝내면 음... 소리가 나오기까지 1, 2, 3초가 뜸하거든요. 반응은 있으니 고장이라 하기엔 애매했고, 그냥 느린 것이었습니다.

 

여기서 다들 하는 짐작이 있습니다. 입력도 음성이고 출력도 음성이니 STT(음성을 텍스트로 바꾸는 단계)나 TTS(텍스트를 음성으로 바꾸는 단계)부터 의심하게 되죠. 그런데 로컬 음성 스택(whisper + Kokoro)을 하나씩 붙여 보며 잡아 본 병목은 그 둘이 아니었습니다. STT는 거의 즉답이었고 TTS는 스트리밍으로 말하기 시작하는데도, 첫 음성이 나오기까지의 공백은 그대로 남았습니다. 전체 지연의 대부분은 LLM이 답의 첫 토큰을 만들어내는 시간, 즉 답을 생각하는 시간에 숨어 있었습니다. 반찬이 아무리 빨리 나와도 종업원이 주문을 받으러 오지 않으면 테이블이 조용한 것과 같은 형국입니다.

 

1. 원격 게이트웨이일수록 오디오 경로부터 줄여야 합니다

헤르메스 데스크톱은 에이전트 본체를 따로 게이트웨이 호스트에 두고 데스크톱이 거기에 붙는 구조가 있습니다. 이 형태에서 오디오 경로를 별도 설정 없이 두면, 데스크톱에서 녹음한 음성이 먼저 게이트웨이 호스트로 올라가서 거기서 STT·TTS 프로바이더를 돌아옵니다. 네트워크가 좋아도 왕복이 쌓이니 본문 채팅보다 더 무거워지지요. 지연보다 신경 쓰이는 것도 있습니다. 음성 대화를 하려고 내 목소리가 계속 서버를 떠돈다는 점입니다.

 

해법은 이미 제공돼 있습니다. 데스크톱을 Client-Direct 모드로 전환하면 오디오가 게이트웨이를 거치지 않고, 데스크톱이 STT/TTS 프로바이더를 직접 호출합니다. 음성이 이동하는 경로가 데스크톱과 프로바이더 사이로만 줄어드니 지연이 떨어지고, 동시에 오디오가 서버로 나가지 않아 사생활 문제까지 함께 줄어듭니다. 경로 한 번 줄이면 지연과 사생활 두 개가 한꺼번에 해결되는 셈입니다.

2. 설정에 local이라 적었다고 데스크톱에서 도는 게 아닙니다

경로가 문제니 local이라고 적으면 되겠다고 생각하시면, 여기에 함정이 하나 있습니다. 헤르메스 내장 provider: local(faster-whisper 기반)은 데스크톱 기준의 local이 아니라 게이트웨이 호스트 기준의 local입니다. Intel 계열 macOS에서는 아예 제외되고, 호출이 Relay 중계로 빠져 게이트웨이 호스트를 돌아오더군요. 커스텀 커맨드형 TTS도 게이트웨이 호스트 실행 전용이라 같은 경로를 돌아다닙니다. 로컬이라 설정했는데 체감은 네트워크 지연인, 이 어긋남의 현장입니다.

 

데스크톱의 즉답을 원하시면 조건이 분명합니다. 데스크톱에 직접 OpenAI 호환 HTTP 서버를 띄우고, STT는 whisper.cpp, TTS는 Kokoro·Piper·GPT-SoVITS 중 하나로 감싼 뒤 provider: openai + base_url로 연결해야 합니다. 이때야 비로소 local이라는 단어가 실제 동작과 일치합니다.

3. STT·TTS를 아무리 빠르게 해도 병목은 LLM 첫 토큰으로 옮겨갑니다

음성 대화는 STT → LLM → TTS 순서로 도는 체인입니다. 이 체인에서 STT와 TTS를 아무리 빠르게 돌려도, LLM이 답을 만들기 전까지는 새 음성이 나올 수가 없습니다. 실측에서도 STT는 거의 즉답이었고 TTS는 생성과 겹쳐 재생됐는데, 첫 마디가 나오기 전까지의 공백은 사라지지 않았습니다. 그 시간이 바로 LLM의 첫 토큰 생성 시간이었습니다.

 

좋은 소식도 있습니다. 헤르메스 데스크톱은 LLM이 텍스트를 만들어내는 대로 speech WebSocket에 실시간으로 주입해서 TTS가 생성과 재생을 겹치게 해 줍니다. 답 텍스트가 다 모이길 문장 단위로 기다리는 오케스트레이터를 따로 짤 필요까지는 없다는 뜻이죠. 그래도 첫 토큰까지의 시간 자체는 못 지웁니다. 그래서 빠릿빠릿함의 핵심은 음성 턴 전용의 빠른 LLM을 지정하고 추론(reasoning)을 끄는 데 있습니다. 음성 턴은 네~ 하는 소리만 나와도 대화가 이어지는 성격이니, 긴 생각의 꼬리를 한 번에 생성할 이유가 애초에 없습니다.

4. 그 위의 즉각성, GPT-Live와 로컬 체인 두 갈래입니다

여기까지 최적화해도 부족하다면 경로를 택해야 합니다.

 

GPT-Live 로컬 체인 최적화
방식 full-duplex 음성 모델(gpt-live-1) whisper.cpp + 로컬 TTS + 빠른 LLM
즉각성 ChatGPT 음성 모드급. 반응어(음~)와 끊어말리기(barge-in)가 자연스럽습니다 첫 마디 1.5~3초
비용 OpenAI API 사용료가 쌓입니다 구축 뒤에는 거의 공짜입니다
사생활 오디오가 외부로 전송됩니다 로컬에 남습니다

 

ChatGPT급 즉각성이 꼭 필요하고 비용과 오디오 외부 전송을 감수할 수 있으면 GPT-Live로 갑니다. 사생활을 지키고 비용을 줄이면서 1~2초의 뜸을 견딜 수 있으면 로컬 체인 최적화가 현실적입니다. 저는 후자를 택했습니다.

5. 데스크톱에 로컬 음성 스택을 붙이는 최종 설정

제가 적용한 설정(config.yaml)은 이렇습니다.

 

stt:
  provider: openai
  base_url: http://localhost:9010/v1   # whisper.cpp (model: small)
  streaming: true

tts:
  provider: openai
  base_url: http://localhost:9011/v1   # Kokoro
  voice: af_heart

voice:
  auto_tts: true
  barge_in: true

auxiliary:
  voice_chat:
    model: gemini-3-flash-preview
    reasoning_effort: none

 

핵심은 세 가지입니다. STT와 TTS를 데스크톱 안에 직접 띄운 OpenAI 호환 서버(http://localhost의 서버)로 향하게 하고, auxiliary.voice_chat으로 음성 턴 전용 빠른 모델을 따로 지정하고, reasoning_effort: none으로 추론을 막는 것이죠. 추론을 끄는 건 깊이 생각해야 하는 글감에서는 아깝지만, 음성 턴에서는 지연을 가장 직접적으로 줄여 주는 항목입니다.

 

하드웨어 여유도 알려드릴게요. M4 맥북(통합 메모리 48GB)에서 STT용 Whisper, TTS용 Kokoro에 14B~32B Q4 양자화 LLM까지 동시 구동해도 메모리에 여유가 남습니다. LLM까지 데스크톱에 올리는 완전 로컬로 한 단계 더 올라갈 여지가 있다는 뜻입니다.

그래도 남는 아쉬움

여기까지 해 놓고도 석연치 않은 부분이 있습니다. Kokoro-82M은 확실히 빠르지만 한국어 품질이 최상은 아닙니다. 한국어 음질을 우선하면 GPT-SoVITS를 로컬 OpenAI 호환 서버로 돌리는 편이 낫습니다. 로컬 체인의 첫 마디 1.5~3초도 ChatGPT 음성 모드급은 아닙니다. 맞장구치며 음~ 하는 그 즉각성은 GPT-Live로 가야만 닿는 영역입니다. 그리고 앞서 말씀드린 대로 LLM 첫 토큰 지연은 스트리밍으로도 못 지웁니다. 이건 체인 구조의 한이지 구현의 성의 문제가 아니에요.

 

M4 맥북에 로컬 Whisper·TTS 서버를 실제로 설치해 돌리는 일은 저의 다음 단계로 남아 있습니다. 이번 시도를 하며 얻은 판정은 하나입니다. 음성 AI에서 음성 자체는 무죄고, 사람을 기다리게 하는 건 LLM의 생각 시간이었습니다. 음성 대화에서 2~3초의 뜸을 느끼셨다면 두 가지를 먼저 확인해 보세요. 오디오가 돌아가는 경로는 없는지, 그리고 음성 턴을 담당하는 모델이 추론을 켜고 달리고 있지 않은지요.

반응형