
Xây dựng Voice Agent trên OpenAI Realtime API: Hướng dẫn 7 bước cho môi trường Production (2026)
Agent thử nghiệm của chúng tôi đã trả lời cuộc gọi Twilio và nói từ đầu tiên chỉ sau 1,1 giây kể từ khi người gọi ngừng nói. Đây là độ trễ vòng lặp p50, được đo qua 40 cuộc gọi trên gpt-realtime-2 với semantic_vad. Không phải phép thuật. OpenAI Realtime API thực hiện chuyển đổi giọng nói thành giọng nói (speech-to-speech) trong cùng một socket, giúp bạn bỏ qua quy trình chuyển tiếp STT → LLM → TTS vốn thường cộng thêm khoảng 600ms xử lý trung gian. Tuy nhiên, các cài đặt mặc định sẽ không đưa bạn xuống dưới mức một giây. Dưới đây là quy trình 7 bước mà chúng tôi đã triển khai, kèm theo mã nguồn và bảng độ trễ.
Đây là hướng dẫn xây dựng kỹ thuật, không phải bài giải thích khái niệm. Nếu bạn muốn tìm hiểu phân tích chi tiết trước, hãy đọc voice agent AI thực chất là gì, sau đó quay lại. Mọi phần dưới đây đều giả định bạn đã có khóa API OpenAI và môi trường Node runtime.
Những điểm chính:
- gpt-realtime-2 thực hiện speech-to-speech trong một socket duy nhất — không cần chuyển tiếp STT/LLM/TTS, tiết kiệm ~600ms.
- Tạo khóa ephemeral ở phía server; không bao giờ gửi khóa API tiêu chuẩn của bạn lên trình duyệt.
- Luồng media của Twilio là 8kHz μ-law; cần lấy mẫu lại (resample) sang PCM16 24kHz cho Realtime API.
- Chúng tôi đo được độ trễ vòng lặp p50 1,1s / p95 1,9s. Tính năng barge-in hoạt động thông qua
response.cancel.
Bạn sẽ xây dựng gì trong 7 bước
Hướng dẫn này xây dựng một voice agent OpenAI Realtime API trả lời cuộc gọi điện thoại, phản hồi trong dưới 1,5 giây, gọi một hàm thực tế giữa chừng cuộc hội thoại và cho phép người gọi ngắt lời. Quy trình rất ngắn gọn: người gọi quay số, âm thanh được truyền trực tuyến đến server của bạn, server kết nối nó với gpt-realtime-2 qua một socket duy nhất, mô hình sẽ nói và có thể kích hoạt các lệnh gọi công cụ (tool calls), sau đó âm thanh được truyền ngược lại.
Dưới đây là lộ trình, và bạn có thể dừng lại ở bất kỳ bước nào phù hợp với nhuuse case của mình:
- Tạo khóa ephemeral (tuyến server)
- Mở và cấu hình phiên làm việc (session)
- Thêm chức năng gọi hàm (function calling)
- Kết nối với số điện thoại qua Twilio
- Xử lý barge-in và ngắt lời
- Tinh chỉnh độ trễ xuống dưới một giây
- Triển khai và củng cố bảo mật
Ba phương thức truyền tải (transport) mang âm thanh, và lựa chọn của bạn phụ thuộc vào nguồn gốc âm thanh. Trình duyệt thu âm trực tiếp (WebRTC), server của bạn đã có luồng thô (WebSocket), hoặc mạng điện thoại cung cấp nó (SIP). Chúng ta sẽ sử dụng WebSocket cho cầu nối Twilio và đề cập đến các phương thức khác khi phù hợp.
Bước 1: Tạo khóa Ephemeral (bước bắt buộc không thể bỏ qua)
Không bao giờ phơi bày khóa API OpenAI tiêu chuẩn của bạn cho trình duyệt hoặc thiết bị khách hàng. Realtime API cung cấp khóa ephemeral tồn tại trong thời gian ngắn chính xác cho mục đích này. Server của bạn gọi POST /v1/realtime/client_secrets bằng khóa thật, cung cấp cho client một token hết hạn sau khoảng một phút, và client sẽ kết nối bằng token đó thay thế.
Dưới đây là một tuyến Express tối thiểu để tạo khóa:
// server.js
import express from "express";
const app = express();
app.get("/session", async (req, res) => {
const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { type: "realtime", model: "gpt-realtime-2" },
}),
});
const data = await r.json();
res.json({ client_secret: data.value, expires_at: data.expires_at });
});
app.listen(3000);Trình duyệt fetch /session, đọc secret ngắn hạn và mở kết nối Realtime với nó. Nếu agent của bạn chỉ chạy trên server (trường hợp Twilio ở Bước 4), bạn có thể bỏ qua bước bàn giao client và mở socket trực tiếp từ backend bằng khóa tiêu chuẩn. Quy trình ephemeral tồn tại để bảo vệ các client không đáng tin cậy.
Bước 2: Mở Phiên làm việc và Cấu hình gpt-realtime-2
Mở một kết nối, sau đó gửi session.update để thiết lập mô hình, định dạng âm thanh, giọng nói và phát hiện lượt nói. Tài liệu của OpenAI khuyến nghị bắt đầu với reasoning.effort đặt ở mức low và chỉ tăng lên nếu logic công cụ của bạn cần độ chính xác cao hơn, vì nỗ lực suy luận cao hơn sẽ gây tốn độ trễ. Âm thanh chạy ở định dạng PCM16 24kHz theo cả hai chiều.
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2",
output_modalities: ["audio"],
audio: {
input: { format: "pcm16", sample_rate: 24000 },
output: { format: "pcm16", sample_rate: 24000, voice: "marin" },
},
instructions: "You are a reservations agent for a restaurant. Be brief.",
reasoning: { effort: "low" },
turn_detection: { type: "semantic_vad" },
},
}));Phương thức truyền tải mà bạn bọc socket vào đó phụ thuộc vào nguồn âm thanh:
| Transport | Sử dụng khi | Nguồn âm thanh |
|---|---|---|
| WebRTC | Trình duyệt hoặc ứng dụng di động thu âm mic trực tiếp | Thiết bị client |
| WebSocket | Server của bạn đã giữ một luồng âm thanh thô | Pipeline server |
| SIP | Bạn muốn OpenAI xử lý phần kết nối điện thoại | PSTN / viễn thông |
Để xem danh sách đầy đủ các trường session và bộ tính năng GA, tài liệu OpenAI Realtime API là nguồn tham chiếu chính xác. Chúng ta sẽ sử dụng WebSocket vì Twilio cung cấp cho chúng ta âm thanh thô ở Bước 4.
Bước 3: Thêm Function Calling (để agent thực sự làm được việc)
Một voice agent không thể hành động chỉ là giọng đọc lồng tiếng. Function calling cho phép gpt-realtime-2 tạm dừng giữa cuộc hội thoại, yêu cầu mã của bạn chạy một tác vụ, và tiếp tục nói với kết quả nhận được. Bạn khai báo một công cụ trong session, mô hình sẽ phát ra sự kiện function_call_arguments.done khi nó muốn gọi, bạn thực thi công việc và gửi kết quả trở lại.
Khai báo công cụ, sau đó xử lý sự kiện:
// in session.update -> session.tools:
tools: [{
type: "function",
name: "book_reservation",
description: "Book a table for a given party size and time.",
parameters: {
type: "object",
properties: {
party_size: { type: "integer" },
time: { type: "string", description: "ISO 8601 datetime" },
},
required: ["party_size", "time"],
},
}]
// handling the call:
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const result = await bookTable(args); // your real logic
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" })); // let it speak the result
}Lý do phổ biến nhất khiến các công cụ không bao giờ được kích hoạt một cách âm thầm: không lắng nghe sự kiện function_call_arguments.done và không gửi response.create sau đó. Mô hình đã tạo ra lệnh gọi, bạn bỏ qua nó, và người gọi nghe thấy sự im lặng chết chóc.
Nếu agent của bạn xử lý nhiều công cụ, OpenAI Agents SDK đã thay đổi cục diện ở điểm này. Bản cập nhật ngày 15 tháng 4 năm 2026 đã đưa Model Context Protocol (MCP) thành tính năng hạng nhất và biến việc bàn giao giữa các sub-agent thành một nguyên tắc runtime. Thay vì nhồi nhét mọi công cụ vào một prompt, một agent định tuyến có thể chuyển việc đặt chỗ cho một sub-agent chuyên về đặt phòng và câu hỏi thanh toán cho một sub-agent khác. Hướng dẫn nhanh về voice agents của Agents SDK bọc cùng phiên Realtime trong một RealtimeAgent và cung cấp khả năng bàn giao mà không cần viết vòng lặp điều phối riêng.
Bước 4: Kết nối với Số điện thoại (Twilio)
Để trả lời các cuộc gọi thực tế, bạn cần kết nối nhà cung cấp dịch vụ viễn thông vào socket. Với Twilio, bạn trỏ một cuộc gọi đến vào TwiML <Connect><Stream> để mở WebSocket đến server của bạn, và chuyển tiếp các khung âm thanh giữa Twilio và Realtime API. SIP là một lựa chọn thay thế — OpenAI Realtime chấp nhận SIP trực tiếp, giúp loại bỏ hoàn toàn bộ phận chuyển tiếp media nếu bạn không cần chạm vào âm thanh.
TwiML khởi động luồng:
<Response>
<Connect>
<Stream url="wss://your-server.com/twilio-stream" />
</Connect>
</Response>Đây là điểm dễ mắc lỗi khiến bạn mất cả ngày nếu bỏ sót: luồng media của Twilio là 8kHz μ-law, trong khi Realtime API yêu cầu PCM16 24kHz. Bạn phải lấy mẫu lại (resample) theo cả hai chiều, nếu không âm thanh sẽ bị méo tiếng hoặc nghe như chipmunk.
// inbound: Twilio (8kHz μ-law base64) -> Realtime (24kHz PCM16)
const pcm16 = upsample(muLawDecode(Buffer.from(msg.media.payload, "base64")), 8000, 24000);
realtime.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: pcm16.toString("base64"),
}));
// outbound: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
event: "media",
media: { payload: ulaw.toString("base64") },
}));Định dạng khung đầy đủ nằm trong tài liệu Twilio Media Streams. Hãy giữ quá trình resample nhẹ nhàng, vì một thư viện nặng nề ở đây sẽ thêm độ trễ mà bạn phải trả giá cho mỗi khung hình.
Bước 5: Xử lý Barge-in và Ngắt lời
Một agent production cho phép người gọi nói chồng lên nó. Barge-in nghĩa là phát hiện người gọi bắt đầu nói trong khi agent đang nói dở câu, sau đó cắt ngang agent một cách sạch sẽ. Realtime API xử lý việc này bằng response.cancel: khi phát hiện lượt nói báo cáo có giọng nói bắt đầu trong lúc phát lại, bạn hủy phản hồi đang hoạt động và xóa sạch bất kỳ âm thanh nào đã được đệm sẵn hướng về phía người gọi.
if (event.type === "input_audio_buffer.speech_started") {
realtime.send(JSON.stringify({ type: "response.cancel" }));
twilioWs.send(JSON.stringify({ event: "clear" })); // drop queued playback
}Phát hiện lượt nói có hai chế độ, và lựa chọn này rất quan trọng. server_vad kích hoạt dựa trên ngưỡng im lặng thô và có xu hướng cắt ngang người gọi ở những khoảng nghỉ tự nhiên. semantic_vad chờ cho đến khi mô hình nghĩ rằng người gọi thực sự đã kết thúc một ý nghĩ, do đó nó tạo ra ít lần ngắt lời sai hơn trong những khoảng nghỉ suy nghĩ. Đối với cuộc gọi điện thoại, semantic VAD là lựa chọn mang lại cảm giác con người hơn.
Bước 6: Tinh chỉnh Độ trễ xuống dưới Một Giây
Đây là lúc bản demo trở thành sản phẩm, vì vậy dưới đây là các con số từ chính hệ thống của chúng tôi, không phải ngân sách lý thuyết. Chúng tôi đã chạy cùng một agent nhà hàng qua 40 cuộc gọi thử nghiệm vào tháng 5 năm 2026, trên một máy chủ nhỏ đặt gần khu vực của OpenAI, chỉ thay đổi cài đặt phát hiện lượt nói và suy luận.
| Cấu hình | Vòng lặp p50 | p95 | Ghi chú |
|---|---|---|---|
| server_vad, reasoning low | ~1,4s | ~2,3s | nhiều lần barge-in sai hơn ở các khoảng nghỉ |
| semantic_vad, reasoning low | ~1,1s | ~1,9s | mặc định production của chúng tôi |
| semantic_vad, reasoning medium | ~1,8s | ~3,1s | độ chính xác công cụ tốt hơn, chậm hơn |
Các đòn bẩy thực sự tạo ra sự khác biệt, theo thứ tự tác động:
- Giữ
reasoning.effortở mức low trừ khi một công cụ cụ thể thực sự cần độ chính xác cao. Mức medium gần như làm đôi độ trễ p50 của chúng tôi. - Không đẩy âm thanh nhanh hơn thời gian thực. Việc tràn ngập
input_audio_buffer.appendsẽ làm tràn bộ đệm và gây trôi lệch; hãy điều chỉnh khung hình theo thời gian đồng hồ thực. - Giữ socket luôn ấm. Việc mở lạnh kết nối cho mỗi cuộc gọi sẽ thêm thời gian bắt tay vào độ trễ từ đầu tiên. Gom nhóm các kết nối nơi khối lượng cuộc gọi cho phép.
- Resample hiệu quả. Một bộ resample ngây thơ trong đường dẫn nóng đã thêm ~80ms mỗi lượt cho chúng tôi.
Chi phí chạy hệ thống này mỗi phút là bao nhiêu khi đi vào hoạt động? Chúng tôi đã tính toán riêng phần bring-your-own-key — hãy xem chi phí mỗi phút của voice agent BYOK thay vì tính toán lại ở đây.
Bước 7: Triển khai và Củng cố cho Production
Khoảng cách giữa "nó hoạt động trên laptop của tôi" và "nó sống sót qua 500 cuộc gọi mỗi ngày" là một vài lỗi thất bại nổi tiếng. Dưới đây là danh sách kiểm tra củng cố, rút ra từ những sai lầm thực sự phá vỡ các agent Realtime:
| Cạm bẫy | Triệu chứng | Khắc phục |
|---|---|---|
| Sai tốc độ mẫu | âm thanh méo / chipmunk | PCM16 24kHz cả hai chiều |
Bỏ qua function_call_arguments.done | công cụ không bao giờ chạy | lắng nghe và gửi response.create |
| Đẩy âm thanh nhanh hơn thời gian thực | tràn bộ đệm, trôi lệch | điều chỉnh khung hình theo thời gian thực |
| Không có logic kết nối lại | cuộc gọi rớt khi socket gặp sự cố | tự động kết nối lại + khôi phục phiên |
Không xử lý response.done | các lượt nói chồng chéo | chặn lượt tiếp theo dựa trên response.done |
Hai điểm nữa cho lưu lượng truy cập thực tế. Đối với các cuộc gọi dài, hãy xoay vòng hoặc tái tạo seed phiên làm việc sau mỗi vài lượt để ngữ cảnh không bị trôi lệch, vì một cuộc gọi 20 phút tích lũy trạng thái mà mô hình bắt đầu vấp phải. Và ghi log mọi lệnh gọi công cụ kèm đối số và kết quả; khi người gọi nói "agent đã đặt sai giờ", bản ghi chép alone sẽ không cho bạn biết liệu mô hình hay mã của bạn đã sai.
Nếu bạn áp dụng lộ trình Agents SDK từ Bước 3, sandbox container mới của nó chạy mã công cụ trong môi trường cô lập, điều này rất quan trọng khi các công cụ của bạn chạm vào hệ thống tệp hoặc shell thay vì chỉ một API.
Khi nào bạn nên mua Nền tảng Quản lý thay vì Tự xây dựng
Xây dựng trực tiếp trên Realtime API mang lại cho bạn quyền kiểm soát lớn nhất và chi phí mỗi phút thấp nhất, nhưng bạn phải sở hữu logic kết nối lại, cầu nối viễn thông, tuân thủ và khả năng quan sát — tất cả những phần kém hấp dẫn của Bước 4 đến Bước 7. Nếu bạn cần một agent điện thoại hoạt động trong tuần này và không muốn duy trì bộ chuyển tiếp media, một nền tảng quản lý là lựa chọn nhanh hơn.
Chúng tôi đã xây dựng cùng một agent trên ba nền tảng lớn và so sánh chúng một cách trung thực: Retell, Vapi, hoặc Bland. Nếu bạn vẫn đang phân vân nên đứng về phía nào, hãy xem qua khung quyết định build-vs-buy đầy đủ trước khi cam kết thời gian kỹ thuật.
Khi các đội nhóm muốn quyền kiểm soát của một bản xây dựng Realtime tùy chỉnh mà không cần nhân sự vận hành, đó là công việc chúng tôi làm: phát triển voice agent production, từ cầu nối viễn thông đến tinh chỉnh độ trễ như trên. Sẵn lòng xem xét use case của bạn nếu bạn đang cân nhắc.
Về tác giả — Mert Batur Gurbuz là Đồng sáng lập Techsy.io, nơi đội ngũ cung cấp các AI agent, hệ thống tự động hóa và pipeline voice/SDR cho khách hàng B2B. Anh ấy đang học tại Đại học Birmingham và viết về ngăn xếp công cụ LLM mà đội ngũ Techsy thực sự sử dụng trong production. LinkedIn
Câu hỏi Thường gặp
Độ trễ của voice agent OpenAI Realtime API là bao nhiêu?
Trong bản xây dựng của chúng tôi trên gpt-realtime-2 với semantic_vad và nỗ lực suy luận thấp, độ trễ vòng lặp đo được là p50 1,1s và p95 1,9s qua 40 cuộc gọi thử nghiệm. Speech-to-speech trong một socket tránh được quy trình chuyển tiếp STT/LLM/TTS, điều khiến các phản hồi dưới một giây trở nên khả thi.
Tôi cần WebRTC, WebSocket hay SIP cho voice agent của mình?
Sử dụng WebRTC khi trình duyệt hoặc ứng dụng di động thu âm mic trực tiếp, WebSocket khi server của bạn đã giữ một luồng âm thanh thô (trường hợp cầu nối Twilio), và SIP khi bạn muốn OpenAI xử lý phần kết nối điện thoại mà không cần bộ chuyển tiếp media riêng. Hầu hết các agent điện thoại sử dụng WebSocket hoặc SIP.
Làm thế nào để kết nối OpenAI Realtime API với Twilio?
Trỏ một cuộc gọi Twilio đến vào TwiML <Connect><Stream> để mở WebSocket đến server của bạn, sau đó chuyển tiếp âm thanh giữa Twilio và socket Realtime. Lấy mẫu lại 8kHz μ-law của Twilio sang PCM16 24kHz của API theo cả hai chiều, nếu không âm thanh sẽ bị méo.
Function calling hoạt động như thế nào trong Realtime API?
Bạn khai báo các công cụ trong cấu hình session. Khi mô hình muốn gọi một công cụ, nó phát ra sự kiện function_call_arguments.done. Bạn thực thi công việc, gửi kết quả trở lại dưới dạng mục hội thoại function_call_output, sau đó gửi response.create để agent nói ra kết quả. Quên bước cuối cùng này là lý do khiến các công cụ thường "thất bại âm thầm".
Bạn xử lý ngắt lời (barge-in) trong Realtime API như thế nào?
Khi phát hiện lượt báo cáo input_audio_buffer.speech_started trong lúc phát lại, hãy gửi response.cancel để dừng phản hồi đang hoạt động và xóa bất kỳ âm thanh đầu ra nào đã xếp hàng hướng về người gọi. Kết hợp nó với semantic_vad để các khoảng nghỉ tự nhiên không kích hoạt ngắt lời sai giữa câu.
OpenAI Realtime API sử dụng tốc độ mẫu âm thanh nào?
Realtime API sử dụng âm thanh PCM16 24kHz theo cả hai chiều. Các nhà cung cấp viễn thông như Twilio cung cấp 8kHz μ-law, vì vậy cầu nối điện thoại phải lấy mẫu lên khi vào và lấy mẫu xuống khi ra. Tốc độ mẫu không khớp là nguyên nhân phổ biến nhất gây ra âm thanh bị biến dạng.
Chi phí chạy voice agent trên Realtime API là bao nhiêu?
Chi phí được thúc đẩy bởi số phút âm thanh đầu vào và đầu ra trên gpt-realtime-2, và kinh tế học bring-your-own-key khác biệt rõ rệt so với nền tảng quản lý tính phí mỗi phút. Chúng tôi đã tính toán đầy đủ trong phân tích giá voice agent thay vì ước tính ở đây.
Tôi nên xây dựng trên Realtime API hay sử dụng Retell, Vapi, hoặc Bland?
Hãy xây dựng trực tiếp khi bạn muốn kiểm soát tối đa và chi phí mỗi phút thấp nhất, đồng thời có thể tự sở hữu việc kết nối lại, viễn thông và tuân thủ. Mua nền tảng quản lý khi tốc độ ra mắt quan trọng hơn. So sánh Retell vs Vapi vs Bland và khung build-vs-buy của chúng tôi bao gồm các đánh đổi.
Bản cập nhật OpenAI Agents SDK tháng 4 năm 2026 thay đổi điều gì cho voice agents?
Bản nâng cấp ngày 15 tháng 4 năm 2026 đã đưa Model Context Protocol thành tính năng hạng nhất, thêm sandbox container cho mã công cụ và biến việc bàn giao sub-agent thành nguyên tắc runtime. Đối với voice agents, điều này có nghĩa là một agent định tuyến có thể bàn giao cho các sub-agent chuyên biệt thay vì nhồi nhét mọi công cụ vào một prompt.