RAG pipeline ของคุณผ่านการทดสอบ load test มาตรฐานได้อย่างสวยงาม ค่า p95 latency ดูดี อัตรา error rates เกือบเป็นศูนย์ แต่ผู้ใช้กลับรายงานว่าคำตอบนั้นเลี่ยงคำถาม อ้างอิงเอกสารที่ไม่มีอยู่จริง หรือขุดเอาเนื้อหาที่ไม่เกี่ยวข้องจาก white paper ที่อัปโหลดไว้เมื่อหกเดือนก่อนขึ้นมา หน้าแดชบอร์ดบอกว่าทุกอย่างปกติดี แต่ประสบการณ์การใช้งานจริงกลับบอกว่ามันพัง

ความไม่สอดคล้องกันนี้เกิดขึ้นเพราะการทดสอบประสิทธิภาพแบบดั้งเดิมถูกสร้างขึ้นสำหรับระบบ request-response ไม่ใช่สำหรับระบบที่ "คิด" ได้ เมื่อคุณส่งคำขอขนานกันหนึ่งพันรายการไปยัง REST endpoint คุณจะได้รู้ว่าเซิร์ฟเวอร์ของคุณยังทำงานไหวหรือไม่ แต่คุณจะไม่รู้อะไรเลยว่า retrieval layer ดึง chunk ที่ถูกต้องมาหรือไม่ prompt template ของคุณยังรักษาบริบทไว้ได้ไหม หรือโมเดลจะสร้างแหล่งอ้างอิงปลอมขึ้นมาหรือไม่เมื่อ vector store ไม่มีข้อมูล การทดสอบ load testing แบบมาตรฐานวัดความเร็ว แต่แอปพลิเคชัน RAG ต้องการให้คุณวัดความเข้าใจ

เหนือกว่า Status Code 200

การทดสอบ load test ของ API ทั่วไปจะตรวจสอบสามสิ่ง: availability, latency และ throughput มันถามว่าเซิร์ฟเวอร์ตอบสนองหรือไม่ ใช้เวลานานแค่ไหน และรองรับผู้ใช้พร้อมกันได้เท่าไหร่ สำหรับแอปพลิเคชัน RAG ตัวเลขเหล่านี้เป็นเพียงเงื่อนไขเบื้องต้น ไม่ใช่บทสรุป คำตอบที่ผิดอย่างรวดเร็วก็ยังคงเป็นคำตอบที่ผิด และคำตอบที่ผิดในระดับสเกลใหญ่มีต้นทุนสูงกว่าคำตอบที่ช้า

RAG เพิ่มสองขั้นตอนที่แตกต่างกันในทุกๆ คำขอ ขั้นแรก ระบบจะเปลี่ยนคำถามของผู้ใช้ให้เป็น embedding ค้นหาใน vector store และดึงชุด context chunks กลับมา ขั้นที่สอง มันจะนำ chunks เหล่านั้นใส่ลงใน prompt ส่งทุกอย่างไปยัง language model และสตรีมคำตอบกลับมา การทดสอบแบบดั้งเดิมมักจะรวมขั้นตอนเหล่านี้เข้าด้วยกันเป็นเมทริกซ์ "response time" เพียงตัวเดียว โดยมองว่า retrieval engine และ generator เป็น black box ใบเดียว

คุณต้องเปิดกล่องนั้นออก หาก vector database ของคุณช้าลงภายใต้ load ค่า retrieval latency จะพุ่งสูงขึ้น LLM อาจยังตอบสนองได้อย่างรวดเร็ว แต่มันกำลังตอบโดยอิงจาก context ขยะที่ถูกดึงมาอย่างเร่งรีบ ในทางกลับกัน การค้นหาแบบ vector อาจจะยังรวดเร็วในขณะที่คิวของ LLM ติดขัด ทำให้ time-to-first-token เพิ่มขึ้นจนผู้ใช้ต้องจ้องมองเคอร์เซอร์ที่กะพริบอยู่ ตัวจับเวลาแบบ end-to-end เพียงตัวเดียวสามารถซ่อนความล้มเหลวทั้งสองรูปแบบนี้ไว้ได้

ทดสอบการดึงข้อมูล ไม่ใช่แค่ฐานข้อมูล

ทีมส่วนใหญ่รันการทดสอบ benchmark การค้นหาแบบ vector อย่างรวดเร็วแล้วสรุปว่า retrieval layer ผ่านการทดสอบแล้ว การทดสอบ benchmark นั้นมักจะวัดแค่ว่าฐานข้อมูลคืนค่า nearest neighbors สำหรับคำถามที่เลือกไว้ได้เร็วแค่ไหน แต่มันแทบไม่ได้วัดเลยว่า neighbors เหล่านั้นมีคำตอบอยู่จริงหรือไม่

คุณภาพของการดึงข้อมูล (retrieval quality) เปลี่ยนแปลงไปตาม load ในลักษณะที่ละเอียดอ่อน ภายใต้แรงกดดันจากการใช้งานพร้อมกัน (concurrent pressure) ดัชนี approximate nearest neighbor อาจทำงานแตกต่างจากตอนที่ทำงานแยกเดี่ยวๆ กลยุทธ์การแบ่ง chunk (chunking strategies) ที่ดูสมบูรณ์แบบใน notebook อาจเริ่มทำให้บริบทปนกันข้ามขอบเขตเมื่อมีเอกสารหนึ่งหมื่นฉบับแย่งชิงพื้นที่ embedding space เดียวกัน คำถามที่คืนค่าพารากราฟที่เหมาะสมที่สุดในสภาพแวดล้อมที่เงียบสงบ อาจจะแสดงสไลด์การตลาดที่ทำให้เข้าใจผิดเมื่อดัชนีกำลังถูกสร้างใหม่ หรือเมื่อการกรอง metadata ถูกตัดออกภายใต้แรงกดดันของคำขอ

เพื่อทดสอบเรื่องนี้อย่างเหมาะสม คุณต้องมี ground-truth dataset ให้รวบรวมคำถามที่คุณทราบอยู่แล้วว่าควรใช้เอกสารแหล่งที่มาใด รันคำถามเหล่านั้นด้วยระดับ concurrency ที่แตกต่างกัน และตรวจสอบว่า chunks ที่คาดหวังอยู่ในผลลัพธ์ top-k หรือไม่ ให้ติดตาม hit rate ไม่ใช่แค่ระยะเวลาในการ query หาก top-five chunks ของคุณพลาดแหล่งข้อมูลสำคัญไปเลย แสดงว่า retrieval pipeline ของคุณล้มเหลวตั้งแต่ก่อนที่ LLM จะตื่นขึ้นมาเสียอีก

คุณควรทำ stress-test ที่ขอบเขต (edges) ด้วย ส่งคำถามที่ไม่มีคำตอบใน corpus ส่งคำถามที่กำกวมซึ่งอาจเชื่อมโยงได้กับหลายโดเมน ส่งคำถามที่ยาวเกินขีดจำกัด token ของ embedding model และถูกตัดทิ้งไปอย่างเงียบๆ คอยดูว่า retrieval layer ส่งอะไรกลับมา ในแต่ละกรณี รูปแบบความล้มเหลว (failure mode) มีความสำคัญมากกว่ามิลลิวินาทีที่ผ่านไป

เมื่อโมเดลสำลักอย่างเงียบเชียบ

เมื่อ chunks ไปถึง LLM การทดสอบ load testing แบบมาตรฐานก็ยังคงโกหก