허깅페이스 speech-to-speech v0.2.12 변경 정리 — 로컬 음성 에이전트
허깅페이스의 speech-to-speech는 마이크 입력을 받아 음성으로 대답하는 음성 에이전트 서버입니다. 2026년 8월 5일에 나온 v0.2.12가 WebRTC 전송을 새로 붙이고 말이 끝났는지 판단하는 Smart Turn 모델을 기본으로 켜고 raw PCM 모드의 이름을 바꾸는 브레이킹 체인지를 하나 넣었습니다. 음성 파이프라인을 직접 세워 보려는 개발자에게 해당하는 이야기입니다.
먼저 흔한 오해 하나를 짚고 갑니다. 이 프로젝트가 "API 키 없이 완전 로컬로 돈다"고 정리된 글이 있는데, 원문 기준으로는 정확하지 않습니다. 음성 인식(STT)과 음성 합성(TTS)은 로컬 모델이 기본값이 맞지만 정작 답을 만드는 LLM은 기본 구성이 OpenAI의 원격 API입니다. quickstart 문서가 export OPENAI_API_KEY=...로 시작하는 이유가 여기 있습니다. 완전 로컬로 돌리는 것도 가능한데, 그러려면 옵션을 따로 바꿔야 합니다. 아래에서 어디를 만져야 하는지까지 정리합니다.
VAD부터 TTS까지 네 단이 순서대로 이어지고, 각 단은 플래그로 교체한다. 기본 구성에서 STT·TTS만 로컬이다.
무엇을 하는 프로젝트인가
README는 이 프로젝트를 "저지연의 완전 모듈식 음성 에이전트 파이프라인(A low-latency, fully modular voice-agent pipeline)"이라고 소개합니다. 파이프라인은 네 단으로 나뉩니다. 먼저 사람이 말을 하는지 감지하는 VAD(Voice Activity Detection, 음성 구간 검출)가 있고 그다음 음성을 글자로 바꾸는 STT(Speech-to-Text), 글자를 받아 답을 만드는 LLM, 그 답을 다시 음성으로 바꾸는 TTS(Text-to-Speech)가 이어집니다. 네 단은 각각 --stt, --llm_backend, --tts 같은 플래그로 통째로 갈아 끼울 수 있습니다. VAD 기본값은 Silero VAD v5입니다.
정체성을 좀 더 정확히 옮기면, 이 서버는 "OpenAI Realtime의 GA 이벤트셋을 WebSocket과 WebRTC로 노출(exposed through the core OpenAI Realtime GA event set over WebSocket and WebRTC)"합니다. 즉 OpenAI Realtime API와 단순히 "호환된다"기보다, 그 프로토콜과 이벤트셋을 구현해 같은 클라이언트가 붙을 수 있게 한 서버라고 보는 편이 정확합니다. 기본 STT는 Parakeet TDT, 기본 TTS는 Qwen3-TTS(v0.2.11부터 GGML이 기본)이고, 기본 LLM은 OpenAI Responses API를 통한 gpt-5.6-terra(reasoning effort none)입니다.
어디서 실제로 쓰이나
README는 이 파이프라인이 "수천 대(thousands)의 Reachy Mini 로봇의 대화 백엔드로 프로덕션에서 돈다(runs in production as the conversation backend for thousands of Reachy Mini robots)"고 밝히고 있습니다. 여기서 구체적인 대수는 원문에 없고 "수천 대"라는 표현만 있습니다. 실제로 몇 대인지는 확인되지 않으니 그 이상으로 넘겨짚지 않는 편이 좋습니다.
저장소 지표는 시점을 붙여서 봐야 합니다. 2026년 8월 23일 조회 기준으로 별 약 1만 2천 개(12,779), 포크 약 1,600개(1,579)이고 라이선스는 Apache-2.0입니다. 이 수치는 계속 변합니다. README에 "#1 Repository of the Day" 같은 배지가 박혀 있지만 이것은 저자가 넣어 둔 정적 이미지라, "지금 트렌딩 1위"라는 뜻으로 읽으면 안 됩니다.
v0.2.12에서 바뀐 것
바뀐 것은 크게 네 가지입니다. 하나씩 보겠습니다.
첫째, WebRTC 전송이 추가되었습니다(PR #352). 기존에는 WebSocket으로만 붙었는데 이제 WebRTC로도 연결할 수 있습니다. 클라이언트가 POST /v1/realtime/calls로 SDP 협상을 하고 오디오는 RTP로 오가며 이벤트는 oai-events 데이터 채널로 흐릅니다. WebRTC를 쓰려면 별도 extra 설치가 필요합니다(아래 설치 절 참고).
둘째, Smart Turn v3.2 엔드포인팅이 기본으로 켜졌습니다(PR #192). 엔드포인팅은 사용자가 말을 끝냈는지 판단해 대답을 시작하는 시점을 정하는 기능입니다. 릴리스 노트 원문은 "Realtime mode now enables the quantized CPU Smart Turn model by default"라고 적고 있고 예전처럼 Silero만으로 판단하게 되돌리려면 --no_smart_turn을 붙이면 됩니다.
셋째, 오디오 직접 입력이 생겼습니다(PR #298). STT 단을 거치지 않고 오디오를 LLM에 바로 넣는 방식인데, 원문 기준으로 세 조건을 모두 만족해야 동작합니다. --stt none으로 STT를 끄고 Chat Completions 백엔드를 쓰고 오디오를 처리할 수 있는(audio-capable) 모델을 명시적으로 골라야 합니다.
네 변경 중 브레이킹 체인지는 마지막 하나다. 나머지는 기본값 변경이거나 새 기능 추가다.
넷째, 이것이 유일한 브레이킹 체인지입니다. raw PCM WebSocket 모드의 이름이 바뀌었습니다(PR #401). 원문은 "The raw PCM WebSocket mode is now named raw-websocket. Commands using --mode websocket must change to --mode raw-websocket; the old value is no longer accepted"라고 못 박고 있습니다. 즉 --mode websocket으로 돌리던 명령은 --mode raw-websocket으로 바꿔야 하고 구값은 더 이상 받지 않습니다. 다만 이건 raw PCM 모드에 한정된 이야기라, Realtime 모드로 쓰던 구성은 손댈 필요가 없습니다.
띄우는 방법과 완전 로컬화
기본 설치와 실행은 다음과 같습니다.
pip install speech-to-speech
# 서버를 띄운다 (기본: STT·TTS 로컬, LLM은 원격 gpt-5.6-terra)
export OPENAI_API_KEY=... # 기본 LLM이 원격이라 필요하다
speech-to-speech serve
# 다른 터미널에서 붙어 말을 걸어 본다
speech-to-speech talk --url ws://127.0.0.1:8765/v1/realtime여기까지가 기본 구성입니다. 다시 강조하면 이 상태에서는 STT와 TTS만 로컬에서 돌고, LLM은 OpenAI로 나갑니다. WebRTC로 붙이려면 extra를 함께 설치합니다.
pip install "speech-to-speech[webrtc]"LLM까지 로컬로 내리려면 --llm_backend를 바꾸고 로컬 모델을 지정합니다. 백엔드로는 transformers, mlx-lm, responses-api, chat-completions를 고를 수 있습니다. 릴리스 노트가 제시하는 두 예시입니다.
# llama.cpp 서버를 로컬 LLM으로 띄우는 예시
llama-server -hf ggml-org/gemma-4-E4B-it-GGUF -np 2 -c 65536 -fa on --swa-full
# 맥에서 MLX로 로컬 LLM을 돌리는 예시
speech-to-speech local --mac-optimal-settings \
--model_name mlx-community/Qwen3-4B-Instruct-2507-bf16speech-to-speech local 서브커맨드는 로컬 구성을 한 번에 잡아 주는 진입점입니다. 정리하면, 이 프로젝트로 "완전 로컬"을 하는 것은 분명히 가능하지만 기본값이 아니라 명시적으로 백엔드와 모델을 지정하는 선택입니다.
기본 구성과 완전 로컬 구성의 차이는 LLM 단 하나다. STT·TTS는 양쪽 다 로컬이다.
써보기 전에 알아둘 것
두 가지를 짚어 둡니다. 첫째는 보안 패치입니다. 이번 릴리스는 의존성 NLTK를 3.10.0으로 올려 CVE-2026-54293(GHSA-p4gq-832x-fm9v)을 막았습니다. 이 취약점은 nltk.data.load()가 nltk: URL 스킴을 처리할 때 URL 인코딩된 경로 구분자를 통해 경로를 벗어나 임의 파일을 읽을 수 있던 경로 순회 문제로, CVSS 7.5(High)이고 3.9.4 이하가 영향을 받습니다. 릴리스 노트에는 번호만 적혀 있고, 상세는 GitHub Advisory에 있습니다.
둘째는 프록시 옵션입니다. --enable_llm_proxy를 켜면 LLM 라우트를 프록시하는데 README는 "서버가 자체적으로 인증도 스로틀링도 하지 않는다(performs no authentication and no throttling of its own)"고 경고하며 "신뢰할 수 있는 네트워크에서만" 켜라고 명시합니다. 외부에 노출된 환경에서 이 옵션을 켜지 않는 편이 안전합니다.
마지막으로 버전 흐름 하나. 릴리스 노트는 v0.2.12를 "0.2.x 라인에서 계획된 마지막 릴리스(planned final release)"로 언급합니다. 다만 이것은 "계획상 마지막"이라는 뉘앙스라, 이후 패치가 절대 없다고 단정하기보다 0.2.x가 마무리 단계에 들어갔다는 정도로 이해하는 것이 원문에 맞습니다.
정리
- v0.2.12는 WebRTC 전송(PR #352), Smart Turn v3.2 기본 활성화(PR #192), 오디오 직접 입력(PR #298)을 더했고, 브레이킹 체인지는 raw PCM 모드 이름 변경(
--mode websocket→--mode raw-websocket, PR #401) 하나뿐입니다. - 기본 구성은 STT·TTS만 로컬이고 LLM은 원격(gpt-5.6-terra)입니다. 완전 로컬은
--llm_backend와 로컬 모델을 명시해야 가능합니다. - NLTK 3.10.0으로 CVE-2026-54293을 패치했고,
--enable_llm_proxy는 인증·스로틀을 하지 않으니 신뢰 네트워크에서만 켜야 합니다.