30/06/2026 04:01น.

Golang The Series EP.156: Semantic Search ระบบค้นหาอัจฉริยะตามความหมาย
#สอนเขียน Go
#Golang
#ระบบค้นหาอัจฉริยะ
#RAG
#Vector Database
ยินดีต้อนรับเข้าสู่ EP.156 ครับ! ในตอนที่แล้วเราได้เรียนรู้วิธีการหั่นข้อความยาวๆ ออกเป็นชิ้นๆ (Chunking) อย่างมีกลยุทธ์เพื่อคงบริบทเอาไว้ และในตอนนี้ก็ถึงเวลาที่จิ๊กซอว์ทุกชิ้นที่เราสะสมมาตั้งแต่ต้นซีซัน จะโคจรมาเจอกันเพื่อสร้างฟีเจอร์ที่เรียกว่า Semantic Search หรือระบบค้นหาข้อมูลอัจฉริยะตามความหมายเชิงลึกของภาษาคนครับ!
ลองจินตนาการว่าหากผู้ใช้พิมพ์ค้นหาคำว่า "วิธีแก้ไขปัญหาต่อเน็ตไม่ได้" ระบบแบบเก่าอย่าง Keyword Search จะไม่มีทางเจอเอกสารคู่มือที่เขียนว่า "คู่มือการเซ็ตอัปเราเตอร์และการแก้ปัญหาสัญญาณ Wi-Fi หลุด" เลย เพราะไม่มีคีย์เวิร์ดคำว่า "เน็ต" หรือ "อินเทอร์เน็ต" อยู่ในนั้นเลยสักคำ แต่สำหรับ Semantic Search มันสามารถรับรู้ได้ทันทีว่าทั้งสองประโยคนี้กำลังพูดถึงเรื่องเดียวกัน... ผ่านพิกัดเวกเตอร์!
วันนี้เราจะมาเขียน Go เพื่อสร้างระบบค้นหานี้กันครับ
ลำดับขั้นตอนการทำงานของ Semantic Search Pipeline
เมื่อมีคำถามยิงเข้ามาในระบบ Backend ของเรา กระบวนการทำงานหลังบ้านจะถูกแบ่งออกเป็น 3 ขั้นตอนหลักๆ ดังนี้ครับ:
Query Embedding: นำคำถามดิบของ User (เช่น "เปลี่ยนรหัสผ่านยังไง") ส่งไปที่ Embedding API เพื่อแปลงให้เป็น Vector (
[]float32) ขนาด 1,536 มิติVector Similarity Search: นำ Vector คำถามนั้น ยิงไปค้นหาใน Qdrant Vector Database โดยระบุให้ค้นหาแบบ Nearest Neighbor เพื่อหาข้อความ (Chunks) ที่อยู่ใกล้กับพิกัดความหมายของคำถามที่สุด
Payload Extraction: แกะข้อความดิบและ Metadata ที่เก็บอยู่ใน Payload ของ Qdrant กลับมาแสดงผลให้ผู้ใช้ หรือเตรียมส่งต่อให้ LLM นำไปสรุปคำตอบในสเต็ปถัดไป
เขียน Go ทำระบบ Semantic Search ร่วมกับ Qdrant
มาดูตัวอย่างโค้ด Go ที่จำลองการรับคำถามจากผู้ใช้ นำไปแปลงเป็น Vector แล้วยิงไปค้นหาใน Qdrant Collection ที่เราสร้างไว้ใน EP.154 กันครับ
Go
package main
import (
"context"
"fmt"
"log"
"github.com/qdrant/go-client/qdrant"
"github.com/sashabaranov/go-openai"
)
func main() {
ctx := context.Background()
// 1. Setup Client ของ OpenAI และ Qdrant (ในโปรดักชันจริงควรดึงผ่าน Env นะครับ)
openaiClient := openai.NewClient("YOUR_OPENAI_API_KEY")
qdrantClient, err := qdrant.NewClient(&qdrant.Config{
Host: "localhost",
Port: 6334, // ใช้พอร์ต gRPC เพื่อความเร็ว
})
if err != nil {
log.Fatalf("เชื่อมต่อ Qdrant ไม่สำเร็จ: %v", err)
}
defer qdrantClient.Close()
// สมมุติตัวอย่างคำถามจาก User
userQuery := "อยากเปลี่ยนพาสเวิร์ดระบบต้องทำตรงไหน"
// 2. แปลงคำถามให้เป็น Vector (Query Embedding)
embReq := openai.EmbeddingRequest{
Input: []string{userQuery},
Model: openai.SmallEmbedding3Small, // โมเดลยอดนิยม 1,536 มิติ
}
embResp, err := openaiClient.CreateEmbeddings(ctx, embReq)
if err != nil {
log.Fatalf("ทำ Embedding คำถามไม่สำเร็จ: %v", err)
}
queryVector := embResp.Data[0].Embedding
// 3. ยิงค้นหาแบบ Semantic Search ใน Qdrant
searchLimit := uint64(3) // ดึงผลลัพธ์ที่ใกล้เคียงที่สุด 3 อันดับแรก
searchResp, err := qdrantClient.Query(ctx, &qdrant.QueryPoints{
CollectionName: "ai_knowledge_base",
Query: qdrant.NewQuery(queryVector...), // กระจายอาร์เรย์ float32
Limit: &searchLimit,
})
if err != nil {
log.Fatalf("ค้นหาข้อมูลใน Qdrant ล้มเหลว: %v", err)
}
// 4. ดึงข้อมูลใน Payload ออกมาโชว์
fmt.Printf("🔍 ผลการค้นหาสำหรับคำถาม: '%s'\n\n", userQuery)
for i, point := range searchResp {
payloadMap := point.Payload
// ดึงข้อความต้นฉบับในคีย์ "content" ออกมาเช็คอย่างปลอดภัย
contentValue, exists := payloadMap["content"]
if !exists {
continue
}
content := contentValue.GetStringValue()
score := point.Score // ค่า Cosine Similarity ยิ่งเข้าใกล้ 1 ยิ่งหมายความตรงกัน
fmt.Printf("[%d] Score ความคล้ายคลึง: %.4f\n", i+1, score)
fmt.Printf(" เนื้อหาเอกสาร: %s\n\n", content)
}
}
ทำไมระบบค้นหาแบบ Semantic Search บน Go ถึงเร็วและทรงพลัง?
Low Latency Serialization: ตัว Go Client ของ Qdrant คุยกันผ่านโปรโตคอล gRPC ซึ่งจะบีบอัดพิกัดเวกเตอร์จำนวนมากให้อยู่ในรูปแบบ Binary ตั้งแต่ต้นทาง ทำให้ไม่มีโหลด (Overhead) เรื่องการแปลงตัวเลขทศนิยมยาว ๆ ให้เป็น JSON Text ระบบเลยดึงคำตอบกลับมาได้ไวระดับมิลลิวินาทีครับ
Ready for RAG: ข้อความดิบที่เราดึงออกมาตามอันดับ Score ในโค้ดด้านบนนี่แหละครับ มันคือ "หน้าคู่มืออ้างอิง" ที่เราจะเอาไปมัดรวมกับคำถามของ User แล้วส่งต่อให้ AI อ่านเพื่อสรุปเป็นคำตอบที่ถูกต้องในสเต็ปการทำ RAG ตอนต่อไปนั่นเอง
🎯 ท้าให้ลอง (Daily Mission)
ลองนำโค้ดโครงสร้าง Semantic Search ชุดนี้ ไปเชื่อมต่อเข้ากับ Gin Web Server ที่เราเคยลุยกันใน Workshop EP.150 ดูครับ โดยเปลี่ยนจาก Endpoint ที่เคยสุ่มพ่นข้อความดิบ ให้กลายเป็นระบบค้นหาเอกสารอัจฉริยะแทน
💡 การบ้านชวนคิด: ลองส่งคำถามแบบ "ใช้คำตรงกับคลังเอกสารเป๊ะ ๆ" เปรียบเทียบกับคำถามที่ "ใช้คำพ้องความหมาย (Synonym)" แล้วสังเกตค่า Score ที่ส่งกลับมาจาก Qdrant ดูครับว่ามีความต่างกันขนาดไหน? และถ้าต้องทำระบบบน Production จริง เราควรตั้งค่าขั้นต่ำของ Score (Threshold) ไว้ที่เท่าไหร่ดี เพื่อตัดข้อมูลขยะที่ไม่เกี่ยวข้องทิ้งไป? ลองไปลองเล่นกันดูนะ!
💬 FAQ (คำถามที่พบบ่อยประจำตอน)
ค่า Score เท่าไหร่ ถึงจะถือว่ามีความหมาย "ใกล้เคียงพอ" ที่จะนำไปใช้งานจริงได้?
สำหรับโมเดล text-embedding-3-small ของ OpenAI ที่วัดระยะห่างแบบ Cosine Similarity ค่า Score ที่ปลอดภัยสำหรับภาษาไทยมักจะอยู่ที่ 0.45 - 0.60 ขึ้นไป ครับ (ขึ้นอยู่กับความยาวของ Chunk ด้วย) แนะนำให้ดัก Threshold เบื้องต้นไว้ที่ 0.50 ก่อน หากต่ำกว่านี้ก็ปัดตกเป็นข้อมูลที่ไม่เกี่ยวข้องได้เลย
ในหน้างานจริง เราค้นหาโดยใช้ทั้ง Keyword แบบเก่า ร่วมกับ Semantic Search พร้อมกันได้ไหม?
ทำได้ครับ และนี่คือท่ามาตรฐานระดับ Enterprise ที่เรียกว่า Hybrid Search โดยระบบจะใช้การจับคำศัพท์แบบคลาสสิก (เช่น BM25) ควบคู่ไปกับ Vector Search จากนั้นค่อยเอาผลลัพธ์ทั้งสองฝั่งมาจัดอันดับใหม่ด้วยอัลกอริทึม RRF (Reciprocal Rank Fusion) ซึ่งข่าวดีคือ Qdrant มีฟีเจอร์นี้เตรียมไว้ให้เราเรียกใช้ได้ทันทีครับ
📝 สรุป
ระบบค้นหาอัจฉริยะในตอนนี้เปิดทางให้เราเข้าใกล้ระบบ AI ที่ตอบคำถามได้ตรงใจผู้ใช้เข้าไปอีกขั้น ผ่านการทำความเข้าใจบริบทด้วยพิกัดเวกเตอร์แทนการดักคีย์เวิร์ดแบบเดิม ๆ
ในตอนต่อไป (EP.157): ตอนนี้ระบบค้นหาก็พร้อม คลังเวกเตอร์ก็ทำงานได้ฉลาดแล้ว แต่ในโลกความเป็นจริง เราคงไม่มานั่งพิมพ์ข้อความใส่ Payload เองทีละประโยค เอกสารส่วนใหญ่ในองค์กรส่วยใหญ่มาเป็นไฟล์ PDF หรือ Word หนาเป็นร้อย ๆ หน้า ตอนหน้าเราจะมาสร้างระบบ "Document Ingestion Pipeline: ดึงข้อมูลจาก PDF/Word เข้าสู่ Database แบบอัตโนมัติ" ห้ามพลาดครับ!
ฝากกดติดตามพวกเราได้ที่ Superdev Academy ในทุกช่องทางนะครับ!
🔵 Facebook: Superdev Academy Thailand (อัปเดตข่าวสารและบทความใหม่)
🎬 YouTube: Superdev Academy Channel (ติวเข้มแบบวิดีโอ)
📸 Instagram: @superdevacademy (เกร็ดความรู้สั้นๆ และเบื้องหลังการทำงาน)
🎬 TikTok: @superdevacademy (Tips & Tricks ฉบับย่อยง่าย)
🌐 Website: superdevacademy.com (คลังบทความและคอร์สเรียนฉบับเต็ม)