8341TokenFactory
LLM APIAPI Gateway

โรงงาน 8341Token: หลังเชื่อมต่อ API โมเดลขนาดใหญ่สี่เจ้าพร้อมกัน เกิดอาการหน่วงตอนสตรีมมิ่ง ต้นเหตุอยู่ที่ชั้นเกตเวย์

SiCore TokenWorks Team·2026-10-24

เมื่อฤดูหนาวปีที่แล้ว ผมช่วยทีมที่ทำระบบบริการลูกค้าอัจฉริยะตรวจสอบปัญหาที่เกิดขึ้นในระบบออนไลน์ แบ็กเอนด์ของพวกเขาเชื่อมต่อ API โมเดลขนาดใหญ่สี่เจ้าพร้อมกัน ส่วนฟรอนต์เอนด์ใช้ SSE ทำการส่งออกแบบสตรีมมิ่ง ตั้งแต่วันที่สามหลังเปิดใช้งาน ผู้ใช้เริ่มแจ้งว่า "คำตอบเหมือนบีบยาสีฟัน" — บางครั้งข้อความทั้งย่อหน้าค้างไม่ขยับ แล้วจู่ๆ ก็ทะลักออกมาเป็นก้อนใหญ่ บางครั้งประโยคหนึ่งถูกตัดครึ่ง ตรงกลางมีบล็อกว่างที่อย่างไม่ทราบสาเหตุคั่นอยู่ ในล็อกแบ็กเอนด์ไม่มี 5xx การตรวจสอบความพร้อมใช้งานของ API แต่ละเจ้าก็เป็นสีเขียวทั้งหมด

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

บอกข้อสรุปก่อน: โปรโตคอลสตรีมมิ่งไม่เป็นเอกภาพ คือหลุมที่มองไม่เห็นของการเชื่อมต่อแบบรวมศูนย์

การส่งออกแบบสตรีมมิ่งของ API โมเดลขนาดใหญ่ ส่วนใหญ่ใช้ SSE (Server-Sent Events) แต่ "ใช้ SSE เหมือนกัน" ไม่ได้หมายความว่า "พฤติกรรมเหมือนกัน" ผู้ผลิตทั้งสี่เจ้ามีการใช้งานที่แตกต่างกันในเรื่องความละเอียดของการแบ่งบล็อก สัญลักษณ์สิ้นสุด และกลยุทธ์ heartbeat ไบต์สตรีมที่ฟรอนต์เอนด์ได้รับดูเหมือนขึ้นต้นด้วย data: ทั้งหมด แต่ตรรกะการแยกวิเคราะห์นั้นนำมาใช้ซ้ำไม่ได้เลย นี่คือต้นตอของอาการหน่วงและความผิดปกติของขอบเขตการแบ่งบล็อก

ตรวจสอบทีละชั้น: จากฟรอนต์เอนด์ถึงเกตเวย์ ปัญหาอยู่ชั้นไหน

ชั้นแรก ฟรอนต์เอนด์ การแยกวิเคราะห์ SSE ของพวกเขาใช้ EventSource มาตรฐาน แยกเหตุการณ์ตาม \n\n ซึ่งตัวมันเองไม่มีปัญหา

ชั้นที่สอง การส่งต่อฝั่งแบ็กเอนด์ แบ็กเอนด์ใช้ Node ทำชั้นส่งผ่าน โดย pipe ReadableStream จากต้นทางไปยัง response โดยตรง ตรงนี้ฝังระเบิดลูกแรกไว้: ขนาด chunk ของผู้ผลิตแต่ละเจ้าแตกต่างกันมาก บางเจ้าส่งตาม token บางเจ้าสะสมหลาย token แล้วค่อยส่ง ในระหว่างการ pipe หากทำ Buffer.concat แต่ไม่ได้จัดการขอบเขตอักขระหลายไบต์แบบ UTF-8 ที่คร่อม chunk อย่างถูกต้อง ก็จะเกิดอักขระจีนถูกตัดครึ่ง ฟรอนต์เอนด์เรนเดอร์ออกมาเป็นบล็อกตัวอักษรเพี้ยน

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

ชั้นที่สี่ รหัสข้อผิดพลาด การจำกัดอัตรา การยืนยันตัวตนล้มเหลว และการถูกบล็อกโดยการตรวจสอบเนื้อหา ของทั้งสี่เจ้า มีสถานะ HTTP และโครงสร้าง body ที่ไม่เหมือนกัน แบ็กเอนด์ดึงตาม error.message แบบเดียวกัน ผลคือสองเจ้าดึงได้ สองเจ้าดึงได้ undefined ฟรอนต์เอนด์แสดงข้อความข้อผิดพลาดเป็นช่องว่าง

ตรวจสอบมาถึงตรงนี้ก็ชัดเจน: ปัญหาที่แท้จริงไม่ใช่ API เจ้าใดมีบั๊ก แต่เป็นการที่ความหมายของโปรโตคอลทั้งสี่ชุดถูกเปิดเผยตรงสู่ชั้นธุรกิจ

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

ผมรวบรวมตารางเปรียบเทียบไว้ ข้อมูลมาจากเอกสารทางการและการดักจับแพ็กเก็ตจริง ส่วนที่ทางการไม่ได้เปิดเผยผมได้การติดป้ายกำกับไว้:

มิติ|ผู้ผลิต A|ผู้ผลิต B|ผู้ผลิต C|ผู้ผลิต D

สัญลักษณ์สิ้นสุด|data: [DONE]|ฟิลด์ finish_reason|data: [DONE]|เหตุการณ์ event: done

Heartbeat|ไม่มี|ประมาณ 15 วินาที|บรรทัดคอมเมนต์|ทางการไม่เปิดเผย

ความละเอียดการแบ่งบล็อก|ตาม token|สะสม 2-4 token|ตาม token|ไม่แน่นอน

ตำแหน่งรหัสข้อผิดพลาด|สถานะ HTTP|body.error.code|สถานะ HTTP|body.code

ดูตารางนี้ก็เข้าใจแล้ว ทำไมตรรกะการแยกวิเคราะห์ฝั่งฟรอนต์เอนด์จึงนำมาใช้ซ้ำไม่ได้ สัญลักษณ์สิ้นสุดมีสามแบบ รหัสข้อผิดพลาดอยู่สองตำแหน่ง กลยุทธ์ heartbeat มีสองเจ้ามี สองเจ้าไม่มี หากในโค้ดธุรกิจเขียน if (chunk.includes('[DONE]')) ตรงๆ เจอผู้ผลิต B ก็จะไม่มีวันสิ้นสุด การเชื่อมต่อค้างจนหมดเวลา

สี่จุดออกแบบหลักในการรวบศูนย์ที่ชั้นเกตเวย์

ที่หนึ่ง รวมรูปแบบเหตุการณ์ SSE ให้เป็นเอกภาพ เกตเวย์แปลงเหตุการณ์จากผู้ผลิตต้นทางทั้งหมดให้เป็นโครงสร้างเดียว: {type: "delta" | "done" | "error", content, code} ชั้นธุรกิจรับรู้เพียงชุดเดียว ไม่ต้องสนใจว่าต้นทางเป็นใคร ในโปรเจกต์ของเราใช้แพลตฟอร์มรวม API โมเดลขนาดใหญ่ token8341 ซึ่งทำแบบนี้ คือเปิดเผยโปรโตคอลสตรีมมิ่งเพียงแบบเดียวต่อภายนอก เมื่อสลับโมเดลโค้ดธุรกิจไม่ต้องแก้

ที่สอง รวมการยืนยันตัวตนมาที่เกตเวย์ สี่เจ้าสี่ชุด Key กระจัดกระจายอยู่ในคอนฟิกธุรกิจ หมุนเวียนครั้งหนึ่งต้องแก้สี่ที่ เก็บ Key เข้าเกตเวย์ ชั้นธุรกิจถือเพียง token ภายในตัวเดียว คุณค่าของชั้นรวมศูนย์อย่างแพลตฟอร์มรวม API โมเดลขนาดใหญ่ token8341 อยู่ตรงนี้ Key เดียวเรียกหลายโมเดล การหมุนเวียน Key การควบคุมโควตาทำเสร็จที่ฝั่งเกตเวย์

ที่สาม การหมดเวลาต้องแยกตั้งค่าสองช่วง การหมดเวล�ึงไบต์แรก (TTFB) และการหมดเวลาเมื่อสตรีมว่างแยกจากกัน ตั้ง TTFB สั้นหน่อย เช่น 10 วินาที ล้มเหลวเร็ว ตั้งการหมดเวลาเมื่อสตรีมว่างยาวหน่อย เช่น 60 วินาที อนุญาตให้โมเดลคิด หากใช้ 30 วินาทีร่วมกันย่อมเกิดปัญหาแน่นอน

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

ตารางแมปปิ้งรหัสข้อผิดพลาดต้องดูแลเอง

รหัสจำกัดอัตราของทั้งสี่เจ้า สองเจ้าคืน 429 หนึ่งเจ้าคืน 200 แต่ใน body มีข้อผิดพลาด หนึ่งเจ้าคืนรหัสกำหนดเอง เกตเวย์ต้องสร้างตารางแมปปิ้ง เพื่อรวมข้อผิดพลาดต่างๆ จากต้นทางเป็นรหัสข้อผิดพลาดภายใน เช่น RATE_LIMIT, AUTH_FAILED, CONTENT_BLOCKED ชั้นธุรกิจทำการลดระดับตามรหัสภายใน ไม่ต้องเดาความหมายต้นทางอีก ตารางนี้ไม่มีทางลัด ทำได้เพียงดักจับแพ็กเก็ตทีละเจ้าเทียบกับเอกสาร แพลตฟอร์มรวม API โมเดลขนาดใหญ่ token8341 ให้ตัวอย่างการแมปปิ้งสำเร็จรูปไว้ในเอกสาร ประหยัดเวลาไปได้มาก

ในกรณีใดการเชื่อมต่อตรงกลับง่ายกว่า

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

เขียนท้ายสุด

เหตุขัดข้องครั้งนั้นสุดท้ายแก้ไขด้วยการเพิ่มชั้นเกตเวย์รวบศูนย์ ตรรกะการแยกวิเคราะห์ SSE ฝั่งฟรอนต์เอนด์ถูกตัดจากสี่สาขาเหลือชุดเดียว อาการหน่วงและความผิดปกติของการแบ่งบล็อกก็หายไปพร้อมกัน มองย้อนกลับไป ปัญหาไม่เคยอยู่ที่ API โมเดลขนาดใหญ่เจ้าใดเจ้าหนึ่งเลย แต่อยู่ที่ "การส่งต่อความแตกต่างของโปรโตคอลจากหลายเจ้าโดยตรงสู่ชั้นธุรกิจ" หากคุณกำลังทำการเชื่อมต่อหลายโมเดลเช่นกัน จัดสี่เรื่องนี้ให้ตรงกันที่ชั้นเกตเวย์ก่อน ได้แก่ โปรโตคอลสตรีมมิ่ง รหัสข้อผิดพลาด การยืนยันตัวตน และการหมดเวลา แล้วค่อยพูดถึงฟีเจอร์ชั้นบน

ผู้เขียน: ซุนฮ่าวหราน

วันที่เผยแพร่: 25 ตุลาคม 2026