# Vapi (agent thoại)

Source: https://docs.vieneu.io/vi/docs/cloud-api/vapi

[Vapi](https://vapi.ai) dựng các agent thoại biết nghe máy và gọi đi. Nó phát
tiếng qua một nhà cung cấp TTS do bạn chọn — và provider `custom-voice` cho phép
nhà cung cấp đó là bất kỳ endpoint HTTPS nào. VieNeu hiện thực đúng những gì Vapi
mong đợi, nên agent của bạn có thể trả lời bằng tiếng Việt.

Điều này quan trọng vì tiếng Việt gần như vắng bóng trong các nhà cung cấp TTS
realtime mà Vapi có sẵn.

## Cấu hình assistant {#configure-the-assistant}

```json
{
  "voice": {
    "provider": "custom-voice",
    "server": {
      "url": "https://api.vieneu.io/api/v1/vapi/speech?voiceId=Ngọc%20Lan",
      "secret": "vn_sk_your_key_here",
      "timeoutSeconds": 30
    }
  }
}
```

Tích hợp chỉ có vậy. Hai chi tiết gánh toàn bộ phần việc:

**`secret` chính là API key VieNeu của bạn.** Vapi gửi nó trong header
`X-VAPI-SECRET`, còn cấu hình của Vapi không có trường nào cho header
`Authorization` — nên key phải đặt ở đây. Vẫn là key đó, vẫn được kiểm tra theo
cách đó, và vẫn tính tiền vào cùng một tài khoản.

**Giọng đọc nằm trong URL.** Payload request của Vapi không có trường nào cho
giọng, nên hãy truyền `?voiceId=` (đã URL-encode). Xem danh sách lựa chọn bằng
`GET /v1/audio/voices`. Thêm `&engine=v4` để dùng engine cao cấp nếu gói của bạn
có. Bỏ hẳn `voiceId` thì bạn nhận giọng mặc định của engine.

## Điều gì diễn ra ở mỗi lượt nói {#what-happens-on-each-utterance}

Vapi gửi POST:

```json
{
  "message": {
    "type": "voice-request",
    "text": "Xin chào, tôi có thể giúp gì cho bạn?",
    "sampleRate": 24000
  }
}
```

VieNeu trả về `200` với `Content-Type: application/octet-stream` cùng dữ liệu PCM
thô, mono, 16-bit little-endian, đúng sample rate đó, được stream ngay trong lúc
sinh chứ không đệm lại — nhờ vậy agent bắt đầu nói sớm hơn.

Tuỳ transport, Vapi sẽ yêu cầu 8000, 16000, 22050 hoặc 24000 Hz. Cả bốn đều được
hỗ trợ, và phản hồi luôn ở đúng sample rate được yêu cầu: PCM thô không tự mang
thông tin sample rate, nên trả về khác đi là âm thanh sẽ sai cao độ.

## Tính phí và hành vi {#billing-and-behaviour}

Tính phí theo số ký tự gửi lên, như phần còn lại của `/v1`. Audio bị cắt ngang sẽ
được hoàn tiền tự động.

**Tinh chỉnh bằng AI không bao giờ chạy trên endpoint này**, bất kể cài đặt tài
khoản của bạn là gì. Việc của một agent là trả lời nhanh, mà thêm một vòng gọi
model trước mỗi lượt nói thì đi ngược lại điều đó. Bước chuẩn bị văn bản tất định
vẫn chạy, nên tiếng Việt vẫn được phát âm đúng. Nếu văn bản của agent có những
thứ bạn muốn đọc theo một cách nhất định — tiền tệ, ngày tháng, mã sản phẩm —
hãy chuẩn hoá chúng ngay trong prompt, nơi bạn nhìn thấy được kết quả.

## Nói thật về độ trễ {#latency-honestly}

**Với các gói thường**, gói âm thanh đầu tiên rời server sau khoảng 1–2 giây.
Chừng đó đủ cho một agent trả lời một câu hỏi, đọc một thực đơn, xác nhận một
lượt đặt chỗ. Nó **chậm hơn mức 200–300 ms** mà những nhà cung cấp nhanh nhất
trong nhóm chỉ hỗ trợ tiếng Anh đạt được, và người nghe sẽ nhận ra một nhịp chờ
trước khi agent cất tiếng.

Hợp đồng Enterprise chạy trên node riêng, trần độ trễ do hai bên thoả thuận —
xuống tới ≤ 250 ms — và đó là phiên bản bạn cần nếu nhịp chờ ấy là thứ không
chấp nhận được. Xem [Nói thật về độ trễ](./streaming#latency-honestly).

Hai việc đáng làm: giữ `timeoutSeconds` ở 30 trở lên, và giữ mỗi lượt nói ngắn
gọn — một đoạn văn bắt người ta chờ lâu hơn ba câu.

**Mỗi lượt nói bị giới hạn 800 ký tự** (khoảng 50 giây âm thanh), và giới hạn này
tồn tại đúng vì lý do đó: một lượt dài hơn không thể trả kịp trong hạn 30 giây
của Vapi, nên nó sẽ bị cắt ngang giữa từ mà vẫn bị tính tiền. Nếu agent của bạn
sinh ra câu trả lời dài hơn, hãy tách nhỏ — Vapi sẽ yêu cầu từng mảnh riêng và
người gọi nghe chúng nối liền nhau.

Nếu sau 12 giây vẫn chưa worker nào bắt đầu trả lời, request sẽ trả về `503`
thay vì giữ socket cho tới khi Vapi bỏ cuộc. Khác biệt đó có ý nghĩa: một mã
trạng thái mà `fallbackPlan` của bạn xử lý được thì hơn hẳn một cú timeout, vốn
chỉ trông như một nhà cung cấp đã ngừng phản hồi.

Hãy đặt `fallbackPlan` cho assistant nếu việc hụt một lượt nói còn tệ hơn việc
dùng một giọng không phải tiếng Việt.

## Xử lý sự cố {#troubleshooting}

| Triệu chứng | Nguyên nhân |
|---|---|
| `401` | `secret` không phải API key VieNeu hợp lệ, hoặc key đã bị thu hồi. |
| `400` kèm thông báo về giọng | `voiceId` không có trong danh mục của engine đó — hãy kiểm tra `GET /v1/audio/voices`. |
| `403` | Token grant của key đã cạn hoặc hết hạn, hoặc gói của bạn không bao gồm engine đó. |
| `400` về độ dài `message.text` | Lượt nói dài quá 800 ký tự — hãy tách nhỏ. |
| `503` có nêu một sample rate | Chưa worker nào trong pool được cập nhật để xuất PCM thô. Chúng tôi failover khắp cụm trước đã, nên lỗi này nghĩa là tất cả đều chưa. Hãy thử lại; liên hệ chúng tôi nếu vẫn còn. |
| `503` báo không worker nào trả lời | Không có gì bắt đầu tạo audio trong vòng 12 giây. Thường là do đột biến tải; `fallbackPlan` của bạn đỡ lượt nói đó. |
| `502` báo không có audio | Worker đã nhận request rồi không sinh ra gì cả. Không bị tính phí. |

Mọi phản hồi đều mang `X-Request-Id`. Gửi kèm mã đó, chúng tôi tìm được đúng lệnh
gọi ấy.
