สวัสดีครับ/ค่ะ ขอแนะนำ Jsiphon เครื่องมือนี้ช่วยแก้ปัญหาที่พบบ่อยเมื่อใช้การตอบกลับแบบมีโครงสร้างกับการสตรีมของ LLM

เมื่อใช้การตอบกลับแบบมีโครงสร้าง (โหมด JSON) ของ LLM ร่วมกับการสตรีม ก็มักจะเจอปัญหาการพาร์สคำตอบบางส่วนอยู่บ่อย ๆ ในกรณีแบบนี้จะใช้ JSON.parse() ตรง ๆ ไม่ได้ และมักต้องใช้วิธีพยายามกู้คืน JSON ที่ยังไม่สมบูรณ์ซ้ำ ๆ

แต่ถ้าอาศัยคุณสมบัติของคำตอบจาก LLM ที่เป็นแบบ append-only โดยมีแต่ข้อมูลเพิ่มเข้ามาโดยไม่ย้อนลำดับเดิม ก็จะสามารถแก้ปัญหานี้ได้อย่างสะอาดกว่ามาก และ Jsiphon มี 3 ความสามารถดังต่อไปนี้

  • การพาร์สแบบ append-only — เมื่อมีคำตอบบางส่วนเข้ามา เช่น {"msg": "Hel ก็จะคืนค่าคำตอบที่สมบูรณ์ได้ทันทีในรูป {msg: "Hel"} โดยจะถือว่าสคีมาก่อนหน้าฟิลด์ msg ในตัวอย่างนี้สมบูรณ์แล้ว

  • การติดตามเดลตา — ในแต่ละครั้งที่ส่งผลลัพธ์ออกมา นอกจากสแนปช็อตทั้งหมดแล้ว ยังมีข้อมูลที่เพิ่งถูกเพิ่มเข้ามาล่าสุดแยกให้อีกด้วย ตัวอย่างเช่น ถ้าคุณกำลังวาดบอลลูนข้อความของแชตบอตหลายอัน ก็ไม่ต้องวาดทั้งหมดใหม่ แต่สามารถวาดเฉพาะส่วนที่เพิ่มขึ้นของบอลลูนล่าสุดได้ ในตัวอย่างก่อนหน้า หาก LLM ส่งต่อด้วย lo, World! คุณก็จะพบ {msg: "lo, World!"} ใต้ delta ของคำตอบได้ทันที ทำให้ไม่จำเป็นต้องกู้คืนและพาร์ส JSON ใหม่พร้อมทำ diff ทุกครั้งที่ได้รับสแนปช็อต

  • การตรวจจับความกำกวม — จะคืนค่า ambiguity tree ที่มีโครงสร้างต้นไม้เหมือนกับคำตอบทุกประการ โดยใช้บอกได้ในหลายระดับชั้นว่าข้อมูลที่อยู่ในสแนปช็อตนั้นยืนยันแล้วหรือยัง หรือยังอยู่ระหว่างการอ่านคำตอบต่อไป ตัวอย่างเช่น ขณะสตรีมข้อมูลต่อไปนี้

    1. {"header":
    2. {"title": "abcd
    3. efghijk",
    4. "date": "..."
    5. },
    6. "body": "..."}

    ถ้าใช้ isAmbiguous(ambiguous.header.title) ก็จะสามารถใช้งาน title ได้อย่างปลอดภัยตั้งแต่จังหวะที่ title สมบูรณ์ (คำตอบลำดับที่ 3) โดยไม่ต้องรอให้ฟิลด์ถัดไปสมบูรณ์ทั้งหมด ไม่ได้ให้แค่สถานะว่าทั้งหมดสมบูรณ์หรือไม่ แต่ให้สถานะความสมบูรณ์บางส่วนในทุกระดับชั้นด้วย ดังนั้น isAmbiguous(ambiguous.header) จะคืนค่า isAmbiguous = false ก็ต่อเมื่อทุกลูกหลานของ header สมบูรณ์แล้ว

แม้ตอนนี้จะมีไลบรารีสำหรับกู้คืน/พาร์ส partial JSON อยู่แล้วหลายตัว (เช่น partial-json, gjp-4-gpt ฯลฯ) และต่างก็ช่วยแก้ปัญหาการพาร์สพื้นฐานได้ดี แต่ Jsiphon ใช้ประโยชน์จากธรรมชาติของการสตรีม LLM ที่เป็นแบบ append-only จึงไม่ได้แค่ให้สแนปช็อตเท่านั้น แต่ยังให้ข้อมูลเพิ่มขึ้นรายฟิลด์ (delta) และช่วยระบุได้ในแต่ละรอบว่าฟิลด์ใดสมบูรณ์แล้ว
ถ้าคุณกำลังแก้ปัญหาแนวนี้อยู่ ก็น่าจะเข้าใจประโยชน์ของมันได้ดี ผม/ฉันเองกำลังผสาน Jsiphon เข้ากับ multi-type SSE เพื่อให้แชตบอตสตรีมข้อความไปพร้อมกับตัดสินหลายแฟลกแบบเรียลไทม์ เช่น is_adult, need_admin เป็นต้น

ในด้านการใช้งานจริง ยังมีความสะดวกเพิ่มเติม เช่น zero dependency, ไม่ throw error แม้คำตอบจะผิดรูป, และการตัดข้อความที่ไม่มีความหมายก่อนหรือหลัง JSON ออก

GitHub: https://github.com/webtoon-today/jsiphon
npm install jsiphon

ถ้าเห็นว่ามีประโยชน์ก็ลองนำไปใช้ดู แล้วช่วยส่งความคิดเห็นกันได้ครับ/ค่ะ
สำหรับ ambiguity tree เองก็ดูเป็นการออกแบบที่ท้าทายพอสมควร จึงอยากรู้เหมือนกันว่ามีวิธีที่ดีกว่านี้ไหม หากมีข้อเสนอแนะเรื่องการออกแบบ API ก็จะขอบคุณมากครับ/ค่ะ

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น