ข้ามไปยังเนื้อหาหลัก

บทช่วยสอน Grok Voice Think Fast 2.0 API: สร้างเอเจนต์เสียงแบบเรียลไทม์ด้วย Python

เรียนรู้การใช้ Grok Voice Think Fast 2.0 เพื่อสร้างเอเจนต์เสียงแบบเรียลไทม์ที่จัดการบทสนทนาแบบพูด เรียกใช้เครื่องมือ จัดการการขัดจังหวะ และกู้คืนเซสชันที่หลุดได้
อัปเดตแล้ว 9 ส.ค. 2569  · 15 นาที อ่าน

สำรวจด้วย AI

ChatGPTClaudePerplexity

Grok Voice Think Fast 2.0 ของ SpaceXAI เป็นโมเดลแปลงเสียงเป็นเสียง ส่งเสียงเข้าไปผ่าน WebSocket และรับเสียงกลับมา ระหว่างนั้นตัวโมเดลสามารถให้เหตุผลและพูดต่อได้ขณะฟังก์ชันที่มันตัดสินใจเรียกกำลังทำงานอยู่ ไม่มีขั้นตอนแยกเป็น speech-to-text และไม่มีขั้นตอนแยกเป็น text-to-speech

SpaceXAI ประกาศ Think Fast 2.0 เมื่อ 29 กรกฎาคม 2026: เสียงแรกมาไวขึ้น พฤติกรรม full-duplex เสถียรขึ้น (ฟังไปพูดไปแทนที่จะผลัดกันอย่างเคร่งครัด) และเรียกใช้เครื่องมือได้เร็วตั้งแต่ช่วงต้นของเทิร์น จะไม่ยืดเยื้อเรื่องเบนช์มาร์ก เพราะสำหรับบทช่วยสอน สิ่งที่สำคัญคือโค้ดต้องเปลี่ยนตรงไหน

เราจะสร้างเอเจนต์เสียงฝ่ายบริการลูกค้าสำหรับร้านค้าออนไลน์ ผู้โทรสามารถสอบถามคำสั่งซื้อ เปลี่ยนคำแนะนำการจัดส่ง ยกเลิก ขัดจังหวะเอเจนต์กลางประโยค และกลับมาสนทนาต่อหลังการเชื่อมต่อหลุด นี่คือเส้นทางผ่าน API ไม่ใช่เครื่องมือสร้าง Voice Agent แบบ no-code ที่บทช่วยสอน Grok Voice Agent Builder ของเราได้อธิบายไว้ เริ่มจากตรงนั้นหากเน้นทำผ่านคอนโซลก่อน

Grok Voice Think Fast 2.0 คืออะไร?

Grok Voice Think Fast 2.0 คือโมเดลใหม่ล่าสุดของ SpaceXAI สำหรับ Speech to Speech API ซึ่งเป็นชื่อผลิตภัณฑ์ที่คนส่วนใหญ่เรียกรวม ๆ ว่า Grok Voice หากยังคุ้นกับชื่อบริษัทว่า xAI ก็เจ้าเดียวกัน: ถูกควบรวมเข้า SpaceX และรีแบรนด์เป็น SpaceXAI เมื่อ 6 กรกฎาคม 2026 ฝั่ง API ไม่ได้เปลี่ยนตามแบรนด์ ดังนั้นตัวระบุทั้งหมดด้านล่างยังคงเป็น xai ตั้งแต่ตัวแปร XAI_API_KEY ไปจนถึงโฮสต์ api.x.ai.

สแตกเสียงแบบดั้งเดิมจะต่อเรียงสามบริการ: speech-to-text, โมเดลภาษา, แล้ว text-to-speech และทุกการส่งต่อเพิ่มทั้งความหน่วงและโอกาสที่บริบทจะสูญหาย Think Fast 2.0 รวมทั้งหมดไว้ในโมเดลเดียว รับเข้าเป็นเสียงหรือข้อความ และส่งออกเป็นเสียงหรือข้อความผ่านการเชื่อมต่อเดียวกัน

Diagram comparing a modular STT-LLM-TTS pipeline against a single Grok Voice WebSocket connection.

WebSocket แบบเสียงต่อเสียง เทียบกับสถาปัตยกรรมแบบสามบริการ ภาพโดยผู้เขียน

สำหรับเอเจนต์ที่ “ลงมือทำ” ไม่ใช่แค่พูด สิ่งสำคัญคือการให้เหตุผลและการพูดทำงานขนานกัน SpaceXAI ระบุว่าการเรียกใช้เครื่องมือมักจะเริ่มทำงานก่อนที่เอเจนต์จะพูดประโยคแรกจบ และคำว่า “มักจะ” ตรงนี้มีนัยสำคัญ

บนเบนช์มาร์กที่ SpaceXAI อ้างถึงจาก Artificial Analysis Think Fast 2.0 ทำคะแนน 82.9% บน Speech to Speech Index เทียบกับ 75.7% ของรุ่น 1.0 และลดเวลาเสียงแรกจาก 1.25 วินาที เหลือ 0.70 วินาที ตัวเลขจากผู้ขายบนเบนช์มาร์กทั่วไปเป็นสมมติฐานเกี่ยวกับโฟลว์ของผู้โทรของคุณ ไม่ใช่แผนการทดสอบ

จะเจอสตริงชื่อโมเดลสามแบบ: grok-voice-latest, grok-voice-think-fast-2.0 และ grok-voice-think-fast-1.0 อะลิแอสสะดวกตอนทำต้นแบบ แต่ไม่เสถียรพอสำหรับงานอื่น

เมื่อทดสอบวันที่ 4 สิงหาคม 2026 grok-voice-latest ยังชี้ไปที่ grok-voice-think-fast-1.0 โดย บันทึกประจำรุ่นของ SpaceXAI กำหนดตารางย้ายไป Think Fast 2.0 ในวันถัดไป การสลับครั้งนี้คือการเปลี่ยนราคาเช่นกัน จาก $0.05 เป็น $0.08 ต่อนาทีของเสียง ดังนั้นการใช้อะลิแอสที่ไม่ปักหมุดเวอร์ชันจะทำให้แพงขึ้นโดยที่โค้ดคุณไม่เปลี่ยนสักบรรทัด ปักหมุดสตริงเวอร์ชันในทุกสิ่งที่นำขึ้นใช้งาน

เราจะสร้างอะไร

เอเจนต์ครอบคลุมสิ่งที่สายสนับสนุนมักถูกถาม: ตรวจสอบคำสั่งซื้อ ค้นหาจากอีเมลเมื่อผู้โทรไม่มีหมายเลข เปลี่ยนคำแนะนำการจัดส่ง ยกเลิก เปิดหรือตรวจสอบทิกเก็ต และโอนสายให้พนักงาน ระหว่างทางจะมีทั้งการขัดจังหวะและการเชื่อมต่อหลุด

โปรเจ็กต์แบ่งเป็นไฟล์เล็กหลายไฟล์แทนการเขียนสคริปต์ไฟล์เดียว เพราะแต่ละส่วนทำหน้าที่ต่างกัน และจะได้ทดสอบแยกกัน นี่คือโครงสร้าง:

  • config.py โหลดคีย์ API และเก็บสตริงโมเดล อัตราสุ่มตัวอย่าง และ URL ของเอนด์พอยต์

  • voice_client.py หุ้ม WebSocket ติดตามการคิดเงิน และเปิดให้ใช้ตัวช่วยส่ง/รับ

  • tools.py กำหนดฟังก์ชันเกี่ยวกับออเดอร์ และสโตร์ออเดอร์ขนาดเล็กในหน่วยความจำแทนฐานข้อมูลจริง

  • assistant.py เก็บพรอมป์ระบบ การตั้งค่าเซสชัน และลูปเหตุการณ์ที่เชื่อมทุกอย่างเข้าด้วยกัน

  • token_server.py เป็นเอนด์พอยต์ FastAPI ขนาดเล็กที่สร้างโทเค็นชั่วคราว

  • app_streamlit.py ใช้ไคลเอนต์เดียวกันบนการโทรผ่านเบราว์เซอร์สด เดี๋ยวจะกลับมาพูดถึงหลังส่วนการทดสอบ

เส้นทางการสอนเริ่มจากเทอร์มินัล เดโมจะเพิ่มไมโครโฟน

ข้อกำหนดเบื้องต้น

ต้องมี บัญชี SpaceXAIพร้อมคีย์ API การจัดการเรียกเก็บเงินที่เติมเงินไว้ (ไม่มีฟรีเทียร์ถาวร และเครดิตโปรโมชันสำหรับบัญชีใหม่ไม่เพียงพอ) และความคุ้นเคยกับ asyncio และ WebSocket พอที่จะตามได้โดยไม่ต้องอธิบาย await แบบบรรทัดต่อบรรทัด

ตัวอย่างเริ่มต้นอย่างรวดเร็วของ SpaceXAI ใช้แพ็กเกจ websockets ตรง ๆ แทน SDK เฉพาะ และเราก็เช่นกัน เอกสารไม่ได้ระบุเวอร์ชัน Python ที่ต้องการ ฉันทดสอบบน 3.11

เก็บคีย์ API ไว้บนเซิร์ฟเวอร์ หากเบราว์เซอร์หรือแอปมือถือคุยกับ Voice API โดยตรง ให้ใช้ โทเค็นชั่วคราว แทนคีย์จริง ซึ่งจะกล่าวถึงในส่วนความปลอดภัยด้านล่าง

ตั้งค่าโปรเจ็กต์

ทุกไฟล์ด้านล่างอยู่ใน repo ของโปรเจ็กต์ จึงโคลนได้แทนการก็อปปี้สแน็กเก็ต:

git clone https://github.com/KhalidAbdelaty/grok-voice-think-fast-2.0.git
cd grok-voice-think-fast-2.0
pip install -r requirements.txt

websockets จัดการการเชื่อมต่อเรียลไทม์ และ python-dotenv อ่านคีย์ของคุณ ที่เหลือครอบคลุมเอนด์พอยต์โทเค็นและเดโมบนเบราว์เซอร์ ใส่คีย์ของคุณใน .env:

XAI_API_KEY=xai-your-key-here

นั่นคือส่วนใหญ่ของการตั้งค่า ส่วนที่น่าสนใจคือการเชื่อมต่อ

ทำความเข้าใจกับ Grok Voice Realtime API

Grok Voice คือชื่อผลิตภัณฑ์ สิ่งที่คุณเขียนโค้ดคุยด้วยจริง ๆ คือเอนด์พอยต์ WebSocket ที่ wss://api.x.ai/v1/realtime และทั้งบทสนทนาเกิดขึ้นเป็นสตรีมของเหตุการณ์ JSON ผ่านซ็อกเก็ตเดียว

วงจรชีวิตของเหตุการณ์

การเชื่อมต่อมีรูปแบบตายตัว: เซิร์ฟเวอร์ส่ง session.created และ conversation.created ทันทีที่คุณเชื่อมต่อ คุณส่ง session.update เพื่อกำหนดค่าเสียงและเครื่องมือ เซิร์ฟเวอร์ยืนยันด้วย session.updated และจากนั้นคุณสร้างรายการบทสนทนาและร้องขอคำตอบ ฉันรันทดสอบด้วยคีย์จริงและลำดับตรงตามเอกสารทุกอย่าง

  • session.update (ไคลเอนต์) กำหนดค่าเสียง คำสั่ง เครื่องมือ และรูปแบบเสียง

  • conversation.item.create (ไคลเอนต์) เพิ่มข้อความผู้ใช้ ข้อความผู้ช่วย หรือผลลัพธ์เครื่องมือ

  • response.create (ไคลเอนต์) ขอให้โมเดลพูด; ระบบ VAD ของเซิร์ฟเวอร์ส่งให้อัตโนมัติ

  • response.output_audio.delta และ response.output_audio_transcript.delta (เซิร์ฟเวอร์) สตรีมคำตอบขณะกำลังสร้าง

  • response.done (เซิร์ฟเวอร์) ปิดเทิร์น

มีสองอย่างที่มักทำให้สะดุด หน้าเอกสาร Speech to Speech ที่ลิงก์ไว้ด้านบนพูดถึงเหตุการณ์ conversation.item.created ระหว่างการกู้คืนเซสชัน แต่ รายการอ้างอิงเหตุการณ์ที่เป็นมาตรฐานมีแค่ conversation.item.added และนั่นคือสิ่งที่มาถึงในทุกการทดสอบที่ฉันรัน ดังนั้นให้โค้ดอิงกับอันนี้ คุณยังจะเห็นเหตุการณ์ ping ที่ไม่มีเอกสารหลังเชื่อมต่อไปไม่กี่วินาที กล่าวถึงไว้เพื่อจะได้ไม่อ่านว่าเป็นข้อผิดพลาด

รูปแบบเสียงและการขนส่ง

โค้เดกและการขนส่งเป็นตัวเลือกแยกกัน โค้เดกกำหนดภายใต้ audio.input.format และ audio.output.format ได้แก่ audio/pcm (Linear16, ค่าเริ่มต้น 24000 Hz), audio/pcmu หรือ audio/pcma (G.711 ที่ 8 kHz สำหรับโทรศัพท์) หรือ audio/opus (24 kHz) ส่วนการขนส่งคือวิธีส่งไบต์เหล่านั้นบนสาย:

  • json (ค่าเริ่มต้น) ส่งเสียงเป็นข้อความ base64 ภายใน input_audio_buffer.append และ response.output_audio.delta ตรวจสอบและดีบักง่าย

  • binary ส่งไบต์โค้เดกดิบเป็นเฟรมไบนารีของ WebSocket ข้ามโอเวอร์เฮด base64 โดยต้องให้ลูปรับแยกสาขาตามชนิดข้อความ

เริ่มด้วย JSON ตัวอย่างในเอกสารทั้งหมดใช้แบบนี้ ตรวจสอบได้ง่าย และโอเวอร์เฮด base64 ไม่ใช่คอขวดของงานเอเจนต์สนับสนุน ย้ายไป binary เมื่อมีเหตุผลจากการวัดจริง

ความเข้ากันได้กับ OpenAI Realtime API

ข้ามส่วนนี้ได้หากไม่เคยใช้ OpenAI Realtime API สำหรับคนอื่น ๆ Speech to Speech API ตาม OpenAI Realtime API ได้ใกล้เคียงพอที่โค้ดไคลเอนต์ส่วนใหญ่จะพอร์ตโดยเปลี่ยน base URL และคีย์ แต่ไม่ถึงกับแทนที่ได้ทันที

ทรานสคริปต์มาถึงเป็น conversation.item.input_audio_transcription.updated ที่นี่แทน delta ของ OpenAI เหตุการณ์บางอย่างของ OpenAI ไม่รองรับ และ SpaceXAI เพิ่มส่วนขยายของตัวเอง: force_message สำหรับบรรทัดเปิดเผยแบบสคริปต์, resumption สำหรับการเชื่อมต่อใหม่ และ replace สำหรับแก้การออกเสียงชื่อแบรนด์ก่อน text-to-speech

สร้างเอเจนต์เสียงแบบเรียลไทม์

พอเรื่องโปรโตคอลแล้ว นี่คือไคลเอนต์ที่คุยกับมัน

เชื่อมต่อและกำหนดค่าเซสชัน

การเชื่อมต่อเปิดด้วย bearer token และพารามิเตอร์โมเดลใน query ข้อความแรกที่คุณส่งจะกำหนดทุกอย่างเกี่ยวกับพฤติกรรมของเอเจนต์:

import asyncio
import json
import os
import websockets

MODEL = "grok-voice-think-fast-2.0"  # pin the version, not grok-voice-latest

async def connect():
    url = f"wss://api.x.ai/v1/realtime?model={MODEL}"
    ws = await websockets.connect(
        url, additional_headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"}
    )
    await ws.send(json.dumps({
        "type": "session.update",
        "session": {
            "voice": "eve",
            "instructions": SYSTEM_PROMPT,
            "turn_detection": {"type": "server_vad"},
            "tools": ORDER_TOOLS,
            "resumption": {"enabled": True},
        }
    }))

return ws

instructions คือพรอมป์ระบบ และโมเดลนี้ต้องการพรอมป์สั้น ๆ บันทึกการย้ายเวอร์ชันของ SpaceXAI แนะนำให้ทำพรอมป์ที่เขียนสำหรับโมเดลเสียงยุค GPT ให้เรียบง่ายขึ้น แทนการพอร์ตแบบคำต่อคำ ของฉันสั่งให้เอเจนต์ตอบสั้น ถามทีละคำถาม และอ่านสิ่งที่จะเขียนกลับก่อนลงมือ ยืนยันแบบพูดเป็นเรื่องประสบการณ์ใช้งานที่ดี ไม่ใช่มาตรการความปลอดภัย แอปพลิเคชันของคุณยังต้องบังคับใช้อำนาจอนุญาตบนการเขียนนั้นเอง

สิ่งหนึ่งที่ทำให้ประหลาดใจ: หากสตริงชื่อโมเดลไม่รู้จัก จะไม่ยกข้อผิดพลาดตอนเชื่อมต่อ แต่ถอยกลับไป grok-voice-think-fast-1.0 เงียบ ๆ ลดระดับคำขอที่เสียเงินเพราะพิมพ์ผิดโดยไม่บอก เป็นค่าเริ่มต้นที่แปลก บันทึกฟิลด์ session.model ของ session.created ครั้งหนึ่งตอนเริ่มระบบและตรวจว่าตรงกับที่ขอ

Terminal printing the session.created event after opening the WebSocket connection

เอาต์พุตเทอร์มินัลแสดง session.created หลังการเชื่อมต่อ ภาพโดยผู้เขียน

สตรีมเสียงของผู้ใช้

เมื่อ turn_detection.type ตั้งเป็น server_vad คุณแค่ส่งเสียงเพิ่มไปเรื่อย ๆ เซิร์ฟเวอร์ตัดสินใจเองว่าเมื่อใดผู้โทรหยุดพูดและทริกเกอร์คำตอบให้คุณ หากตั้งเป็น null คุณจะต้องตัดสินใจเอง โดยคอมมิตบัฟเฟอร์อย่างชัดเจนเมื่อคิดว่าเทิร์นจบ

async def send_audio_chunk(ws, pcm_bytes: bytes):
    await ws.send(json.dumps({
        "type": "input_audio_buffer.append",
        "audio": base64.b64encode(pcm_bytes).decode(),
    }))

Server VAD มีปุ่มปรับสามตัว และการตั้งค่าผิดเป็นสาเหตุที่พบบ่อยที่สุดที่ทำให้เอเจนต์เสียง “ดูเสีย” ขณะที่ล็อกไม่แสดงข้อผิดพลาด ค่าเหล่านี้ไม่ปรากฏในเอคโค่ของ session.updated โดยค่าเริ่มต้น จึงควรตรวจตามเอกสารแทนการเดา

  • threshold (0.1 ถึง 0.9 ค่าเริ่มต้น 0.85) ระดับความดังที่นับว่าเป็นคำพูด; เพิ่มสำหรับห้องที่มีเสียงรบกวน ลดหากคนพูดเบาถูกมองข้าม

  • silence_duration_ms ระยะเวลาที่ผู้โทรเงียบก่อนที่เซิร์ฟเวอร์จะปิดเทิร์น; สั้นไปจะตัดบทกลางความคิด ยาวไปจะรู้สึกเชื่องช้า

  • prefix_padding_ms (ค่าเริ่มต้น 333) ชิ้นเสียงที่เก็บไว้ก่อนตรวจจับคำพูดเล็กน้อย เพื่อไม่ให้พยางค์แรกถูกตัด

ปรับ silence_duration_ms ก่อนหากผู้โทรถูกตัดบทตอนหยุดคิดบ่อย ๆ นี่คือค่าที่ฉันจับก่อนอีกสองตัว

รับและเล่นคำตอบ

เสียงมาถึงเป็นชิ้นเล็ก ๆ ในรูปแบบ response.output_audio.delta และจุดประสงค์ของการสตรีมคือเล่นทีละชิ้นทันทีที่มาถึง แทนการรอ response.done.

async def play_response(ws):
    async for message in ws:
        event = json.loads(message)
        if event["type"] == "response.output_audio.delta":
            chunk = base64.b64decode(event["delta"])
            speaker.write(chunk)  # your playback call goes here
        elif event["type"] == "response.output_audio_transcript.delta":
            print(event["delta"], end="", flush=True)

เก็บทรานสคริปต์ไว้แม้ในโปรดักชัน มันคือเครื่องมือดีบักที่ถูกที่สุดเมื่อผู้โทรบอกว่าเอเจนต์ “พูดอะไรแปลก ๆ”

เพิ่มเครื่องมือให้เอเจนต์เสียง

เอเจนต์เสียงที่ทำได้แค่พูด คือแชตบอตที่มีไมโครโฟน

สร้างเครื่องมือเกี่ยวกับออเดอร์

แต่ละเครื่องมือคือ JSON schema บวกฟังก์ชัน Python ธรรมดาฝั่งเรา โมเดลไม่แตะฐานข้อมูลโดยตรง มันเห็นเฉพาะสิ่งที่ฟังก์ชันของเราส่งกลับ

ORDER_TOOLS = [
    {
        "type": "function",
        "name": "check_order_status",
        "description": "Look up the status, ETA, and delivery instructions for an order.",
        "parameters": {
            "type": "object",
            "properties": {
                "order_number": {"type": "string", "description": "e.g. ORD-1042"},
            },
            "required": ["order_number"],
        },
    },
    # find_orders, update_delivery_instructions, cancel_order,
    # create_support_ticket, check_ticket_status and transfer_to_human
    # all follow the same shape
]

งานอ่านอย่าง check_order_status ปลอดภัยสำหรับการลองใหม่หากเกิด timeout แต่งานเขียนไม่ใช่: ลองใหม่ที่ update_delivery_instructions หลัง timeout ที่คลุมเครืออาจทำให้การเปลี่ยนแปลงเดิมถูกใช้ซ้ำ บรรทัดยืนยันในพรอมป์ไม่หยุดสิ่งนี้ ให้คีย์ idempotency หรือการตรวจจับซ้ำกับงานเขียนแทน

ใส่เงื่อนไขปฏิเสธในฟังก์ชันด้วย cancel_order ส่งเหตุผลและทางเลือกกลับแทนการยกเลิกออเดอร์ที่จัดส่งแล้ว เพราะพรอมป์ที่ว่า “อย่ายกเลิกออเดอร์ที่จัดส่งแล้ว” เป็นเพียงคำแนะนำ แต่ฟังก์ชันที่ปฏิเสธคือข้อบังคับ

จัดการลูปการเรียกใช้เครื่องมือ

มีสี่ขั้นตอน และลำดับสำคัญกว่าที่คิด โมเดลส่ง response.function_call_arguments.done โค้ดของคุณรันฟังก์ชัน คุณส่งผลลัพธ์กลับเป็นรายการ function_call_output แล้วค่อยขอให้โมเดลดำเนินต่อ

async def handle_tool_call(ws, event):
    args = json.loads(event["arguments"])
    result = execute(event["name"], args)  # never raises; errors come back as {"error": ...}
    await ws.send(json.dumps({
        "type": "conversation.item.create",
        "item": {
            "type": "function_call_output",
            "call_id": event["call_id"],
            "output": json.dumps(result),
        },
    }))

หากโมเดลต้องใช้มากกว่าหนึ่งเครื่องมือสำหรับคำขอ มันจะส่งหลายเหตุการณ์ function_call_arguments.done ก่อนมีเสียงใด ๆ เล่น แก้ไขทั้งหมดและส่งผลลัพธ์ทุกอันก่อน response.create เพียงครั้งเดียว หากส่งเร็วเกินไป โมเดลจะตอบโดยไม่มีบริบทจากการเรียกที่ยังทำงานอยู่

มีข้อควรระวังที่ SpaceXAI ระบุไว้และฉันก็เจอในครั้งแรก: การส่ง response.create ทันทีที่ผลลัพธ์เครื่องมือออก อาจทับซ้อนกับประโยคเกริ่นนำที่เอเจนต์ยังพูดอยู่ ครั้งหนึ่งมันเริ่มด้วย “ฉันจะตรวจสอบสถานะออเดอร์ ORD-1042 เดี๋ยวนี้” แล้วเรียกเครื่องมือกลางประโยค ดังนั้นการตอบกลับทันทีจะพูดทับตัวเอง

รอให้เสียงของเทิร์นปัจจุบันจบ และแสดงสถานะ “กำลังคิด” สั้น ๆ คั่นกลาง

Flow from function_call_arguments.done to executing the handler, sending function_call_output, then response.create.

โฟลว์การเรียกใช้เครื่องมือก่อนจะดำเนินคำตอบต่อ ภาพโดยผู้เขียน

จัดการการขัดจังหวะและสถานะบทสนทนา

มีสองปัญหาแยกกัน ผู้โทรพูดทับเอเจนต์กลางคำตอบ และ WebSocket หลุดต้องกลับมาต่อ

รองรับการขัดจังหวะตามธรรมชาติ

เมื่อเปิด server_vad การพูดแทรก (barge-in) จะเกิดขึ้นอัตโนมัติฝั่งเซิร์ฟเวอร์: ทันทีที่ตรวจว่าผู้โทรเริ่มพูดอีกครั้ง มันจะส่งสัญญาณ input_audio_buffer.speech_started และหยุดสร้างคำตอบเดิม หน้าที่ของคุณคือฝั่งไคลเอนต์ ล้างเสียงที่เข้าคิวไว้เพื่อให้เอเจนต์เงียบแทนที่จะพูดจบประโยคที่ไม่มีใครอยากฟัง

if event["type"] == "input_audio_buffer.speech_started":
    playback_queue.clear()

สำหรับเซสชันแบบแมนนวลที่ไม่ใช้ VAD response.cancel ทำงานเดียวกันนี้ตามคำขอ นอกจากนี้ยังมี conversation.item.truncate เพื่อหั่นรายการของผู้ช่วยให้เหลือเท่าที่ได้ยินจริง เอกสารยืนยันว่ามี แต่ไม่ได้บอกว่าจะยิงเมื่อใดระหว่างการพูดแทรกแบบสด ดังนั้นต้องทดสอบจังหวะด้วยตนเอง

ฉันทดสอบโดยเปลี่ยนคำแนะนำการจัดส่งกลางคำตอบ: เริ่มคำขอ แล้วขัดจังหวะด้วยที่อยู่ใหม่กลางประโยคยืนยันของเอเจนต์ สิ่งสำคัญคือเอเจนต์ใช้คำแนะนำที่แก้ไขแล้วแทนการแอบทำของเดิมให้เสร็จ ไม่ใช่แค่ว่าเสียงหยุดหรือไม่ ตรวจสอบกับระเบียนคำสั่งซื้อ ไม่ใช่กับความเงียบ เดโมบนเบราว์เซอร์ท้ายบทช่วยให้ได้ยินกรณีนี้

กลับเข้าเซสชันที่หลุด

การกู้คืนเซสชันต้องเปิดใช้งานเองและไม่ใช่ความจำ ตั้งค่า resumption.enabled: true บน session.update ดึง ID จากเหตุการณ์ conversation.created และถ้าซ็อกเก็ตหลุด ให้เชื่อมต่อใหม่ด้วย ?conversation_id=<id> ใน URL และเปิดใช้งานอีกครั้งบนการเชื่อมต่อใหม่

async def reconnect(conversation_id):
    url = f"wss://api.x.ai/v1/realtime?model={MODEL}&conversation_id={conversation_id}"
    ws = await websockets.connect(url, additional_headers=auth_header)
    await ws.send(json.dumps({"type": "session.update", "session": {"resumption": {"enabled": True}}}))
    return ws

เทิร์นที่แคชไว้ ทรานสคริปต์ การเรียกเครื่องมือ และผลลัพธ์เครื่องมือจะเล่นซ้ำก่อนคำถามถัดไป และแคชจะหายไปหลังไม่มีการใช้งาน 30 นาที ฉันทดสอบโดยถามเรื่องคำสั่งซื้อ ทำการตัดการเชื่อมต่อ แล้วเชื่อมใหม่เพื่อถามต่อโดยไม่ต้องพูดซ้ำ เอเจนต์หยิบยก ETA ต่อได้ถูกต้อง

มีข้อควรระวังที่ไม่มีเอกสาร: การเล่นซ้ำไม่ได้มาทันที ดังนั้นคำถามที่ยิงทันทีที่ซ็อกเก็ตเปิดอาจแซงหน้าและตอบกลับโดยไม่มีความทรงจำของเทิร์นก่อนหน้า รอสักครู่ก่อนจะโทษ resumption

Terminal transcript of a dropped connection, a reconnect with conversation_id, and a correct follow-up answer.

บันทึกเทอร์มินัลของเซสชันที่กู้คืน ภาพโดยผู้เขียน

อย่าใช้สิ่งนี้แทนการบันทึกสถานะออเดอร์ในฐานข้อมูลของคุณเอง หากแคชหมดอายุหรือผู้โทรโทรกลับมาวันพรุ่งนี้ คุณจะเริ่มจากศูนย์ตามที่ออกแบบไว้

รักษาความปลอดภัยและติดตามเอเจนต์

อย่าใส่คีย์ API ถาวรในโค้ดเบราว์เซอร์หรือมือถือ หากไคลเอนต์เชื่อมต่อโดยตรงแทนผ่านเซิร์ฟเวอร์ของคุณ ให้สร้างโทเค็นอายุสั้น:

from fastapi import FastAPI
import httpx, os

app = FastAPI()

@app.post("/session")
async def create_session():
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.x.ai/v1/realtime/client_secrets",
            headers={"Authorization": f"Bearer {os.environ['XAI_API_KEY']}"},
            json={"expires_after": {"seconds": 300}},
        )
    return response.json()  # {"value": "xai-realtime-client-secret-...", "expires_at": ...}

เบราว์เซอร์ไม่สามารถตั้งค่าเฮดเดอร์ Authorization ที่กำหนดเองบนการจับมือ WebSocket ได้ จึงส่งโทเค็นผ่านเฮดเดอร์ sec-websocket-protocol แทน โดยมีคำนำหน้า xai-client-secret..

Diagram of a server minting a short-lived client secret for a browser to open the WebSocket.

เซิร์ฟเวอร์สร้างโทเค็น เบราว์เซอร์เข้าร่วมการโทร ภาพโดยผู้เขียน

การคิดเงินมีสองมิเตอร์ เสียงที่ส่งหรือรับ คิดที่ $0.08 ต่อนาทีดังที่กล่าวไปก่อนหน้า ซึ่งเท่ากับ $4.80 ต่อชั่วโมง และทุก conversation.item.create ที่ไม่ใช่เสียงและไม่ใช่ function_call_output มีค่าใช้จ่ายคงที่ $0.004 response.create ไม่คิดเงินเลย แต่ละ response.done มีออบเจกต์ usage ซึ่งในการทดสอบของฉันมีรายงาน output_audio_seconds ควบคู่กับ billable_audio_seconds แยกต่างหาก ให้คิดเงินจากสิ่งเหล่านั้น ไม่ใช่จากการประมาณ

ข้อจำกัดที่มีเอกสารของ Speech to Speech API คือ 10 เซสชันพร้อมกันต่อทีม และขีดจำกัดเซสชัน 120 นาที ทั้งหมดใน us-east-1 อย่าวางแผนความจุจากตัวเลขของ Voice Agent API ซึ่งต่างกัน

ด้านความเป็นส่วนตัว ควรระบุให้ชัดเจน คำถาม-คำตอบด้านความปลอดภัยของ SpaceXAI ระบุว่าคำขอและคำตอบของ API ถูกเก็บไว้แบบเข้ารหัส 30 วันเพื่อมอนิเตอร์การละเมิด และไม่ถูกใช้เพื่อฝึกโดยไม่ได้รับอนุญาต และทีมสามารถเปิดใช้ Zero Data Retention ได้ แม้ ZDR จะทำให้ประวัติบทสนทนาของเอเจนต์เสียงที่ถูกเก็บหายไปและจึงใช้ร่วมกับการกู้คืนเซสชันไม่ได้

หากต้องเปิดเผยว่ามีการบันทึกสายหรือจัดการด้วย AI นั่นคือหน้าที่ของส่วนขยาย force_message ที่กล่าวถึงก่อนหน้า บรรทัดจะเล่นตามที่เขียน ไม่ใช่ตามที่โมเดลถอดความ

ทดสอบเอเจนต์เสียง

สถานะ 200 บนการจับมือ WebSocket ไม่ได้บอกว่าเอเจนต์ทำถูกต้องหรือไม่ ทดสอบผลลัพธ์ ไม่ใช่แค่การเชื่อมต่อ

  • การค้นหาออเดอร์ปกติ ตรวจคำตอบที่พูดกับระเบียน ไม่ใช่แค่ว่ามีคำตอบมา
  • การตอบที่ถูกขัดจังหวะ ยืนยันว่าเสียงหยุดและเอเจนต์ตอบรับคำขอใหม่
  • การอัปเดตการจัดส่งที่ต้องยืนยัน ตรวจเทียบกับระเบียนออเดอร์
  • การปฏิเสธ เช่นการยกเลิกออเดอร์ที่จัดส่งแล้ว ซึ่งเอเจนต์ต้องอธิบายกฎแทนการขอโทษ
  • หมายเลขออเดอร์ที่ไม่รู้จัก ตรวจให้แน่ว่าเอเจนต์บอกตามนั้นแทนการแต่งสถานะขึ้นมา
  • เครื่องมือที่ส่งข้อผิดพลาดกลับมา ตรวจว่าเอเจนต์พูดข้อผิดพลาดแทนการนิ่งไป
  • การเชื่อมต่อใหม่และการกู้คืน รวมถึงหน้าต่างการเล่นซ้ำที่เจอข้างต้น
  • เสียงรบกวนมาก การพูดเร็ว และผู้โทรที่สะกดตัวเลขและที่อยู่ทีละตัว

ฉันรันทดสอบส่วนใหญ่กับคีย์จริงระหว่างเขียน ความล้มเหลวที่น่าสนใจเป็นเชิงพฤติกรรม ไม่ใช่ข้อผิดพลาด: เวลาเล่นซ้ำใน resumption ข้างต้น และค่า threshold ของ VAD ที่เกินช่วงถูกยอมรับแทนการปฏิเสธ เรื่องแบบนี้จะหลุดไปแบบเสีย ๆ หากทดสอบแค่เส้นทางปกติ เพิ่มการทดสอบหลายภาษา และดูส่วนคำถามที่พบบ่อยสำหรับลูกเล่นในการระบุชื่อภาษา

สองในนั้นคุณพิมพ์ทดสอบไม่ได้ app_streamlit.py เป็นหน้า Streamlit ที่ใส่การโทรสดในเบราว์เซอร์: ไมโครโฟนสตรีมเข้า WebSocket เดียวกันผ่าน WebRTC เสียงของเอเจนต์สตรีมกลับ และซ็อกเก็ตเปิดอยู่ตลอด

streamlit run app_streamlit.py
ขัดจังหวะเอเจนต์กลางประโยค วิดีโอโดยผู้เขียน.

พูดทับเอเจนต์แล้วมันจะหยุด เพราะ speech_started มาถึงและหน้าจะล้างคิวเสียงที่รอเล่น นี่คือการจับมือจากส่วนการขัดจังหวะที่ทำงานจริง

ให้ดูระเบียนออเดอร์แทนทรานสคริปต์: เอเจนต์อ่านการเปลี่ยนแปลงการจัดส่งกลับและบอกว่าทำเสร็จแล้ว และระเบียนจะเปลี่ยนหรือไม่เปลี่ยนก็ตามนั้น ใส่หูฟัง หากเปิดลำโพง เอเจนต์จะได้ยินตัวเอง นับว่าเป็นการพูดแทรก และตัดประโยคของตัวเอง ซึ่งเป็นภาพพรีวิวของสิ่งที่ผู้ใช้สปีกเกอร์โฟนทำกับคุณ

ข้อจำกัดและข้อพิจารณาในการใช้งาน Grok Voice Think Fast 2.0

เตรียมแผนสำหรับสิ่งเหล่านี้: การเรียกเครื่องมือที่ล้มเหลวกลางเทิร์น โมเดลที่พูดยืนยันมั่นใจเกินกว่าความสำเร็จของการกระทำจริง VAD ที่ปรับมาสำหรับออฟฟิศเงียบ ๆ แล้วพังบนสายโทรศัพท์ และผู้โทรที่เปลี่ยนใจกลางประโยค 

สำหรับการชำระเงิน การเข้าถึงบัญชี หรือผู้โทรที่ดูสับสนหรือไม่พอใจ ให้โอนหามนุษย์ ให้โมเดลมีเครื่องมือ transfer_to_human สำหรับกรณีนี้ ถ้าไม่มี มันจะด้นขอโทษแทนการยกระดับ

สแตกแบบแยกบริการ speech-to-text, language model, text-to-speech ยังมีที่ทาง: ควบคุมแต่ละองค์ประกอบแยกกันได้ และได้ทรานสคริปต์ที่กำหนดแน่ชัดก่อนเริ่มให้เหตุผล แลกกับงานอินทิเกรชันที่มากกว่า และถ้างานของคุณไม่ต้องโต้ตอบสดเลย แชตบอตข้อความหรือจ๊อบถอดเสียงแบบแบตช์เรียบง่ายและถูกกว่าท่อเรียลไทม์ที่ไม่มีใครคุยด้วย

สรุป

จากการทดสอบในบทความนี้ grok-voice-think-fast-2.0 โดยมากทำตามที่เอกสารบอก วงจรเหตุการณ์เชื่อถือได้ การเชื่อมต่อที่หลุดกลับมาพร้อมเทิร์นก่อนหน้า และโมเดลเรียกเครื่องมือขณะยังพูดประโยคเปิด

นอกเหนือจากความคลาดเคลื่อนด้านชื่อบน conversation.item.added สิ่งที่ควรเน้นคือส่วนงานที่เหลืออยู่มากมายอยู่ฝั่งคุณ: คิวการเล่นเสียง เมื่อใดควรเงียบ และเมื่อใดไม่ควรถามคำถามถัดไปทันที

หากเริ่มโปรเจ็กต์วันนี้ ค่าตั้งต้นของฉันคือใช้สตริงเวอร์ชันแทนอะลิแอส server_vad พร้อมปรับ silence_duration_ms ก่อนอีกสองตัวเลือก การขนส่งแบบ JSON จนกว่าจะมีเหตุผลวัดได้ว่าต้องเป็นไบนารี เปิด resumption.enabled ตั้งแต่ session.update ครั้งแรก และบันทึก session.model ตอนเริ่มระบบ

นิสัยที่ควรมีในเอเจนต์เสียงใด ๆ: ตรวจงานเขียนกับระเบียนแทนคำยืนยันที่พูด ใส่การปฏิเสธไว้ในเครื่องมือแทนพรอมป์ ปล่อยให้การเล่นเสียงไหลหมดก่อน response.create ถัดไป และทดสอบกับสำเนียงจริง เสียงรบกวนจริง และเครื่องมือที่ล้มเหลวแบบที่มันล้มเหลวจริง

ส่วนต่อยอดที่ชัดเจนคือโทรศัพท์ (SpaceXAI มีเอกสารรองรับ SIP โดยตรง) ไคลเอนต์บนเบราว์เซอร์ด้วยโทเค็นชั่วคราว การเชื่อมต่อ MCP เข้ากับ CRM จริง และเวอร์ชันหลายภาษาที่สมบูรณ์ และหาก Voice Agent API ที่ฉันเปรียบเทียบข้อจำกัดเซสชันไว้ตรงกว่าในความต้องการของคุณ บทช่วยสอน Grok Voice Agent API ของเราได้ครอบคลุมเส้นทางนั้น

FAQs

ใช้ grok-voice-latest ในโปรดักชันปลอดภัยหรือไม่?

ไม่ควร ตามที่กล่าวไว้ในส่วนเวอร์ชันด้านบน มันเปลี่ยนตามวันที่ SpaceXAI กำหนด ไม่ใช่คุณ และมันพาบิลของคุณเปลี่ยนไปด้วย ปักหมุด grok-voice-think-fast-2.0 และเก็บอะลิแอสไว้สำหรับการลองในเครื่องที่การสลับแบบไม่คาดคิดจะไม่ไปตกบนสายลูกค้าจริง

Grok Voice Think Fast 2.0 รองรับภาษาอื่นนอกจากอังกฤษหรือไม่?

รองรับ มีเอกสารมากกว่ายี่สิบภาษาและมีการตรวจจับอัตโนมัติ และสามารถเอนเอียงการถอดเสียงไปยังภาษาหนึ่งด้วย language_hint โปรดทราบว่าสเปนและโปรตุเกสต้องใช้รหัสภูมิภาค เช่น es-MX หรือ pt-BR การใส่เพียง es หรือ pt ไม่ได้รับการยอมรับ และรหัสที่ไม่รู้จักจะถูกละเลยเงียบ ๆ และกลับไปตรวจจับอัตโนมัติ ดังนั้นการพิมพ์ผิดตรงนี้ไม่เสียค่าใช้จ่าย แต่ก็ไม่เกิดผลอะไรเช่นกัน

เปลี่ยนเสียงได้ไหม และมีทั้งหมดกี่เสียง?

eve เป็นเสียงในเอกสารและที่ฉันใช้ พร้อมด้วย ara, rex, sal และ leo และยังมีรหัสเสียงแบบกำหนดเอง GET /v1/tts/voices คืนรายชื่อปัจจุบัน หากจังหวะการพูดไม่ถูกใจ audio.output.speed รับค่า 0.7 ถึง 1.5

ทำให้เอเจนต์ตอบเร็วขึ้นกว่านี้ได้ไหม?

ลองใช้ reasoning.effort ซึ่งฉันข้ามไปใน walkthrough เพราะค่าเริ่มต้นมักจะเหมาะสม มันมากับค่า "high" และยังรับค่า "none" ซึ่งลดการวางแผนของโมเดลต่อเทิร์น ใช้ได้กับโฟลว์ค้นหาข้อมูลง่าย ๆ ฉันไม่แตะต้องมันกับสิ่งที่ต้องเลือกใช้เครื่องมือ

ต้องใช้ SDK อย่างเป็นทางการของ SpaceXAI เพื่อสร้างสิ่งนี้หรือไม่?

ไม่ ตามที่กล่าวไว้ในส่วนข้อกำหนดเบื้องต้น แพ็กเกจ websockets ธรรมดาหรือไคลเอนต์ที่เข้ากันได้กับ OpenAI ที่ชี้ไปยัง base URL api.x.ai ใช้งานได้ทั้งคู่ สิ่งหนึ่งที่ควรรู้: xai-sdk อย่างเป็นทางการเป็นไคลเอนต์ gRPC แยกต่างหากที่ไม่คุยกับ WebSocket นี้ ดังนั้นอย่าไปหาวิธีเรียลไทม์บนมัน หากต้องการจุดเริ่มอื่นที่ไม่ใช่ของฉัน xai-cookbook มีตัวอย่าง iOS เว็บ WebRTC และโทรศัพท์

หัวข้อ
ปัญญาประดิษฐ์

เรียนกับ DataCamp

Courses

ทำความเข้าใจปัญญาประดิษฐ์

2 ชม.
421.9K
เรียนรู้แนวคิดพื้นฐานของปัญญาประดิษฐ์ เช่น machine learning, deep learning, NLP, generative AI และอื่นๆ
ดูรายละเอียดRight Arrow
เริ่มหลักสูตร
ดูเพิ่มเติมRight Arrow