Hybrid Search RAG คืออะไร ใช้ทำอะไร

สวัสดีครับ วันนี้ผมจะมาสอนเทคนิคการค้นหาข้อมูลแบบล้ำลึกที่เรียกว่า "Hybrid Search" ซึ่งเป็นการรวมพลังระหว่างสองวิธีการค้นหา ได้แก่ Vector Search และ Keyword Search วิธีนี้จะช่วยให้ระบบ AI ของเราค้นหาข้อมูลได้แม่นยำและครบถ้วนมากขึ้น

ลองนึกภาพว่าคุณมีห้องสมุดขนาดใหญ่ แล้วต้องการหาหนังสือที่เกี่ยวข้องกับคำถามของคุณ วิธีแรกคือดูจากปกและคำอธิบาย (คล้าย Vector Search) ส่วนวิธีที่สองคือค้นหาจากชื่อเรื่องหรือคำสำคัญ (คล้าย Keyword Search) Hybrid Search ก็คือการใช้ทั้งสองวิธีพร้อมกันแล้วเลือกผลลัพธ์ที่ดีที่สุดจากทั้งคู่

สำหรับผู้เริ่มต้นที่ยังไม่เคยใช้ API เลย ไม่ต้องกังวลครับ บทความนี้จะพาคุณทำทีละขั้นตอนตั้งแต่ต้นจนจบ โดยเราจะใช้ บริการของ HolyShehe AI ซึ่งมีความเร็วต่ำกว่า 50ms และราคาประหยัดมาก (อัตรา ¥1=$1 ประหยัดสูงสุด 85%+ เมื่อเทียบกับบริการอื่น) เริ่มกันเลยครับ

ทำความเข้าใจ Vector Search และ Keyword Search (BM25) ก่อน

Vector Search คืออะไร

Vector Search เป็นวิธีการค้นหาที่เปลี่ยนข้อความให้เป็นตัวเลขหลายตัว (เรียกว่า Vector หรือ Embedding) แล้วหาข้อความที่มีตัวเลขคล้ายคลึงกันมากที่สุด วิธีนี้เหมาะกับการค้นหาความหมาย เช่น คุณพิมพ์ว่า "สุนัขเลี้ยง" ระบบก็จะหาข้อความที่เกี่ยวกับ "สัตว์ฟันแหลมคม" หรือ "ปลาแมว" ได้ด้วยแม้ไม่มีคำว่า "สุนัข" ตรงๆ

Keyword Search (BM25) คืออะไร

BM25 เป็นวิธีการค้นหาแบบดั้งเดิมที่ใช้คำค้นหาตรงๆ เหมาะกับการหาชื่อเฉพาะ คำศัพท์ทางเทคนิค หรือหมายเลขโทรศัพท์ เช่น ถ้าคุณค้นหา "API key" ระบบ BM25 จะหาข้อความที่มีคำว่า "API" และ "key" ตรงๆ ได้ดีกว่า

ทำไมต้องผสมทั้งสองวิธี

เพราะแต่ละวิธีมีจุดแข็งต่างกัน ถ้าใช้อย่างเดียวจะมีข้อจำกัด ลองดูตัวอย่าง:

เตรียมพร้อมก่อนเริ่มต้น

สิ่งที่ต้องมี

  1. บัญชี HolyShehe AI - สมัครได้ที่ ลิงก์นี้ จะได้รับเครดิตฟรีเมื่อลงทะเบียน
  2. Python 3.8 ขึ้นไป - ดาวน์โหลดได้จาก python.org
  3. ความรู้พื้นฐานการเขียนโค้ด - แค่รู้ว่า copy-paste คืออะไรก็เพียงพอ

ติดตั้งโปรแกรมที่จำเป็น

เปิด Terminal (หรือ Command Prompt) แล้วพิมพ์คำสั่งด้านล่าง:

pip install requests numpy scikit-learn

หลังจากติดตั้งเสร็จ คุณจะมีทุกอย่างที่ต้องใช้สำหรับบทความนี้แล้วครับ

ขั้นตอนที่ 1: สร้าง Vector Embedding ด้วย HolyShehe AI

ก่อนอื่นเราต้องเปลี่ยนข้อความให้เป็นตัวเลข Vector ก่อน นี่คือโค้ดสำหรับเรียก API ของ HolyShehe AI:

import requests
import json

ตั้งค่า API Key ของคุณ

API_KEY = "YOUR_HOLYSHEEP_API_KEY" BASE_URL = "https://api.holysheep.ai/v1" def create_embedding(text): """ ส่งข้อความไปสร้าง Vector Embedding ผ่าน HolyShehe AI ข้อความจะถูกแปลงเป็นตัวเลข 1536 ตัว (สำหรับ model text-embedding-ada-002) """ url = f"{BASE_URL}/embeddings" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } data = { "input": text, "model": "text-embedding-ada-002" } response = requests.post(url, headers=headers, json=data) if response.status_code == 200: result = response.json() # ค่า embedding จะอยู่ใน result['data'][0]['embedding'] return result['data'][0]['embedding'] else: print(f"เกิดข้อผิดพลาด: {response.status_code}") print(response.text) return None

ทดสอบการสร้าง Embedding

test_text = "วิธีสร้าง API Key สำหรับ chatbot" embedding = create_embedding(test_text) if embedding: print(f"สร้าง Embedding สำเร็จ!") print(f"มีค่าทั้งหมด {len(embedding)} ตัว") print(f"5 ค่าแรก: {embedding[:5]}")

เมื่อรันโค้ดนี้ คุณจะเห็นว่าข้อความ "วิธีสร้าง API Key สำหรับ chatbot" ถูกแปลงเป็นตัวเลข 1536 ตัว นี่คือ Vector ที่เราจะนำไปใช้เปรียบเทียบความคล้ายคลึงกัน

ขั้นตอนที่ 2: คำนวณความคล้ายคลึงด้วย Cosine Similarity

หลังจากมี Vector แล้ว เราต้องมีวิธีวัดว่า Vector สองตัว "คล้ายกันแค่ไหน" วิธีที่นิยมใช้คือ Cosine Similarity ครับ:

import numpy as np

def cosine_similarity(vec1, vec2):
    """
    คำนวณความคล้ายคลึงระหว่าง Vector สองตัว
    ค่าที่ได้จะอยู่ระหว่าง -1 ถึง 1
    ยิ่งใกล้ 1 ยิ่งคล้ายกันมาก
    """
    vec1 = np.array(vec1)
    vec2 = np.array(vec2)
    
    # คำนวณ dot product
    dot_product = np.dot(vec1, vec2)
    
    # คำนวณ magnitude (ความยาวของ vector)
    magnitude1 = np.linalg.norm(vec1)
    magnitude2 = np.linalg.norm(vec2)
    
    # คำนวณ cosine similarity
    if magnitude1 == 0 or magnitude2 == 0:
        return 0
    
    similarity = dot_product / (magnitude1 * magnitude2)
    return similarity

ทดสอบ

vector_a = [0.1, 0.3, 0.5, 0.2, 0.9] vector_b = [0.15, 0.28, 0.48, 0.22, 0.85] sim = cosine_similarity(vector_a, vector_b) print(f"ความคล้ายคลึง: {sim:.4f}") print(f"แปลงเป็นเปอร์เซ็นต์: {sim * 100:.2f}%")

สำหรับค่า Vector ที่ได้จาก HolyShehe AI จะมีค่าอยู่ในช่วง -1 ถึง 1 เสมอ ดังนั้น Cosine Similarity จึงเหมาะมากสำหรับเปรียบเทียบ

ขั้นตอนที่ 3: สร้างฟังก์ชัน BM25 Keyword Search

ต่อไปเราจะสร้างระบบ Keyword Search แบบ BM25 ซึ่งเป็นอัลกอริทึมที่ใช้กันมากในโปรแกรมค้นหาข้อมูล:

import math
from collections import Counter

class SimpleBM25:
    """
    ระบบค้นหาคำสำคัญแบบ BM25 (Best Matching 25)
    เป็นอัลกอริทึมที่ใช้ใน search engine ยอดนิยม
    """
    
    def __init__(self, k1=1.5, b=0.75):
        self.k1 = k1
        self.b = b
        self.documents = []
        self.avgdl = 0
        self.doc_freqs = {}
        self.idf = {}
        
    def fit(self, documents):
        """
        เรียนรู้เอกสารทั้งหมด
        documents: รายการข้อความ
        """
        self.documents = documents
        self._calculate_idf()
        
    def _calculate_idf(self):
        """คำนวณค่า IDF (Inverse Document Frequency)"""
        df = {}
        num_docs = len(self.documents)
        
        for doc in self.documents:
            words = set(doc.lower().split())
            for word in words:
                df[word] = df.get(word, 0) + 1
        
        # คำนวณ IDF สำหรับแต่ละคำ
        for word, freq in df.items():
            self.idf[word] = math.log((num_docs - freq + 0.5) / (freq + 0.5) + 1)
            
    def search(self, query, top_k=5):
        """
        ค้นหาคำที่ตรงกับ query
        query: ข้อความที่ต้องการค้นหา
        top_k: จำนวนผลลัพธ์ที่ต้องการ
        """
        scores = []
        query_words = query.lower().split()
        
        for idx, doc in enumerate(self.documents):
            score = self._compute_score(query_words, doc)
            scores.append((idx, score, doc))
            
        # เรียงตามคะแนนจากมากไปน้อย
        scores.sort(key=lambda x: x[1], reverse=True)
        return scores[:top_k]
        
    def _compute_score(self, query_words, document):
        """คำนวณคะแนน BM25 สำหรับเอกสาร一篇"""
        doc_len = len(document.split())
        words = document.lower().split()
        word_freq = Counter(words)
        
        score = 0.0
        for word in query_words:
            if word not in self.idf:
                continue
                
            freq = word_freq.get(word, 0)
            if freq == 0:
                continue
                
            idf = self.idf[word]
            numerator = freq * (self.k1 + 1)
            denominator = freq + self.k1 * (1 - self.b + self.b * doc_len / self.avgdl)
            score += idf * (numerator / denominator)
            
        return score

ทดสอบ BM25

bm25 = SimpleBM25()

ฐานข้อมูลเอกสารตัวอย่าง

docs = [ "วิธีสร้าง API Key สำหรับ chatbot ฉบับมือใหม่", "การใช้งาน Python สำหรับ Machine Learning เบื้องต้น", "สอนติดตั้ง Docker บน Ubuntu Server", "คู่มือการใช้งาน React Framework", "วิธีแก้ปัญหา Error 404 ในเว็บไซต์" ] bm25.fit(docs)

ค้นหาคำว่า "API"

results = bm25.search("API chatbot") print("ผลลัพธ์การค้นหา 'API chatbot':") for idx, score, doc in results: print(f" [{score:.2f}] {doc}")

ผลลัพธ์จะแสดงเอกสารที่มีคำว่า "API" และ "chatbot" ตรงๆ มากที่สุด พร้อมคะแนนความเกี่ยวข้อง

ขั้นตอนที่ 4: รวม Vector Search และ BM25 เป็น Hybrid Search

นี่คือหัวใจของบทความนี้ครับ เราจะรวมทั้งสองวิธีเข้าด้วยกันโดยใช้เทคนิค Reciprocal Rank Fusion:

import requests
import numpy as np
from collections import Counter
import math

class HybridSearchRAG:
    """
    ระบบ Hybrid Search ที่รวม Vector Search และ Keyword Search (BM25)
    ใช้เทคนิค Reciprocal Rank Fusion (RRF) ในการรวมผลลัพธ์
    """
    
    def __init__(self, api_key, base_url="https://api.holysheep.ai/v1"):
        self.api_key = api_key
        self.base_url = base_url
        self.documents = []
        self.embeddings = []
        self.bm25 = SimpleBM25()
        self.embedding_model = "text-embedding-ada-002"
        
    def _create_embedding(self, text):
        """สร้าง Vector Embedding ผ่าน HolyShehe AI API"""
        url = f"{self.base_url}/embeddings"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        data = {
            "input": text,
            "model": self.embedding_model
        }
        
        response = requests.post(url, headers=headers, json=data)
        if response.status_code == 200:
            return response.json()['data'][0]['embedding']
        else:
            raise Exception(f"Embedding API Error: {response.status_code}")
            
    def add_documents(self, documents):
        """
        เพิ่มเอกสารเข้าสู่ระบบ
        ระบบจะสร้าง Vector สำหรับทุกเอกสารโดยอัตโนมัติ
        """
        self.documents = documents
        print(f"กำลังสร้าง Embedding สำหรับ {len(documents)} เอกสาร...")
        
        # สร้าง Embedding ทีละเอกสาร
        self.embeddings = []
        for i, doc in enumerate(documents):
            embedding = self._create_embedding(doc)
            self.embeddings.append(embedding)
            if (i + 1) % 5 == 0:
                print(f"  สร้างได้ {i + 1}/{len(documents)} เอกสาร")
                
        # เทรน BM25
        self.bm25.fit(documents)
        print("พร้อมใช้งานแล้ว!")
        
    def vector_search(self, query, top_k=5):
        """ค้นหาด้วย Vector"""
        query_embedding = self._create_embedding(query)
        
        scores = []
        for idx, doc_embedding in enumerate(self.embeddings):
            sim = cosine_similarity(query_embedding, doc_embedding)
            scores.append((idx, sim, self.documents[idx]))
            
        scores.sort(key=lambda x: x[1], reverse=True)
        return scores[:top_k]
        
    def keyword_search(self, query, top_k=5):
        """ค้นหาด้วย Keyword (BM25)"""
        return self.bm25.search(query, top_k)
        
    def hybrid_search(self, query, top_k=5, vector_weight=0.5):
        """
        ค้นหาแบบ Hybrid โดยรวมผลลัพธ์จากทั้งสองวิธี
        
        พารามิเตอร์:
        - query: ข้อความที่ต้องการค้นหา
        - top_k: จำนวนผลลัพธ์ที่ต้องการ
        - vector_weight: น้ำหนักของ Vector Search (0.0 - 1.0)
        """
        # ค้นหาทั้งสองวิธี
        vector_results = self.vector_search(query, top_k * 2)
        keyword_results = self.keyword_search(query, top_k * 2)
        
        # ใช้ Reciprocal Rank Fusion (RRF)
        rrf_scores = {}
        k = 60  # ค่าคงที่สำหรับ RRF
        
        for rank, (idx, score, doc) in enumerate(vector_results):
            rrf_score = score * vector_weight * (1 / (k + rank + 1))
            rrf_scores[idx] = rrf_scores.get(idx, 0) + rrf_score
            
        for rank, (idx, score, doc) in enumerate(keyword_results):
            rrf_score = score * (1 - vector_weight) * (1 / (k + rank + 1))
            rrf_scores[idx] = rrf_scores.get(idx, 0) + rrf_score
            
        # เรียงลำดับตามคะแนนรวม
        ranked = sorted(rrf_scores.items(), key=lambda x: x[1], reverse=True)
        
        results = []
        for idx, score in ranked[:top_k]:
            results.append({
                'document': self.documents[idx],
                'score': score,
                'index': idx
            })
            
        return results

ใช้งาน Hybrid Search

print("=" * 60) print("เริ่มต้นใช้งาน Hybrid Search RAG") print("=" * 60)

สร้างระบบค้นหา

searcher = HybridSearchRAG("YOUR_HOLYSHEEP_API_KEY")

เพิ่มเอกสารตัวอย่าง

sample_docs = [ "วิธีสร้าง API Key สำหรับ chatbot ฉบับมือใหม่", "การใช้งาน Python สำหรับ Machine Learning เบื้องต้น", "สอนติดตั้ง Docker บน Ubuntu Server 22.04", "คู่มือการใช้งาน React Framework 2024", "วิธีแก้ปัญหา Error 404 และ Error 500 ในเว็บไซต์", "การเชื่อมต่อ Database MySQL กับ Python", "สอนใช้งาน Git และ GitHub สำหรับมือใหม่", "เขียนโปรแกรม Python รับ Input จากผู้ใช้" ] searcher.add_documents(sample_docs)

ทดสอบการค้นหา

print("\n" + "=" * 60) print("ทดสอบการค้นหา: 'วิธีสร้าง API สำหรับ Python chatbot'") print("=" * 60) results = searcher.hybrid_search( "วิธีสร้าง API สำหรับ Python chatbot", top_k=3, vector_weight=0.6 # ให้น้ำหนัก Vector Search 60% ) print("\nผลลัพธ์จาก Hybrid Search:") for i, result in enumerate(results, 1): print(f"\n{i}. {result['document']}") print(f" คะแนน: {result['score']:.4f}")

จากโค้ดด้านบน ระบบจะค้นหาทั้งจากความหมาย (Vector) และคำตรง (Keyword) แล้วรวมกันด้วยวิธี RRF ซึ่งให้ผลลัพธ์ที่ดีที่สุดจากทั้งสองโลกครับ

ขั้นตอนที่ 5: นำไปใช้กับระบบ RAG จริง

หลังจากได้ผลลัพธ์การค้นหาแล้ว เราจะนำไปสร้างคำตอบด้วย LLM ต่อไปนี้คือโค้ดสำหรับสร้างคำตอบ:

def generate_answer_with_rag(query, context_documents, api_key):
    """
    สร้างคำตอบโดยใช้ RAG (Retrieval-Augmented Generation)
    โดยดึงเอกสารที่เกี่ยวข้องมาประกอบในคำถาม
    """
    # รวมเอกสารที่ดึงมาเป็น context
    context = "\n\n".join([f"- {doc}" for doc in context_documents])
    
    # สร้าง prompt สำหรับ LLM
    prompt = f"""คุณเป็นผู้ช่วย AI ที่ตอบคำถามโดยอ้างอิงจากเอกสารที่ให้มา

เอกสารที่เกี่ยวข้อง:
{context}

คำถาม: {query}

กำลังจะตอบคำถามโดยอ้างอิงจากเอกสารข้างต้น หากไม่มีข้อมูลในเอกสารให้ตอบว่า "ไม่พบข้อมูลที่เกี่ยวข้อง"
"""
    
    # เรียก Chat API จาก HolyShehe AI
    url = "https://api.holysheep.ai/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    data = {
        "model": "gpt-4o-mini",  # ใช้โมเดลที่คุ้มค่าที่สุด