Replace ElevenLabs with local STT and TTS

This commit is contained in:
2026-04-30 03:21:30 +09:00
parent 5d636e8619
commit 73546c15b9
24 changed files with 943 additions and 326 deletions

188
README.md
View File

@@ -1,127 +1,138 @@
# realtime_voice_bot
디스코드 음성 채널 또는 로컬 PC 마이크/스피커에서 한국어 음성을 인식하고, 로컬 LLM 응답을 생성한 뒤 ElevenLabs TTS로 다시 읽어주는 최소 프로토타입입니다.
디스코드 음성 채널 또는 로컬 PC 마이크에서 한국어 음성을 인식하고, 완전 로컬 스택으로 답변을 생성한 뒤 다시 음성으로 읽어주는 최소 프로토타입입니다.
## 현재 스택
- STT: `faster-whisper` + Whisper multilingual
- LLM: `Ollama` + `qwen3:0.6b`
- TTS: `MeloTTS` Korean
- VAD: `avr-vad`
외부 유료 API나 무료 한도형 API는 쓰지 않습니다.
## 현재 구현 범위
- Discord slash command 기반 제어: `/join`, `/leave`, `/status`, `/reset`, `/say`
- 로컬 테스트 모드: `pw-record` 입력, `pw-play` 출력
- 로컬 테스트 모드: PC 마이크로 직접 말하고 바로 응답 확인
- `@discordjs/voice` 기반 음성 채널 입장 및 유저별 오디오 수신
- 48k stereo PCM을 16k mono로 내려서 유저별 VAD 처리
- Silero 계열 VAD(`avr-vad`)로 발화 시작/종료 감지
- ElevenLabs Scribe Realtime WebSocket으로 발화 단위 STT
- Ollama 로컬 LLM으로 짧은 한국어 답변 생성
- ElevenLabs Flash v2.5 스트리밍 TTS
- 채널 단위 단일 재생 큐
- 사용자 발화 시작 시 현재 TTS와 대기열 중단(barge-in)
- 화자 발화 시작 시 현재 재생과 대기열 즉시 중단
- Python 로컬 워커를 한 번 띄워 STT/TTS 모델을 메모리에 유지
## 권장 환경
## 필수 준비물
- Bun `1.3+`
- Node.js `22.12+`
- Python `3.11+`
- `ffmpeg`
- Ollama
- Discord bot with Voice permissions
- ElevenLabs API key + 사용할 Voice ID
Discord 모드까지 쓸 거면 추가로:
- Discord bot token
- Discord application id
## 빠른 시작
```bash
bun install
ollama pull qwen3:0.6b
bun run setup:local-ai
```
그다음 로컬 장치 확인:
```bash
bun run devices
```
실행:
```bash
bun run start:local
```
Discord 모드:
```bash
bun run start:discord
```
## 환경 변수
`.env.example`참고해서 `.env`를 채우면 됩니다.
필수:
- `ELEVENLABS_API_KEY`
- `ELEVENLABS_VOICE_ID`
`.env.example`복사해서 `.env`를 채우면 됩니다.
Discord 모드에서만 필수:
- `DISCORD_BOT_TOKEN`
- `DISCORD_APPLICATION_ID`
기본값이 이미 들어있는 로컬 AI 설정:
- `OLLAMA_BASE_URL`
- `OLLAMA_MODEL`
- `OLLAMA_KEEP_ALIVE`
- `OLLAMA_NUM_CTX`
- `LOCAL_AI_VENV_PATH`
- `LOCAL_AI_CACHE_DIR`
- `LOCAL_STT_MODEL`
- `LOCAL_STT_DEVICE`
- `LOCAL_STT_COMPUTE_TYPE`
- `LOCAL_STT_BEAM_SIZE`
- `LOCAL_TTS_LANGUAGE`
- `LOCAL_TTS_SPEAKER`
- `LOCAL_TTS_DEVICE`
- `LOCAL_TTS_SPEED`
선택:
- `DISCORD_COMMAND_GUILD_ID`
- 테스트 서버에만 slash command를 즉시 반영하려면 설정
- `OLLAMA_BASE_URL`
- 기본값: `http://localhost:11434`
- `OLLAMA_MODEL`
- 기본값: `qwen3:0.6b`
- 가장 빠른 무료 오픈웨이트 로컬 기본값
- `OLLAMA_KEEP_ALIVE`
- 기본값: `5m`
- `OLLAMA_NUM_CTX`
- 기본값: `4096`
- `LOCAL_AI_PYTHON`
- Python 경로 자동 탐지가 안 되면 설정
- 예시: `python`
- Windows 예시: `py -3`
- `LOCAL_AUDIO_SOURCE`
- `pw-record --target` 에 넣을 PipeWire source id 또는 node name
- 로컬 입력 장치
- Linux는 `pw-record --target`, Windows는 `ffmpeg dshow` 장치 이름
- `LOCAL_AUDIO_SINK`
- `pw-play --target` 에 넣을 PipeWire sink id 또는 node name
- Linux 로컬 출력 장치
- Windows는 현재 시스템 기본 출력 장치 사용
- `LOCAL_SPEAKER_NAME`
- 로컬 테스트에서 프롬프트에 넣을 화자 이름
- `ELEVENLABS_STT_MODEL`
- 기본값: `scribe_v2_realtime`
- `ELEVENLABS_TTS_MODEL`
- 기본값: `eleven_flash_v2_5`
- `BOT_DEFAULT_LANGUAGE`
- 기본값 `ko`
- `DEBUG_TEXT_EVENTS`
- `true` 명령을 실행한 텍스트 채널에 transcript/reply를 같이 올림
- `true`면 transcript/reply를 콘솔에 같이 출력
## 실행
## 속도 우선 기본값
```bash
bun install
- STT 기본 모델은 `tiny`
- LLM 기본 모델은 `qwen3:0.6b`
- TTS 기본 속도는 `1.12`
정확도가 아쉬우면:
```env
LOCAL_STT_MODEL=small
OLLAMA_MODEL=qwen3:1.7b
```
Ollama 준비:
## 로컬 테스트 순서
```bash
ollama pull qwen3:0.6b
```
속도보다 품질이 더 중요하면:
```bash
ollama pull qwen3:1.7b
# 또는
ollama pull qwen3:4b
```
디스코드 모드:
```bash
bun run start:discord
```
로컬 장치 목록:
```bash
bun run audio:devices
```
로컬 테스트 모드:
```bash
bun run start:local
```
타입 체크:
```bash
bun run check
```
## 사용 흐름
1. 봇을 서버에 초대하고 음성 권한을 부여합니다.
2. 음성 채널에 들어갑니다.
3. 텍스트 채널에서 `/join` 실행
4. 말을 하면 봇이 발화 단위로 인식하고 음성으로 짧게 답합니다.
5. 다시 말하면 현재 읽고 있던 TTS는 즉시 중단됩니다.
로컬 테스트:
1. `bun run audio:devices` 로 source/sink id 또는 이름 확인
1. `bun install`
2. `ollama pull qwen3:0.6b`
3. 필요하면 `.env``LOCAL_AUDIO_SOURCE`, `LOCAL_AUDIO_SINK`, `OLLAMA_MODEL` 설정
3. `bun run start:local`
4. 마이크로 바로 말해서 응답 확인
3. `bun run setup:local-ai`
4. `bun run devices`
5. 필요하면 `.env``LOCAL_AUDIO_SOURCE` 설정
6. `bun run start:local`
## Windows 메모
- `bun run devices` 와 Windows 로컬 녹음은 `ffmpeg`가 필요합니다.
- 출력 장치 직접 선택은 아직 미구현이라 시스템 기본 출력 장치로 재생됩니다.
- Python 탐지가 안 되면 `.env``LOCAL_AI_PYTHON=py -3` 또는 `LOCAL_AI_PYTHON=python` 을 넣으면 됩니다.
## 설계 메모
@@ -129,5 +140,4 @@ bun run check
- 출력은 길드 세션당 단일 큐
- 로컬 모드는 단일 화자 입력 기준
- 화자 구분은 `speaker_id`, `speaker_name`을 LLM 프롬프트에 항상 포함
- 현재 기본 LLM은 `qwen3:0.6b` 이며 속도 우선 설정이라 답변 품질이 약하면 `qwen3:1.7b` 또는 `qwen3:4b` 로 올리는 것을 권장합니다.
- STT/TTS는 아직 ElevenLabs API를 사용하므로 프로젝트 전체가 완전 무과금은 아닙니다.
- 모델 다운로드 캐시는 기본적으로 `.local-ai/cache` 아래에 저장