spinny:~/writing $ vim rag-langchain-deep-dive.md
1~2โมเดลภาษาขนาดใหญ่ (LLM) เช่น GPT-4 และ Claude มีความสามารถสูงอย่างยิ่ง แต่มีข้อจำกัดพื้นฐาน: ความรู้ของพวกมันถูกแช่แข็งไว้ ณ เวลาที่ฝึกอบรม พวกมันไม่สามารถเข้าถึงเอกสารภายใน ฐานข้อมูล หรือข้อมูลแบบเรียลไทม์ของคุณได้ **Retrieval-Augmented Generation (RAG)** แก้ปัญหานี้โดยผสมผสานพลังในการสร้างของ LLM เข้ากับความสามารถในการดึงข้อมูลจากแหล่งภายนอก3~4## ปัญหา: ข้อจำกัดของ LLM5~6ก่อนพูดถึง RAG สิ่งสำคัญคือต้องเข้าใจว่าทำไมเราถึงต้องการมัน7~81. **ความรู้แบบสถิต**: LLM รู้เฉพาะสิ่งที่เห็นระหว่างการฝึกอบรม หากคุณถามเกี่ยวกับเหตุการณ์ที่เกิดขึ้นหลังจุดตัด มันไม่สามารถตอบได้92. **ภาพหลอน**: เมื่อ LLM ไม่รู้คำตอบ มันมีแนวโน้มที่จะสร้างขึ้นมา สร้างข้อมูลที่ดูน่าเชื่อถือแต่เป็นเท็จโดยสิ้นเชิง103. **ไม่สามารถเข้าถึงข้อมูลส่วนตัว**: LLM ทั่วไปไม่มีสิทธิ์เข้าถึงเอกสารภายในของบริษัท ตั๋ว หรือ codebase ของคุณ11~12RAG แก้ไขปัญหาทั้งสามนี้โดยให้โมเดลได้รับ **บริบทที่เกี่ยวข้อง** ที่ดึงมาจากแหล่งภายนอก ณ เวลาที่ส่งคำถาม13~14## RAG คืออะไร?15~16Retrieval-Augmented Generation เป็นสถาปัตยกรรมที่เพิ่มความสมบูรณ์ให้กับ prompt ที่ส่งไปยัง LLM ด้วยข้อมูลที่ดึงมาจากฐานความรู้ภายนอก แทนที่จะพึ่งพาเฉพาะความรู้ที่อยู่ในพารามิเตอร์ของโมเดล RAG จะ **ค้นหา** ข้อมูลที่เกี่ยวข้องก่อน จากนั้นจึง **ฉีด** เข้าไปใน prompt ทำให้โมเดลสามารถสร้างคำตอบที่แม่นยำและมีหลักฐานสนับสนุน17~18```mermaid19graph LR20 User["User"] -- "Question" --> Retriever21 Retriever -- "Search relevant\ndocuments" --> VectorStore["Vector Store"]22 VectorStore -- "Relevant\ndocuments" --> Retriever23 Retriever -- "Context + Question" --> LLM24 LLM -- "Grounded\nresponse" --> User25```26~27## RAG ทำงานอย่างไรในรายละเอียด28~29สถาปัตยกรรม RAG ประกอบด้วยสองเฟสหลัก: **การสร้างดัชนี** (ออฟไลน์) และ **การดึงข้อมูล + การสร้าง** (ออนไลน์)30~31### เฟส 1: การสร้างดัชนี (การนำเข้าเอกสาร)32~33เฟสการสร้างดัชนีเตรียมเอกสารของคุณสำหรับการค้นหาเชิงความหมาย ประกอบด้วยสี่ขั้นตอน34~35```mermaid36graph TD37 A["Documents\n(PDF, HTML, MD, DB)"] --> B["Document Loader"]38 B --> C["Text Splitter"]39 C --> D["Text Chunks"]40 D --> E["Embedding Model"]41 E --> F["Numerical Vectors"]42 F --> G["Vector Store\n(ChromaDB, Pinecone, FAISS)"]43```44~45#### 1. การโหลดเอกสาร46~47เอกสารสามารถมาจากแหล่งใดก็ได้: ไฟล์ PDF, หน้าเว็บ, ฐานข้อมูล, ไฟล์ Markdown, API **Document Loader** อ่านเอกสารเหล่านี้และแปลงเป็นข้อความที่มีโครงสร้าง48~49#### 2. การแบ่งข้อความ (Chunking)50~51LLM มี context window ที่จำกัด และเอกสารอาจยาวมาก **Text Splitter** แบ่งเอกสารเป็นชิ้นส่วนเล็กๆ ที่เรียกว่า *chunks* คุณภาพของการ chunking มีความสำคัญมาก: chunks ที่เล็กเกินไปจะสูญเสียบริบท ขณะที่ chunks ที่ใหญ่เกินไปจะลดความเกี่ยวข้อง52~53กลยุทธ์ที่พบบ่อยที่สุดคือ:54- **การแบ่งอักขระแบบเวียนซ้ำ**: แบ่งข้อความแบบเวียนซ้ำโดยใช้ตัวคั่นเช่น `\n\n`, `\n`, `. ` โดยเคารพโครงสร้างเอกสาร55- **การแบ่งเชิงความหมาย**: ใช้ embeddings เพื่อหาจุดแบ่งตามธรรมชาติในข้อความ56- **Chunk ทับซ้อน**: รวมการทับซ้อนระหว่าง chunks ที่ต่อเนื่องกันเพื่อรักษาบริบทที่ขอบเขต57~58#### 3. Embedding59~60แต่ละ chunk จะถูกแปลงเป็น **เวกเตอร์ตัวเลข** (embedding) ผ่านโมเดล embedding (เช่น `text-embedding-3-small` ของ OpenAI) เวกเตอร์เหล่านี้จับความหมายเชิงความหมายของข้อความ: ประโยคที่มีความหมายใกล้เคียงกันจะมีเวกเตอร์ที่อยู่ใกล้กันในปริภูมิหลายมิติ61~62#### 4. Vector Store63~64เวกเตอร์จะถูกบันทึกใน **Vector Store** (หรือฐานข้อมูลเวกเตอร์) เช่น ChromaDB, Pinecone, Weaviate หรือ FAISS ฐานข้อมูลนี้ถูกปรับให้เหมาะสมสำหรับ **การค้นหาความคล้ายคลึง**: เมื่อให้คำถาม มันจะค้นหาเวกเตอร์ที่คล้ายที่สุด (และดังนั้นจึงเป็น chunks ข้อความที่เกี่ยวข้องที่สุด)65~66### เฟส 2: การดึงข้อมูล + การสร้าง67~68เมื่อผู้ใช้ถามคำถาม:69~701. คำถามจะถูกแปลงเป็น embedding โดยใช้โมเดล embedding เดียวกัน712. Vector Store ค้นหา chunks ที่คล้ายที่สุดผ่าน **การค้นหาความคล้ายคลึง** (โดยทั่วไปคือ cosine similarity หรือระยะทาง Euclidean)723. chunks ที่ดึงมาจะถูกแทรกเข้าไปใน prompt เป็นบริบท734. LLM สร้างคำตอบตามบริบทที่ให้มา74~75## การสร้าง RAG Pipeline ด้วย LangChain76~77**LangChain** เป็น framework Python (และ JavaScript) ที่ได้รับความนิยมมากที่สุดสำหรับสร้างแอปพลิเคชันที่ขับเคลื่อนด้วย LLM มันให้ abstraction ระดับสูงสำหรับทุกส่วนประกอบของ RAG pipeline78~79### การติดตั้ง80~81```bash82pip install langchain langchain-openai langchain-community chromadb83```84~85### ขั้นตอนที่ 1: โหลดเอกสาร86~87LangChain มี Document Loader หลายสิบตัวสำหรับแหล่งข้อมูลต่างๆ88~89```python90from langchain_community.document_loaders import (91 PyPDFLoader,92 WebBaseLoader,93 DirectoryLoader,94 TextLoader,95)96~97# Load a PDF98pdf_loader = PyPDFLoader("docs/manual.pdf")99pdf_docs = pdf_loader.load()100~101# Load a web page102web_loader = WebBaseLoader("https://docs.example.com/guide")103web_docs = web_loader.load()104~105# Load all .md files from a directory106dir_loader = DirectoryLoader("./knowledge_base", glob="**/*.md", loader_cls=TextLoader)107md_docs = dir_loader.load()108~109all_docs = pdf_docs + web_docs + md_docs110```111~112### ขั้นตอนที่ 2: แบ่งเอกสารเป็น Chunks113~114```python115from langchain.text_splitter import RecursiveCharacterTextSplitter116~117text_splitter = RecursiveCharacterTextSplitter(118 chunk_size=1000,119 chunk_overlap=200,120 separators=["\n\n", "\n", ". ", " ", ""],121)122~123chunks = text_splitter.split_documents(all_docs)124print(f"Original documents: {len(all_docs)}, Chunks: {len(chunks)}")125```126~127พารามิเตอร์ `chunk_overlap` มีความสำคัญมาก: มันสร้างการทับซ้อนระหว่าง chunks ที่ต่อเนื่องกันเพื่อไม่ให้บริบทสูญหายที่ขอบเขต128~129### ขั้นตอนที่ 3: สร้าง Embeddings และ Vector Store130~131```python132from langchain_openai import OpenAIEmbeddings133from langchain_community.vectorstores import Chroma134~135embedding_model = OpenAIEmbeddings(model="text-embedding-3-small")136~137vectorstore = Chroma.from_documents(138 documents=chunks,139 embedding=embedding_model,140 persist_directory="./chroma_db",141)142```143~144### ขั้นตอนที่ 4: สร้าง Retriever145~146Retriever คือส่วนประกอบที่เมื่อได้รับคำถาม จะดึง chunks ที่เกี่ยวข้องที่สุดจาก vector store147~148```python149retriever = vectorstore.as_retriever(150 search_type="similarity",151 search_kwargs={"k": 4},152)153~154relevant_docs = retriever.invoke("How does authentication work?")155for doc in relevant_docs:156 print(doc.page_content[:200])157 print("---")158```159~160### ขั้นตอนที่ 5: สร้าง RAG Chain161~162ตอนนี้มารวมทุกอย่างเข้าด้วยกันกับ LLM และ prompt template163~164```python165from langchain_openai import ChatOpenAI166from langchain_core.prompts import ChatPromptTemplate167from langchain_core.runnables import RunnablePassthrough168from langchain_core.output_parsers import StrOutputParser169~170llm = ChatOpenAI(model="gpt-4o", temperature=0)171~172prompt = ChatPromptTemplate.from_template("""173Answer the question based only on the provided context.174If the context does not contain enough information, say you don't know.175~176Context:177{context}178~179Question: {question}180~181Answer:182""")183~184def format_docs(docs):185 return "\n\n".join(doc.page_content for doc in docs)186~187rag_chain = (188 {"context": retriever | format_docs, "question": RunnablePassthrough()}189 | prompt190 | llm191 | StrOutputParser()192)193~194response = rag_chain.invoke("How does authentication work in the system?")195print(response)196```197~198## เทคนิค RAG ขั้นสูง199~200pipeline พื้นฐานทำงานได้ดี แต่มีหลายเทคนิคที่สามารถปรับปรุงคุณภาพคำตอบได้อย่างมีนัยสำคัญ201~202### การดึงข้อมูลแบบหลายคำถาม203~204บางครั้งคำถามของผู้ใช้คลุมเครือหรือไม่สอดคล้องกับภาษาที่ใช้ในเอกสาร **Multi-Query Retriever** สร้างรูปแบบต่างๆ ของคำถามเดิมโดยอัตโนมัติเพื่อจับมุมมองที่หลากหลาย205~206```python207from langchain.retrievers import MultiQueryRetriever208~209multi_retriever = MultiQueryRetriever.from_llm(210 retriever=vectorstore.as_retriever(),211 llm=llm,212)213~214docs = multi_retriever.invoke("What are the security best practices?")215```216~217### การบีบอัดบริบท218~219เนื้อหาทั้งหมดใน chunk ไม่ได้เกี่ยวข้องกับคำถาม **Contextual Compression Retriever** ใช้ LLM เพื่อดึงเฉพาะส่วนที่เกี่ยวข้องจากแต่ละ chunk ที่ดึงมา220~221```python222from langchain.retrievers import ContextualCompressionRetriever223from langchain.retrievers.document_compressors import LLMChainExtractor224~225compressor = LLMChainExtractor.from_llm(llm)226compression_retriever = ContextualCompressionRetriever(227 base_compressor=compressor,228 base_retriever=retriever,229)230```231~232### การค้นหาแบบผสม233~234การค้นหาเชิงความหมายอย่างเดียวไม่ได้เหมาะสมเสมอไป **การค้นหาแบบผสม** รวมการค้นหาเชิงความหมาย (embeddings) กับการค้นหาเชิงคำศัพท์ (BM25, การจับคู่คำสำคัญ) เพื่อให้ได้ผลลัพธ์ที่ดีกว่า235~236```python237from langchain.retrievers import EnsembleRetriever238from langchain_community.retrievers import BM25Retriever239~240bm25_retriever = BM25Retriever.from_documents(chunks)241bm25_retriever.k = 4242~243semantic_retriever = vectorstore.as_retriever(search_kwargs={"k": 4})244~245hybrid_retriever = EnsembleRetriever(246 retrievers=[bm25_retriever, semantic_retriever],247 weights=[0.4, 0.6],248)249```250~251### RAG แบบสนทนา (พร้อมหน่วยความจำ)252~253เพื่อสร้าง chatbot RAG ที่จำบริบทการสนทนาได้ คุณต้องเพิ่มหน่วยความจำที่จัดรูปแบบคำถามของผู้ใช้ใหม่โดยคำนึงถึงประวัติการสนทนา254~255```python256from langchain.chains import create_history_aware_retriever257from langchain_core.prompts import MessagesPlaceholder258~259contextualize_prompt = ChatPromptTemplate.from_messages([260 ("system", "Given the chat history and the user's latest question, "261 "reformulate the question so it is understandable without the history."),262 MessagesPlaceholder("chat_history"),263 ("human", "{input}"),264])265~266history_aware_retriever = create_history_aware_retriever(267 llm, retriever, contextualize_prompt268)269```270~271## แนวทางปฏิบัติที่ดีที่สุด272~2731. **เลือกขนาด chunk ที่เหมาะสม**: ทดลองกับขนาดต่างๆ (500-1500 โทเค็น) chunks เล็กสำหรับคำตอบที่แม่นยำ ใหญ่กว่าสำหรับบริบทที่กว้างขึ้น2742. **ใช้ metadata ของเอกสาร**: เพิ่มแหล่งที่มา วันที่ และหมวดหมู่เป็น metadata ให้กับ chunks ซึ่งช่วยให้กรองผลลัพธ์ระหว่างการดึงข้อมูล2753. **ประเมินคุณภาพ**: ใช้ framework เช่น [RAGAS](https://docs.ragas.io/) เพื่อวัดเมตริกเช่น *faithfulness*, *relevancy* และ *context precision*2764. **จัดการการอัปเดตเอกสาร**: สร้าง pipeline การนำเข้าซ้ำเพื่อให้ vector store ซิงค์กับแหล่งข้อมูลของคุณ2775. **เพิ่ม re-ranker**: หลังจากการดึงข้อมูลเบื้องต้น ใช้โมเดล re-ranking (เช่น Cohere Rerank) เพื่อจัดเรียงผลลัพธ์ใหม่ตามความเกี่ยวข้องจริง278~279## สรุป280~281RAG ได้กลายเป็นสถาปัตยกรรมมาตรฐานสำหรับการสร้างแอปพลิเคชัน AI ที่ต้องการเข้าถึงความรู้เฉพาะทางและเป็นปัจจุบัน LangChain ลดความซับซ้อนของการนำไปใช้อย่างมาก โดยให้ abstraction สำหรับทุกส่วนประกอบของ pipeline282~283**ขั้นตอนถัดไป:**284- **ทดลองในเครื่อง**: เริ่มต้นด้วย ChromaDB และเอกสารไม่กี่ชิ้นเพื่อทำความคุ้นเคยกับ pipeline285- **สำรวจ LangSmith**: ใช้ [LangSmith](https://smith.langchain.com/) เพื่อตรวจสอบและแก้ไขข้อผิดพลาดของ chain ใน production286- **ลองโมเดล embedding ต่างๆ**: เปรียบเทียบโมเดลเช่น `text-embedding-3-small`, `text-embedding-3-large` และโมเดลโอเพนซอร์สจาก Sentence Transformers287- **ตรวจสอบเอกสาร**: [เอกสาร LangChain](https://python.langchain.com/docs/) เป็นแหล่งข้อมูลที่ยอดเยี่ยมและอัปเดตอย่างต่อเนื่อง288~
NORMAL · rag-langchain-deep-dive.md [readonly]288 lines · :q to close