Back

เริ่มต้นกับ Vector Database และ RAGBlur image

บทความนี้จะพาคุณสร้าง RAG (Retrieval-Augmented Generation) ตั้งแต่เริ่มต้น โดยใช้ Ollama ร่วมกับฐานข้อมูลเวกเตอร์ Chroma

ก่อนลงมือสร้างระบบ เราจะทำความเข้าใจพื้นฐานของ ฐานข้อมูลเวกเตอร์ (Vector Database), Embedding และ RAG ให้เห็นภาพก่อน จากนั้นจึงค่อยติดตั้งเครื่องมือและสร้างเวิร์กชอปจริงทีละขั้นตอน

โปรเจกต์นี้ตั้งใจ ไม่ใช้ LangChain หรือเฟรมเวิร์ก (framework) สำหรับจัดการ workflow อื่น ๆ เพื่อให้เห็นการทำงานของ RAG อย่างครบทุกขั้นตอน และเข้าใจว่าแต่ละองค์ประกอบทำหน้าที่อะไร

สิ่งที่เวิร์กชอปนี้จะสาธิต ได้แก่

  1. เอกสารข้อความ
  2. Embeddings
  3. ฐานข้อมูลเวกเตอร์ (Vector Database)
  4. การค้นคืนตามความหมาย (Semantic Retrieval)
  5. Retrieval-Augmented Generation (RAG)

1. ทำความรู้จักฐานข้อมูลเวกเตอร์ (Vector Database)#

ก่อนเข้าสู่เวิร์กชอป เรามาทบทวนพื้นฐานกันก่อน เพราะแนวคิดเหล่านี้เป็นหัวใจสำคัญของระบบ RAG

ฐานข้อมูลแบบดั้งเดิม เช่น MySQL หรือ PostgreSQL ค้นหาข้อมูลด้วย คีย์เวิร์ด ที่ตรงกันแบบตัวอักษรต่อตัวอักษร เช่น

SELECT * FROM products WHERE name LIKE '%cat%';
sql

คำสั่งนี้จะค้นพบเฉพาะแถวที่มีคำว่า “cat” อยู่ในชื่อเท่านั้น กล่าวคือระบบพิจารณาจาก ตัวอักษรที่ตรงกัน โดยไม่ได้พิจารณาความหมายของคำ

Keyword Search มีข้อดีคือ รวดเร็ว แม่นยำ และใช้ทรัพยากรน้อย จึงเหมาะกับข้อมูลที่ต้องการความตรงเป๊ะ เช่น

  • ค้นหาด้วยรหัสสินค้า SKU-1024
  • ค้นหาด้วยเลขคำสั่งซื้อ INV-2026-001
  • ค้นหาด้วยอีเมลหรือชื่อผู้ใช้ที่ต้องตรงทุกตัวอักษร

อย่างไรก็ตาม ระบบไม่สามารถเข้าใจความหมายของภาษาได้ หากผู้ใช้ค้นหาด้วยคำว่า “feline”, “kitten” หรือ “เจ้าเหมียว” ระบบจะไม่สามารถเชื่อมโยงได้ว่าเป็นคำที่เกี่ยวข้องกับ “cat” นอกจากนี้ หากสะกดผิดเพียงตัวเดียว ผลการค้นหาก็อาจไม่พบข้อมูลเลย

Keyword Search มองเฉพาะตัวอักษรที่ตรงกัน ไม่ได้เข้าใจความหมายของข้อความ

keyword search เทียบกับ semantic search

ภาพด้านบนเปรียบเทียบการค้นหาทั้งสองรูปแบบ ฝั่งซ้ายเป็น Keyword Search เมื่อค้นหาคำว่า “feline” ระบบไม่พบข้อมูล เพราะในฐานข้อมูลมีเพียงคำว่า “cat” ส่วนฝั่งขวาเป็น Semantic Search ซึ่งสามารถดึงเอกสารเกี่ยวกับ “cat”, “kitten” และ “meow” กลับมาได้ เนื่องจากเข้าใจว่าคำเหล่านี้มีความหมายใกล้เคียงกัน

Semantic Search: ค้นหาตามความหมาย#

Semantic Search ถูกออกแบบมาเพื่อแก้ข้อจำกัดของ Keyword Search โดยเปรียบเทียบ ความหมายของข้อความ แทนการเปรียบเทียบตัวอักษร (กลไกเบื้องหลังจะอธิบายในหัวข้อฐานข้อมูลเวกเตอร์ถัดไป)

ข้อดีของ Semantic Search ได้แก่

  • เข้าใจ คำพ้องความหมาย และประโยคที่เรียบเรียงใหม่ (Paraphrase)
  • ทนต่อการสะกดผิดหรือการใช้คำที่แตกต่างกัน
  • ในโมเดลหลายภาษา (Multilingual Model) สามารถเปรียบเทียบความหมาย ข้ามภาษา ได้

อย่างไรก็ตาม Semantic Search ก็มีข้อจำกัดเช่นกัน

  • ช้ากว่าและใช้ทรัพยากรมากกว่า เพราะต้องสร้าง Embedding ทุกครั้ง
  • เป็นการค้นหาแบบ ประมาณ (Approximate) จึงอาจดึงเอกสารที่ “ใกล้เคียง” แต่ไม่ตรงที่สุดกลับมา
  • ไม่เหมาะกับข้อมูลที่ต้องการความแม่นยำแบบ 100% เช่น รหัสสินค้า หมายเลขเอกสาร หรือเลขอ้างอิงต่าง ๆ ซึ่ง Keyword Search ยังให้ผลลัพธ์ที่ดีกว่า

ดังนั้นทั้งสองวิธีจึง เสริมกัน ไม่ใช่แข่งขันกัน ในระบบจริงมักนำมาใช้ร่วมกัน เรียกว่า Hybrid Search เพื่อให้ได้ทั้งความแม่นยำของ Keyword Search และความสามารถในการเข้าใจความหมายของ Semantic Search

Keyword Search = แม่นยำที่ตัวอักษร · Semantic Search = เข้าใจความหมาย

Embedding คืออะไร#

Embedding คือการแปลงข้อความ (หรือรูปภาพ) ให้กลายเป็น เวกเตอร์ (Vector) ของตัวเลข

ข้อความแปลงเป็น embedding เวกเตอร์

เวกเตอร์นี้เป็นตัวแทนของ ความหมายของข้อความ ไม่ใช่ตัวอักษรโดยตรง

ข้อความที่มีความหมายใกล้เคียงกันจะถูกแปลงเป็นเวกเตอร์ที่อยู่ใกล้กันในพื้นที่หลายมิติ (High-dimensional Space)

ตัวเลขแต่ละตำแหน่งในเวกเตอร์ไม่ได้มีความหมายเฉพาะตัว แต่เมื่อรวมกันทั้งหมดแล้ว จะสามารถแทนความหมายของข้อความทั้งประโยคได้

แล้วแปลงจากข้อความเป็นตัวเลขได้อย่างไร?#

การแปลงข้อความเป็นเวกเตอร์ไม่ได้ทำด้วยมือหรือใช้สูตรคำนวณตายตัว แต่ทำโดย Embedding Model ซึ่งเป็นโครงข่ายประสาทเทียม (Neural Network)

กระบวนการโดยสรุปมี 3 ขั้นตอน

  1. ตัดข้อความเป็น Token

    ข้อความจะถูกแบ่งออกเป็นหน่วยย่อยที่เรียกว่า Token ซึ่งอาจเป็นคำ พยางค์ หรือส่วนหนึ่งของคำ จากนั้นแต่ละ Token จะถูกแทนด้วยหมายเลขประจำตัว (Token ID)

    "A cat sat"
    
    ["A", "cat", "sat"]
    
    [32, 1245, 876]

    ขั้นตอนนี้เป็นเพียงการแปลงตัวอักษรให้เป็นตัวเลข ยังไม่ได้สะท้อนความหมายของข้อความ

  2. โมเดลสร้างเวกเตอร์ความหมาย

    Embedding Model รับ Token ทั้งหมดเข้าไป แล้วสร้างเวกเตอร์หนึ่งก้อนที่สรุปความหมายของข้อความทั้งหมด จำนวนมิติขึ้นอยู่กับโมเดล เช่น 768 หรือ 3072 มิติ

  3. การเรียนรู้ของโมเดล

    Embedding Model ถูกฝึกด้วยข้อความจำนวนมหาศาล จนสามารถเรียนรู้ได้ว่าข้อความที่มีบริบทหรือความหมายใกล้เคียงกัน ควรมีเวกเตอร์อยู่ใกล้กัน

    ดังนั้นตัวเลขในเวกเตอร์จึงไม่ได้เกิดจากสูตรที่มนุษย์กำหนด แต่เกิดจาก การเรียนรู้ของโมเดล

ลองนึกถึงการปักหมุดบนแผนที่ โมเดลเป็นผู้กำหนดตำแหน่งให้แต่ละข้อความ เช่น “cat” และ “dog” จะอยู่ใกล้กัน ส่วน “cat” และ “car” จะอยู่ห่างกัน ตำแหน่งเหล่านี้ก็คือเวกเตอร์นั่นเอง

ในโปรเจกต์นี้ เราจะใช้ Embedding Model ชื่อ embeddinggemma ซึ่งรันผ่าน Ollama

ฐานข้อมูลเวกเตอร์ (Vector Database)#

ฐานข้อมูลเวกเตอร์ (Vector Database) คือฐานข้อมูลที่ออกแบบมาเพื่อจัดเก็บและค้นหา Embedding โดยเฉพาะ

แตกต่างจากฐานข้อมูลทั่วไปที่ค้นหาด้วยคีย์เวิร์ด ฐานข้อมูลเวกเตอร์จะค้นหาจาก เวกเตอร์ที่อยู่ใกล้กัน ซึ่งหมายถึงข้อความที่มี ความหมายใกล้เคียงกัน นั่นเอง

แต่ละระเบียนเก็บข้อมูลอะไรบ้าง

โดยทั่วไปหนึ่งระเบียนจะประกอบด้วย

  • id — รหัสประจำข้อมูล
  • embedding — เวกเตอร์ที่แทนความหมายของข้อความ
  • document — ข้อความต้นฉบับ (Chunk)
  • metadata — ข้อมูลประกอบ เช่น ชื่อไฟล์ หมายเลข Chunk หรือหมวดหมู่

การทำงานแบ่งออกเป็น 2 ขั้นตอน

  1. Index (จัดเก็บ)

    • แบ่งเอกสารออกเป็น Chunk
    • สร้าง Embedding ของแต่ละ Chunk
    • บันทึก Embedding พร้อมข้อความและ Metadata ลงฐานข้อมูล
  2. Query (ค้นหา)

    • แปลงคำถามเป็น Embedding
    • เปรียบเทียบกับ Embedding ทั้งหมดในฐานข้อมูล
    • ส่งคืน Chunk ที่มีความหมายใกล้เคียงที่สุด

ภาพด้านล่างแสดงขั้นตอนของการค้นหาแบบ Semantic Search

ขั้นตอน semantic search ในฐานข้อมูลเวกเตอร์

วัดว่า “ใกล้กัน” อย่างไร

การวัดความใกล้เคียงของเวกเตอร์เรียกว่า Similarity Metric หรือ Distance Metric

วิธีที่นิยม ได้แก่

  • Cosine Similarity — วัดมุมระหว่างเวกเตอร์ เหมาะกับข้อมูลข้อความมากที่สุด
  • Euclidean Distance (L2) — วัดระยะห่างแบบเส้นตรง
  • Dot Product — ใช้ผลคูณของเวกเตอร์

ในเวิร์กชอปนี้ เราจะใช้ Cosine Similarity

แล้วค้นหาได้เร็วอย่างไร?

หากต้องเปรียบเทียบคำถามกับเวกเตอร์ทุกตัวทีละรายการ (Brute Force) การค้นหาจะช้ามากเมื่อข้อมูลมีจำนวนหลายล้านรายการ

ฐานข้อมูลเวกเตอร์จึงใช้โครงสร้างดัชนีพิเศษ เช่น HNSW (Hierarchical Navigable Small World) เพื่อค้นหา Approximate Nearest Neighbor (ANN) ได้อย่างรวดเร็ว โดยยอมแลกกับความคลาดเคลื่อนเพียงเล็กน้อย

ฐานข้อมูลเวกเตอร์ยอดนิยม ได้แก่ Chroma, Pinecone, Milvus, Qdrant, Weaviate รวมถึงส่วนขยาย pgvector สำหรับ PostgreSQL

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

ในเวิร์กชอปนี้ เราจะใช้ Chroma เป็นฐานข้อมูลเวกเตอร์

ใช้ทำอะไรได้บ้าง#

ฐานข้อมูลเวกเตอร์สามารถประยุกต์ใช้ได้หลากหลาย เช่น

  • Semantic Search — ค้นหาตามความหมายของข้อความ
  • Recommendation System — แนะนำสินค้าหรือเนื้อหาที่คล้ายกัน
  • RAG — ค้นหาข้อมูลที่เกี่ยวข้องมาใช้เป็นบริบทให้ LLM ตอบคำถามได้ถูกต้องและอ้างอิงข้อมูลจริงมากขึ้น

2. ทำความรู้จัก RAG (Retrieval-Augmented Generation)#

ทำไมต้องมี RAG#

แม้ว่าโมเดลภาษา (LLM) จะมีความสามารถในการตอบคำถามและสร้างข้อความได้อย่างน่าประทับใจ แต่ก็ยังมีข้อจำกัดหลายประการ ได้แก่

  • ไม่รู้ข้อมูลเฉพาะของผู้ใช้ — LLM รู้เพียงข้อมูลที่ใช้ในการฝึก (training) เท่านั้น จึงไม่สามารถเข้าถึงเอกสารส่วนตัว รายวิชา หรือข้อมูลภายในองค์กรของผู้ใช้ได้
  • ข้อมูลอาจล้าสมัย — โมเดลมีช่วงเวลาที่สิ้นสุดการฝึก (Knowledge Cutoff) ทำให้ไม่ทราบข้อมูลที่เกิดขึ้นหลังจากนั้น
  • Hallucination — บางครั้งโมเดลอาจสร้างคำตอบที่ดูน่าเชื่อถือ แต่ไม่ถูกต้อง เพราะไม่มีข้อมูลจริงมารองรับ

แนวทางหนึ่งคือการนำข้อมูลทั้งหมดไป Fine-tune โมเดลใหม่ แต่กระบวนการนี้มีต้นทุนสูง ใช้เวลานาน และเมื่อข้อมูลเปลี่ยนแปลงก็ต้องฝึกโมเดลใหม่อีกครั้ง ทำให้ไม่เหมาะกับข้อมูลที่มีการอัปเดตอยู่เสมอ

RAG คืออะไร#

RAG (Retrieval-Augmented Generation) เป็นเทคนิคที่ช่วยให้ LLM สามารถตอบคำถามจากข้อมูลของผู้ใช้ได้ โดยไม่ต้องฝึกโมเดลใหม่

แนวคิดคือ ค้นคืน (Retrieve) ข้อมูลที่เกี่ยวข้องจากฐานความรู้ของผู้ใช้ก่อน แล้วจึงนำข้อมูลเหล่านั้นมาใส่เป็น Context ให้ LLM ใช้ประกอบการสร้างคำตอบ (Generate)

กล่าวอีกนัยหนึ่ง แทนที่โมเดลจะตอบจากความจำเพียงอย่างเดียว โมเดลจะตอบโดยอ้างอิงข้อมูลจริงที่ถูกค้นคืนมา

ภาพด้านล่างแสดง Pipeline ของ RAG ตั้งแต่ต้นจนจบ โดยอ่านจากซ้ายไปขวา

  1. คำถามของผู้ใช้ — เริ่มต้นจากคำถามที่ผู้ใช้ส่งเข้ามา
  2. แปลงคำถามเป็น Embedding — แปลงคำถามเป็นเวกเตอร์เพื่อใช้ค้นหาตามความหมาย
  3. ค้นคืน Chunks ที่เกี่ยวข้อง — ฐานข้อมูลเวกเตอร์ค้นหาและส่งกลับข้อความ (Chunks) ที่มีความหมายใกล้เคียงกับคำถามมากที่สุด
  4. ป้อน Chunks เป็น Context ให้ LLM — นำ Chunks ที่ค้นพบมาใส่ใน Prompt ร่วมกับคำถามของผู้ใช้
  5. สร้างคำตอบที่อ้างอิงข้อมูลจริง (Grounded Answer) — LLM สร้างคำตอบโดยอ้างอิงข้อมูลจาก Context ที่ได้รับ แทนการตอบจากความจำเพียงอย่างเดียว

ขั้นตอนของ RAG pipeline

ข้อดีของ RAG#

RAG มีข้อดีหลายประการเมื่อเทียบกับการให้ LLM ตอบจากความรู้เดิมของโมเดลเพียงอย่างเดียว

  • ตอบคำถามจากข้อมูลของผู้ใช้ได้ โดยไม่ต้อง Fine-tune โมเดล
  • อัปเดตข้อมูลได้ง่าย เพียงเพิ่มหรือแก้ไขเอกสาร แล้วสร้าง Index ใหม่ คำตอบก็จะอ้างอิงข้อมูลล่าสุดทันที
  • สามารถอ้างอิงแหล่งที่มาของข้อมูลได้ ทำให้ผู้ใช้ตรวจสอบความถูกต้องของคำตอบได้ง่ายขึ้น
  • ช่วยลด Hallucination เพราะโมเดลมีข้อมูลจริงให้ใช้อ้างอิงระหว่างการสร้างคำตอบ

RAG = Retrieval (ค้นคืนข้อมูล) + Generation (สร้างคำตอบด้วย LLM)

อย่างไรก็ตาม RAG เป็นเพียงวิธีที่ช่วย ลด Hallucination ไม่ใช่กำจัดได้ทั้งหมด หากข้อมูลที่ค้นคืนมาไม่เกี่ยวข้อง หรือโมเดลตีความ Context ผิด ก็ยังอาจตอบผิดได้ ดังนั้นการตรวจสอบผลลัพธ์ยังคงเป็นสิ่งสำคัญเสมอ

ในเวิร์กชอปนี้ เราจะสร้างระบบ RAG ตั้งแต่ต้นจนจบ เพื่อให้เห็นการทำงานของทุกขั้นตอนอย่างชัดเจน โดย ไม่ใช้ LangChain หรือเฟรมเวิร์กอื่น ๆ เพื่อให้เข้าใจกลไกของ RAG อย่างแท้จริง


3. การติดตั้ง#

ก่อนเริ่มเวิร์กชอป เราจะเตรียมเครื่องมือที่จำเป็นทั้งหมดให้พร้อม โดยประกอบด้วย VS Code, Python, Ollama, โมเดลภาษา และไลบรารีที่ใช้ในโปรเจกต์

ติดตั้ง VS Code#

Visual Studio Code (VS Code) เป็น Editor ยอดนิยมสำหรับการพัฒนาโปรแกรม รองรับภาษา Python ได้เป็นอย่างดี ใช้งานฟรี และมีส่วนขยาย (Extension) ให้เลือกใช้งานจำนวนมาก

ดาวน์โหลดได้จาก:

https://code.visualstudio.com/

เลือกเวอร์ชันที่ตรงกับระบบปฏิบัติการของคุณ แล้วติดตั้งตามขั้นตอน

แนะนำให้ติดตั้งส่วนขยาย (Extension) Python โดย Microsoft ผ่าน VS Code เพื่อช่วยในการเติมโค้ดอัตโนมัติ (Auto-complete), การไฮไลต์ Syntax และการรันโปรแกรม Python ได้สะดวกยิ่งขึ้น

ติดตั้ง Python 3.10 ขึ้นไป#

ดาวน์โหลดและติดตั้ง Python จากเว็บไซต์ทางการ

https://www.python.org/downloads/

เมื่อติดตั้งเสร็จแล้ว ให้ตรวจสอบเวอร์ชันด้วยคำสั่ง

python --version
bash

หากแสดงเวอร์ชัน 3.10 หรือใหม่กว่า ก็พร้อมใช้งาน

ติดตั้ง Ollama#

Ollama เป็นเครื่องมือสำหรับรันโมเดลภาษา (LLM) และ Embedding Model บนเครื่องของผู้ใช้เอง โดยไม่ต้องพึ่งบริการบนคลาวด์

ดาวน์โหลดได้จาก

https://ollama.com/download

เลือกเวอร์ชันที่ตรงกับระบบปฏิบัติการของคุณ (Windows, macOS หรือ Linux) แล้วติดตั้งตามขั้นตอน

หลังจากติดตั้งเสร็จ Ollama จะเปิด Local API ไว้ที่

http://localhost:11434

สามารถเปิด URL นี้ผ่านเว็บเบราว์เซอร์เพื่อตรวจสอบการทำงานได้ หาก Ollama ทำงานอยู่ จะปรากฏข้อความ

Ollama is running

ซึ่งเป็นวิธีง่ายที่สุดในการตรวจสอบว่า Ollama พร้อมใช้งานแล้ว

หน้าเว็บ "Ollama is running"

ดาวน์โหลดโมเดล#

เปิด Terminal หรือ Command Prompt แล้วดาวน์โหลดโมเดลที่ใช้ในเวิร์กชอป

ollama pull embeddinggemma
ollama pull qwen3:1.7b
bash

ผลลัพธ์ตอนรัน ollama pull embeddinggemma

ผลลัพธ์ตอนรัน ollama pull qwen3:1.7b

โมเดลทั้งสองมีหน้าที่แตกต่างกัน

หากคุณมี Chat Model อื่นอยู่แล้ว เช่น Llama, Gemma หรือ Mistral ก็สามารถใช้แทน qwen3:1.7b ได้

หลังจากดาวน์โหลดเสร็จ ให้ตรวจสอบว่าโมเดลถูกติดตั้งเรียบร้อยแล้ว

ollama list
bash

ผลลัพธ์ตอนรัน ollama list

จากนั้นทดลองรันโมเดลด้วยคำถามง่าย ๆ

ollama run qwen3:1.7b "What is the capital of France?"
bash

ผลลัพธ์ตอนรัน ollama run ถามเมืองหลวงฝรั่งเศส

หากโมเดลสามารถตอบกลับมาได้ เช่น Paris หรือประโยคที่กล่าวถึง Paris แสดงว่า Ollama และโมเดลพร้อมใช้งานแล้ว

ติดตั้งแพ็กเกจ Python#

เวิร์กชอปนี้ใช้ไลบรารี Python เพียง 2 ตัว ได้แก่

  • ollama — ไลบรารีสำหรับเรียกใช้งาน Ollama ผ่าน Python
  • chromadb — ฐานข้อมูลเวกเตอร์ที่ใช้เก็บและค้นหา Embedding (https://www.trychroma.com/)

ติดตั้งด้วยคำสั่ง

python -m pip install ollama chromadb
bash

โมเดลและไลบรารีทั้งหมดจะใช้พื้นที่บนดิสก์ประมาณ 3–6 GB ขึ้นอยู่กับโมเดลที่ติดตั้ง

เมื่อเตรียมเครื่องมือทั้งหมดเรียบร้อยแล้ว ก็พร้อมเริ่มสร้างระบบ RAG ในเวิร์กชอปได้ทันที


4. สถาปัตยกรรม#

ภาพด้านล่างแสดงภาพรวมของสถาปัตยกรรมระบบ RAG ที่จะสร้างในเวิร์กชอปนี้ ซึ่งสามารถแบ่งการทำงานออกเป็น 2 ส่วนหลัก

1) ขั้นตอนการจัดเก็บข้อมูล (Indexing)

เริ่มจากนำเอกสารหรือฐานความรู้เข้าสู่ระบบ จากนั้นข้อความจะถูกส่งไปยัง Embedding Model ของ Ollama เพื่อแปลงเป็นเวกเตอร์ตัวเลข (Embedding) แล้วจึงจัดเก็บทั้งข้อความ เวกเตอร์ และข้อมูลประกอบ (Metadata) ลงในฐานข้อมูลเวกเตอร์ Chroma

2) ขั้นตอนการตอบคำถาม (Retrieval + Generation)

เมื่อผู้ใช้ส่งคำถามเข้ามา ระบบจะนำคำถามไปสร้าง Embedding แล้วทำ Semantic Search ใน Chroma เพื่อค้นหา Chunks ที่มีความหมายใกล้เคียงที่สุด หลังจากนั้น Chunks ที่ค้นพบจะถูกนำมารวมกับคำถามของผู้ใช้เป็น Context ก่อนส่งต่อให้ Ollama LLM สร้างคำตอบที่อ้างอิงข้อมูลจริง (Grounded Answer)

จุดที่ Chunks จากฐานข้อมูลมารวมกับคำถามของผู้ใช้ ถือเป็นหัวใจสำคัญของ RAG เพราะทำให้โมเดลไม่ได้ตอบจากความจำเพียงอย่างเดียว แต่สามารถอ้างอิงข้อมูลจริงจากฐานความรู้ที่ค้นคืนมาได้

สถาปัตยกรรมของระบบ RAG


5. โครงสร้างโปรเจกต์#

เมื่อเตรียมเครื่องมือทั้งหมดเรียบร้อยแล้ว ขั้นตอนต่อไปคือการสร้างโครงสร้างโปรเจกต์สำหรับระบบ RAG

สร้างโครงสร้างโฟลเดอร์ดังนี้

rag-demo/
├── rag_demo.py
├── embeddings.py
├── vector_store.py
├── rag.py
├── knowledge/
│   ├── remote-work.txt
│   ├── leave.txt
│   ├── expenses.txt
│   ├── it-security.txt
│   ├── dress-code.txt
│   └── benefits.txt
└── chroma_db/

โฟลเดอร์ knowledge/ ใช้เก็บ ไฟล์ข้อความ (.txt) ซึ่งทำหน้าที่เป็นฐานความรู้ของบริษัท ส่วนโฟลเดอร์ chroma_db/ จะถูกสร้างขึ้นอัตโนมัติเมื่อรันโปรแกรมครั้งแรก เพื่อใช้เก็บฐานข้อมูลเวกเตอร์ของ Chroma

โปรแกรมจะอ่านไฟล์ .txt ทุกไฟล์ภายในโฟลเดอร์ knowledge/ โดยอัตโนมัติ ดังนั้นหากต้องการเพิ่ม แก้ไข หรือลบข้อมูล ก็สามารถจัดการที่ไฟล์ข้อความได้ทันที โดยไม่ต้องแก้ไขโค้ดของโปรแกรม

ไฟล์ข้อมูลตัวอย่าง: นโยบายบริษัท#

เพื่อให้เห็นภาพการทำงานของ RAG ในเวิร์กชอปนี้ เราจะใช้ นโยบายบริษัท เป็นฐานความรู้ตัวอย่าง

สร้างไฟล์ข้อความต่อไปนี้ภายในโฟลเดอร์ knowledge/

knowledge/remote-work.txt

Employees may work from home up to two days per week with manager approval.
Remote employees must be online during core hours of 10:00 to 16:00.
A stable internet connection is required. Equipment such as laptops is
provided by the company.

knowledge/leave.txt

Full-time employees receive 15 days of paid vacation per year. Sick leave
is granted for up to 30 days per year with a medical certificate. Unused
vacation days can be carried over up to 5 days into the next year. All
leave requests must be submitted through the HR portal.

knowledge/expenses.txt

Employees can claim reimbursement for business expenses within 30 days.
Travel and meal expenses require original receipts. The daily meal
allowance is capped at 500 baht. Approved reimbursements are paid with
the next salary cycle.

knowledge/it-security.txt

Passwords must be at least 12 characters and changed every 90 days.
Two-factor authentication is required for all company accounts. Employees
must lock their screens when away from their desk. Report any suspicious
emails to the security team immediately.

knowledge/dress-code.txt

The dress code is business casual from Monday to Thursday. Friday is
casual day. Closed shoes are required in the office at all times.
Client-facing meetings require formal business attire.

knowledge/benefits.txt

Full-time employees receive health insurance covering the employee and
their family. The company contributes 5% to the provident fund. Employees
are entitled to an annual health check-up. Parental leave of 90 days is
available for new parents.

6. โค้ดต้นฉบับ#

เพื่อให้โค้ดอ่านง่ายและดูแลรักษาได้สะดวก โปรเจกต์นี้แบ่งการทำงานออกเป็นหลายไฟล์ แต่ละไฟล์รับผิดชอบหน้าที่เฉพาะของตนเอง

ไฟล์หน้าที่
embeddings.pyสร้าง Embedding ด้วย Ollama
vector_store.pyแบ่ง Chunk จัดเก็บ และค้นคืนข้อมูลจาก Chroma
rag.pyสร้างคำตอบด้วย LLM (RAG Generation)
rag_demo.pyจุดเริ่มต้นของโปรแกรม (Main Program) เชื่อมทุกส่วนเข้าด้วยกัน

embeddings.py#

ไฟล์นี้รับผิดชอบเพียงหน้าที่เดียว คือ การสร้าง Embedding

เมื่อรับข้อความเข้ามา ฟังก์ชัน embed_texts() จะเรียก ollama.embed() เพื่อแปลงข้อความให้เป็นเวกเตอร์ จากนั้นส่งเวกเตอร์กลับไปให้ส่วนอื่นของโปรแกรมใช้งาน

ตัวแปร EMBEDDING_MODEL ใช้กำหนดชื่อโมเดลสำหรับสร้าง Embedding

from __future__ import annotations

import ollama

# Ollama embedding model
EMBEDDING_MODEL = "embeddinggemma"


def embed_texts(texts: list[str]) -> list[list[float]]:
    """Create embeddings for one or more texts using Ollama."""
    if not texts:
        return []

    response = ollama.embed(
        model=EMBEDDING_MODEL,
        input=texts,
    )

    return response["embeddings"]
python

vector_store.py#

ไฟล์นี้ทำหน้าที่เป็นชั้นของ ฐานข้อมูลเวกเตอร์ (Vector Database Layer)

การทำงานแบ่งออกเป็นหลายส่วน ได้แก่

  • split_text() แบ่งข้อความออกเป็น Chunk ขนาดเล็ก พร้อมกำหนดส่วนที่ซ้อนกัน (Overlap)
  • load_chunks() อ่านไฟล์ .txt ทั้งหมดในโฟลเดอร์ knowledge/ แล้วสร้างรายการของ Chunk พร้อม ID และ Metadata
  • get_collection() สร้างหรือเปิดฐานข้อมูล Chroma
  • index_documents() สร้าง Embedding และจัดเก็บข้อมูลทั้งหมดลงใน Chroma
  • retrieve() ค้นหา Chunk ที่มีความหมายใกล้เคียงกับคำถามมากที่สุดด้วย Semantic Search
...
python

rag.py#

ไฟล์นี้รับผิดชอบขั้นตอน Generation ของ RAG

เมื่อได้รับ Chunk ที่ค้นคืนมา โปรแกรมจะนำข้อความทั้งหมดมารวมเป็น Context แล้วส่ง Context + Question ไปยัง Chat Model ของ Ollama

ภายใน System Prompt มีการกำหนดกฎให้โมเดล

  • ตอบโดยอ้างอิงเฉพาะ Context ที่ได้รับ
  • หาก Context ไม่มีข้อมูล ให้ตอบตามตรงว่าไม่สามารถตอบได้
  • ตอบสั้น กระชับ และระบุแหล่งที่มาของข้อมูล
...
python

rag_demo.py#

ไฟล์นี้เป็น จุดเริ่มต้นของโปรแกรม (Entry Point) โดยเรียกใช้งานผ่านคำสั่ง

python rag_demo.py
bash

หน้าที่ของไฟล์นี้คือเชื่อมทุกองค์ประกอบของระบบเข้าด้วยกัน ได้แก่

  1. เปิดฐานข้อมูล Chroma
  2. อ่านเอกสารและสร้าง Embedding
  3. จัดเก็บข้อมูลลงในฐานข้อมูลเวกเตอร์
  4. รับคำถามจากผู้ใช้
  5. ค้นหา Chunk ที่เกี่ยวข้อง
  6. ส่ง Context ไปให้ LLM
  7. แสดงผลการค้นคืนและคำตอบจาก RAG
...
python

7. รันแอปพลิเคชัน#

เมื่อสร้างไฟล์ทั้งหมดเรียบร้อยแล้ว ให้เปิด Terminal ภายในโฟลเดอร์โปรเจกต์ แล้วรันโปรแกรมด้วยคำสั่ง

python rag_demo.py
bash

ผลลัพธ์ตอนรัน rag_demo.py ถามตอบเกี่ยวกับนโยบายบริษัท

เมื่อโปรแกรมเริ่มทำงาน ระบบจะสร้าง Embedding ของเอกสารทั้งหมด จัดเก็บลงใน Chroma และเข้าสู่โหมดรับคำถามจากผู้ใช้

ตัวอย่างการใช้งาน

Question: Can I work from home?

Retrieved chunks:

1. remote-work.txt (approx. similarity: 0.542)
   Employees may work from home up to two days per week with manager approval. Remote employees must be online during core hours of 10:00 to 16:00. A stable internet connection is required. Equipment such as laptops is provided by the company.

2. dress-code.txt (approx. similarity: 0.377)
   The dress code is business casual from Monday to Thursday. Friday is casual day. Closed shoes are required in the office at all times. Client-facing meetings require formal business attire.

3. leave.txt (approx. similarity: 0.309)
   Full-time employees receive 15 days of paid vacation per year. Sick leave is granted for up to 30 days per year with a medical certificate. Unused vacation days can be carried over up to 5 days into the next year. All leave requests must be submitted through the HR portal.

RAG answer:
Yes, you can work from home up to two days per week with manager approval. This policy is outlined in **Source 1: remote-work.txt**. A stable internet connection and provided equipment (laptops) are required.

ก่อนที่ LLM จะสร้างคำตอบ โปรแกรมจะแสดง Retrieved Chunks ที่ค้นพบจากฐานข้อมูลเวกเตอร์ เพื่อให้เห็นว่าระบบเลือกข้อมูลใดมาใช้เป็น Context จากนั้นจึงแสดง RAG Answer ซึ่งเป็นคำตอบที่สร้างขึ้นโดยอ้างอิงข้อมูลจาก Chunks เหล่านั้น

คะแนนความคล้ายคลึง (Similarity Score) และข้อความคำตอบอาจแตกต่างกันเล็กน้อย ขึ้นอยู่กับเวอร์ชันของโมเดลที่ใช้ รวมถึงสภาพแวดล้อมของเครื่องคอมพิวเตอร์แต่ละเครื่อง


8. สิ่งที่ผู้อ่านควรสังเกต#

หลังจากทดลองใช้งานระบบ RAG แล้ว ลองสังเกตประเด็นสำคัญต่อไปนี้ เพื่อให้เข้าใจการทำงานของแต่ละองค์ประกอบได้ชัดเจนยิ่งขึ้น

A. การค้นหาด้วยคีย์เวิร์ดเทียบกับการค้นหาตามความหมาย#

ลองถามคำถามต่อไปนี้

How many days off am I entitled to?

แม้ว่าภายในไฟล์จะไม่มีคำว่า “days off” ปรากฏอยู่ตรงตัว ระบบก็ยังสามารถค้นคืนข้อมูลจาก leave.txt ได้ เพราะฐานข้อมูลเวกเตอร์ไม่ได้เปรียบเทียบตัวอักษร แต่เปรียบเทียบ ความหมาย ของข้อความ จึงเข้าใจได้ว่า “days off” มีความหมายใกล้เคียงกับ “vacation” หรือวันลาพักร้อน

นี่คือความแตกต่างสำคัญระหว่าง Semantic Search และ Keyword Search

B. การค้นคืนเกิดขึ้นก่อนการสร้างคำตอบ#

ก่อนที่ LLM จะสร้างคำตอบ โปรแกรมจะแสดง Retrieved Chunks ที่ค้นพบจากฐานข้อมูลเวกเตอร์ก่อนเสมอ

การแสดงผลลัพธ์ส่วนนี้ช่วยให้ผู้อ่านสามารถตรวจสอบได้ว่า LLM ได้รับข้อมูลอะไรเป็น Context และสามารถวิเคราะห์ได้ว่าทำไมโมเดลจึงตอบออกมาในลักษณะนั้น

C. RAG ไม่ได้เทรนโมเดลใหม่แบบถาวร#

RAG ไม่ใช่การ Fine-tune โมเดล

ทุกครั้งที่มีคำถาม ระบบจะค้นหาเอกสารที่เกี่ยวข้องจากฐานข้อมูลเวกเตอร์แบบเรียลไทม์ แล้วส่งข้อมูลเหล่านั้นไปเป็น Context ให้ LLM

กล่าวคือ น้ำหนัก (Weights) ของโมเดลภาษาไม่เคยเปลี่ยนแปลง ข้อมูลใหม่จะถูกนำมาใช้เฉพาะในคำถามนั้น ๆ เท่านั้น

D. ฐานความรู้กำหนดขอบเขตของคำตอบ#

ลองถามคำถามที่ไม่มีอยู่ในฐานความรู้ เช่น

What is the company's profit this year?

เนื่องจากไม่มีข้อมูลดังกล่าวอยู่ในไฟล์ความรู้ คำตอบที่คาดหวังคือ

I cannot answer from the provided knowledge base.

ตัวอย่างนี้แสดงให้เห็นว่า System Prompt ช่วยกำหนดให้โมเดลตอบโดยอ้างอิงเฉพาะข้อมูลที่ค้นคืนมา จึงช่วยลดโอกาสเกิด Hallucination ได้

อย่างไรก็ตาม ไม่มี Prompt ใดที่สามารถรับประกันได้ว่าจะกำจัด Hallucination ได้ทั้งหมด ดังนั้นการตรวจสอบผลลัพธ์ยังคงเป็นสิ่งสำคัญเสมอ


9. กิจกรรม#

กิจกรรมต่อไปนี้จะช่วยให้เข้าใจการทำงานของ Embedding, Vector Database และ RAG ได้ลึกยิ่งขึ้น

กิจกรรม 1: สำรวจ Embedding#

สร้างไฟล์ใหม่ในโปรเจกต์ เช่น inspect_embedding.py แล้ว Import ฟังก์ชัน embed_texts() จาก embeddings.py

from embeddings import embed_texts

vector = embed_texts(["Employees can work from home two days a week."])[0]

print("Vector dimensions:", len(vector))
print("First 10 values:", vector[:10])
python

รันโปรแกรมด้วยคำสั่ง

python inspect_embedding.py
bash

จากนั้นลองสังเกตผลลัพธ์ที่ได้

เวกเตอร์หนึ่งชุดอาจมีตัวเลขหลายร้อยหรือหลายพันค่า แม้ว่าจะไม่สามารถตีความตัวเลขแต่ละมิติได้โดยตรง แต่ความหมายของข้อความจะ กระจายอยู่ทั่วทั้งเวกเตอร์

กล่าวคือ ไม่มีมิติใดมิติหนึ่งที่แทนความหมายเฉพาะ เช่น “มิติที่ 5 คือความน่ารัก” แต่ความหมายเกิดจากการทำงานร่วมกันของตัวเลขทุกตำแหน่ง

ลองนึกถึง พิกเซลของรูปภาพ พิกเซลเพียงจุดเดียวไม่สามารถบอกได้ว่าภาพคืออะไร แต่เมื่อรวมพิกเซลจำนวนมากเข้าด้วยกัน ก็สามารถกลายเป็นภาพที่สมบูรณ์ได้ เวกเตอร์ก็ทำงานในลักษณะเดียวกัน

กิจกรรม 2: เปรียบเทียบคำค้น#

ลองถามคำถามต่อไปนี้

How many vacation days do I get?
Can I work remotely?
Tell me about the expense reimbursement policy.

เปรียบเทียบว่าแต่ละคำถามค้นคืนข้อมูลจากไฟล์ใดบ้าง และสังเกตว่าคำที่ใช้แตกต่างกัน แต่ระบบยังสามารถค้นหาเอกสารที่เกี่ยวข้องจากความหมายได้

กิจกรรม 3: แก้ไขฐานความรู้#

ลองแก้ไขหรือเพิ่มไฟล์ .txt ภายในโฟลเดอร์ knowledge/

จากนั้นรันโปรแกรมใหม่ แล้วถามคำถามเกี่ยวกับข้อมูลที่เพิ่งเพิ่มเข้าไป เพื่อสังเกตว่าระบบสามารถตอบจากข้อมูลใหม่ได้ทันที โดยไม่ต้อง Fine-tune โมเดล

กิจกรรม 4: สาธิตความรู้ที่ขาดหายไป#

ลองถามคำถามที่อยู่นอกเหนือจากฐานความรู้อย่างชัดเจน แล้วสังเกตว่าโมเดลสามารถปฏิบัติตาม System Prompt และตอบว่าไม่มีข้อมูลได้หรือไม่

กิจกรรมนี้ช่วยให้เห็นบทบาทของ Grounding และข้อจำกัดของ LLM ได้อย่างชัดเจน

กิจกรรม 5: เปลี่ยนค่า TOP_K#

ทดลองแก้ค่าตัวแปร TOP_K ภายในไฟล์ vector_store.py

TOP_K = 1
python

เปรียบเทียบกับ

TOP_K = 5
python

จากนั้นทดลองถามคำถามเดิม แล้วสังเกตความแตกต่างของผลลัพธ์

ลองอภิปรายประเด็นต่อไปนี้

  • หากดึง Chunks น้อยเกินไป อาจพลาดข้อมูลสำคัญที่เกี่ยวข้อง
  • หากดึง Chunks มากเกินไป อาจมีข้อมูลที่ไม่เกี่ยวข้องเข้ามาปะปน และกลายเป็นสัญญาณรบกวน (Noise)
  • การเพิ่ม Context ไม่ได้หมายความว่าคำตอบจะดีขึ้นเสมอไป คุณภาพของข้อมูลสำคัญกว่าปริมาณของข้อมูล

10. ทางเลือก: เวอร์ชันค้นคืนอย่างเดียว#

เพื่อให้เข้าใจบทบาทของฐานข้อมูลเวกเตอร์อย่างชัดเจน ลองสร้างโปรแกรมที่มีเฉพาะ Semantic Search โดยไม่ใช้ LLM สร้างคำตอบ

สร้างไฟล์ใหม่ชื่อ search_only.py

from vector_store import get_collection, index_documents, retrieve

collection = get_collection()
index_documents(collection)

question = input("Search: ")
matches = retrieve(collection, question)

for match in matches:
    print(match["source"])
    print(match["document"])
    print()
python

รันโปรแกรมด้วยคำสั่ง

python search_only.py
bash

โปรแกรมนี้จะแสดงเฉพาะผลการค้นคืนจากฐานข้อมูลเวกเตอร์ โดยไม่มีขั้นตอน Generation

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

Vector Search = Retrieving relevant information

RAG = Retrieval + LLM Generation

กล่าวคือ Vector Search ทำหน้าที่ค้นหาข้อมูลที่เกี่ยวข้อง ส่วน RAG จะนำข้อมูลที่ค้นพบไปให้ LLM สร้างคำตอบอีกขั้นหนึ่ง


11. สรุป#

ในบทความนี้ เราได้สร้างระบบ Retrieval-Augmented Generation (RAG) ตั้งแต่พื้นฐานจนถึงการใช้งานจริง โดยไม่พึ่งพา Framework อย่าง LangChain เพื่อให้เห็นกลไกการทำงานของแต่ละขั้นตอนอย่างชัดเจน

สิ่งสำคัญที่ได้เรียนรู้ ได้แก่

  • Embedding — แปลงข้อความให้เป็นเวกเตอร์ที่เก็บความหมายของข้อความ โดยใช้โมเดล embeddinggemma ผ่าน Ollama
  • ฐานข้อมูลเวกเตอร์ (Vector Database) — จัดเก็บ Embedding และค้นหาข้อมูลด้วย Semantic Search ซึ่งในเวิร์กชอปนี้ใช้ Chroma
  • RAG (Retrieval-Augmented Generation) — ค้นคืนข้อมูลที่เกี่ยวข้องจากฐานความรู้ แล้วนำมาเป็น Context ให้ LLM (qwen3:1.7b) ใช้สร้างคำตอบที่อ้างอิงข้อมูลจริง
  • การออกแบบโค้ด — แยกความรับผิดชอบออกเป็น embeddings.py, vector_store.py, rag.py และ rag_demo.py พร้อมเก็บฐานความรู้เป็นไฟล์ .txt ภายในโฟลเดอร์ knowledge/ ทำให้สามารถอัปเดตข้อมูลได้โดยไม่ต้องแก้ไขโค้ด

ข้อดีสำคัญของ RAG คือสามารถตอบคำถามจากข้อมูลของผู้ใช้ได้โดยไม่ต้อง Fine-tune โมเดล รองรับการอัปเดตข้อมูลได้ง่าย อ้างอิงแหล่งที่มาของคำตอบได้ และช่วยลดโอกาสเกิด Hallucination

แม้ระบบที่สร้างในบทความนี้จะเป็นตัวอย่างขนาดเล็ก แต่แนวคิดเดียวกันสามารถขยายไปสู่การสร้างระบบ Chatbot สำหรับเอกสารองค์กร ระบบค้นหาความรู้ภายใน หรือผู้ช่วย AI ที่ตอบคำถามจากฐานข้อมูลเฉพาะทางได้ โดยทั้งหมดสามารถรันได้บนเครื่องของผู้ใช้เองผ่าน Ollama และ Chroma โดยไม่ต้องพึ่งบริการคลาวด์


เริ่มต้นกับ Vector Database และ RAG
ผู้เขียน กานต์ ยงศิริวิทย์ / Karn Yongsiriwit
เผยแพร่เมื่อ July 17, 2026
ลิขสิทธิ์ CC BY-NC-SA 4.0

กำลังโหลดความคิดเห็น...

ความคิดเห็น 0