บทนำ: ทำไมต้องเรียนรู้ Realtime API

เมื่อปีที่แล้วผมเคยต้องสร้างแชทบอทที่ตอบเสียงได้ให้ลูกค้าบริษัทหนึ่ง ใช้เวลาหลายสัปดาห์กว่าจะทำให้ระบบทำงานได้อย่างเสถียร แต่ตอนนี้ด้วย GPT-4o Realtime API จาก HolySheep AI คุณสามารถสร้างระบบสนทนาเสียงที่ตอบได้ภายในเวลาไม่ถึง 1 ชั่วโมง ความหน่วงเสียง (latency) ต่ำกว่า 50 มิลลิวินาที ทำให้การสนทนารู้สึกเป็นธรรมชาติเหมือนคุยกับคนจริงๆ ในบทความนี้ผมจะพาทุกคนที่ไม่เคยเขียนโค้ด API มาก่อน เริ่มต้นจากศูนย์จนสามารถสร้างแอปสนทนาเสียงได้จริง

ขั้นตอนที่ 1: สมัครบัญชีและรับ API Key ฟรี

ก่อนจะเขียนโค้ดได้ เราต้องมี "กุญแจ" สำหรับเข้าใช้งาน API ก่อน ซึ่งเรียกว่า API Key วิธีสมัคร:
  1. เปิดเบราว์เซอร์ไปที่ https://www.holysheep.ai/register
  2. กรอกอีเมลและรหัสผ่านที่ต้องการ
  3. ยืนยันอีเมลที่ได้รับ
  4. เข้าสู่ระบบแล้วไปที่หน้า Dashboard
  5. คลิกปุ่ม "สร้าง API Key" แล้วคัดลอกคีย์ที่ปรากฏ
หมายเหตุสำคัญ: API Key จะแสดงเพียงครั้งเดียว คุณต้องเก็บรักษาไว้อย่างปลอดภัย หากหายต้องสร้างใหม่เท่านั้น หลังจากสมัครเสร็จ คุณจะได้รับ เครดิตฟรีเมื่อลงทะเบียน สำหรับทดลองใช้งาน ซึ่งเพียงพอสำหรับเรียนรู้และทดสอบโปรเจกต์ขนาดเล็ก

ขั้นตอนที่ 2: ติดตั้งโปรแกรมที่จำเป็น

สำหรับผู้เริ่มต้น ผมแนะนำให้ใช้ Python เพราะเข้าใจง่ายและมีไลบรารีสนับสนุนมากมาย ติดตั้ง Python:
  1. ไปที่ https://www.python.org/downloads/
  2. ดาวน์โหลด Python เวอร์ชันล่าสุด (3.9 ขึ้นไป)
  3. รันไฟล์ติดตั้ง โดยต้องติ๊กถูกที่ "Add Python to PATH" ด้วย
  4. เปิด Command Prompt (พิมพ์ cmd ในช่องค้นหา Windows)
  5. พิมพ์คำสั่ง: 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
วิธีรันโค้ด:
  1. เปิด Command Prompt
  2. พิมพ์: pip install openai pyaudio numpy
  3. พิมพ์: 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) {
            // ส่งไฟ