Skip to main content
Glama
nhattan-dev

Voice Assistant MCP Demo

by nhattan-dev
README.md
# Voice Assistant MCP Demo — Phase 1: Cloud Backend

Ba service Python chạy độc lập, kết nối qua URL env vars.

```
api_server  →  mcp_server  →  gateway  ←  Android app / curl
```

---

## Chạy local (test Phase 1)

```bash
pip install -r requirements.txt
cp .env.example .env        # điền GEMINI_API_KEY và GATEWAY_SHARED_TOKEN

# Terminal 1
python api_server.py        # :8000

# Terminal 2
python mcp_server.py        # :8001  (MCP_BASE_URL=http://localhost:8000 đã có trong .env)

# Terminal 3
python gateway.py           # :8002
```

Test bằng curl:

```bash
# Không có auth token (để trống GATEWAY_SHARED_TOKEN trong .env khi test local)
curl -X POST http://localhost:8002/command \
  -H "Content-Type: application/json" \
  -d '{"text": "việc hôm nay có gì", "session_id": "test-1"}'

# Kết quả mong đợi:
# {"speech": "Hôm nay bạn có 4 việc, trong đó 3 việc chưa xong...", "session_id": "test-1"}

# Multi-turn — follow-up cùng session
curl -X POST http://localhost:8002/command \
  -H "Content-Type: application/json" \
  -d '{"text": "đánh dấu xong việc gọi khách hàng", "session_id": "test-1"}'
```

---

## Deploy lên Render (3 service riêng)

### Bước 1 — Push repo lên GitHub

```bash
git init && git add . && git commit -m "init phase 1"
git remote add origin <your-github-repo-url>
git push -u origin main
```

### Bước 2 — Tạo Service 1: API Server

- **Render Dashboard** → New → Web Service → chọn repo
- Name: `task-api`
- Build Command: `pip install -r requirements.txt`
- Start Command: `python api_server.py`
- Environment Variables:
  - `PORT` → để Render tự điền

Sau khi deploy xong, copy URL dạng `https://task-api-xxxx.onrender.com`.

### Bước 3 — Tạo Service 2: MCP Server

- Name: `task-mcp`
- Start Command: `python mcp_server.py`
- Environment Variables:
  - `API_BASE_URL` → URL của Service 1 (bước 2)

Sau khi deploy, copy URL: `https://task-mcp-xxxx.onrender.com`.

### Bước 4 — Tạo Service 3: Gateway

- Name: `task-gateway`
- Start Command: `python gateway.py`
- Environment Variables:
  - `MCP_BASE_URL` → URL của Service 2 (bước 3)
  - `GEMINI_API_KEY` → key từ Google AI Studio
  - `GATEWAY_SHARED_TOKEN` → chuỗi ngẫu nhiên (dùng `openssl rand -hex 16`)

### Bước 5 — Test end-to-end trên cloud

```bash
GATEWAY_URL=https://task-gateway-xxxx.onrender.com
TOKEN=your_shared_token

curl -X POST $GATEWAY_URL/command \
  -H "Content-Type: application/json" \
  -H "X-Auth-Token: $TOKEN" \
  -d '{"text": "việc hôm nay có gì", "session_id": "s1"}'
```

> **Lưu ý cold-start:** Render free tier ngủ sau 15 phút idle. Request đầu tiên mất ~30s. Chấp nhận được cho demo.

---

## Biến môi trường tổng hợp

| Service | Biến | Bắt buộc | Ghi chú |
|---|---|---|---|
| api_server | `PORT` | auto | Render tự set |
| mcp_server | `PORT` | auto | |
| mcp_server | `API_BASE_URL` | ✓ | URL của api_server |
| gateway | `PORT` | auto | |
| gateway | `MCP_BASE_URL` | ✓ | URL của mcp_server |
| gateway | `GEMINI_API_KEY` | ✓ | Google AI Studio |
| gateway | `GATEWAY_SHARED_TOKEN` | khuyến nghị | Bảo vệ endpoint public |

---

## Cấu trúc request/response gateway

**POST `/command`**

Headers: `X-Auth-Token: <token>` (nếu có GATEWAY_SHARED_TOKEN)

Body:
```json
{
  "text": "lệnh giọng nói đã chuyển thành text",
  "session_id": "uuid-từ-app"
}
```

Response:
```json
{
  "speech": "Câu trả lời tiếng Việt tự nhiên để đọc lên",
  "session_id": "uuid-từ-app"
}
```

Session tự reset sau 30 phút không có request.

---

## Mốc Phase 1 ✓

```
curl POST /command {"text": "việc hôm nay có gì"}
→ {"speech": "Hôm nay bạn có ..."}
```

---

## Phase 2 — Android App (Push-to-Talk)

### Cấu trúc

```
android/
├── app/src/main/java/com/demo/voiceassistant/
│   ├── Config.kt           ← URL gateway + auth token
│   ├── MainActivity.kt     ← 1 nút "Nói", quản lý luồng
│   ├── SpeechController.kt ← STT vi-VN (SpeechRecognizer)
│   ├── ApiClient.kt        ← POST /command qua OkHttp
│   └── SpeakController.kt  ← TTS đọc kết quả
└── app/src/main/res/
    └── layout/activity_main.xml
```

### Mở trong Android Studio

1. **File → Open** → chọn thư mục `android/`
2. Android Studio tự tải Gradle và sync dependencies
3. Cắm máy thật (hoặc bật AVD) → **Run**

> Nếu dùng máy ảo (AVD): chọn image có **Google Play** để `SpeechRecognizer` hoạt động với vi-VN.

### Luồng hoạt động Phase 2

```
Bấm nút "Nói"
  → SpeechRecognizer (vi-VN) nghe ~5 giây
  → text → POST https://task-gateway-0le7.onrender.com/command
             header: X-Auth-Token: <token>
  → {"speech": "..."} → TextToSpeech đọc lên
```

**Lưu ý cold-start:** Request đầu tiên sau 15 phút idle mất ~30s (Render free tier). Timeout của ApiClient đã set 90s.

### Đổi gateway / token

Sửa [android/app/src/main/java/com/demo/voiceassistant/Config.kt](android/app/src/main/java/com/demo/voiceassistant/Config.kt):

```kotlin
object Config {
    const val GATEWAY_URL = "https://task-gateway-xxxx.onrender.com"
    const val AUTH_TOKEN  = "your_token_here"
}
```

### Mốc Phase 2 ✓

Bấm nút → nói "việc hôm nay có gì" → app đọc lại danh sách bằng giọng tiếng Việt.