คู่มือ Qdrant ฉบับสมบูรณ์: จากการเริ่มด้วย Docker สู่การค้นเวกเตอร์แบบมีตัวกรอง สร้างชั้นล่างของ RAG ตัวแรกของคุณ
การเลือกฐานข้อมูลเวกเตอร์วนไปวนมา Qdrant เป็นหนึ่งในไม่กี่ตัวที่ติดตั้งเองง่ายและฟังก์ชันตัวกรองก็แข็งแรงพอ บทความนี้พาไปตั้งแต่ docker run จนถึงการกรอง payload และการปรับจูนดัชนี
คู่มือ Qdrant ฉบับสมบูรณ์: จากการเริ่มด้วย Docker สู่การค้นเวกเตอร์แบบมีตัวกรอง สร้างชั้นล่างของ RAG ตัวแรกของคุณ
คนที่ทำ RAG คงเคยผ่านช่วงเดียวกัน เริ่มจากใช้ list ในหน่วยความจำเก็บเวกเตอร์ ใช้ numpy คำนวณ cosine similarity รันได้อย่างสนุก แล้วพอปริมาณข้อมูลถึงแสนรายการ การค้นเริ่มใช้เวลาหลายวินาที คุณถึงเพิ่งรู้ตัวว่าควรหาฐานข้อมูลเวกเตอร์จริง ๆ สักตัว
Qdrant เป็นหนึ่งในตัวเลือกที่ถูกแนะนำบ่อยที่สุดในช่วงนี้ มันเป็นฐานข้อมูลเวกเตอร์โอเพนซอร์สที่เขียนด้วย Rust ติดตั้งเองง่าย ประสิทธิภาพหน่วยความจำดี และฟังก์ชันตัวกรองออกแบบได้ค่อนข้างครบถ้วนในบรรดาผลิตภัณฑ์ประเภทเดียวกัน
คู่มือนี้จะพาตั้งแต่ศูนย์จนถึงใช้งานได้ เริ่มระบบ สร้างตาราง เขียนข้อมูล ค้น กรอง และหลุมพรางไม่กี่อย่างที่ควรรู้ตั้งแต่แรก
นี่คืออะไร
สิ่งที่ Qdrant ทำ พูดสั้น ๆ คือ เก็บเวกเตอร์มิติสูง และหาไม่กี่รายการที่คล้ายกับเวกเตอร์ค้นหาของคุณมากที่สุดภายในไม่กี่มิลลิวินาที
แต่มันไม่ได้ทำแค่การคำนวณความคล้าย เวกเตอร์แต่ละตัวสามารถแนบ payload ได้หนึ่งชุด (คุณเข้าใจได้ว่าเป็น metadata) ข้างในใส่ข้อมูล JSON อะไรก็ได้ ชื่อเอกสาร ผู้เขียน วันที่ ราคา สถานะสต๊อก ตอนค้นสามารถทำทั้ง "ความคล้ายทางความหมาย" และ "การกรองเงื่อนไข payload" พร้อมกัน นี่คือความสามารถที่มีค่าที่สุดในสถานการณ์ RAG
ทำไมการกรองถึงสำคัญขนาดนี้? เพราะข้อมูลหลายอย่างใส่เข้าไปในเวกเตอร์ไม่ได้ เอกสารทางการพูดชัดมาก เมื่อคุณลักษณะบางอย่างของวัตถุเป็นไปไม่ได้ที่จะแสดงด้วยเวกเตอร์ฝัง (เช่นสถานะสต๊อกหรือช่วงราคา) การตั้งเงื่อนไขเพิ่มเติมก็กลายเป็นสิ่งจำเป็น คุณคงไม่อยากให้ RAG แนะนำเอกสารเก่าที่เลิกใช้ไปสามปีแล้ว
ทำอะไรได้บ้าง
ในทางปฏิบัติการใช้งานที่พบบ่อยที่สุดมีไม่กี่แบบ
- ชั้นค้นข้อมูลของ RAG: ตัดเอกสารเป็นชิ้น ฝัง เก็บลง Qdrant ตอนค้นดึงย่อหน้าที่เกี่ยวข้องที่สุดกลับมาป้อนให้ LLM
- การค้นเชิงความหมาย: การค้นในเว็บไม่ต้องอาศัยแค่การเทียบคำสำคัญอีกต่อไป เข้าใจได้ว่า "โน้ตบุ๊กราคาถูก" กับ "คอมพิวเตอร์พกพาราคาประหยัด" เป็นเรื่องเดียวกัน
- ระบบแนะนำ: ใช้เวกเตอร์ฝังของสินค้าหรือเนื้อหาหารายการที่คล้ายกัน
- การกำจัดข้อมูลซ้ำและการตรวจจับความคล้าย: หาข้อมูลที่เนื้อหาซ้ำหรือคล้ายกันสูง
วิธีใช้ (ขั้นตอน)
ขั้นที่หนึ่ง: เริ่มด้วย Docker
วิธีเริ่มต้นที่เร็วที่สุดคือ Docker:
docker pull qdrant/qdrant
docker run -p 6333:6333 -p 6334:6334 \
-v "$(pwd)/qdrant_storage:/qdrant/storage:z" \
qdrant/qdrant
พอรันขึ้นมาแล้วมีสามพอร์ตที่ต้องจำ:
localhost:6333— REST APIlocalhost:6333/dashboard— อินเทอร์เฟซจัดการแบบเว็บ (แนะนำอย่างยิ่งให้เปิดดู ช่วยดีบักได้มาก)localhost:6334— gRPC
การ mount -v นั้นสำคัญมาก ถ้าไม่ mount พอคอนเทนเนอร์หยุดข้อมูลก็หาย นี่เป็นหลุมพรางแรกที่มือใหม่พบบ่อยที่สุด
ขั้นที่สอง: ติดตั้งและเชื่อมต่อ client
ยกตัวอย่างด้วย Python:
pip install qdrant-client
from qdrant_client import QdrantClient
client = QdrantClient(url="http://localhost:6333")
ทางการยังมี client สำหรับ JavaScript, Rust, Java, C# และ Go ด้วย แนวคิดของไวยากรณ์เหมือนกัน
ขั้นที่สาม: สร้าง collection
Collection เทียบได้กับ table ในฐานข้อมูลเชิงสัมพันธ์ ตอนสร้างต้องตัดสินใจสองเรื่อง มิติของเวกเตอร์และการวัดระยะทาง
from qdrant_client.models import Distance, VectorParams
client.create_collection(
collection_name="test_collection",
vectors_config=VectorParams(size=4, distance=Distance.DOT),
)
size ต้องตรงกับมิติผลลัพธ์ของโมเดลฝังของคุณเป๊ะ ใส่ผิดก็จะแจ้ง error ตอนเขียนข้อมูล การวัดระยะทางรองรับ inner product (DOT), cosine (COSINE) และ Euclidean distance (EUCLID)
คำแนะนำเชิงปฏิบัติ: หากคุณใช้ OpenAI หรือโมเดลฝังโอเพนซอร์สส่วนใหญ่ เลือก COSINE มักเสถียรที่สุด เว้นแต่คุณแน่ใจว่าผลลัพธ์ของโมเดลถูก normalize แล้ว ไม่เช่นนั้นอย่าตั้งค่าเริ่มต้นใช้ DOT
ขั้นที่สี่: เขียนเวกเตอร์ (upsert)
from qdrant_client.models import PointStruct
client.upsert(
collection_name="test_collection",
wait=True,
points=[
PointStruct(
id=1,
vector=[0.05, 0.61, 0.76, 0.74],
payload={"city": "Taipei", "category": "tech", "price": 1200},
),
PointStruct(
id=2,
vector=[0.19, 0.81, 0.75, 0.11],
payload={"city": "Taichung", "category": "food", "price": 300},
),
],
)
สังเกต wait=True มันจะรอจนเขียนเสร็จค่อยคืนค่า ช่วงทดสอบต้องเปิดไว้เสมอ ไม่งั้นคุณจะค้นข้อมูลไม่เจอในบรรทัดถัดไปแล้วสงสัยในชีวิต ในสภาพแวดล้อมจริงตอนเขียนเป็นชุดสามารถปิดเพื่อเพิ่ม throughput ได้
ขั้นที่ห้า: ค้นและกรอง
นี่คือส่วนที่ควรเรียนที่สุดของ Qdrant เงื่อนไขการกรองมีสามอนุประโยค สามารถผสมและซ้อนกันได้ตามใจ
must— ทุกเงื่อนไขต้องเป็นจริง เท่ากับ ANDshould— เป็นจริงอย่างน้อยหนึ่งข้อ เท่ากับ ORmust_not— ทุกเงื่อนไขต้องไม่เป็นจริง เท่ากับ NOT A AND NOT B
ตัวอย่างจริงหนึ่งอัน:
{
"filter": {
"must": [
{ "key": "city", "match": { "value": "Taipei" } }
],
"must_not": [
{ "key": "category", "match": { "value": "food" } }
]
}
}
ส่วนนี้หมายความว่า เมืองคือ Taipei และหมวดหมู่ไม่ใช่ food นอกจากการเทียบแบบตรงตัวแล้ว ยังรองรับการค้นแบบช่วง (ช่วงราคา) การค้นเชิงพื้นที่ภูมิศาสตร์ การเทียบแบบ full-text และการกรองวัตถุแบบซ้อน
เทคนิคขั้นสูง
ต้องสร้าง payload index เสมอ ทางการแนะนำชัดเจน: สร้าง payload index ให้ฟิลด์ที่ใช้กรองบ่อย ไม่เช่นนั้นการกรองจะถดถอยกลายเป็นการสแกนทั้งตาราง นี่เป็นหลุมพรางด้านประสิทธิภาพที่เหยียบง่ายที่สุดและแก้ง่ายที่สุด
คิด schema ของ payload ให้ชัดก่อน เวกเตอร์คำนวณใหม่ได้ แต่โครงสร้าง payload แก้ทีเจ็บมาก ก่อนเริ่มคิดให้ดีว่าอนาคตคุณจะใช้เงื่อนไขอะไรกรอง ช่วงเวลา? ระดับสิทธิ์? แหล่งข้อมูล? ในระบบ RAG การกรองสิทธิ์แบบ "เอกสารนี้ผู้ใช้คนนี้ดูได้หรือไม่" แทบจะต้องใช้แน่นอน อย่ารอทำเสร็จแล้วค่อยเพิ่ม
ใช้ dashboard ดีบัก localhost:6333/dashboard ดูสถานะ collection เรียกดู points ทดลองค้นได้โดยตรง เร็วกว่าการ print ไปเรื่อย ๆ มาก
ตอนเขียนเป็นชุดให้ปิด wait ตอนเขียนจำนวนมาก wait=True จะทำให้ช้าลงอย่างรุนแรง เปลี่ยนเป็นส่งเป็นชุดแล้วค่อยยืนยันครั้งเดียวก็พอ
ใช้ค่าที่มีความหมายเป็น id Qdrant รองรับ integer และ UUID เป็น id ใช้ primary key ของข้อมูลต้นฉบับหรือค่าแฮชของมัน ตอนรันดัชนีใหม่จึงจะเขียนทับได้ถูกต้องไม่ใช่สร้างซ้ำ
ข้อควรระวัง
ต้องวางแผนหน่วยความจำก่อน ฐานข้อมูลเวกเตอร์กินหน่วยความจำ วิธีประมาณคร่าว ๆ คือ "จำนวนเวกเตอร์ x มิติ x 4 bytes" บวกกับ overhead ของดัชนีและ payload เวกเตอร์ 1536 มิติหนึ่งล้านรายการ เฉพาะเวกเตอร์ดิบก็ประมาณ 6GB แล้ว เมื่อข้อมูลมากต้องศึกษาตัวเลือก quantization และการเก็บ on-disk ของ Qdrant
ใส่มิติผิดกู้คืนไม่ได้ มิติเวกเตอร์ของ collection พอสร้างแล้วแก้ไม่ได้ ทำได้แค่สร้างใหม่ ตอนเปลี่ยนโมเดลฝังต้องระวังเป็นพิเศษ
อย่าใช้ Qdrant เป็นฐานข้อมูลหลัก มันเป็นชั้นค้นข้อมูล เอกสารต้นฉบับและข้อมูลธุรกิจควรเก็บในฐานข้อมูลเดิมของคุณ payload ของ Qdrant ใส่แค่ฟิลด์ที่การค้นและการกรองต้องใช้ ปนกันแล้วดูแลต่อยาก
อย่าประเมินต้นทุนการดำเนินงานของการติดตั้งเองต่ำไป Docker รันขึ้นมาง่าย แต่การสำรองข้อมูล การเฝ้าติดตาม การขยาย การอัปเกรดเวอร์ชัน ล้วนต้องมีคนดูแล ทีมที่ไม่มีกำลังคนดูแลระบบ Qdrant Cloud หรือโซลูชันแบบโฮสต์อื่นอาจคุ้มกว่า
ความเห็นจาก TheAI Academy
จากประสบการณ์ของผมเอง การเลือกฐานข้อมูลเวกเตอร์จริง ๆ ไม่ได้สำคัญอย่างที่คิด Qdrant, Weaviate, Milvus ไม่กี่เจ้านี้ในการค้นความคล้ายพื้นฐานต่างกันจำกัด สิ่งที่กำหนดคุณภาพของ RAG จริง ๆ คือกลยุทธ์การตัดชิ้น โมเดลฝัง และการจัดอันดับใหม่ (rerank)
แต่หากต้องเลือกเหตุผลเดียวในการเลือก Qdrant ผมจะบอกว่าคือฟังก์ชันตัวกรอง การออกแบบ must/should/must_not ของมันสะอาด ซ้อนกันได้ และประสิทธิภาพผ่านการออกแบบมาอย่างจริงจัง (โดยมีเงื่อนไขว่าคุณสร้างดัชนี) ในสถานการณ์ RAG ระดับองค์กรจริง การกรองสิทธิ์และการกรองตามช่วงเวลาเป็นความต้องการที่หลีกเลี่ยงไม่ได้ ตอนนั้นความสามารถในการกรองก็เปลี่ยนจาก "คะแนนเสริม" เป็น "สิ่งจำเป็น"
ประโยชน์จริงอีกอย่างคืออุปสรรคในการติดตั้งเองต่ำ docker run บรรทัดเดียวก็ขึ้น ไม่ต้อง Kubernetes ไม่ต้องมีบริการพึ่งพาเต็มไปหมด สำหรับทีมที่อยากพิสูจน์ไอเดียในเครื่องก่อน ค่าสัมประสิทธิ์แรงเสียดทานนี้สำคัญมาก
ความเห็น: Qdrant ไม่ใช่ตัวที่เร็วที่สุด ไม่ใช่ตัวที่ฟังก์ชันมากที่สุด แต่มันเป็นหนึ่งในฐานข้อมูลเวกเตอร์ที่เส้นทาง "จากไอเดียถึงรันได้" สั้นที่สุด สำหรับทีมส่วนใหญ่ในไทย คุณสมบัตินี้มีค่ากว่าคะแนนเบนช์มาร์กสวย ๆ
คำแนะนำสำหรับนักพัฒนาชาวไทย: หากทำ RAG ครั้งแรก ให้ใช้ Docker รันทั้งกระบวนการในเครื่องให้ผ่านก่อน คุมปริมาณข้อมูลไว้ที่ไม่กี่พันรายการ ทุ่มความสนใจไปที่การตัดชิ้นและคุณภาพการค้น อย่ารีบปรับประสิทธิภาพ พอคุณภาพมั่นคงแล้วค่อยพูดเรื่องขนาด หากคุณต้องการ GPU มารันโมเดลฝังหรือ LLM ในเครื่อง ดูได้ที่ คู่มือ RunPod ของเรา อยากดูเครื่องมือที่เกี่ยวกับการพัฒนาเพิ่ม หมวด เฟรมเวิร์กและโครงสร้างพื้นฐานการพัฒนา AI บนเว็บไซต์มีรวบรวมไว้
แหล่งข้อมูล
(บทความนี้เรียบเรียงจากเอกสารทางการ ไวยากรณ์ API ยึดตามเอกสารทางการล่าสุด)
คำถามที่พบบ่อย
Qdrant กับ Pinecone, Weaviate ควรเลือกอย่างไร?
ความสามารถการค้นความคล้ายพื้นฐานต่างกันจำกัด จุดเด่นของ Qdrant คืออุปสรรคในการติดตั้งเองต่ำ (docker run บรรทัดเดียว) และฟังก์ชันตัวกรองที่ออกแบบครบถ้วน ส่วน Pinecone เป็นแบบโฮสต์เต็มรูปแบบ ไม่ต้องดูแลแต่ต้นทุนสูงกว่า Weaviate มีฟังก์ชันโมดูลาร์ในตัวมากกว่า หากทีมอยากพิสูจน์อย่างรวดเร็วในเครื่องก่อน แรงเสียดทานของ Qdrant น้อยที่สุด
ทำไมการค้นแบบมีตัวกรองของผมช้า?
สาเหตุที่พบบ่อยที่สุดคือไม่ได้สร้าง payload index ให้กับฟิลด์ที่ใช้กรอง ทางการแนะนำชัดเจนให้สร้างดัชนีสำหรับฟิลด์ที่ใช้กรองบ่อย ไม่เช่นนั้นการกรองจะถดถอยกลายเป็นการสแกนทั้งตาราง นี่เป็นปัญหาด้านประสิทธิภาพที่แก้ง่ายที่สุด
มิติเวกเตอร์ของ collection แก้ได้ไหม?
ไม่ได้ มิติถูกกำหนดตายตัวตอนสร้าง collection จะแก้ต้องสร้าง collection ใหม่และเขียนข้อมูลทั้งหมดใหม่ ดังนั้นก่อนเปลี่ยนโมเดลฝังต้องยืนยันมิติผลลัพธ์ของโมเดลใหม่ นี่เป็นจุดที่พลาดบ่อยที่สุดตอนเปลี่ยนโมเดล
Qdrant ที่รันบน Docker พอรีสตาร์ตแล้วข้อมูลจะหายไหม?
ถ้าไม่ได้ mount volume จะหาย ต้องเพิ่มพารามิเตอร์ -v ตอน docker run เพื่อ mount ไดเรกทอรี qdrant_storage เข้ากับ /qdrant/storage ในคอนเทนเนอร์ นี่เป็นข้อผิดพลาดแรกที่มือใหม่พบบ่อยที่สุด