RAG (Retrieval-Augmented Generation) เป็นเทคนิคที่ช่วยให้ LLM (large language model) ตอบคำถามได้แม่นยำขึ้น และไม่ถูกจำกัดด้วย knowledge cutoff หรือความรู้ที่จำกัดจากตอน train model
RAG ทำงานใน 2 ขั้นตอน:
- Retrieve: ดึงเอกสารที่เกี่ยวข้อง
- Generate: สร้างคำตอบจากเอกสารที่ได้มา
RAG มีข้อดี 3 ข้อ:
- คำตอบมีความแม่นยำมากขึ้น
- คำตอบมีความเกี่ยวข้องกับคำถามมากขึ้น
- ช่วยอัปเดตความรู้ให้กับ LLM ได้โดยไม่ต้อง train model ใหม่
ในบทความนี้ เราจะมาดูวิธีการสร้าง RAG pipeline ด้วย langchain ซึ่งเป็น framework ในการพัฒนาแอปพลิเคชัน LLM กัน
ถ้าพร้อมแล้ว ไปเริ่มกันเลย
- 🔆 High-Level View
- 📑 Step 1. Load Documents
- 📚 Step 2. Split Text
- 💾 Step 3. Embed & Store Chunks
- 🔎 Step 4. Create a Retriever
- 🤖 Step 5. Generate a Response
- 💪 Summary
- 😺 GitHub
- 📃 References
🔆 High-Level View
เราใช้ langchain สร้าง RAG pipeline ได้ใน 5 ขั้นตอน
- Load documents
- Split text
- Embed and store chunks
- Create a retriever
- Generate a response
เราไปดูการสร้าง RAG pipeline กับตัวอย่างบ็อตตอบคำถามเกี่ยวกับนโยบาย HR เช่น การลาและสวัสดิ กัน
📑 Step 1. Load Documents
ในขั้นแรก เราจะโหลดเอกสารที่เป็นข้อมูลของ RAG pipeline ก่อน
langchain มีหลาย functions สำหรับโหลดเอกสาร เช่น:
| Function | Document |
|---|---|
TextLoader() | Text file |
UnstructuredMarkdownLoader() | Markdown file |
CSVLoader() | CSV file |
JSONLoader() | JSON file |
PyPDFLoader() | PDF file |
DirectoryLoader() | ไฟล์จากในโฟลเดอร์ |
ในตัวอย่าง เราจะใช้ DirectoryLoader() เพราะเราเก็บเอกสารไว้ในโฟลเดอร์ชื่อ documents:
documents/├── benefits_policy.txt├── compensation_policy.txt├── leave_policy.txt└── remote_work_policy.txt
ตัวอย่างข้อมูลในเอกสาร benefits_policy.txt:
DataWise Co. Benefits PolicyFull-time employees receive health insurance after completing probation.The company provides annual health checkups once per year.Employees can claim up to 2,000 THB per month for wellness activities such as fitness memberships, yoga classes, or mental health support.Employees are also eligible for learning support. The company reimburses up to 10,000 THB per year for approved online courses, books, or professional certificates.
วิธีใช้ DirectoryLoader():
# Import packagesfrom langchain_community.document_loaders import DirectoryLoaderfrom langchain_community.document_loaders import TextLoader# Initialise loaderloader = DirectoryLoader( path="documents", glob="*.txt", loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"})# Load documentsdocs = loader.load()
การใช้งาน DirectoryLoader():
path= โฟลเดอร์ที่ต้องการโหลดglob= pattern ชื่อไฟล์ที่ต้องการโหลด (เช่น"*.txt"หมายถึง ไฟล์ที่ลงชื่อด้วย.txtทั้งหมด)loader_cls= function ที่จะใช้โหลด (เช่นTextLoader())loader_kwargs= argument เพิ่มเติมสำหรับ function ที่จะใช้โหลด
เราสามารถดูตัวอย่างเอกสารที่โหลดแล้วได้แบบนี้:
# View loaded documentsfor doc in docs: print("=" * 50) print(doc.metadata["source"]) print("=" * 50) print(doc.page_content[:200])
ผลลัพธ์:
==================================================documents/remote_work_policy.txt==================================================DataWise Co. Remote Work PolicyEmployees may work from home up to 2 days per week.Remote work must be approved by the employee's direct manager.Employees must be reachable on Slack during core w==================================================documents/benefits_policy.txt==================================================DataWise Co. Benefits PolicyFull-time employees receive health insurance after completing probation.The company provides annual health checkups once per year.Employees can claim up to 2,000 THB ==================================================documents/compensation_policy.txt==================================================DataWise Co. Compensation PolicySalary is paid on the last working day of each month.Performance bonuses are reviewed once per year in December.Employees may receive an annual salary adjustment ==================================================documents/leave_policy.txt==================================================DataWise Co. Leave PolicyFull-time employees receive 10 days of annual leave per year after completing probation.Employees receive 15 days of paid sick leave per year.Sick leave of 3 consecutive
📚 Step 2. Split Text
ในขั้นที่ 2 เราจะแบ่ง text ในเอกสารออกเป็นก้อน ๆ หรือ chunk เพราะการแบ่ง text จะช่วยให้การค้นหาข้อมูลง่ายขึ้น
langchain มี 3 functions หลักในการแบ่ง text:
| Function | Method |
|---|---|
CharacterTextSplitter() | แบ่งตามจำนวน character ที่กำหนด |
TokenTextSplitter() | แบ่งตามจำนวน token ที่กำหนด |
RecursiveCharacterTextSplitter() | แบ่งตามย่อหน้า บรรทัด และประโยค |
ในตัวอย่าง เราจะใช้ RecursiveCharacterTextSplitter() เพราะเป็นวิธีที่เก็บรักษาความหมายของ text ได้ดีกว่าวิธีอื่น:
วิธีใช้ RecursiveCharacterTextSplitter():
# Import packagefrom langchain_text_splitters import RecursiveCharacterTextSplitter# Create splittertext_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=100)# Split documentschunks = text_splitter.split_documents(docs)
ดูตัวอย่าง text ที่แบ่งแล้วได้ตามนี้:
# View resultsfor i, chunk in enumerate(chunks[:5]): print(f"Chunk {i+1}") print("Source:", chunk.metadata["source"]) print(chunk.page_content) print("-" * 50)
ผลลัพธ์:
Chunk 1Source: documents/remote_work_policy.txtDataWise Co. Remote Work PolicyEmployees may work from home up to 2 days per week.Remote work must be approved by the employee's direct manager.Employees must be reachable on Slack during core working hours from 10:00 AM to 4:00 PM.Employees working remotely are responsible for maintaining a stable internet connection and a quiet work environment.New employees may request remote work only after completing their first month.--------------------------------------------------Chunk 2Source: documents/benefits_policy.txtDataWise Co. Benefits PolicyFull-time employees receive health insurance after completing probation.The company provides annual health checkups once per year.Employees can claim up to 2,000 THB per month for wellness activities such as fitness memberships, yoga classes, or mental health support.Employees are also eligible for learning support. The company reimburses up to 10,000 THB per year for approved online courses, books, or professional certificates.--------------------------------------------------Chunk 3Source: documents/compensation_policy.txtDataWise Co. Compensation PolicySalary is paid on the last working day of each month.Performance bonuses are reviewed once per year in December.Employees may receive an annual salary adjustment based on company performance, individual performance, and market benchmarks.Overtime pay is available only for non-managerial employees and must be approved by a manager before the overtime work begins.--------------------------------------------------Chunk 4Source: documents/leave_policy.txtDataWise Co. Leave PolicyFull-time employees receive 10 days of annual leave per year after completing probation.Employees receive 15 days of paid sick leave per year.Sick leave of 3 consecutive days or more requires a medical certificate.Employees should submit annual leave requests at least 7 days in advance through the HR system.Unused annual leave can be carried over for up to 5 days into the next calendar year.--------------------------------------------------
สังเกตว่า text ถูกแบ่งย่อหน้า ทำให้ chunk ที่ได้มีความหมายที่ครบถ้วนในตัวเอง
💾 Step 3. Embed & Store Chunks
ในขั้นที่ 3 เราจะ embed และเก็บข้อมูลลงใน vector database
Embedding คือ การแปลง chunk ให้กลายเป็น vector คือ ชุดตัวเลขที่เป็นตัวแทนของ chunk
ตัวอย่าง chunk:
"Employees can work from home up to two days per week."
ตัวอย่าง vector:
[ 0.021, -0.184, 0.736, 0.094, -0.511, 0.302, 0.087, -0.624]
Vector เป็นสิ่งที่ระบบจะใช้ในการค้นหาเอกสารที่เกี่ยวข้อง โดย vector ที่มีความหมายใกล้เคียงกัน จะมีตัวเลขที่ใกล้เคียงกัน เมื่อเราต้องการหาเอกสาร ระบบจะดึงเอกสารที่มี vector ใกล้เคียงกับคำถามของเราขึ้นมาให้
ใน langchain เราสามารถเลือก model ที่จะใช้ embedding ได้ ในตัวอย่าง เราจะใช้ Gemini กัน:
# Import packagesimport osfrom langchain_google_genai import GoogleGenerativeAIEmbeddings# Get API keyGEMINI_API_KEY = os.getenv("GEMINI_API_KEY")# Create embedderdocument_embedder = GoogleGenerativeAIEmbeddings( model="gemini-embedding-001", task_type="retrieval_document", google_api_key=GEMINI_API_KEY)
หลังจากได้ embedding model แล้ว เราจะสร้าง vector database เพื่อเก็บ vector โดยในตัวอย่างเราจะใช้ FAISS database:
# Import packagefrom langchain_community.vectorstores import FAISS# Build vector DBvectorstore = FAISS.from_documents( documents=chunks, embedding=document_embedder)
สังเกตว่า เราใส่ document_embedder ไปใน vector database ด้วย เพื่อแปลง chunk เป็น vector และเก็บลงใน database
🔎 Step 4. Create a Retriever
ในขั้นที่ 4 เราจะสร้าง retriever ที่ทำหน้าที่ค้นหา vector โดยใช้ .as_retriever() แบบนี้:
# Creater retrieverretriever = vectorstore.as_retriever( search_type="similarity", search_kwargs={"k": 2})
เราสามารถทดสอบ retriever เพื่อดูว่า จะได้เอกสารอะไรกลับมา ได้แบบนี้:
# Test retrieverquestion = "Do I need a medical certificate for sick leave?"relevant_docs = retriever.invoke(question)for i, doc in enumerate(relevant_docs, start=1): print(f"Retrieved chunk {i}") print("Source:", doc.metadata["source"]) print(doc.page_content) print("-" * 60)
ผลลัพธ์:
Retrieved chunk 1Source: documents/leave_policy.txtDataWise Co. Leave PolicyFull-time employees receive 10 days of annual leave per year after completing probation.Employees receive 15 days of paid sick leave per year.Sick leave of 3 consecutive days or more requires a medical certificate.Employees should submit annual leave requests at least 7 days in advance through the HR system.Unused annual leave can be carried over for up to 5 days into the next calendar year.------------------------------------------------------------Retrieved chunk 2Source: documents/compensation_policy.txtDataWise Co. Compensation PolicySalary is paid on the last working day of each month.Performance bonuses are reviewed once per year in December.Employees may receive an annual salary adjustment based on company performance, individual performance, and market benchmarks.Overtime pay is available only for non-managerial employees and must be approved by a manager before the overtime work begins.------------------------------------------------------------
🤖 Step 5. Generate a Response
ในขั้นสุดท้าย เราจะให้ LLM สร้างคำตอบโดยใช้ข้อมูลใน vector database
ในตัวอย่างเราจะลองใช้ Gemini ช่วยคิดคำตอบให้กับเรา
เราจะเริ่มจากเชื่อมต่อกับ Gemini และสร้าง prompt ก่อน:
# Import packagesfrom langchain_google_genai import ChatGoogleGenerativeAIfrom langchain_core.prompts import ChatPromptTemplate# Initialise Geminillm = ChatGoogleGenerativeAI( model="gemini-2.5-flash", temperature=0, google_api_key=GEMINI_API_KEY)# Create prompt templateprompt = ChatPromptTemplate.from_template("""You are an HR policy assistant.Answer the user's question using only the policy context below.Rules:- Do not use outside knowledge.- If the answer is not in the context, say: "I could not find this information in the available company policies."- Keep the answer concise.- Mention the source policy file when possible.Policy context:{context}User question:{question}""")
จากนั้น กำหนดคำถามและดึงเอกสารที่เกี่ยวข้องจาก vector database
# Ask a questionquestion = "Do I need a medical certificate for sick leave?"# Retrieve relevant document chunksrelevant_docs = retriever.invoke(question)# Combine retrieved chunks into one context stringcontext = "\n\n".join( [ f"Source: {doc.metadata['source']}\n" f"{doc.page_content}" for doc in relevant_docs ])# Inspect retrieved context before sending it to Geminiprint("Retrieved context:")print(context)
ผลลัพธ์:
Retrieved context:Source: documents/leave_policy.txtDataWise Co. Leave PolicyFull-time employees receive 10 days of annual leave per year after completing probation.Employees receive 15 days of paid sick leave per year.Sick leave of 3 consecutive days or more requires a medical certificate.Employees should submit annual leave requests at least 7 days in advance through the HR system.Unused annual leave can be carried over for up to 5 days into the next calendar year.Source: documents/compensation_policy.txtDataWise Co. Compensation PolicySalary is paid on the last working day of each month.Performance bonuses are reviewed once per year in December.Employees may receive an annual salary adjustment based on company performance, individual performance, and market benchmarks.Overtime pay is available only for non-managerial employees and must be approved by a manager before the overtime work begins.
แล้วส่งข้อมูลคำถามและเอกสารให้กับ Gemini:
# Add context and question to prompt templatemessages = prompt.invoke( { "context": context, "question": question })# Send prompt to Geminiresponse = llm.invoke(messages)# Print Gemini's answerprint(response.content)
ผลลัพธ์:
Yes, sick leave of 3 consecutive days or more requires a medical certificate. (Source: documents/leave_policy.txt)
เพื่อให้เราใช้งาน RAG pipeline ได้ง่าย เราสามารถแปลงโค้ดชุดนี้ให้เป็น function ได้:
# Convert to functiondef ask_policy_question(question: str) -> str: """ Retrieve relevant policy chunks, send them to Gemini, and return Gemini's answer. """ # Retrieve relevant chunks relevant_docs = retriever.invoke(question) # Combine retrieved chunks into context context = "\n\n".join( [ f"Source: {doc.metadata['source']}\n" f"{doc.page_content}" for doc in relevant_docs ] ) # Add context and question to prompt template messages = prompt.invoke( { "context": context, "question": question } ) # Send completed prompt to Gemini response = llm.invoke(messages) # Return answer text return response.content
เพื่อที่เราจะเขียนโค้ดสั้นลงในครั้งถัด ๆ ไป:
# Test functionanswer = ask_policy_question("Who is eligible for health insurance?")print(answer)
ผลลัพธ์:
Full-time employees receive health insurance after completing probation. (Source: documents/benefits_policy.txt)
💪 Summary
ในบทความนี้ เราได้เรียนรู้การสร้าง RAG pipeline ด้วย langchain ใน 5 ขั้นตอน:
- Load documents: โหลดเอกสารสำหรับ RAG pipeline
- Split text: แบ่ง text ในเอกสารเป็น chunk
- Embed and store chunks: แปลง chunk เป็น vector และเก็บลงใน database
- Create a retriever: สร้างตัวค้นหาเอกสารจาก vector database
- Generate a response: สร้างคำตอบจากเอกสาร
😺 GitHub
ดูตัวอย่าง code และเอกสารทั้งหมดได้ที่ GitHub
