#วัตถุประสงค์

เวิร์กช็อปนี้สอนการจัดเก็บและค้นหาข้อมูลด้วยเวกเตอร์ (embeddings) โดยใช้ Qdrant เป็น Vector Database, Sentence Transformers สร้าง embedding และ Python เรียก API เหมาะสำหรับต่อยอดระบบ Semantic Search และ Retrieval-Augmented Generation (RAG)

#1. Vector Database คืออะไร?

Vector Database จัดเก็บเวกเตอร์ตัวเลขที่แทนความหมายของข้อความ รูปภาพ หรือข้อมูลอื่น ๆ และค้นหารายการที่มีความใกล้เคียงกันทางความหมาย (vector similarity search) ต่างจาก keyword search ที่อาศัยคำตรงกันเป็นหลัก

องค์ประกอบหลัก:

  • Embedding model: แปลงข้อความเป็นเวกเตอร์ที่มีจำนวนมิติคงที่
  • Collection: กลุ่มข้อมูลเวกเตอร์ในฐานข้อมูล
  • Point: ID + vector + payload (metadata)
  • Similarity metric: เช่น Cosine similarity
  • ANN index: ช่วยค้นหาเพื่อนบ้านใกล้เคียงอย่างรวดเร็ว เช่น HNSW

#2. สถาปัตยกรรม

flowchart LR
 A[เอกสารภาษาไทย] --> B[Embedding Model]
 B --> C[(Qdrant Vector DB)]
 U[คำถามผู้ใช้] --> E[Embedding Model]
 E --> C
 C --> R[Top-k Similar Documents]
 R --> O[ผลการค้นหา / RAG Context]

#3. สิ่งที่ต้องเตรียม

  • Docker และ Docker Compose
  • Python 3.10 ขึ้นไป
  • พื้นที่ว่างสำหรับดาวน์โหลด embedding model ครั้งแรก

โครงสร้างโปรเจกต์:

vector-db-workshop/
├── compose.yaml
├── requirements.txt
└── app.py

#4. เปิด Qdrant ด้วย Docker Compose

สร้างไฟล์ compose.yaml:

services:
  qdrant:
    image: qdrant/qdrant:v1.15.5
    ports:
      - "127.0.0.1:6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage
volumes:
  qdrant_data:

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

docker compose up -d
curl http://localhost:6333/healthz

Qdrant Dashboard (เฉพาะเครื่อง): http://localhost:6333/dashboard

#5. ติดตั้ง Python Libraries

requirements.txt:

qdrant-client>=1.15,<2
sentence-transformers>=5,<6
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell: .venv\Scripts\Activate.ps1
pip install -r requirements.txt

#6. เขียนโปรแกรมสร้าง Embedding, จัดเก็บ และค้นหา

สร้างไฟล์ app.py:

from qdrant_client import QdrantClient, models
from sentence_transformers import SentenceTransformer

COLLECTION = "thai_software_docs"
MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"

client = QdrantClient(url="http://localhost:6333")
model = SentenceTransformer(MODEL_NAME)

documents = [
    "Docker ใช้สร้างและรันแอปพลิเคชันภายในคอนเทนเนอร์",
    "Kubernetes ช่วยจัดการการทำงานและการขยายระบบคอนเทนเนอร์",
    "GitHub Actions ใช้สร้าง CI/CD pipeline เพื่อทดสอบและ deploy",
    "Vector Database ใช้จัดเก็บ embeddings และค้นหาข้อมูลที่มีความหมายใกล้เคียง",
    "Retrieval-Augmented Generation ใช้ข้อมูลที่ค้นคืนมาเป็นบริบทให้ LLM",
]

vectors = model.encode(documents, normalize_embeddings=True).tolist()
dim = len(vectors[0])

if not client.collection_exists(COLLECTION):
    client.create_collection(
        collection_name=COLLECTION,
        vectors_config=models.VectorParams(size=dim, distance=models.Distance.COSINE),
    )

client.upsert(
    collection_name=COLLECTION,
    points=[
        models.PointStruct(id=i + 1, vector=vector, payload={"text": text, "topic": "software"})
        for i, (text, vector) in enumerate(zip(documents, vectors))
    ],
    wait=True,
)

question = "ฐานข้อมูลที่ใช้ค้นหาข้อความตามความหมายคืออะไร"
query_vector = model.encode(question, normalize_embeddings=True).tolist()
results = client.query_points(
    collection_name=COLLECTION,
    query=query_vector,
    limit=3,
    with_payload=True,
).points

print(f"คำถาม: {question}")
for rank, point in enumerate(results, start=1):
    print(f"{rank}. score={point.score:.4f} | {point.payload['text']}")

รัน:

python app.py

ผลที่คาดหวัง: โปรแกรมแสดงเอกสารที่เกี่ยวข้อง 3 รายการ พร้อม similarity score โดยข้อความเกี่ยวกับ Vector Database ควรอยู่ในกลุ่มผลลัพธ์อันดับต้น ๆ (อันดับจริงอาจแตกต่างตามโมเดลและข้อมูล)

#7. ทดลองปรับคำถาม

เปลี่ยนตัวแปร question เป็น:

question = "เครื่องมือสำหรับจัดการ container จำนวนมาก"

ทดลองอีกครั้ง:

question = "ทำระบบอัตโนมัติเพื่อทดสอบและส่งขึ้น production"

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

#8. การค้นหาแบบ Metadata Filtering

เพิ่มเงื่อนไข filter ให้คำสั่ง query_points:

results = client.query_points(
    collection_name=COLLECTION,
    query=query_vector,
    query_filter=models.Filter(
        must=[models.FieldCondition(key="topic", match=models.MatchValue(value="software"))]
    ),
    limit=3,
    with_payload=True,
).points

ในระบบจริงสามารถใช้ payload เช่น department, document_type, tenant_id และ updated_at เพื่อกรองข้อมูล โดยต้องใช้ authorization ในระดับแอปพลิเคชันร่วมด้วย ไม่ถือว่า filter เพียงอย่างเดียวเป็นระบบรักษาความปลอดภัย

#9. ต่อยอดเป็น RAG

ลำดับการทำงาน:

  1. แยกเอกสารเป็น chunks พร้อม metadata และ source
  2. แปลงแต่ละ chunk เป็น embedding
  3. บันทึกลง Qdrant
  4. แปลงคำถามเป็น embedding และค้น top-k chunks
  5. ส่ง chunks พร้อมคำถามไปยัง LLM เพื่อสร้างคำตอบอ้างอิงข้อมูล
  6. ตรวจสอบความถูกต้อง การอ้างอิง และการปฏิเสธเมื่อข้อมูลไม่เพียงพอ

Vector Database เป็นส่วน Retrieval ไม่ใช่ตัว LLM และคะแนน similarity ไม่ใช่ความน่าจะเป็นว่าคำตอบถูกต้อง

#10. แบบฝึกหัด

  1. เพิ่มข้อมูลเป็น 20 เอกสารและแบ่งหมวดหมู่ backend, devops, ai
  2. เพิ่ม filter ค้นหาเฉพาะหมวด devops
  3. เปรียบเทียบ top-k เท่ากับ 1, 3 และ 5
  4. สร้างชุดคำถาม 10 ข้อพร้อมเอกสารที่เกี่ยวข้อง (ground truth)
  5. คำนวณ Recall@k และ MRR เพื่อประเมินคุณภาพ retrieval

#11. ข้อควรระวังสำหรับ Production

  • กำหนด API key, TLS, network isolation และ backup
  • เลือก embedding model ให้เหมาะกับภาษาและโดเมน และใช้โมเดลเดียวกันสำหรับเอกสารและคำถาม
  • กำหนดกลยุทธ์ chunking, deduplication, versioning และ re-index เมื่อเปลี่ยนโมเดล
  • ตรวจสอบ latency, throughput, recall, disk/RAM และความสามารถในการขยายระบบ
  • พิจารณา hybrid search (dense + sparse) และ reranking สำหรับข้อมูลจริง
  • ประเมินสิทธิ์เข้าถึงเอกสารก่อนส่งบริบทให้ LLM

#12. สรุป

Workshop นี้แสดงวงจรพื้นฐานของ Vector Database ตั้งแต่สร้าง embedding, บันทึกเวกเตอร์พร้อม metadata, ค้นหาแบบ semantic similarity ไปจนถึงการต่อยอดเป็นระบบ RAG โดย Qdrant ทำหน้าที่จัดเก็บและค้นคืนเวกเตอร์ ส่วน Sentence Transformers ทำหน้าที่แปลงข้อความเป็น embeddings

#เอกสารอ้างอิง