Dịch vụ tương tác giọng nói thời gian thực với kiến trúc streaming pipeline cho tiếng Việt.
Voice Interaction Service là hệ thống xử lý giọng nói real-time kết hợp VAD → ASR → LLM → TTS sử dụng 100% Cloud API. Kiến trúc 3 tầng tách biệt:
| Tầng | Công nghệ | Provider |
|---|---|---|
| VAD | Voice Activity Detection | WebRTC VAD (CPU, no model) |
| ASR | Speech-to-Text | OpenAI Whisper API |
| LLM | Chatbot | OpenRouter API (Gemini, GPT-4o, Claude, Llama...) |
| TTS | Text-to-Speech | OpenAI TTS API |
Điểm đặc biệt: Không cần GPU, không cần download local models, chạy trên bất kỳ máy nào.
Mục tiêu: Latency P95 < 900ms, hỗ trợ barge-in để user chen ngang khi AI đang nói.
Client (Web/App)
│
▼ WebSocket (opus 48kHz)
┌──────────────────┐
│ Gateway │ ◄── WebSocket management, session
│ (FastAPI) │ ◄── Audio decode, resample
└────────┬─────────┘
│ Audio (16kHz PCM)
▼
┌──────────────────┐
│ Orchestrator │ ◄── Pipeline coordinator
│ (FastAPI) │ ◄── ASR → LLM → TTS
└────────┬─────────┘
│
▼ OpenAI API
┌──────────────────┐
│ Whisper API │ ◄── Audio → Text
│ (openai.com) │
└──────────────────┘
│
▼ OpenRouter API
┌──────────────────┐
│ OpenRouter │ ◄── Gemini, GPT-4o, Claude...
│ (openrouter.ai) │
└──────────────────┘
│
▼ OpenAI API
┌──────────────────┐
│ TTS API │ ◄── Text → Audio
│ (openai.com) │
└──────────────────┘
| Tiêu chí | Local (cũ) | Cloud API (mới) |
|---|---|---|
| GPU | Cần GPU T4/L4 | Không cần |
| Model download | 5-10GB models | Không cần |
| Docker image | ~5GB (cuda) | ~200MB |
| Latency | ~200ms | ~500ms |
| Scale | Cố định GPU | Vô hạn |
| Chi phí vận hành | ~$200/tháng | ~$100/tháng |
sequenceDiagram
participant Client
participant Gateway
participant VAD
participant ASR
participant LLM
participant TTS
Note over Client,Gateway: 1. Client connect WS
Client->>Gateway: WS connect /stream/{session_id}
Gateway->>Gateway: Create session
Note over Client,Gateway: 2. Audio streaming loop
loop
Client->>Gateway: Audio (opus 48kHz)
Note over Gateway: VAD.detect (WebRTC, local)
Gateway->>TTS: TTS.cancel (if barge-in)
Gateway->>TTS: TTS.cancel (if interrupt)
Gateway->>ASR: ASR.recognize (OpenAI Whisper API)
ASR-->>Gateway: ASRResult
Gateway->>Client: transcript result
Gateway->>LLM: chat (OpenRouter API)
LLM-->>Gateway: response text
Gateway->>TTS: TTS.speak (OpenAI TTS API)
loop
TTS-->>Gateway: TTSResult(audio)
Gateway->>Client: Audio (PCM)
end
Gateway->>Client: done
end
flowchart TD
subgraph Client
WS[WebSocket Client]
HTTP[HTTP Client]
end
subgraph Gateway
WSE[WebSocket Endpoint]
HTTPE[HTTP Endpoint]
SM[Session Manager]
AP[AudioProcessor]
VAD[VAD - WebRTC]
end
subgraph Orchestrator
PE[Pipeline Endpoint]
OE[Orchestrator Engine]
CA[ChatbotAdapter]
ASR[ASR - Whisper]
TTS[TTS - OpenAI]
end
subgraph External
OAI[OpenAI API]
OR[OpenRouter API]
end
WS -->|1. Connect| WSE
WSE --> SM
SM --> AP
WS -->|2. Audio| AP
AP --> VAD
VAD -->|2a. Barge-in| AP
AP --> ASR
ASR -->|3. Transcript| AP
HTTP -->|4. POST| PE
PE --> OE
OE --> ASR
ASR -->|API| OAI
OAI --> ASR
ASR --> OE
OE --> CA
CA -->|API| OR
OR --> CA
CA --> OE
OE --> TTS
TTS -->|API| OAI
OAI --> TTS
TTS --> OE
OE -->|5. Response| HTTP
AP --> TTS
TTS -->|6. Audio| AP
AP --> WS
| Trạng thái | Mô tả |
|---|---|
IDLE |
Session đang chờ, không hoạt động |
LISTENING |
AI đang nghe input từ user |
PROCESSING |
Đang xử lý (ASR → LLM → TTS) |
SPEAKING |
AI đang nói, output TTS đang streaming |
INTERRUPTED |
User đã chen ngang (barge-in) |
sequenceDiagram
participant User
participant Gateway
participant TTS
Note over User,Gateway: User speaks while AI is speaking
User->>Gateway: Audio (barge-in)
Note over Gateway: VAD.detect (WebRTC, local)
alt confidence > 0.7
Gateway->>TTS: cancel(stream_id)
Gateway->>User: {type: interrupted}
else confidence <= 0.7
Note over Gateway: Ignore audio
end
voice-interaction/
├── apps/
│ ├── gateway/ # FastAPI WebSocket server
│ │ ├── src/
│ │ │ ├── routes/ # websocket.py, http.py
│ │ │ ├── session/ # manager.py
│ │ │ ├── pipeline/ # audio_processor.py
│ │ │ └── dependencies.py
│ │ ├── tests/
│ │ └── pyproject.toml
│ │
│ └── orchestrator/ # Pipeline coordinator
│ ├── src/
│ │ ├── routes/ # pipeline.py, session.py
│ │ ├── orchestrator/ # engine.py, context.py
│ │ └── adapters/ # chatbot.py (OpenRouter)
│ ├── tests/
│ └── pyproject.toml
│
├── services/
│ ├── asr/ # ASR service (OpenAI Whisper API)
│ │ ├── src/
│ │ │ ├── interface.py
│ │ │ ├── openai_asr.py # OpenAI Whisper API wrapper
│ │ │ └── factory.py
│ │ ├── tests/
│ │ └── pyproject.toml
│ │
│ ├── tts/ # TTS service (OpenAI TTS API)
│ │ ├── src/
│ │ │ ├── interface.py
│ │ │ ├── openai_tts.py # OpenAI TTS API wrapper
│ │ │ └── factory.py
│ │ ├── tests/
│ │ └── pyproject.toml
│ │
│ └── vad/ # VAD service (WebRTC VAD)
│ ├── src/
│ │ ├── interface.py
│ │ ├── webrtc_vad.py # WebRTC VAD (CPU, no model)
│ │ └── factory.py
│ ├── tests/
│ └── pyproject.toml
│
├── packages/
│ ├── common/ # Shared utilities
│ │ └── src/voice_common/
│ │ ├── audio/ # utils.py, resampler.py
│ │ ├── config/ # loader.py (load models.yaml)
│ │ ├── logging/ # setup.py
│ │ └── metrics/ # prometheus.py
│ │
│ ├── proto/ # gRPC definitions
│ │ └── voice.proto
│ │
│ └── sdk-web/ # JavaScript client SDK
│ └── voice-client.js
│
├── infra/
│ ├── docker/ # Dockerfiles
│ ├── k8s/ # Helm charts
│ ├── prometheus/ # prometheus.yml
│ └── docker-compose*.yml
│
├── configs/
│ └── models.yaml # Model configuration
│
├── scripts/ # Utility scripts
├── tests/ # Integration tests
├── .github/workflows/ # CI/CD
├── pyproject.toml # Root workspace
├── Makefile # Task runner
└── README.md # This file
- Python 3.11+
- GPU (T4/L4) cho ASR/TTS inference
- Docker & Docker Compose
- [Optional] Kubernetes cho production
# Clone và cài
git clone https://github.com/108-PJIOrthoGen/108POG_Voice_interaction.git
cd voice-interaction
# Tạo virtual environment (bắt buộc với Python 3.12+)
python3 -m venv .venv
source .venv/bin/activate
# Install dependencies trước
pip install editables hatchling aiohttp structlog numpy pydantic pydantic-settings pyyaml scipy prometheus-client webrtcvad-wheels fastapi uvicorn openai python-multipart
# Install all packages (editable mode)
pip install --no-build-isolation -e packages/common/
pip install --no-build-isolation -e services/vad/
pip install --no-build-isolation -e services/asr/
pip install --no-build-isolation -e services/tts/
pip install --no-build-isolation -e apps/gateway/
pip install --no-build-isolation -e apps/orchestrator/
# Tạo symlinks để Python tìm thấy modules
cd services/asr && ln -sf src voice_asr && cd ../..
cd services/vad && ln -sf src voice_vad && cd ../..
cd services/tts && ln -sf src voice_tts && cd ../..
cd packages/common/src && ln -sf voice_common voice_common && cd ../../../..
# Install ffmpeg cho audio recording (Linux)
sudo apt install ffmpeg
# Hoặc macOS
brew install ffmpeg
# Hoặc dùng Makefile (sau khi activate venv)
make installLưu ý: Dự án dùng 100% Cloud API - không cần download local models.
# Copy .env.example thành .env và điền API keys
cp .env.example .env
# Hoặc set env vars:
# export ASR_API_KEY=sk-...
# export TTS_API_KEY=sk-...
# export CHATBOT_API_KEY=sk-or-v1-...# Terminal 1: Gateway
cd /home/sotatek/Projects/voice-interaction
source .venv/bin/activate
make run-gateway
# Terminal 2: Orchestrator (cần cài python-multipart trước)
pip install python-multipart
make run-orchestratorHoặc chạy trực tiếp:
# Terminal 1
source .venv/bin/activate
uvicorn voice_gateway:app --host 0.0.0.0 --port 8000
# Terminal 2
source .venv/bin/activate
uvicorn voice_orchestrator:app --host 0.0.0.0 --port 8001Test:
curl http://localhost:8000/health
curl http://localhost:8001/health# Production
docker compose -f infra/docker-compose.yml up --build
# Development (hot reload)
docker compose -f infra/docker-compose.yml -f infra/docker-compose.dev.yml up# Tất cả tests
make test
# Chỉ unit tests
make test-unit
# Integration tests
make test-integration
# Lint
make lint# Chạy script test voice (cần ffmpeg đã cài ở bước 1)
python scripts/test_voice.pyYêu cầu:
- Đang chạy Gateway (port 8000)
- Đang chạy Orchestrator (port 8001)
- Microphone được cấp quyền
Script sẽ:
- Kết nối WebSocket tới
/stream/{client_id} - Ghi âm từ microphone (16kHz mono)
- Gửi audio lên Gateway
- Nhận transcript và phản hồi từ AI
Test thủ công qua cURL:
# Test HTTP endpoint với file audio
curl -X POST http://localhost:8000/v1/process \
-F "audio=@test.wav" \
-F "language=vi"<script src="packages/sdk-web/voice-client.js"></script>
<script>
const client = new VoiceClient({
url: 'wss://your-server.com',
debug: true
});
client.onTranscript((result) => {
console.log(result.isFinal ? 'Final:' : 'Partial:', result.text);
});
client.onAudio((event) => {
if (event.type === 'done') console.log('AI done speaking');
});
await client.connect();
await client.startRecording();
// Khi muốn ngắt AI đang nói (barge-in)
client.interrupt();
// Dừng recording
client.stopRecording();
// Disconnect
client.disconnect();
</script>const ws = new WebSocket('ws://localhost:8000/stream/session-123');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'transcript') {
console.log(data.isFinal ? data.text : `Partial: ${data.text}`);
} else if (data.type === 'done') {
console.log('TTS done');
}
};
// Gửi audio (base64 encoded PCM 16-bit)
ws.send(audioBase64);
// Gửi text (fallback)
ws.send(JSON.stringify({ type: 'text', text: 'Xin chào' }));
// Interrupt
ws.send(JSON.stringify({ type: 'interrupt' }));# Health check
curl http://localhost:8000/health
# TTS fallback
curl -X POST http://localhost:8000/tts/speak \
-H "Content-Type: application/json" \
-d '{"text": "Xin chào bạn", "session_id": "abc"}'# .env
GATEWAY_HOST=0.0.0.0
GATEWAY_PORT=8000
ORCHESTRATOR_HOST=0.0.0.0
ORCHESTRATOR_PORT=8001
# VAD (WebRTC - không cần API key)
VAD_AGGRESSIVENESS=3
# ASR - OpenAI Whisper API
ASR_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
ASR_BASE_URL=https://api.openai.com/v1
ASR_MODEL=whisper-1
# TTS - OpenAI TTS API
TTS_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx
TTS_BASE_URL=https://api.openai.com/v1
TTS_MODEL=tts-1
TTS_VOICE=alloy
# Chatbot - OpenRouter API
CHATBOT_API_KEY=sk-or-v1-xxxxxxxxxxxxxxxxxxxx
CHATBOT_BASE_URL=https://openrouter.ai/api/v1
CHATBOT_MODEL=google/gemini-2.0-flash-001
# Config
CONFIG_PATH=configs/models.yaml
LOG_LEVEL=INFOLưu ý: KHÔNG bao giờ commit API keys thật. File
.envđã được thêm vào.gitignore.
models:
vad:
name: "webrtc_vad"
config:
sample_rate: 16000
frame_duration_ms: 30
aggressiveness: 3
asr:
name: "openai_whisper"
config:
base_url: "https://api.openai.com/v1"
model: "whisper-1"
language: "vi"
response_format: "json"
timeout: 30
tts:
name: "openai_tts"
config:
base_url: "https://api.openai.com/v1"
model: "tts-1"
voice: "alloy"
speed: 1.0
response_format: "pcm"
timeout: 30
# OpenRouter LLM configuration
chatbot:
base_url: "https://openrouter.ai/api/v1"
model: "google/gemini-2.0-flash-001"
timeout: 30
max_retries: 3
max_tokens: 256
temperature: 0.7
streaming: true
system_prompt: "Bạn là trợ lý ảo thân thiện, trả lời ngắn gọn bằng tiếng Việt."| Model | Provider | Latency | Quality | Khuyến nghị |
|---|---|---|---|---|
google/gemini-2.0-flash-001 |
Rất thấp (~200ms) | Tốt | Recommended cho voice | |
google/gemini-2.5-flash-preview |
Thấp | Rất tốt | Reasoning tốt hơn | |
anthropic/claude-3.5-sonnet |
Anthropic | Trung bình | Xuất sắc | Chất lượng cao nhất |
meta-llama/llama-3.1-8b-instruct |
Meta | Thấp | Khá | Budget option |
openai/gpt-4o-mini |
OpenAI | Thấp | Tốt | Alternative tốt |
# Tạo secret cho tất cả API keys
kubectl create secret generic api-keys \
--from-literal=asr-api-key='sk-xxxxxxxxxxxxxxxxxxxxxxxx' \
--from-literal=tts-api-key='sk-xxxxxxxxxxxxxxxxxxxxxxxx' \
--from-literal=chatbot-api-key='sk-or-v1-xxxxxxxxxxxxxxxxxxxx'
# Thêm Helm chart
helm repo add voice-interaction https://yourorg.github.io/voice-interaction
# Cài đặt
helm install voice-release voice-interaction/voice-interactionSecret api-keys sẽ được inject vào các pods qua environment variables:
ASR_API_KEY→ ASR serviceTTS_API_KEY→ TTS serviceCHATBOT_API_KEY→ Orchestrator (LLM)
Metrics available tại:
- Gateway:
http://localhost:8000/metrics - Orchestrator:
http://localhost:8001/metrics
Metrics chính:
voice_latency_asr_seconds: ASR processing timevoice_latency_tts_seconds: TTS processing timevoice_latency_e2e_seconds: End-to-end latencyvoice_sessions_active: Active sessionsvoice_sessions_total: Total sessionsvoice_vad_detections_total: VAD detectionsvoice_barge_in_total: Barge-in events
| Stage | Target P50 | Target P95 |
|---|---|---|
| VAD → ASR | 100ms | 200ms |
| ASR → LLM | 200ms | 500ms |
| LLM → TTS | 100ms | 200ms |
| Total E2E | 500ms | 900ms |
- Fork repository
- Tạo branch:
git checkout -b feature/your-feature - Commit changes:
git commit -m 'Add something' - Push:
git push origin feature/your-feature - Tạo Pull Request