บทนำ: ทำไมต้องเรียนรู้ Realtime API
เมื่อปีที่แล้วผมเคยต้องสร้างแชทบอทที่ตอบเสียงได้ให้ลูกค้าบริษัทหนึ่ง ใช้เวลาหลายสัปดาห์กว่าจะทำให้ระบบทำงานได้อย่างเสถียร แต่ตอนนี้ด้วย
GPT-4o Realtime API จาก
HolySheep AI คุณสามารถสร้างระบบสนทนาเสียงที่ตอบได้ภายในเวลาไม่ถึง 1 ชั่วโมง ความหน่วงเสียง (latency) ต่ำกว่า 50 มิลลิวินาที ทำให้การสนทนารู้สึกเป็นธรรมชาติเหมือนคุยกับคนจริงๆ
ในบทความนี้ผมจะพาทุกคนที่ไม่เคยเขียนโค้ด API มาก่อน เริ่มต้นจากศูนย์จนสามารถสร้างแอปสนทนาเสียงได้จริง
ขั้นตอนที่ 1: สมัครบัญชีและรับ API Key ฟรี
ก่อนจะเขียนโค้ดได้ เราต้องมี "กุญแจ" สำหรับเข้าใช้งาน API ก่อน ซึ่งเรียกว่า API Key
วิธีสมัคร:
- เปิดเบราว์เซอร์ไปที่ https://www.holysheep.ai/register
- กรอกอีเมลและรหัสผ่านที่ต้องการ
- ยืนยันอีเมลที่ได้รับ
- เข้าสู่ระบบแล้วไปที่หน้า Dashboard
- คลิกปุ่ม "สร้าง API Key" แล้วคัดลอกคีย์ที่ปรากฏ
หมายเหตุสำคัญ: API Key จะแสดงเพียงครั้งเดียว คุณต้องเก็บรักษาไว้อย่างปลอดภัย หากหายต้องสร้างใหม่เท่านั้น
หลังจากสมัครเสร็จ คุณจะได้รับ
เครดิตฟรีเมื่อลงทะเบียน สำหรับทดลองใช้งาน ซึ่งเพียงพอสำหรับเรียนรู้และทดสอบโปรเจกต์ขนาดเล็ก
ขั้นตอนที่ 2: ติดตั้งโปรแกรมที่จำเป็น
สำหรับผู้เริ่มต้น ผมแนะนำให้ใช้ Python เพราะเข้าใจง่ายและมีไลบรารีสนับสนุนมากมาย
ติดตั้ง Python:
- ไปที่ https://www.python.org/downloads/
- ดาวน์โหลด Python เวอร์ชันล่าสุด (3.9 ขึ้นไป)
- รันไฟล์ติดตั้ง โดยต้องติ๊กถูกที่ "Add Python to PATH" ด้วย
- เปิด Command Prompt (พิมพ์ cmd ในช่องค้นหา Windows)
- พิมพ์คำสั่ง:
python --version เพื่อตรวจสอบว่าติดตั้งสำเร็จ
ขั้นตอนที่ 3: เขียนโค้ดพื้นฐานสำหรับเชื่อมต่อ Realtime API
สร้างไฟล์ใหม่ชื่อ
realtime_voice.py แล้วพิมพ์โค้ดตามด้านล่างนี้ ซึ่งเป็นโค้ดพื้นฐานที่สุดสำหรับเริ่มต้นใช้งาน
import openai
import pyaudio
import numpy as np
import threading
import time
ตั้งค่าการเชื่อมต่อกับ HolySheep AI
สิ่งสำคัญ: ต้องใช้ base_url นี้เท่านั้น
client = openai.OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY", # แทนที่ด้วย API Key ของคุณ
base_url="https://api.holysheep.ai/v1"
)
ตั้งค่าการบันทึกเสียง
CHUNK_SIZE = 1024
AUDIO_FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000
class RealtimeVoiceAssistant:
def __init__(self):
self.audio = pyaudio.PyAudio()
self.is_recording = False
self.stream = None
def start_recording(self):
"""เริ่มบันทึกเสียงจากไมค์"""
self.stream = self.audio.open(
format=AUDIO_FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK_SIZE
)
self.is_recording = True
print("เริ่มบันทึกเสียงแล้ว...")
def stop_recording(self):
"""หยุดบันทึกเสียง"""
self.is_recording = False
if self.stream:
self.stream.stop_stream()
self.stream.close()
print("หยุดบันทึกเสียงแล้ว")
def record_and_send(self):
"""บันทึกเสียงและส่งไปยัง API"""
audio_frames = []
while self.is_recording:
try:
data = self.stream.read(CHUNK_SIZE)
audio_frames.append(data)
except:
break
# รวมเฟรมเสียงทั้งหมด
audio_data = b''.join(audio_frames)
# แปลงเป็นข้อความด้วย Whisper API
audio_array = np.frombuffer(audio_data, dtype=np.int16)
# ส่งไฟล์เสียงไปยัง API เพื่อแปลงเป็นข้อความ
import io
import wave
# สร้างไฟล์ WAV ในหน่วยความจำ
wav_buffer = io.BytesIO()
with wave.open(wav_buffer, 'wb') as wav_file:
wav_file.setnchannels(CHANNELS)
wav_file.setsampwidth(2)
wav_file.setframerate(RATE)
wav_file.writeframes(audio_data)
wav_buffer.seek(0)
# ส่งไปยัง Whisper API
transcript = client.audio.transcriptions.create(
model="whisper-1",
file=("audio.wav", wav_buffer, "audio/wav")
)
print(f"คุณพูดว่า: {transcript.text}")
return transcript.text
def get_ai_response(self, text):
"""ขอคำตอบจาก AI"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "คุณเป็นผู้ช่วยที่เป็นมิตร ตอบสั้นๆ เป็นภาษาไทย"},
{"role": "user", "content": text}
],
max_tokens=150
)
return response.choices[0].message.content
def text_to_speech(self, text):
"""แปลงข้อความเป็นเสียงพูด"""
response = client.audio.speech.create(
model="tts-1",
voice="alloy",
input=text
)
# บันทึกไฟล์เสียง
with open("response.mp3", "wb") as f:
f.write(response.content)
# เล่นไฟล์เสียง
import subprocess
subprocess.run(["start", "response.mp3"], shell=True)
ทดสอบการทำงาน
if __name__ == "__main__":
assistant = RealtimeVoiceAssistant()
print("=== ระบบสนทนาเสียง AI ===")
print("พิมพ์ 'เริ่ม' เพื่อเริ่มบันทึกเสียง")
print("พิมพ์ 'หยุด' เพื่อหยุดและส่งข้อความ")
print("พิมพ์ 'ออก' เพื่อออกจากโปรแกรม")
while True:
command = input("\n> ")
if command == "เริ่ม":
assistant.start_recording()
elif command == "หยุด":
assistant.stop_recording()
text = assistant.record_and_send()
if text:
response = assistant.get_ai_response(text)
print(f"AI: {response}")
assistant.text_to_speech(response)
elif command == "ออก":
print("ขอบคุณที่ใช้บริการ!")
break
วิธีรันโค้ด:
- เปิด Command Prompt
- พิมพ์:
pip install openai pyaudio numpy
- พิมพ์:
python realtime_voice.py
ขั้นตอนที่ 4: ปรับปรุงโค้ดให้ใช้ WebSocket สำหรับ Realtime
โค้ดข้างต้นเป็นแบบ "ส่งข้อความไป-รอ-รับข้อความกลับ" ซึ่งเหมาะสำหรับเรียนรู้ แต่ถ้าต้องการสนทนาแบบเรียลไทม์จริงๆ ต้องใช้ WebSocket ซึ่งทำให้การส่งและรับข้อมูลเกิดขึ้นพร้อมกัน
import websockets
import asyncio
import json
import base64
import pyaudio
import threading
import queue
ตั้งค่าการเชื่อมต่อ
API_KEY = "YOUR_HOLYSHEEP_API_KEY"
BASE_URL = "https://api.holysheep.ai/v1"
MODEL = "gpt-4o-realtime-preview"
class RealtimeVoiceSession:
def __init__(self):
self.audio_queue = queue.Queue()
self.is_running = False
self.audio = pyaudio.PyAudio()
def start_audio_capture(self):
"""เริ่มจับเสียงจากไมค์ในเธรดแยก"""
def capture_thread():
stream = self.audio.open(
format=pyaudio.paInt16,
channels=1,
rate=16000,
input=True,
frames_per_buffer=1024
)
while self.is_running:
try:
data = stream.read(1024, exception_on_overflow=False)
# เข้ารหัสเสียงเป็น base64
audio_base64 = base64.b64encode(data).decode()
self.audio_queue.put(audio_base64)
except Exception as e:
print(f"ข้อผิดพลาดในการจับเสียง: {e}")
break
stream.stop_stream()
stream.close()
self.is_running = True
thread = threading.Thread(target=capture_thread)
thread.daemon = True
thread.start()
async def send_audio(self, websocket):
"""ส่งเสียงไปยัง API อย่างต่อเนื่อง"""
while self.is_running:
try:
if not self.audio_queue.empty():
audio_data = self.audio_queue.get()
await websocket.send(json.dumps({
"type": "input_audio_buffer.append",
"audio": audio_data
}))
await asyncio.sleep(0.01)
except Exception as e:
print(f"ข้อผิดพลาดในการส่งเสียง: {e}")
break
async def receive_responses(self, websocket):
"""รับคำตอบจาก AI แบบเรียลไทม์"""
while self.is_running:
try:
response = await websocket.recv()
data = json.loads(response)
# จัดการกับข้อความตอบกลับประเภทต่างๆ
if data.get("type") == "session.created":
print("เริ่มการสนทนาเรียลไทม์แล้ว...")
print("พูดอะไรก็ได้เลย (กด Ctrl+C เพื่อออก)")
elif data.get("type") == "conversation.item.created":
if "content" in data:
for item in data["content"]:
if item.get("type") == "input_audio_transcript":
print(f"\nคุณ: {item.get('transcript', '')}")
elif data.get("type") == "response.audio.delta":
# รับเสียงตอบกลับทีละส่วน
audio_delta = data.get("delta", "")
# ถอดรหัสและเล่นเสียงที่นี่
elif data.get("type") == "response.done":
# ได้รับคำตอบเต็มแล้ว
if "output" in data:
for item in data["output"]:
if item.get("type") == "message":
text = item["content"][0]["transcript"]
print(f"AI: {text}")
except websockets.exceptions.ConnectionClosed:
print("การเชื่อมต่อถูกปิดแล้ว")
break
except Exception as e:
print(f"ข้อผิดพลาด: {e}")
await asyncio.sleep(0.1)
async def run_session(self):
"""เรียกใช้งาน WebSocket session"""
# สร้าง WebSocket URL
ws_url = BASE_URL.replace("https://", "wss://") + "/realtime"
headers = {
"Authorization": f"Bearer {API_KEY}",
"OpenAI-Beta": "realtime=v1"
}
async with websockets.connect(ws_url, extra_headers=headers) as ws:
# ตั้งค่า session
await ws.send(json.dumps({
"type": "session.update",
"session": {
"modalities": ["audio", "text"],
"model": MODEL,
"voice": "alloy",
"input_audio_transcription": {"model": "whisper-1"}
}
}))
# รับ event เริ่มต้น
await ws.recv()
# เริ่มจับเสียง
self.start_audio_capture()
# รันทั้งส่งและรับข้อมูลพร้อมกัน
await asyncio.gather(
self.send_audio(ws),
self.receive_responses(ws)
)
def stop(self):
"""หยุดการทำงานทั้งหมด"""
self.is_running = False
self.audio.terminate()
async def main():
session = RealtimeVoiceSession()
try:
await session.run_session()
except KeyboardInterrupt:
print("\nกำลังหยุดการทำงาน...")
finally:
session.stop()
if __name__ == "__main__":
print("=== Realtime Voice Chat ===")
print("ติดตั้งไลบรารีก่อน: pip install websockets")
print()
asyncio.run(main())
ขั้นตอนที่ 5: สร้างหน้าตาแอปที่สวยงามด้วย HTML
ถ้าต้องการให้โปรเจกต์ดูเป็นมืออาชีพมากขึ้น สามารถสร้างหน้าเว็บสำหรับควบคุมการสนทนาได้ โค้ดนี้ใช้ได้กับทั้งคอมพิวเตอร์และโทรศัพท์มือถือ
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Voice Assistant - HolySheep</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.container {
background: white;
border-radius: 20px;
padding: 40px;
max-width: 500px;
width: 100%;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
h1 {
text-align: center;
color: #333;
margin-bottom: 10px;
}
.subtitle {
text-align: center;
color: #666;
margin-bottom: 30px;
}
.status {
text-align: center;
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
font-weight: bold;
}
.status.disconnected {
background: #fee;
color: #c00;
}
.status.connected {
background: #efe;
color: #060;
}
.status.listening {
background: #ffe0b2;
color: #e65100;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
.button-group {
display: flex;
gap: 10px;
justify-content: center;
margin-bottom: 20px;
}
button {
padding: 15px 30px;
font-size: 16px;
border: none;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s;
font-weight: bold;
}
.btn-start {
background: #4caf50;
color: white;
}
.btn-start:hover {
background: #45a049;
transform: scale(1.05);
}
.btn-stop {
background: #f44336;
color: white;
}
.btn-stop:hover {
background: #da190b;
transform: scale(1.05);
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.chat-log {
background: #f5f5f5;
border-radius: 10px;
padding: 15px;
max-height: 300px;
overflow-y: auto;
margin-bottom: 20px;
}
.message {
padding: 10px 15px;
border-radius: 15px;
margin-bottom: 10px;
clear: both;
}
.message.user {
background: #e3f2fd;
float: right;
max-width: 80%;
}
.message.ai {
background: #f3e5f5;
float: left;
max-width: 80%;
}
.message .label {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.api-key-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #eee;
}
.api-key-section label {
display: block;
margin-bottom: 5px;
color: #666;
}
.api-key-section input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
.visualizer {
height: 60px;
background: #f0f0f0;
border-radius: 10px;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.visualizer-bar {
width: 4px;
height: 20px;
background: #667eea;
margin: 0 2px;
border-radius: 2px;
transition: height 0.1s;
}
.wave-indicator {
width: 100px;
height: 100px;
border-radius: 50%;
background: #667eea;
margin: 0 auto 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
}
.wave-indicator:hover {
transform: scale(1.05);
}
.wave-indicator.active {
animation: wave 1s infinite;
background: #4caf50;
}
@keyframes wave {
0%, 100% { box-shadow: 0 0 0 0 rgba(102, 126, 234, 0.7); }
50% { box-shadow: 0 0 0 20px rgba(102, 126, 234, 0); }
}
.mic-icon {
font-size: 40px;
}
</style>
</head>
<body>
<div class="container">
<h1>🎙️ AI Voice Assistant</h1>
<p class="subtitle">สนทนากับ AI ได้เลย</p>
<div id="status" class="status disconnected">
ยังไม่ได้เชื่อมต่อ - กรุณาใส่ API Key
</div>
<div class="wave-indicator" id="micButton" onclick="toggleRecording()">
<span class="mic-icon">🎤</span>
</div>
<div class="button-group">
<button id="startBtn" class="btn-start" onclick="startSession()">
เริ่มต้น
</button>
<button id="stopBtn" class="btn-stop" onclick="stopSession()" disabled>
หยุด
</button>
</div>
<div class="visualizer" id="visualizer">
<!-- แท่งเสียงจะปรากฏที่นี่ -->
<span style="color: #999;">กดไมค์เพื่อเริ่มสนทนา</span>
</div>
<div class="chat-log" id="chatLog">
<!-- ข้อความจะแสดงที่นี่ -->
</div>
<div class="api-key-section">
<label>HolySheep API Key:</label>
<input type="password" id="apiKey" placeholder="ใส่ API Key ของคุณ">
</div>
</div>
<script>
let isRecording = false;
let mediaRecorder = null;
let audioContext = null;
let apiKey = '';
// สร้างแท่ง Visualizer
const visualizer = document.getElementById('visualizer');
for (let i = 0; i < 30; i++) {
const bar = document.createElement('div');
bar.className = 'visualizer-bar';
bar.id = 'bar' + i;
visualizer.appendChild(bar);
}
function startSession() {
apiKey = document.getElementById('apiKey').value;
if (!apiKey) {
alert('กรุณาใส่ API Key ก่อน');
return;
}
document.getElementById('status').className = 'status connected';
document.getElementById('status').textContent = 'เชื่อมต่อแล้ว - พร้อมสนทนา';
document.getElementById('startBtn').disabled = true;
document.getElementById('stopBtn').disabled = false;
addMessage('ai', 'สวัสดีค่ะ! หนูชื่อ AI จาก HolySheep ค่ะ มีอะไรให้ช่วยไหมคะ? 😊');
}
function stopSession() {
isRecording = false;
document.getElementById('status').className = 'status disconnected';
document.getElementById('status').textContent = 'ยังไม่ได้เชื่อมต่อ';
document.getElementById('startBtn').disabled = false;
document.getElementById('stopBtn').disabled = true;
document.getElementById('micButton').classList.remove('active');
}
async function toggleRecording() {
if (!apiKey) {
alert('กรุณากดปุ่มเริ่มต้นก่อน แล้วใส่ API Key');
return;
}
if (isRecording) {
// หยุดบันทึก
if (mediaRecorder && mediaRecorder.state === 'recording') {
mediaRecorder.stop();
}
isRecording = false;
document.getElementById('micButton').classList.remove('active');
} else {
// เริ่มบันทึก
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
// ตั้งค่า Audio Context สำหรับ Visualizer
audioContext = new (window.AudioContext || window.webkitAudioContext)();
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 64;
source.connect(analyser);
// อัพเดท Visualizer
const dataArray = new Uint8Array(analyser.frequencyBinCount);
function updateVisualizer() {
if (!isRecording) return;
analyser.getByteFrequencyData(dataArray);
for (let i = 0; i < dataArray.length; i++) {
const bar = document.getElementById('bar' + i);
if (bar) {
bar.style.height = Math.max(5, dataArray[i]) + 'px';
}
}
requestAnimationFrame(updateVisualizer);
}
mediaRecorder.ondataavailable = async (e) => {
if (e.data.size > 0) {
const text = await sendToAPI(e.data);
if (text) {
addMessage('user', text);
// จำลองการตอบกลับ
setTimeout(() => {
addMessage('ai', 'ข้อความของคุณน่าสนใจมากค่ะ! ให้หนูคิดหน่อยนะ...');
}, 500);
}
}
};
mediaRecorder.start();
isRecording = true;
document.getElementById('micButton').classList.add('active');
updateVisualizer();
} catch (err) {
alert('ไม่สามารถเข้าถึงไมค์ได้: ' + err.message);
}
}
}
async function sendToAPI(audioBlob) {
// ส่งไฟ
แหล่งข้อมูลที่เกี่ยวข้อง
บทความที่เกี่ยวข้อง