บทที่ 7: RAG — Retrieval Augmented Generation
RAG คืออะไร?
Section titled “RAG คืออะไร?”Retrieval Augmented Generation (RAG) คือเทคนิคที่ช่วยให้ LLM ตอบคำถามจาก ข้อมูลของคุณเอง โดยไม่ต้อง fine-tune model
graph LR
Q["คำถามผู้ใช้"] --> R["ค้นหาข้อมูลที่เกี่ยวข้อง"]
R --> A["รวมกับ prompt"]
A --> LLM["LLM"]
LLM --> Ans["คำตอบที่แม่นยำ"]
RNote["Retrieval"] -.-> R
ANote["Augmentation"] -.-> A
ทำไมต้อง RAG?
Section titled “ทำไมต้อง RAG?”| ปัญหาของ LLM | RAG แก้ได้ |
|---|---|
| ข้อมูลไม่ทันสมัย | ✅ ดึงข้อมูลล่าสุดจากฐานข้อมูล |
| ข้อมูลส่วนตัว/องค์กร | ✅ ใช้เอกสารภายในตอบได้ |
| Hallucination | ✅ ตอบจากข้อมูลจริง มี source |
| ค่า Fine-tuning แพง | ✅ ไม่ต้อง fine-tune |
RAG Pipeline Overview
Section titled “RAG Pipeline Overview”กระบวนการ RAG ประกอบด้วย 2 ส่วนหลักคือ Indexing (เตรียมข้อมูล) และ Retrieval & Generation (ค้นหาและตอบ)
graph TD
subgraph Indexing [Phase 1: Indexing]
direction TB
Docs[Documents] --> Split[Text Splitter]
Split --> Chunks[Small Chunks]
Chunks --> Embed[Embedding Model]
Embed --> VectorDB[(Vector Store)]
end
subgraph Retrieval [Phase 2: Retrieval & Generation]
direction TB
Query([User Query]) --> EmbedQ[Embedding Model]
EmbedQ --> VectorQ[Query Vector]
VectorQ --> VectorDB
VectorDB --> Context[Relevant Context]
Context --> LLM
Query --> LLM
LLM --> Answer([Final Answer])
end
RAG Pipeline
Section titled “RAG Pipeline”- Load — โหลดเอกสาร
- Split — แบ่งเอกสารเป็นชิ้นเล็กๆ
- Embed — แปลงเป็น vectors
- Store — เก็บลง Vector Store
- Retrieve — ค้นหาข้อมูลที่เกี่ยวข้อง
- Generate — ส่งให้ LLM สร้างคำตอบ
Step 1: Document Loaders
Section titled “Step 1: Document Loaders”โหลดจากแหล่งต่างๆ
Section titled “โหลดจากแหล่งต่างๆ”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
# PDFfrom langchain_community.document_loaders import PyPDFLoaderdocs = PyPDFLoader("report.pdf").load()
# Text filefrom langchain_community.document_loaders import TextLoaderdocs = TextLoader("notes.txt", encoding="utf-8").load()
# CSVfrom langchain_community.document_loaders import CSVLoaderdocs = CSVLoader("data.csv").load()
# Web pagefrom langchain_community.document_loaders import WebBaseLoaderdocs = WebBaseLoader("https://example.com/article").load()
# Directory (โหลดทุกไฟล์ในโฟลเดอร์)from langchain_community.document_loaders import DirectoryLoaderdocs = DirectoryLoader("./documents/", glob="**/*.pdf").load()Document Object
Section titled “Document Object”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
# แต่ละ document มีโครงสร้าง:doc = docs[0]print(doc.page_content) # เนื้อหาprint(doc.metadata) # {'source': 'report.pdf', 'page': 0}Step 2: Text Splitters
Section titled “Step 2: Text Splitters”เอกสารยาวๆ ต้องแบ่งเป็นชิ้นเล็กๆ (chunks) เพื่อให้ค้นหาได้แม่นยำ:
from langchain_text_splitters import RecursiveCharacterTextSplitter
# สร้าง text splittersplitter = RecursiveCharacterTextSplitter( chunk_size=1000, # ขนาดแต่ละ chunk (ตัวอักษร) chunk_overlap=200, # ส่วนที่ซ้อนทับกัน separators=["\n\n", "\n", ".", " "], # ลำดับการแบ่ง)
# แบ่งเอกสารchunks = splitter.split_documents(docs)print(f"แบ่งได้ {len(chunks)} chunks")เลือก Splitter ที่เหมาะสม
Section titled “เลือก Splitter ที่เหมาะสม”| Splitter | เหมาะกับ |
|---|---|
RecursiveCharacterTextSplitter | เอกสารทั่วไป (แนะนำ) |
MarkdownHeaderTextSplitter | Markdown files |
PythonCodeTextSplitter | Python source code |
HTMLSectionSplitter | HTML pages |
TokenTextSplitter | ควบคุมจำนวน tokens |
Step 3: Embeddings
Section titled “Step 3: Embeddings”Embeddings แปลงข้อความให้เป็น vector (ตัวเลข) เพื่อเปรียบเทียบความหมาย:
from langchain_openai import OpenAIEmbeddings
# สร้าง embeddings modelembeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# แปลงข้อความเป็น vectorvector = embeddings.embed_query("LangChain คืออะไร?")print(f"มิติ: {len(vector)}") # 1536 มิติprint(f"ตัวอย่าง: {vector[:5]}") # [0.012, -0.034, ...]ทางเลือก Embeddings
Section titled “ทางเลือก Embeddings”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
# Googlefrom langchain_google_genai import GoogleGenerativeAIEmbeddingsembeddings = GoogleGenerativeAIEmbeddings(model="models/text-embedding-004")
# Hugging Face (ฟรี, รันในเครื่อง)from langchain_huggingface import HuggingFaceEmbeddingsembeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")Step 4: Vector Stores
Section titled “Step 4: Vector Stores”เก็บ vectors ลงฐานข้อมูลเพื่อค้นหาแบบ semantic:
ChromaDB (แนะนำสำหรับเริ่มต้น)
Section titled “ChromaDB (แนะนำสำหรับเริ่มต้น)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_community.vectorstores import Chroma
# สร้าง vector store จาก documentsvectorstore = Chroma.from_documents( documents=chunks, embedding=embeddings, collection_name="my_docs", persist_directory="./chroma_db", # บันทึกลง disk)
# ค้นหาresults = vectorstore.similarity_search("LangChain คืออะไร?", k=3)for doc in results: print(doc.page_content[:100]) print(f"Source: {doc.metadata['source']}") print("---")FAISS (เร็วมาก, ใช้ memory)
Section titled “FAISS (เร็วมาก, ใช้ memory)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_community.vectorstores import FAISS
# สร้างvectorstore = FAISS.from_documents(chunks, embeddings)
# บันทึกและโหลดvectorstore.save_local("faiss_index")vectorstore = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)Step 5-6: สร้าง RAG Chain
Section titled “Step 5-6: สร้าง RAG Chain”RAG Chain แบบสมบูรณ์
Section titled “RAG Chain แบบสมบูรณ์”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAI, OpenAIEmbeddingsfrom langchain_community.vectorstores import Chromafrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthrough
# 1. Setupllm = ChatOpenAI(model="gpt-4o-mini")embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# 2. โหลด vector storevectorstore = Chroma( persist_directory="./chroma_db", embedding_function=embeddings,)
# 3. สร้าง retrieverretriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 4}, # ดึง 4 documents)
# 4. RAG Promptprompt = ChatPromptTemplate.from_template("""ตอบคำถามจากข้อมูลที่ให้มา ถ้าไม่มีข้อมูลให้ตอบว่า "ไม่พบข้อมูล"อ้างอิงแหล่งที่มาเสมอ
ข้อมูลอ้างอิง:{context}
คำถาม: {question}
คำตอบ:""")
# 5. Helper functiondef format_docs(docs): return "\n\n---\n\n".join( f"[Source: {doc.metadata.get('source', 'unknown')}]\n{doc.page_content}" for doc in docs )
# 6. สร้าง RAG Chainrag_chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | StrOutputParser())
# 7. ใช้งาน!answer = rag_chain.invoke("นโยบายการลาหยุดของบริษัทเป็นอย่างไร?")print(answer)เทคนิค RAG ขั้นสูง
Section titled “เทคนิค RAG ขั้นสูง”1. Multi-Query Retriever
Section titled “1. Multi-Query Retriever”สร้างหลายคำถามจากคำถามเดียวเพื่อค้นหาได้ครอบคลุมขึ้น:
from langchain.retrievers.multi_query import MultiQueryRetriever
multi_retriever = MultiQueryRetriever.from_llm( retriever=vectorstore.as_retriever(), llm=llm,)
# จะสร้างคำถามหลายรูปแบบอัตโนมัติdocs = multi_retriever.invoke("ผลประกอบการปีนี้เป็นอย่างไร?")2. Contextual Compression
Section titled “2. Contextual Compression”บีบอัดเนื้อหาให้เหลือแต่ส่วนที่เกี่ยวข้อง:
from langchain.retrievers import ContextualCompressionRetrieverfrom langchain.retrievers.document_compressors import LLMChainExtractor
compressor = LLMChainExtractor.from_llm(llm)compression_retriever = ContextualCompressionRetriever( base_compressor=compressor, base_retriever=vectorstore.as_retriever(),)
docs = compression_retriever.invoke("รายได้ Q3 เท่าไหร่?")# ได้เฉพาะประโยคที่เกี่ยวข้องกับรายได้ Q33. Hybrid Search (คำค้น + Semantic)
Section titled “3. Hybrid Search (คำค้น + Semantic)”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain.retrievers import EnsembleRetrieverfrom langchain_community.retrievers import BM25Retriever
# Keyword searchbm25_retriever = BM25Retriever.from_documents(chunks)bm25_retriever.k = 4
# Semantic searchsemantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# รวมทั้ง 2 แบบensemble_retriever = EnsembleRetriever( retrievers=[bm25_retriever, semantic_retriever], weights=[0.3, 0.7], # ให้น้ำหนัก semantic มากกว่า)ตัวอย่าง: สร้าง PDF Q&A Bot
Section titled “ตัวอย่าง: สร้าง PDF Q&A Bot”คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน
from langchain_openai import ChatOpenAI, OpenAIEmbeddingsfrom langchain_community.document_loaders import PyPDFLoaderfrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain_community.vectorstores import Chromafrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthrough
def create_pdf_qa(pdf_path: str): """สร้าง Q&A bot จากไฟล์ PDF"""
# 1. โหลดและแบ่ง PDF docs = PyPDFLoader(pdf_path).load() chunks = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=200 ).split_documents(docs)
print(f"📄 โหลด {len(docs)} หน้า, แบ่งเป็น {len(chunks)} chunks")
# 2. สร้าง Vector Store vectorstore = Chroma.from_documents( chunks, OpenAIEmbeddings(model="text-embedding-3-small") )
# 3. สร้าง RAG Chain prompt = ChatPromptTemplate.from_template(""" ตอบคำถามจากเอกสาร PDF ตอบเป็นภาษาไทย
เอกสาร: {context} คำถาม: {question}
คำตอบ (อ้างอิงหน้าที่พบข้อมูล): """)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
def format_docs(docs): return "\n\n---\n\n".join( f"[Source: {doc.metadata.get('source', 'unknown')}]\n{doc.page_content}" for doc in docs )
chain = ( {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | ChatOpenAI(model="gpt-4o-mini") | StrOutputParser() )
return chain
# ใช้งานqa_bot = create_pdf_qa("company_handbook.pdf")answer = qa_bot.invoke("วันลาพักร้อนมีกี่วัน?")print(answer)