GPT-Live Giọng nói thời gian thực
Tương tác giọng nói song công toàn phần và xử lý sự kiện phiên qua GPT-Live WebSocket.
GPT-Live cung cấp tương tác giọng nói song công toàn phần qua WebSocket liên tục, cho phép gửi âm thanh đầu vào, nhận giọng nói và văn bản chuyển đổi, cập nhật cấu hình phiên và duy trì ngữ cảnh nhiều lượt trên cùng phiên. Mô hình hiện được ghi nhận là gpt-live-1; phạm vi khả dụng thực tế phụ thuộc vào tài khoản hiện tại và danh mục mô hình.
wss://api.agtcloud.cn/v1/live/sessionsXác thực và kết nối
Ứng dụng phía máy chủ dùng Bearer API Key trong Header bắt tay WebSocket. Mô hình không nằm trong tham số truy vấn mà được cấu hình trong sự kiện session.start đầu tiên sau khi kết nối.
| Mục | Giá trị |
|---|---|
| Địa chỉ kết nối | wss://api.agtcloud.cn/v1/live/sessions |
| Authorization | Bearer your-api-key |
| Vị trí mô hình | session.start.session.model |
| Âm thanh đầu vào | Khối âm thanh PCM thô mã hóa Base64 |
Chỉ lưu API Key dài hạn trên máy chủ đáng tin cậy. Tài liệu này chỉ đề cập tuyến WebSocket này, không bao gồm WebRTC, SIP, sideband, fork phiên hoặc tải bản ghi âm.
Quy trình phiên
- Thiết lập kết nối WebSocket bằng Bearer API Key.
- Gửi
session.startđể cấu hình mô hình, chỉ dẫn, âm thanh và phương thức ủy quyền. - Sau khi nhận
session.started, bắt đầu gửi âm thanh hoặc lệnh ứng dụng. - Liên tục xử lý sự kiện văn bản chuyển đổi đầu vào, văn bản đầu ra, âm thanh đầu ra và mức sử dụng.
- Dừng tạo âm thanh rồi gửi
session.close. - Tiếp tục nhận sự kiện và hoàn tất bằng mức sử dụng cuối cùng trong
session.closed.
Kết thúc một lượt nói không có nghĩa là kết thúc phiên. Hội thoại nhiều lượt nên giữ cùng kết nối; chỉ gửi session.close khi nghiệp vụ sẵn sàng kết thúc toàn bộ phiên.
Tham số khởi động
| Trường | Kiểu | Mô tả |
|---|---|---|
type | string | Khung đầu tiên cố định là session.start |
event_id | string | Mã định danh sự kiện máy khách tùy chọn để liên kết lỗi |
session.model | string | Bắt buộc, ID mô hình GPT-Live |
session.instructions | string | Chỉ dẫn tùy chọn cho trợ lý giọng nói |
session.audio.format | object | Định dạng âm thanh, ví dụ audio/pcm, 24 kHz |
session.audio.output.voice | string | Giọng nói đầu ra, ví dụ marin; giá trị hợp lệ phụ thuộc vào mô hình |
session.delegation | object | Phương thức ủy quyền, dùng client hoặc responses |
Sau khi kết nối được khởi động, có thể dùng session.update để cập nhật chỉ dẫn và cấu hình phiên được phép thay đổi, rồi chờ session.updated xác nhận. Không gửi lại session.start trên cùng kết nối và không chuyển mô hình giọng nói cấp cao nhất qua sự kiện cập nhật.
Chuẩn bị âm thanh và ví dụ gọi
Ví dụ dùng PCM signed 16-bit little-endian, 24 kHz, đơn kênh, không có phần đầu tệp WAV. Mỗi 100 ms tương ứng 4800 byte; mã hóa từng khối âm thanh thành Base64 khi gửi.
# Chuẩn bị âm thanh đầu vào
ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ac 1 -ar 24000 audio.pcm
# Phát âm thanh đầu ra đã lưu
ffplay -f s16le -ar 24000 -ac 1 reply.pcmVí dụ Node.js cần ws; ví dụ Python chỉ kiểm tra khởi động và đóng phiên, không gửi âm thanh.
npm install ws
python -m pip install websocket-clientimport fs from 'node:fs';
import WebSocket from 'ws';
const key = process.env.API_KEY;
const file = process.argv[2];
if (!key || !file) throw new Error('Cần API_KEY và đường dẫn tệp PCM');
const pcm = fs.readFileSync(file);
if (!pcm.length || pcm.length % 2) {
throw new Error('PCM không được rỗng và phải căn theo mẫu 16 bit');
}
const ws = new WebSocket('wss://api.agtcloud.cn/v1/live/sessions', {
headers: { Authorization: 'Bearer ' + key },
handshakeTimeout: 10000,
});
const audioOut = fs.createWriteStream('reply.pcm');
let started = false;
let closing = false;
let finalized = false;
let sender;
let drain;
let offset = 0;
let inputFinishedAt = 0;
let lastOutputAt = 0;
function send(event) {
ws.send(JSON.stringify(event));
}
function closeSession(reason) {
if (closing) return;
closing = true;
clearInterval(sender);
console.error('Yêu cầu kết thúc:', reason);
if (started && ws.readyState === WebSocket.OPEN) {
send({ type: 'session.close' });
drain = setTimeout(() => ws.terminate(), 15000);
} else {
ws.terminate();
}
}
const limit = setTimeout(() => closeSession('Giới hạn ví dụ 60 giây'), 60000);
ws.on('open', () => send({
type: 'session.start',
event_id: 'start_001',
session: {
model: 'gpt-live-1',
instructions: 'Hãy trả lời ngắn gọn bằng tiếng Việt và chờ người dùng nói trước.',
audio: {
format: { type: 'audio/pcm', rate: 24000 },
output: { voice: 'marin' },
},
delegation: { type: 'client' },
},
}));
ws.on('message', (raw) => {
let event;
try {
event = JSON.parse(raw.toString());
} catch {
closeSession('JSON không hợp lệ');
return;
}
if (event.type !== 'session.output_audio.delta') {
console.log(raw.toString());
}
if (event.type === 'session.started' && !started) {
started = true;
sender = setInterval(() => {
if (closing || ws.readyState !== WebSocket.OPEN) return;
if (offset >= pcm.length) {
inputFinishedAt ||= Date.now();
if (
lastOutputAt &&
Date.now() - Math.max(lastOutputAt, inputFinishedAt) >= 3000
) {
closeSession('Đầu ra đã hoàn tất');
return;
}
send({
type: 'session.input_audio.append',
audio: Buffer.alloc(4800).toString('base64'),
});
return;
}
const chunk = pcm.subarray(offset, offset + 4800);
offset += chunk.length;
send({
type: 'session.input_audio.append',
audio: chunk.toString('base64'),
});
}, 100);
}
if (event.type === 'session.output_audio.delta') {
lastOutputAt = Date.now();
audioOut.write(Buffer.from(event.delta, 'base64'));
}
if (event.type === 'session.output_transcript.delta') {
lastOutputAt = Date.now();
}
if (event.type === 'session.closed') {
finalized = true;
ws.close(1000);
}
if (event.type === 'error') closeSession('Lỗi dịch vụ');
});
ws.on('error', (error) => {
console.error(error.message);
closeSession('Lỗi kết nối');
});
ws.on('close', (code, reason) => {
clearTimeout(limit);
clearTimeout(drain);
clearInterval(sender);
audioOut.end();
console.error({ code, reason: reason.toString(), finalized });
if (!finalized || code !== 1000) process.exitCode = 1;
});
process.on('SIGINT', () => closeSession('Người dùng đã dừng'));Tham khảo sự kiện
API này gửi âm thanh Base64 bằng session.input_audio.append, không dùng khung âm thanh nhị phân và endStream của Muse, cũng không dùng sự kiện input_audio_buffer.* của OpenAI Realtime.
session.start
Khởi động phiên GPT-Live và gửi cấu hình mô hình, chỉ dẫn, âm thanh và ủy quyền.
| Trường | Kiểu | Bắt buộc | Mô tả | Ví dụ |
|---|---|---|---|---|
type | chuỗi | Có | Cố định là session.start. | session.start |
event_id | chuỗi | Không | Mã định danh sự kiện máy khách. | start_001 |
session.model | chuỗi | Có | ID mô hình GPT-Live. | gpt-live-1 |
session.instructions | chuỗi | Không | Chỉ dẫn hội thoại cho trợ lý giọng nói. | Hãy trả lời ngắn gọn bằng tiếng Trung. |
session.audio.format.type | chuỗi | Không | Loại định dạng âm thanh. | audio/pcm |
session.audio.format.rate | số nguyên | Không | Tần số lấy mẫu âm thanh. | 24000 |
session.audio.output.voice | chuỗi | Không | Giọng nói đầu ra. | marin |
session.delegation.type | chuỗi | Có | Phương thức ủy quyền. | client |
{
"type": "session.start",
"event_id": "start_001",
"session": {
"model": "gpt-live-1",
"instructions": "Hãy trả lời ngắn gọn bằng tiếng Trung.",
"audio": {
"format": {
"type": "audio/pcm",
"rate": 24000
},
"output": {
"voice": "marin"
}
},
"delegation": {
"type": "client"
}
}
}Mức sử dụng và tính phí
session.usage.updated trả về giá trị tích lũy của phiên hiện tại, không phải phần sử dụng tăng thêm của một lượt riêng lẻ. Máy khách không nên cộng các giá trị ở nhiều thời điểm; khi đóng bình thường, dùng session.closed.usage.seconds làm thời lượng phiên cuối cùng.
Chi phí có thể gồm thời lượng phiên giọng nói, mức sử dụng mô hình phía sau và gọi công cụ. Giá cụ thể và phạm vi mô hình khả dụng hiện tại dựa theo Danh mục mô hình.
Phương thức ủy quyền
client: ứng dụng tự xử lý tác vụ cần ủy quyền, phù hợp để xác minh phiên giọng nói cơ bản trước.responses: máy chủ thực thi mô hình và công cụ Responses đã cấu hình. Mô hình mục tiêu, công cụ và quyền phải khả dụng trên trang hiện tại.
Khi dùng responses, chi phí mô hình và công cụ của tác vụ phía sau có thể phát sinh riêng. Không coi MODEL_ID trong ví dụ là mô hình cố định luôn khả dụng.
Xử lý lỗi
| Hiện tượng | Kiểm tra và xử lý |
|---|---|
| Bắt tay trả về 401 | Kiểm tra Bearer API Key, quyền mô hình và địa chỉ trang hiện tại |
| Đường dẫn trả về 404 | Xác nhận đường dẫn kết nối cuối cùng là /v1/live/sessions, không ghép /v1 hai lần |
| Khung đầu tiên sai hoặc khởi động quá thời gian | Sau khi kết nối, gửi session.start trước và chỉ gửi âm thanh khi nhận session.started |
| Không có âm thanh hoặc âm thanh bị méo | Kiểm tra độ sâu bit PCM, thứ tự byte, tần số lấy mẫu, số kênh và phần đầu tệp WAV |
Không nhận được session.closed | Lưu mức sử dụng tích lũy cuối cùng, sự kiện lỗi và lý do đóng; không tự phát lại toàn bộ âm thanh |
| Ủy quyền phía sau bị từ chối | Kiểm tra mô hình Responses, công cụ, quyền và phạm vi hỗ trợ của trang hiện tại |