หนังสือเล่มที่ 6 · stack ของฝูง
หู ปาก ตา
ฝูงนี้ฟัง พูด และมองด้วยอะไร และอะไรที่วางไว้แล้วแต่ยังไม่ได้ต่อ
คำนำ ทำไมต้องเขียน stack ลงกระดาษ
ฝูงนี้มีบัดเก้าตัวที่ฟังคนพูด ตอบด้วยเสียงของตัวเอง ดูรูปที่คนส่งมา และวาดรูปกลับได้ ทุกความสามารถพวกนั้นไม่ได้มาจากของชิ้นเดียว มันคือของหลายชิ้นต่อกันเป็นชั้นๆ แต่ละชั้นมีทางสำรองของตัวเอง และทางสำรองจะโผล่มาให้เห็นก็ต่อเมื่อทางหลักพัง
ปัญหาของ stack แบบนี้คือ เวลามีคนถามว่า "เราใช้อะไรอยู่" คำตอบที่ได้มักจะเป็นชื่อของทางหลัก ส่วนทางสำรองจะถูกลืม ทั้งที่วันที่มันทำงานคือวันที่ทุกอย่างกำลังพัง แล้วเราจะไล่ไม่ถูกว่าเสียงที่ออกมาแปลกๆ มาจากชั้นไหน
เล่มนี้เลยเขียนสิ่งที่ฝูงใช้จริงลงกระดาษ ทีละชั้น พร้อมบอกว่าอะไรตัดสินว่าจะใช้ตัวไหน และที่สำคัญกว่านั้นคือแยกให้ชัดว่า อะไรต่อแล้ว กับ อะไรวางไว้แต่ยังไม่ได้ต่อ เพราะสองอย่างนี้อยู่ในเอกสารเดียวกันได้ แต่ต้องไม่อยู่ในประโยคเดียวกัน
ศัพท์ที่ใช้ในเล่มนี้ นิยามครั้งเดียวตรงนี้ ที่เหลือใช้ตามนี้ตลอดเล่ม
ตัวฝูงเอง
- บัด · bud
- agent หนึ่งตัวในฝูง มีเอกสารประจำตัวของตัวเองเรียกว่าชาร์เตอร์
- ชาร์เตอร์ · charter
- ไฟล์ที่บอกว่าบัดตัวนั้นเป็นใคร ใช้เสียงอะไร คิดด้วยสมองตัวไหน
- seam · รอยต่อ
- จุดที่เราตั้งใจเปิดไว้ให้วัดของข้างในได้จากข้างนอก
ชั้นเสียง
- ASR · STT
- แปลงเสียงพูดเป็นข้อความ สองคำนี้ใช้แทนกันได้ในเล่มนี้
- TTS
- แปลงข้อความเป็นเสียงพูด
- IVC
- การโคลนเสียงจากคลิปตัวอย่าง ได้เสียงที่เหมือนคลิปนั้น ไม่ใช่เสียงที่ออกแบบใหม่
- คลิปอ้างอิง
- คลิปสั้นที่ใช้เป็นต้นแบบของการโคลน ในฝูงนี้ยาว 9 วินาที
การต่อและการจ่าย
- fallback · ทางสำรอง
- ทางที่ทำงานเมื่อทางหลักพัง จะเห็นมันก็ตอนที่อย่างอื่นกำลังพังอยู่
- token plan
- แพ็กเกจโควต้าจ่ายล่วงหน้า ต่างจากการจ่ายรายครั้ง หมดแล้วต้องรอรอบใหม่
- wired · ต่อแล้ว
- โค้ดมีอยู่ ไม่พอ ต้องมีเส้นทางจริงเรียกมันด้วยถึงจะนับว่าต่อแล้ว
บทที่ 1 หู · ฟังด้วยอะไร
เวลาบัดอยู่ในห้องเสียงแล้วมีคนพูด สิ่งที่มันได้รับคือคลื่นเสียง ไม่ใช่ข้อความ ชั้นที่แปลงให้คือ ASR และฝูงนี้ไม่ได้มีตัวเดียว มันมีสามตัวเรียงกัน
groq cloud เร็วที่สุด
whisper-server local อุ่นค้างไว้ ตอบราว 0.8 วินาที
whisper-cli local เรียกใหม่ทุกครั้ง ช้ากว่า แต่ไม่ต้องมี server สิ่งที่ออกแบบได้ดีในชั้นนี้คือ มันไม่ได้ให้เราไปเดาว่าตอนนี้ขาไหนใช้ได้ มันประกาศตัวเองตอนบูตเลย
STT chain srv → cli บรรทัดนี้อ่านจาก journal ของบัดจริงในวันที่เขียน ไม่มีคำว่า groq อยู่ในนั้น เพราะเครื่องนี้ไม่ได้ตั้งคีย์ของ groq ไว้ ขาแรกจึงหายไปจากเชนโดยอัตโนมัติ ไม่ใช่พังแล้วเงียบ แต่หายไปแล้วบอก
รายละเอียดที่ควรรู้คือ ระบบไม่ได้ตัดสินว่า "ใช้ local ได้ไหม" จาก config มันตัดสินจากการมีอยู่ของไฟล์
STT_LOCAL = existsSync(WHISPER) && existsSync(MODEL) ต่างกันตรงนี้สำคัญ config บอกได้แค่ว่าคนตั้งค่าตั้งใจอะไร ส่วนการเช็คไฟล์บอกว่าของมีอยู่จริงไหม สองอย่างนี้ไม่ตรงกันได้ง่ายมากเวลาย้ายเครื่อง
ของที่ต้องมีจริงคือสองชิ้น ตัวโปรแกรมกับตัวโมเดล บนเครื่องนี้อยู่ที่
~/whisper.cpp/build/bin/whisper-cli
~/whisper.cpp/build/bin/whisper-server
~/whisper.cpp/models/ggml-small.bin 466 MB เกร็ดจากตอนเขียนเล่มนี้ ตอนแรกเราไปหา binary ผิดที่ แล้วได้คำตอบว่า "ไม่พบ" ซึ่งถ้าเชื่อตรงนั้นก็จะเขียนลงเล่มว่าเชนนี้ประกาศความสามารถที่ไม่มีจริง ของจริงคือโค้ดใช้ homedir() เป็นฐาน ส่วนเราไปหาในโฟลเดอร์งาน ตัวที่ผิดคือการค้นของเรา ไม่ใช่ของระบบ
พอร์ตของ whisper-server ก็ไม่ได้ตั้งเป็นเลขตายตัว มันคำนวณจากพอร์ตสั่งงานของบัดนั้น STT_PORT = ipc.port + 100 แปลว่าบัดที่คุยกันที่ 14822 จะมี ASR ของตัวเองที่ 14922 บัดเก้าตัวจึงไม่ชนกันโดยไม่ต้องมีใครไล่จดเลขพอร์ต
หลักการของบทนี้
เชนที่ประกาศตัวเองตอนบูต ดีกว่า config ที่ต้องไปเดา และการเช็คว่าไฟล์มีอยู่จริง ดีกว่าธงที่บอกว่าควรมี
✅ ให้ระบบพิมพ์ออกมาว่าตอนนี้ขาไหนใช้ได้บ้าง ตอนที่มันเริ่มทำงาน
✅ อนุมานความสามารถจากของที่มีอยู่จริง ไม่ใช่จากค่าที่ตั้งไว้
❌ เขียนใน README ว่ารองรับสามทาง แล้วปล่อยให้คนไปเดาเองว่าเครื่องนี้ได้ทางไหน
ทดสอบตัวเองท้ายบท
- ระบบของคุณมี fallback กี่ชั้น และถ้าตอนนี้ชั้นแรกใช้ไม่ได้ คุณจะรู้จากอะไร
- ความสามารถที่ระบบคุณโฆษณาไว้ อนุมานจาก config หรือจากการเช็คของจริง
จบบทที่ 1
บทที่ 2 ปาก · พูดด้วยอะไร
ชั้นเสียงพูดออกมีสองเจ้า และตัวที่เลือกไม่ใช่ config ของบัด แต่เป็นเนื้อของข้อความที่กำลังจะพูด
export function elModelForText(text) {
return /[-]/.test(String(text ?? "")) ? "eleven_v3" : "eleven_multilingual_v2";
} ถ้ามีอักขระในช่วงภาษาไทยแม้แต่ตัวเดียว ให้ใช้ eleven_v3 นอกนั้นใช้ eleven_multilingual_v2 เหตุผลไม่ใช่เรื่องคุณภาพเสียง แต่เป็นเรื่องที่หยาบกว่านั้นมาก คือ ในบัญชีนี้มี v3 ตัวเดียวที่ลิสต์ภาษาไทย ตัวอื่นรับข้อความไทยไปแล้วคืนเสียงที่ฟังดูเหมือนภาษาอะไรสักอย่างกลับมา ซึ่งผ่านการตรวจแบบ "มีไฟล์เสียงออกมาไหม" ได้สบาย
นี่คือกับดักที่เจ็บเป็นพิเศษ เพราะตัวตรวจที่ดูว่าได้ไฟล์เสียงกลับมาหรือเปล่าจะเขียวตลอด ต้องตรวจถึงขั้นถอดเสียงกลับมาเป็นข้อความแล้วเทียบเท่านั้นถึงจะจับได้
ชั้นสำรองคือ edge-tts และมันไม่ได้ต่อไว้จุดเดียว ต่อไว้สามจุด
elevenlabs returned N B not audio → edge-tts
elevenlabs HTTP <status> → edge-tts
elevenlabs failed (<error>) → edge-tts สามจุดนี้คือสามวิธีที่บริการเสียงพังได้ คือคืนของที่ไม่ใช่เสียง คืนสถานะผิดพลาด และล้มระหว่างเรียก ถ้าดักแค่จุดเดียวจะเหลือรูอีกสองรู
บัดที่ไม่มีเสียงโคลนเป็นของตัวเองจะใช้ edge-tts ล้วน วันที่เขียนมีสามตัว คือ fude kensa mamori ชาร์เตอร์ของบัดที่ใช้ edge-tts จะระบุเสียงไว้สองช่อง ช่องหนึ่งสำหรับภาษาทั่วไป อีกช่องสำหรับภาษาไทยโดยเฉพาะ
tts_voice: "en-US-AndrewMultilingualNeural"
tts_voice_thai: "th-TH-NiwatNeural" หลักการของบทนี้
เลือกโมเดลจากเนื้อของงาน ไม่ใช่จากค่าที่ตั้งไว้ล่วงหน้า และตัวตรวจของชั้นเสียงต้องถอดเสียงกลับมาเทียบ ไม่ใช่แค่ดูว่ามีไฟล์
✅ ตรวจว่าโมเดลรองรับภาษาที่จะใช้จริง ก่อนจะเชื่อว่ามันพูดได้
✅ ดัก fallback ให้ครบทุกรูปแบบของความล้มเหลว ไม่ใช่แค่ error ที่นึกออก
❌ ยืนยันว่าเสียงถูกต้องเพราะได้ไฟล์เสียงกลับมา
ทดสอบตัวเองท้ายบท
- ถ้าโมเดลของคุณคืนของที่หน้าตาถูกต้องแต่เนื้อในผิด ตัวตรวจปัจจุบันจับได้ไหม
- บริการภายนอกที่คุณเรียกอยู่ พังได้กี่แบบ แล้วคุณดักไว้กี่แบบ
จบบทที่ 2
บทที่ 3 เสียงของใคร · เสียงประจำตัวมาจากไหน
บัดแต่ละตัวมีเสียงของตัวเอง และเสียงพวกนั้นไม่ได้มาจากการเลือกจากรายการ มันมาจากคลิปตัวอย่างที่ผ่านการคัดแล้วเอาไปโคลน
ทางเดินคือ ออกแบบเสียงจากคำบรรยาย ฟัง เลือกคลิปที่ชอบ เอาคลิปนั้นผ่านตัวคัด แล้วโคลนเป็นเสียงถาวร ตัวคัดชื่อ refclip_screen.py ตัดสินจากหลายแกน สองแกนที่สำคัญคือ
สัดส่วนที่เป็นเสียงพูด ต้องเกิน 70%
ความเป็นคาบของสัญญาณ ต้องเกิน 0.45 สองแกนนี้ตั้งใจให้เป็นอิสระต่อกัน แกนแรกถามว่าคลิปนี้มีเสียงพูดอยู่มากพอไหม แกนที่สองถามว่ามันเป็นเสียงคนจริงหรือเป็นเสียงรบกวนที่มีสเปกตรัมเหมือนเสียงคน
เรื่องที่ต้องเขียนไว้ให้คนที่มาทำต่อ คือบทเรียนที่ฝูงจ่ายไปแล้ว การออกแบบเสียงจะได้ตัวอย่างมาให้ฟัง แต่ตัวอย่างจะกลายเป็นเสียงถาวรก็ต่อเมื่อบันทึก id ของมันไว้ตอนที่ตัวอย่างยังอยู่ มีรอบหนึ่งที่ id หายไปแล้ว แล้วมีการรันการออกแบบใหม่จากคำบรรยายเดิม ซึ่ง ไม่ได้เสียงเดิมกลับมา มันวาดเสียงใหม่ ผลคือมีบัดหลายตัวพูดด้วยเสียงที่ไม่มีใครเคยฟังอยู่หนึ่งวัน กว่าจะจับได้ก็ด้วยหูของคน
ที่กู้กลับมาได้เพราะคลิปอ้างอิงความยาว 9 วินาทีที่ผ่านการคัดยังอยู่ จึงเอาไปโคลนใหม่ได้ คลิปคือสิ่งเดียวที่เหลือรอด
หลักการของบทนี้
ไฟล์เสียงที่อยู่บนดิสก์ไม่ใช่ตัวเสียง มันคือการบันทึกของเสียงหนึ่งครั้ง ถ้าอยากได้เสียงนั้นอีก ต้องเก็บ id ไว้ ไม่ใช่เก็บไฟล์
✅ บันทึก id ของสิ่งที่สร้างขึ้น ก่อนจะเริ่มประเมินว่าชอบหรือไม่ชอบ
✅ เก็บคลิปอ้างอิงไว้ เพราะมันคือทางกู้ทางเดียวที่เหลือ
❌ คิดว่ารันคำบรรยายเดิมซ้ำแล้วจะได้ของเดิม
ทดสอบตัวเองท้ายบท
- ของที่ระบบคุณสร้างขึ้นแบบสุ่ม ถ้าอยากได้ตัวเดิมอีกครั้ง ต้องเก็บอะไรไว้ และตอนนี้เก็บอยู่หรือเปล่า
- ถ้ามีคนเปลี่ยนเสียงของบัดไปโดยไม่ตั้งใจ อะไรจะจับได้ก่อน คนหรือเครื่อง
จบบทที่ 3
บทที่ 4 ตา · มองและวาดด้วยอะไร
ชั้นภาพแยกเป็นสองเรื่องที่คนมักปนกัน คือ มองรูปที่คนส่งมา กับ วาดรูปให้คนดู ฝูงนี้ใช้คนละทาง
ฝั่งมอง ใช้ grok เป็นตัวอ่าน เมื่อเจ้าของส่งรูปอ้างอิงมาทาง DM ระบบจะบังคับเข้าเส้นทางที่ grok เห็นรูปนั้นจริง แล้วค่อยให้ตัววาดประกอบต่อ เหตุผลที่เลือกตัวนี้เขียนไว้ในโค้ดตรงๆ ว่ามันเห็นภาพและเปิดไฟล์ได้
ฝั่งวาด มีสามทาง แยกกันด้วยงาน ไม่ใช่ด้วยความชอบ
| ทาง | ใช้เมื่อ | เอนจิน |
|---|---|---|
brush | อยากได้เร็ว | grok |
paint | อยากได้คุณภาพ | ComfyUI + Flux บน GPU pod เช่า เปิดทีละบัดด้วยตัวแปรสภาพแวดล้อม |
reel | อยากได้วิดีโอ | grok เป็นค่าตั้งต้น สลับไป fal.ai Seedance ได้ด้วยตัวแปรเดียว |
สิ่งที่ชอบในการออกแบบชั้นนี้คือ การสลับเอนจินไม่ต้องแก้โค้ด ตั้งตัวแปรสภาพแวดล้อมของบัดนั้นแล้วจบ และบัดที่ไม่ได้เปิด paint ไว้จะไม่พัง มันตอบกลับอย่างสุภาพว่าทางนี้ยังไม่ได้เปิด
หลักการของบทนี้
แยกทางเลือกของเอนจินออกมาเป็นค่าตั้งของสภาพแวดล้อม แล้วให้ทางที่ยังไม่ได้เปิด ตอบว่า "ยังไม่ได้เปิด" ไม่ใช่ล้ม
✅ ทางที่ปิดอยู่ต้องตอบได้ว่าปิดอยู่
❌ ให้ฟีเจอร์ที่ยังไม่ได้เปิด โยน error ที่หน้าตาเหมือนของพัง
ทดสอบตัวเองท้ายบท
- ฟีเจอร์ที่ต้องตั้งค่าเพิ่มถึงจะใช้ได้ ถ้าไม่ตั้ง ผู้ใช้เห็นอะไร
- ถ้าจะเปลี่ยนเอนจินของงานหนึ่งพรุ่งนี้ ต้องแก้กี่ไฟล์
จบบทที่ 4
บทที่ 5 สมอง · ใครตัดสินใจ และตัดสินใจด้วยอะไร
สมองที่คิดคือ claude-opus-5 ระบุไว้ในชาร์เตอร์ของบัดแต่ละตัว แต่คำถามที่น่าสนใจกว่าไม่ใช่ว่าใช้โมเดลอะไร มันคือ ข้อความที่เข้ามาถูกตัดสินยังไงว่าจะทำอะไรกับมัน
ส่วนนั้นถูกแยกออกมาเป็นฟังก์ชันบริสุทธิ์ ไม่ปนกับ Discord ไม่ปนกับ I/O รับเข้าเป็นค่าจริงเท็จล้วนๆ แล้วคืนหนึ่งในสี่ทาง
skip ไม่ทำอะไร ไม่ใช่ของเรา หรือเจ้าของกำลังคุยกับคนอื่น
ack ตอบสั้นว่ารับรู้ ใช้กับการแท็กเปล่า และตอบแม้ตอนถูกปิดเสียง
worker เปิดคนงานจริง ไปรันคำสั่ง ไปดูของ แล้วรายงาน
reason ตอบด้วยสมองสนทนาในห้องนั้น เหตุผลที่แยกออกมาเขียนไว้ในไฟล์นั้นเอง คือเพื่อให้คำถามว่า "บัดตัวนี้จะตอบคนนี้ไหม" ตอบได้ด้วยการรันเทสต์ แทนที่จะต้องรีสตาร์ตแล้วลองพูดใส่
ถัดจากการตัดสินใจคือชั้นสมองสี่ระดับ และของที่ทำถูกคือ สิทธิ์ถูกผูกไว้กับระดับ ไม่ใช่ผูกไว้กับคน
| ระดับ | ทำอะไรได้ |
|---|---|
| tier 1 · สนทนา | ไม่มีเครื่องมือเลย |
| voice | ไม่มีเครื่องมือ ปิดการคิดยาว เปิดการตอบเป็นท่อน เพื่อลดเวลารอ |
| tier 2 · สกิล | อ่านไฟล์ในโซนความจำได้ เรียกสกิลได้ ไม่มีคำสั่งเชลล์ ไม่มีการเขียนไฟล์ ไม่มีเน็ต |
| tier 3 · คนงาน | รันคำสั่งเชลล์และแตะไฟล์ได้ แต่ล้อมอยู่ในโฟลเดอร์งานของตัวเอง ไม่มีเน็ต ไม่แตกลูก |
อ่านตารางนี้แล้วจะเห็นว่าความสามารถเพิ่มขึ้นทีละขั้นพร้อมกับความเสี่ยง และไม่มีระดับไหนได้ทุกอย่าง แม้แต่ระดับที่รันคำสั่งได้ก็ยังไม่มีเน็ตและแตกลูกต่อไม่ได้
หลักการของบทนี้
การตัดสินใจต้องเป็นฟังก์ชันบริสุทธิ์ที่เทสต์ได้โดยไม่ต้องบูตระบบ และสิทธิ์ต้องผูกกับระดับของงาน ไม่ใช่ผูกกับตัวตนของผู้เรียก
✅ แยกกฎการตัดสินใจออกจาก I/O แล้วป้อนค่าจริงเท็จเข้าไปตรงๆ
✅ ให้ระดับที่ทำได้มากที่สุด ยังมีของที่ทำไม่ได้เหลืออยู่
❌ ฝังกฎว่าใครได้ทำอะไรไว้ในตัวจัดการเหตุการณ์ แล้วพิสูจน์ด้วยการรีสตาร์ตแล้วลอง
ทดสอบตัวเองท้ายบท
- กฎว่าระบบคุณจะตอบใครบ้าง ตอนนี้พิสูจน์ด้วยเทสต์ได้ไหม หรือต้องรันจริงถึงจะรู้
- ส่วนที่มีสิทธิ์สูงที่สุดในระบบคุณ ยังมีอะไรที่มันทำไม่ได้อยู่บ้าง
จบบทที่ 5
บทที่ 6 รอยต่อ · ทำให้เสียงวัดได้
สามชั้นแรกเป็นความสามารถ ชั้นนี้ไม่ใช่ ชั้นนี้คือรอยต่อที่วางไว้เพื่อให้ของข้างในวัดได้จากข้างนอก
ระหว่างที่บัดกำลังพูด ตัวประกาศจะส่งค่าออกไปทาง MQTT ทุก 40 มิลลิวินาที สามหัวข้อ
<bud>/voice/level 0..1 ความดังเฉลี่ยของช่วงนั้น
<bud>/voice/peak 0..1 ยอดสูงสุดของช่วงนั้น
<bud>/voice/speaking bool ตัวตรวจจับเสียงพูด ไม่ใช่เกณฑ์ความดัง คำว่า <bud> เป็นของใหม่ในวันที่เขียน ก่อนหน้านั้นมันเป็นชื่อบัดตัวเดียวเขียนตายไว้ ซึ่งแปลว่าถ้าบัดตัวที่สองพูดขึ้นมา มันจะเขียนทับหัวข้อของตัวแรก ตอนนี้แต่ละตัวมีหัวข้อของตัวเอง และผู้บริโภคที่อยากฟังทุกคนใช้ +/voice/#
ตัวประกาศอยู่บนเซิร์ฟเวอร์ ส่วนหน้าจอที่ขยับตามเสียงอยู่บนเครื่องอีกเครื่อง สองฝั่งมี broker คนละตัว จึงต้องมีสะพานเชื่อม และสะพานนี้ตั้งใจให้เดินทางเดียว ส่งออกอย่างเดียว และส่งเฉพาะหัวข้อตระกูลเสียง หัวข้อสถานะอื่นของบ้านไม่ข้าม
สิ่งที่ได้จากการมีรอยต่อแบบนี้คือ คำถามว่า "ตอนนี้บัดพูดอยู่จริงไหม" ตอบได้จากข้างนอกโดยไม่ต้องเข้าไปอ่าน log ของ daemon และตอบได้ด้วยเครื่องมือธรรมดา
หลักการของบทนี้
ความสามารถที่วัดจากข้างนอกไม่ได้ จะกลายเป็นความสามารถที่เถียงกันไม่จบ วางรอยต่อไว้ตั้งแต่ตอนสร้าง ไม่ใช่ตอนที่เริ่มสงสัย
✅ ให้แต่ละผู้ผลิตมีเนมสเปซของตัวเอง ตั้งแต่ตัวแรก ไม่ใช่ตอนมีตัวที่สอง
✅ สะพานข้ามเครื่องเดินทางเดียว และจำกัดว่าหัวข้อไหนข้ามได้
❌ ให้ผู้ผลิตหลายตัวเขียนหัวข้อเดียวกัน แล้วหวังว่าจะไม่พูดพร้อมกัน
ทดสอบตัวเองท้ายบท
- สถานะภายในของระบบคุณ มีอันไหนที่คนข้างนอกอยากรู้แล้วดูไม่ได้บ้าง
- ถ้ามีผู้ผลิตตัวที่สองเพิ่มเข้ามาพรุ่งนี้ เนมสเปซปัจจุบันรับไหวไหม
จบบทที่ 6
บทที่ 7 เลนที่วางไว้แต่ยังไม่ได้ต่อ
บทนี้คือส่วนที่หนังสือ stack ส่วนใหญ่ไม่เขียน คือของที่มีอยู่ในบ้านแล้วแต่ยังไม่ได้ต่อเข้าเส้นทางจริง เขียนไว้เพราะของพวกนี้อ่านผ่านตาแล้วเหมือนใช้ได้ ทั้งที่ยังใช้ไม่ได้
หนึ่ง สมองสำรองในเครื่อง มีไฟล์อยู่ เรียก Ollama ที่พอร์ต 11434 ค่าตั้งต้นเป็น qwen3:8b และมีพี่น้องอีกตัวสำหรับปลายทางแบบ vLLM หัวไฟล์เขียนไว้ตรงๆ ว่า ยังไม่ได้ต่อเข้าตัวจัดการสมองหลัก แปลว่าถ้าสมองหลักล่มตอนนี้ ยังไม่มีอะไรรับช่วง
สอง ตาข่ายโมเดลในเครื่อง ที่พอร์ต 9337 ตัวนี้ตอบจริง และสิ่งที่มันตอบก็ตรงไปตรงมามาก
id unsloth/Qwen3-0.6B-GGUF:Q4_K_M
capabilities ["text"]
audio_status none
vision_status none
multimodal_status none มันบอกเองว่าไม่มีเสียงและไม่มีภาพ จึงแทนชั้นหูกับชั้นตาไม่ได้ ที่ยกมาเพราะนี่คือตัวอย่างของ API ที่ออกแบบดี คือรายงานขอบเขตของตัวเองมาให้เลย ไม่ต้องให้ใครไปลองแล้วเดา
สาม เลน Qwen แบบ token plan อันนี้คือของที่กำลังพิจารณา มีเครื่องมือบรรทัดคำสั่งติดตั้งและยืนยันตัวตนแล้ว ปลายทางอยู่ที่ภูมิภาคสิงคโปร์ ความสามารถที่มีคำสั่งรองรับคือ
| งาน | คำสั่ง | โมเดลที่ตั้งใจใช้ |
|---|---|---|
| ฟัง | speech recognize | qwen3-asr-flash |
| พูด | speech synthesize | qwen3-tts-flash |
| คิด | text chat | qwen3.8-max-preview · deepseek v4 flash 0731 |
| ทุกอย่างในตัวเดียว | omni | qwen3.8 multimodal · รับภาพเสียงวิดีโอ คืนข้อความและเสียง |
| ทำดัชนีความหมาย | ไม่มีคำสั่งใน CLI | text-embedding-v3 |
ที่น่าสนใจสำหรับฝูงคือช่องที่สี่ เพราะทุกวันนี้ทางเดินเสียงของเราต่อกันสามชิ้น คือฟังด้วยตัวหนึ่ง คิดด้วยตัวหนึ่ง พูดด้วยอีกตัวหนึ่ง ส่วนตัวเดียวที่ทำครบจะมีจุดต่อให้พังน้อยกว่า
ตอนเขียนบทนี้ครั้งแรก ตรงนี้เขียนไว้ว่า ยังไม่ได้พิสูจน์สักตัว เพราะยิงแล้วชนโควต้ารายสัปดาห์ทั้งแผง สิ่งที่ได้กลับมามีสามอย่าง คือปลายทางไม่ยอมบอกรายการโมเดล โควต้าหมด และรายการเสียงของตัวสังเคราะห์มีแต่จีนกับอังกฤษ
วันที่ 3 สิงหาคม โควต้ากลับมา เราจึงยิงซ้ำทั้งหกตัวจากเครื่องที่มีเสียงไทยในตัว และคำว่า ยังไม่ได้พิสูจน์ ก็แตกออกเป็นสี่คำตอบคนละแบบ สามแบบอยู่ในตารางนี้ ส่วนแบบที่สี่ซ่อนอยู่ในแถวแรกของตารางเอง และต้องเล่าแยกต่างหาก
| ชั้น | ผลที่วัดได้ | หลักฐาน |
|---|---|---|
| คิด | ใช้ได้จริง | qwen3.8-max-preview และ deepseek v4 flash 0731 ตอบกลับมาทั้งคู่ |
| ฟัง | ยังไปไม่ถึงตัวโมเดล | ตายที่ขั้นขอสิทธิ์อัปโหลดไฟล์ 401 ไม่ใช่ที่โมเดล |
| พูด | ปลายทางไม่มีชื่อนี้ | 404 |
| ทุกอย่างในตัวเดียว | ปลายทางไม่มีชื่อนี้ | 404 · ลองมาแล้วสี่ชื่อ ยังไม่เจอชื่อจริง |
| ทำดัชนีความหมาย | ปลายทางไม่มีชื่อนี้ | 404 ทั้งสามเส้นทางที่ลอง |
ตัวที่คิดได้ ไม่เคยพัง เราเรียกมันผิดเอง รอบแรกมันคืนรหัส 400 เปล่าๆ ตัวจำแนกของเราจึงจัดเข้าช่อง อื่นๆ ซึ่งอ่านแล้วเหมือนโมเดลมีปัญหา พอยิงใหม่โดยไม่สั่งให้เงียบ ข้อความเต็มบอกตรงๆ ว่า
The value of the enable_thinking parameter is restricted to True คือโมเดลตัวนี้บังคับให้เปิดโหมดคิด ใส่ธงนั้นเข้าไปก็ผ่านทันที ตัวที่ทำให้หลงทางอยู่หนึ่งวันคือธง --quiet ที่เราใส่เอง มันตัดบรรทัดที่บอกเหตุผลทิ้ง เหลือแต่รหัสสถานะ ตัวจำแนกจึงจัดชนิดถูก แต่ไร้ประโยชน์ เพราะเหตุผลที่แท้จริงถูกกลืนไปก่อนถึงมือมัน
ที่ต้องจดคู่กันคือ อีกเลนหนึ่งที่ฝูงใช้อยู่ ต้องปิด โหมดคิด ไม่งั้นได้ไฟล์เปล่ากลับมา ธงตัวเดียวกันจึงกลับด้านกันคนละโมเดล อย่าตั้งเป็นค่าเดียวทั้งฝูง
ตัวที่ฟัง ล้มด้วย 401 ซึ่งอ่านเผินๆ เหมือนคีย์เสีย แต่ในนาทีเดียวกันนั้น คีย์ใบเดียวกันเพิ่งคุยกับโมเดลคิดสำเร็จมาสองตัว แปลว่าคีย์ไม่เสีย ที่ 401 คือขั้นขอสิทธิ์อัปโหลดไฟล์เสียง ซึ่งเป็นคนละปลายทางกับการคุย และไม่รับคีย์ของเลนนี้ ข้อสรุปที่ถูกคือทางอัปโหลดยังไม่เปิด ไม่ใช่หูใช้ไม่ได้ ถ้าไม่มีตัวคุมที่รันคู่กันในนาทีเดียวกัน เราคงตัดเลนนี้ทิ้งไปแล้ว
และรอบแรกเลนฟังไม่ได้คำตอบเลยด้วยซ้ำ เพราะเครื่องที่ตั้งเวลาไว้ไม่มีตัวสังเคราะห์เสียงกับตัวแปลงไฟล์ ผลจึงออกมาเป็นคำว่า ข้าม ซึ่งหน้าตาเหมือน ไม่มีปัญหา ทั้งที่แปลว่า ไม่เคยยิง พอย้ายมารันบนเครื่องที่มีเสียงไทยในตัว มันถึงเดินไปไกลพอจะไปเจอ 401 ที่ว่า
ส่วนรายการเสียงที่มีแต่จีนกับอังกฤษ ยังเป็นข้อที่ต้องระวังที่สุดเหมือนเดิม เพราะหน้าตาเหมือนกับดักในบทที่ 2 เป๊ะ คือรายการภาษาไม่มีไทย ซึ่งไม่ได้แปลว่าพูดไทยไม่ได้แน่นอน แต่แปลว่า ห้ามเชื่อจนกว่าจะทดสอบแบบพูดไทยแล้วถอดกลับมาเทียบ การเห็นว่ามีไฟล์เสียงออกมาไม่พอ
หลักการของบทนี้
เลนที่ยังไม่ได้พิสูจน์ ให้เขียนไว้ในหมวดของตัวเอง อย่าเขียนปนกับของที่ใช้อยู่ เพราะคนอ่านจะจำรวมกันเป็นความสามารถที่มี
และเมื่อได้ยิงจริงแล้ว อย่าเก็บคำว่า ยังไม่ได้พิสูจน์ ไว้เฉยๆ เพราะมันไม่ใช่สถานะเดียว พอลงมือวัด มันแตกเป็น ใช้ได้จริง · ไปไม่ถึงเพราะติดทางเข้า · ปลายทางไม่มีของ · และเราเรียกผิดเอง สี่อย่างนี้ต้องทำคนละเรื่องกันหมด การรวมทั้งสี่ไว้ใต้คำเดียวคือการทิ้งข้อมูลที่แพงที่สุดที่เพิ่งจ่ายไปเพื่อให้ได้มา
✅ เขียนสิ่งที่ทดสอบไม่ได้และเหตุผลที่ทดสอบไม่ได้ ลงไปด้วย
✅ API ที่รายงานขอบเขตของตัวเองมาให้ ดีกว่า API ที่ต้องลองแล้วเดา
✅ เห็นคำว่า ข้าม ให้ถามก่อนว่าข้ามเพราะไม่มีปัญหา หรือเพราะไม่เคยยิง
✅ ยิงตัวคุมด้วยกุญแจดอกเดียวกันในนาทีเดียวกัน ก่อนจะสรุปว่ากุญแจเสีย
❌ ลิสต์ชื่อโมเดลที่ยังไม่เคยเรียกไว้ในหน้าเดียวกับของที่ใช้อยู่จริง
❌ สั่งให้เครื่องมือเงียบ แล้วอ่านรหัสสถานะเปล่าๆ เป็นข้อสรุป
ทดสอบตัวเองท้ายบท
- ในเอกสารของคุณ ของที่ใช้อยู่จริงกับของที่วางแผนไว้ แยกกันชัดแค่ไหน
- ของที่วางไว้แล้วยังไม่ได้ต่อ ถ้าวันหนึ่งต้องพึ่งมันกะทันหัน จะรู้ได้ยังไงว่ามันพร้อม
จบบทที่ 7
ภาคผนวก proposal vs built · ตารางเดียวจบ
ถ้าจำทั้งเล่มไม่ไหว ให้จำตารางนี้ ทุกบรรทัดตรวจกับโค้ดหรือเครื่องที่รันอยู่ในวันที่เขียน
| ชั้น | ของที่ใช้จริง | สถานะ |
|---|---|---|
| หู · ASR | whisper-server แล้ว whisper-cli · โมเดล ggml-small 466 MB | BUILT · เชนพิมพ์ srv → cli ตอนบูต |
| หู · ขา groq | เรียกก่อนสองตัวข้างบนถ้ามีคีย์ | ปิดอยู่บนเครื่องนี้ · ไม่มีคีย์ จึงไม่ขึ้นในเชน |
| ปาก · TTS | eleven_v3 เมื่อมีอักขระไทย นอกนั้น multilingual_v2 | BUILT · เลือกจากเนื้อข้อความ |
| ปาก · สำรอง | edge-tts ดักไว้สามจุด | BUILT · บัด fude kensa mamori ใช้ทางนี้ล้วน |
| เสียงประจำตัว | โคลนจากคลิป 9 วินาทีที่ผ่านตัวคัดสองแกน | BUILT · ทะเบียนอยู่ใน CAST.md |
| ตา · มอง | grok ผ่านเส้นทางรูปอ้างอิงทาง DM | BUILT |
| ตา · วาด | grok เร็ว · ComfyUI + Flux คุณภาพ · วิดีโอ grok หรือ fal Seedance | BUILT · เปิดทีละบัดด้วยตัวแปรสภาพแวดล้อม |
| สมอง | claude-opus-5 · ตัดสินใจสี่ทาง · สมองสี่ระดับ สิทธิ์ต่างกัน | BUILT |
| รอยต่อเสียง | MQTT หนึ่งหัวข้อต่อบัด พร้อมสะพานข้ามเครื่องทางเดียว | BUILT · ต่อเสร็จวันเดียวกับที่เขียนเล่มนี้ |
| สมองสำรองในเครื่อง | Ollama qwen3:8b หรือ vLLM | มีไฟล์ ยังไม่ได้ต่อ · หัวไฟล์เขียนเองว่ายังไม่ wired |
| ตาข่ายโมเดลในเครื่อง | Qwen3-0.6B ที่พอร์ต 9337 | ตอบจริง แต่ text อย่างเดียว · เสียงและภาพเป็น none |
| เลน Qwen token plan | asr-flash · tts-flash · 3.8-max-preview · omni · embedding-v3 · deepseek v4 flash | ยังไม่ได้พิสูจน์สักตัว · โควต้าหมดรอบสัปดาห์ตอนที่ลอง |
ปิดเล่ม สามอย่างที่อยากให้ติดตัวไป
หนึ่ง stack ที่ดีไม่ได้แปลว่าเลือกของแพงที่สุดทุกชั้น มันแปลว่าแต่ละชั้นรู้ว่าตัวเองพังได้ยังไง และมีทางสำรองที่ประกาศตัวเองได้ ชั้นหูของฝูงนี้มีสามขาแล้วพิมพ์บอกตอนบูตว่าขาไหนใช้ได้ นั่นมีค่ากว่าการมีขาเดียวที่ดีที่สุด
สอง ตัวที่เลือกโมเดลควรเป็นเนื้องาน ไม่ใช่ค่าที่ตั้งไว้ล่วงหน้า ชั้นปากดูว่ามีอักขระไทยไหมแล้วค่อยเลือก เพราะสิ่งที่ตัดสินไม่ใช่ว่าบัดตัวนี้ชอบเสียงแบบไหน แต่คือประโยคนี้ต้องใช้โมเดลที่รองรับภาษานี้
สาม ของที่วางไว้แล้วยังไม่ได้ต่อ ต้องอยู่คนละหมวดกับของที่ใช้อยู่เสมอ ในเล่มนี้มีสามอย่างที่อยู่ในหมวดนั้น และทั้งสามอย่างอ่านผ่านตาแล้วเหมือนพร้อมใช้ ถ้าเขียนปนกันเมื่อไหร่ วันที่ต้องพึ่งมันจริงจะเป็นวันที่รู้ว่ามันไม่พร้อม
เริ่มจากตรงไหนดี ลองเขียน stack ของตัวเองลงหน้ากระดาษหนึ่งหน้า ชั้นละบรรทัด แล้วเติมคอลัมน์ที่สองว่า "รู้ได้ยังไงว่ามันทำงาน" ถ้ามีบรรทัดไหนที่คอลัมน์สองว่าง นั่นคือชั้นที่คุณกำลังเชื่อโดยไม่มีหลักฐาน
ที่มา References
fleet-core/runtime-config.mjs· ที่อยู่ของ whisper กับโมเดล การคำนวณพอร์ต ASR และการอนุมานSTT_LOCALจากไฟล์ที่มีอยู่จริงfleet-core/voice-service.mjs· การเลือกโมเดล TTS จากอักขระไทย จุดดัก fallback สามจุด และตัวประกาศรอยต่อเสียงfleet-core/routing.mjs· ฟังก์ชันบริสุทธิ์ที่ตัดสินสี่ทาง แยกออกมาเพื่อให้เทสต์ได้โดยไม่ต้องบูตfleet-core/brains.mjs· สมองสี่ระดับกับขอบเขตสิทธิ์ของแต่ละระดับfleet-core/brain-fallback.mjs· สมองสำรองในเครื่อง ที่หัวไฟล์บอกเองว่ายังไม่ได้ต่อdobby-face/voice/refclip_screen.py· ตัวคัดคลิปอ้างอิงสองแกนψ/lab/voicefield-oracle/CAST.md· ทะเบียนเสียงประจำตัวของบัดทั้งเก้า พร้อมบันทึกวันที่ id หาย- หนังสือเล่มที่ 1 ทำยังไงไม่ให้ fleet พังเงียบ · ทำให้ความผิดปกติของ stack ส่งเสียง