บทช่วยสอน RAG ส่วนใหญ่มักจบลงแค่การทำ Demo คุณแบ่งข้อมูล (chunk) ตามจำนวนโทเคน ใส่ทุกอย่างลงใน vector database แล้วก็จบงาน แบบนั้นมันใช้ได้ผลเมื่อผู้ใช้ถามว่า "นโยบายการคืนสินค้าคืออะไร?" ในชุดคำถาม FAQ ที่สะอาดสะอ้าน แต่มันจะพังทันทีเมื่อมีคนวางเนื้อหาสัญญาครึ่งฉบับแล้วถามเกี่ยวกับข้อกำหนดที่สาม หรือเมื่อนักพัฒนาพิมพ์รหัสข้อผิดพลาด (error code) ที่หาได้ยากลงในการค้นหาเอกสารของคุณ
การกำหนดขนาดหน้าต่างโทเคนแบบตายตัวทำให้ข้อตกลงทางกฎหมายถูกตัดขาดกลางประโยค การแบ่ง chunk ขนาดใหญ่เกินไปทำให้ข้อมูลอ้างอิง API ถูกฝังอยู่ใต้พารากราฟที่เต็มไปด้วยข้อมูลขยะ และที่แย่ที่สุดคือ การดึงข้อมูลที่ล่าช้าทำให้ผู้ใช้เลิกถามก่อนที่โมเดลจะเริ่มสร้างคำตอบเสียอีก เราเรียนรู้เรื่องนี้ด้วยบทเรียนที่ยากลำบาก เมื่อเราเปลี่ยนชั้นการดึงข้อมูล (retrieval layer) จากการ "คาดเดา" มาเป็นการ "วัดผล" เราสามารถลดความหน่วง (latency) ลงได้ถึง 40% และเพิ่มค่า recall เป็น 95% และนี่คือสิ่งที่เปลี่ยนไปอย่างชัดเจน
การแบ่งข้อมูลอย่างชาญฉลาด (Smart Chunking)
เลิกคิดว่าขนาดของ chunk คือตัวเลขวิเศษ หน้าต่างขนาด 512 โทเคนไม่มีความหมายเลยสำหรับเอกสารทางกฎหมายที่ข้อกำหนดเดียวอาจครอบคลุมหลายพารากราฟ และมันก็ไร้ประโยชน์พอๆ กันสำหรับเอกสาร API ที่ function signature และคำอธิบายสองบรรทัดควรจะอยู่ด้วยกัน เราจึงเปลี่ยนมาใช้การแบ่งข้อมูลแบบตระหนักถึงโครงสร้าง (structure-aware splitting)
สำหรับข้อความทางกฎหมาย การทำ recursive chunking จะช่วยรักษาลำดับชั้นของเอกสารไว้ ทำให้ข้อกำหนดต่างๆ ยังคงอยู่ครบถ้วน สำหรับเอกสาร API เราใช้การแบ่งแบบ function-aware ที่เก็บ signature, parameters และตัวอย่างไว้ด้วยกันในฐานะหน่วยข้อมูลที่สมบูรณ์ (atomic units) ส่วนตั๋วสนับสนุน (support tickets) และข้อมูลการสนทนาจำเป็นต้องมีขอบเขตเชิงความหมาย (semantic boundaries) โดยการแบ่งตรงจุดที่หัวข้อเปลี่ยนไป แทนที่จะแบ่งตามจำนวนตัวอักษรแบบสุ่ม ผลลัพธ์ที่ได้คือแต่ละ chunk จะมีบริบทที่เพียงพอต่อการใช้งาน แต่ไม่มากเกินไปจนทำให้สัญญาณข้อมูลเจือจาง โมเดล embedding ของคุณมีงบประมาณความสนใจ (attention budget) ที่จำกัด จงใช้มันอย่างชาญฉลาด
การดึงข้อมูลแบบไฮบริด (Hybrid Retrieval)
การค้นหาแบบ vector นั้นยอดเยี่ยมในการหาเนื้อหาที่มีแนวคิดคล้ายกัน หากคุณถามเกี่ยวกับคำสั่งฐานข้อมูลที่ทำงานช้า มันจะดึงคู่มือการปรับแต่งประสิทธิภาพขึ้นมา แต่ถ้าคุณถามหา "Error 0x80070057" การค้นหาเชิงความหมาย (semantic search) จะหลุดออกไปนอกเรื่อง เพราะ dense embeddings ไม่สามารถจัดการกับการจับคู่คำที่ตรงกันเป๊ะๆ (exact matches) ได้ดี ในทางกลับกัน BM25 สามารถจับคำที่ตรงกันและคำที่หายากได้อย่างแม่นยำ แต่ BM25 ก็ไม่รู้ว่า "latency" และ "slow response time" มีความหมายเหมือนกัน
เราใช้วิธีการทั้งสองแบบควบคู่กันไปและรวมผลลัพธ์ด้วย Reciprocal Rank Fusion (RRF) ซึ่ง RRF นั้นเรียบง่ายและมีประสิทธิภาพ โดยจะนำรายการที่จัดลำดับจากแต่ละวิธีมาให้คะแนนเอกสารตามตำแหน่งของมัน ทำให้ตัวเลือกที่แข็งแกร่งจากทั้งสองระบบมีโอกาสถูกเลือกอย่างยุติธรรม หลังจากรวมผลแล้ว เราจะใช้ cross-encoder reranker กับผลลัพธ์ที่รวมกันแล้วและส่งคืนเฉพาะ 5 อันดับแรกเท่านั้น การใช้ reranker เพิ่มความหน่วงประมาณ 50 มิลลิวินาที แต่ช่วยเพิ่มค่า recall ของเราได้ถึง 15% ซึ่งความคุ้มค่านี้ส่งผลดีต่อคุณภาพของการสร้างคำตอบ (generation quality) อย่างมหาศาล
การขยายคำค้นหา (Query Expansion)
ผู้ใช้มักจะตั้งคำถามได้ไม่ดีนัก พวกเขามักจะใช้คำย่อ สะกดผิด หรือรวมคำถามสามข้อไว้ในประโยคยาวๆ ประโยคเดียว หากคุณค้นหาตามสิ่งที่พวกเขาพิมพ์เป๊ะๆ คุณจะพลาดเอกสารที่พวกเขาต้องการจริงๆ
ตอนนี้เราทำการแปลงทุกคำค้นหาก่อนที่จะส่งไปยัง index อย่างแรก เราสร้างคำถามต้นฉบับในรูปแบบใหม่หลายๆ เวอร์ชั่นเพื่อครอบคลุมคำพ้องความหมาย (synonyms) และการใช้สำนวนที่แตกต่างกัน อย่างที่สอง เราแยกคำถามที่ซับซ้อนออกเป็นคำถามย่อยๆ ที่เล็กลง คำถามอย่าง "ทำไมการคืนเงินสำหรับลูกค้าต่างชาติถึงล้มเหลว และฉันจะแก้ไขได้อย่างไร?" จะถูกเปลี่ยนเป็นการค้นหาที่แยกจากกันสองอย่าง: อย่างแรกคือเรื่องความล้มเหลวในการคืนเงินระหว่างประเทศ และอย่างที่สองคือขั้นตอนการแก้ไข การขยายคำค้นหา (Query expansion) เพียงอย่างเดียวช่วยเพิ่มค่า recall ของเราจาก 78% เป็น 94% บทเรียนนี้ตรงไปตรงมา: อย่าเชื่อร่างแรกของผู้ใช้ จงช่วยพวกเขา
เลิกเดา แล้วเริ่มค้นหาอย่างจริงจัง
ขนาดของ chunk, เปอร์เซ็นต์การซ้อนทับ (overlap), ค่า top-k cutoff และความลึกของ reranker มีปฏิสัมพันธ์กันในรูปแบบที่ไม่สามารถปรับจูนด้วยมือได้ เราเสียเวลาไปมากกับการถกเถียงว่า 256 โทเคนดีกว่า 512 โทเคนหรือไม่ ในขณะที่ละเลยการตั้งค่า overlap ซึ่งเป็นตัวการที่ทำลายความต่อเนื่อง (coherence) ของข้อมูลจริงๆ
เราแทนที่สัญชาตญาณด้วย Bayesian optimization แทนที่จะใช้การค้นหาแบบตาราง (grid search) ซึ่งสิ้นเปลืองทรัพยากรคำนวณไปกับพื้นที่ที่แย่อยู่แล้ว วิธีการแบบ Bayesian จะสร้างโมเดลความน่าจะเป็นของสิ่งที่ใช้งานได้จริง และค้นหา Pareto frontier ที่ช่วยเพิ่มค่า recall ให้สูงสุดในขณะที่ลดความหน่วงให้ต่ำสุด สำหรับ stack ของเรา นั่นหมายถึงการหาการผสมผสานที่เฉพาะเจาะจงระหว่างขนาด chunk, การซ้อนทับ และ top-k ที่ให้ค่า recall 95% โดยไม่ทำให้ความหน่วงเกินงบประมาณที่ตั้งไว้ กรณีการใช้งานที่ต่างกันจะไปตกอยู่ที่จุดต่างๆ บน frontier นั้น แชทบอทที่เน้นบริการลูกค้าจะให้ความสำคัญกับความเร็ว ส่วนการวิจัยทางกฎหมายภายในจะให้ความสำคัญกับค่า recall การหาค่าที่เหมาะสมที่สุดแบบอัตโนมัติช่วยให้เราตอบโจทย์ทั้งสองอย่างได้โดยไม่ต้องคัดลอกไฟล์คอนฟิกไปวางด้วยตัวเอง
ผลลัพธ์
ตัวเลขเหล่านี้บ่งบอกความจริงอย่างชัดเจน ค่า Recall@10 ของเราเพิ่มขึ้นจาก 78% เป็น 95% ค่า p95 latency ลดลงจาก 850 มิลลิวินาที เหลือ 320 มิลลิวินาที และเนื่องจากในที่สุดโมเดลก็ได้รับบริบทที่เกี่ยวข้องแทนที่จะเป็น noise อัตราการเกิด hallucination จึงลดลงจาก 12% เหลือเพียง 3% การดึงข้อมูล (retrieval) ที่ดีขึ้นไม่ได้เพียงแค่ทำให้คำตอบเร็วขึ้นเท่านั้น แต่ยังทำให้คำตอบนั้นถูกต้องแม่นยำด้วย
สิ่งที่ควรทำต่อไป
หากคุณกำลังสร้าง retrieval layer ใหม่ ให้เริ่มจากตรงนี้:
- แบ่ง Chunk ตามโครงสร้างเอกสาร ไม่ใช่ตามจำนวน token ปรับกลยุทธ์การแบ่งข้อมูลให้สอดคล้องกับรูปแบบของข้อมูลคุณ
- ใช้ hybrid retrieval ผสมผสานระหว่าง vector search และ BM25 ใช้ Reciprocal Rank Fusion ในการรวมผลลัพธ์ และทำ rerank ก่อนที่จะทำการ generate
- ขยาย query เพื่อให้ครอบคลุมมากขึ้น ทำการ rephrase และ decompose ก่อนที่จะเริ่มการค้นหา
- สร้าง golden dataset สำหรับการทดสอบ คุณไม่สามารถปรับปรุงสิ่งที่คุณไม่ได้วัดผลได้
- ปรับแต่ง parameter ด้วยเครื่องมืออัตโนมัติ Bayesian search จะช่วยค้นหาการตั้งค่าที่ดียิ่งกว่าการใช้สัญชาตญาณของคุณ
Retrieval ไม่ใช่ไฟล์ตั้งค่าที่คุณตั้งค่าเพียงครั้งเดียวแล้วลืมไปได้เลย มันคือโครงสร้างพื้นฐาน (infrastructure) และโครงสร้างพื้นฐานควรได้รับการดูแลอย่างเข้มงวดเช่นเดียวกับ production code นั่นคือต้องมีการทดสอบ การวัดผล และการปรับปรุงอย่างต่อเนื่อง หากคุณปฏิบัติกับมันเช่นนั้น ระบบ RAG ของคุณจะหยุดเป็นเพียงแค่ตัวสาธิต (demo) และเริ่มกลายเป็นผลิตภัณฑ์ (product) ที่ใช้งานได้จริง
ชุมชนแห่งการเรียนรู้ (ทางเลือก): GyaanSetu AI
