เกริ่นนำ

แม้ผมจะเรียนวิศวกรรมคอมพิวเตอร์มาถึง 3 ปี แต่ประสบการณ์การเขียนโปรแกรมจริงจังกลับห่างหายไปนานนับตั้งแต่จบปี 1 ช่วงที่ผ่านมาผมได้เรียนรู้ทั้ง Python, Java (OOP) และ C มาบ้าง แต่การเรียนรู้เพียงทฤษฎีโดยไม่ได้ลงมือทำโปรเจกต์จริงจัง ทำให้ทักษะเหล่านั้นเลือนหายไปตามกาลเวลา

ย้อนกลับไปสมัยเรียนวิชา Java OOP ผมทำเกรดได้ดีในระดับ A และเข้าใจคอนเซปต์ส่วนใหญ่ จนสามารถสร้างโปรเจกต์เว็บไซต์สำหรับจดโน้ตออกมาได้หนึ่งชิ้น แม้คุณภาพจะไม่ได้ดีมากก็ตาม

Tech Stack ในตอนนั้น

หากจำไม่ผิด เทคโนโลยีที่ผมเลือกใช้คือ:

  • Full Stack Framework: เลือกใช้ Next.js
  • Database: ใช้ Convex ซึ่งเป็น NoSQL (ตอนนั้นยังแยกไม่ออกด้วยซ้ำว่า SQL กับ NoSQL ต่างกันอย่างไร หรือทำไมต้องเลือกใช้อย่างใดอย่างหนึ่ง เห็นในอินเทอร์เน็ตแนะนำก็ใช้ตาม)
  • Hosting: ใช้ Vercel เพราะฟรีและรองรับ Next.js ได้ดีที่สุด
  • Authentication: ระบบล็อกอินผ่าน Gmail นับเป็น 1 User ง่าย ๆ
  • Backend: ช่วงแรกเขียนด้วย JavaScript ล้วน แต่ช่วงหลังเริ่มผสม TypeScript เข้าไป ส่งผลให้โค้ดมีความวุ่นวายสูง ขาด Type Safety และไม่แน่ใจด้วยซ้ำว่า Build ผ่านมาได้อย่างไร

แน่นอนว่าเรื่อง Privacy ไม่ต้องพูดถึง ข้อมูลทุกอย่างในหลังบ้านถูกเก็บเป็น Plain Text ทำให้ผมสามารถอ่านโน้ตของผู้ใช้ทุกคนได้

ความท้าทายในตอนนั้นคือโจทย์การสร้างโปรเจกต์ที่ไม่ซ้ำใคร หรือต้องแก้ปัญหาที่แอปพลิเคชันอื่นยังทำไม่ได้ ผมจึงพยายามสร้าง Solution ขึ้นมาแก้ปัญหาที่อาจจะไม่มีอยู่จริง ผลลัพธ์ที่ได้คือแอปจดโน้ตที่มีฟีเจอร์หลากหลายผสมปนเปกันไป เช่น การแชร์ Guest View ให้คนอื่นดูได้ หรือการแสดงผลแบบ Tree Graph (ใจจริงอยากได้กราฟแบบ Obsidian แต่ทำไม่เป็น)

node-note

หน้าตาเว็บไซต์แรกที่ผมทำ ปัจจุบันยังคง Deploy อยู่ แม้ GitHub จะแจ้งเตือนช่องโหว่ (CVE) มากกว่า 5 รายการแล้วก็ตาม

สิ่งที่ทำได้:

  • จดโน้ตและมี Text Editor
  • แชร์ให้ผู้อื่นดูได้แบบ Real-time (ใช้วิธีแยก Path และซ่อน Editor)
  • ระบบ Folder พื้นฐาน
  • แสดงผล Tree View ตามโครงสร้าง Folder

สิ่งที่ทำไม่ได้:

  • ระบบ Collaborate แก้ไขงานร่วมกันแบบ Real-time
  • ระบบ Tag
  • Accessibility ต่ำมาก ไม่เหมาะกับการใช้งานจริง
  • สรุปแล้ว ผมยังไม่พร้อมจะเป็น Developer ไม่ว่าจะ Frontend หรือ Backend

ฝันร้ายที่ผ่านพ้น

หลังจากส่งโปรเจกต์และเอาตัวรอดมาได้อย่างทุลักทุเล ผมได้เรียนรู้บทเรียนสำคัญหลายอย่าง ทั้งการทำงานเป็นทีม และการเขียนโปรแกรมที่ซับซ้อนขึ้น ต้องสู้กับทั้ง Framework และ Git ที่ไม่เคยใช้งานมาก่อน ในหัวผมไม่มีความรู้เรื่อง Deployment Pipeline เลย ไม่รู้จะเริ่มจาก Stack ไหน หรือเลือกเครื่องมืออะไร ทุกอย่างดูมากล้นไปหมด

exploits

แจ้งเตือน Security Concerns รวมกว่า 35 รายการ โปรเจกต์นี้น่าจะเหมาะกับการใช้ฝึก Pentest มากกว่าใช้งานจริง

สิ่งที่ผมควรตระหนักได้ตั้งแต่ปีแรกคือ ผมอาจจะไม่ได้หลงใหลในการ Coding ขนาดนั้น หรืออย่างน้อยก็ไม่อินกับการ Develop ซอฟต์แวร์จากศูนย์ ความรู้สึกเหมือนกับการพยายามปั้นก้อนหินให้กลายเป็นขนมปัง

เมื่อขึ้นปี 2 และปี 3 วิชาเรียนเริ่มเข้าสู่ความเป็น Computer Engineering มากขึ้น การเขียนโปรแกรมระดับ High-level ลดลง แต่แทนที่ด้วยความเป็นวิศวกรรมไฟฟ้า สัญญาณ (Signal & System) และภาษา Low-level อย่าง Assembly หรือ C แทน จะมีก็แต่วิชา Network ที่รู้สึกว่าเรียนแล้วสนุกและเข้าใจได้ดี

กลับสู่การทำเว็บอีกครั้ง

ผมไม่ได้แตะการทำเว็บอีกเลย จนกระทั่งช่วงกลางปี 2 แม่ของผม (ซึ่งเป็นตัวแทนประกันชีวิต) อยากสร้าง Personal Branding และ Team Branding แม่จึงมาขอให้ช่วยทำเว็บไซต์ให้ แต่ด้วยภาระการเรียนที่หนักหนา และเกรดที่แค่ประคองตัวก็ยากแล้ว ผมจึงปฏิเสธและผัดผ่อนไปตลอด

สุดท้ายแม่จึงให้รุ่นพี่ในบริษัท ซึ่งมีความรู้เรื่องโค้ดบ้าง มาช่วยสร้างเว็บไซต์ด้วย WordPress ให้ ซึ่งผลลัพธ์ก็ออกมาใช้งานได้ มีรูปภาพประกอบ มี Slideshow แสดงผลงาน

แต่ปัญหาที่แท้จริงตามมาภายหลัง:

  1. เว็บไซต์ WordPress นั้นพึ่งพา Dependency และ Extension จำนวนมาก แม้จะสวยงาม แต่ปรับแต่งได้ยาก
  2. การอัปเดต Extension บางครั้งทำให้เว็บไซต์พังเสียหาย จนต้องกู้คืนระบบ (Restore) อยู่บ่อยครั้ง
  3. ผมไม่เข้าใจหลักการทำงานเบื้องหลังของ WordPress ทำให้รู้สึกว่าควบคุมระบบไม่ได้ดั่งใจ ทำได้เพียงจัดการในฐานะ Admin เท่านั้น

ปัดฝุ่นและเริ่มต้นใหม่

ผมตัดสินใจกลับมาทบทวน Codebase เดิม และลองใช้วิธี Reverse Engineering (จริง ๆ คือการลอก UX/UI) จากเว็บ WordPress เดิมของรุ่นพี่มาเป็นจุดตั้งต้น ด้วยความที่ยังไม่สันทัดเครื่องมืออื่น ผมจึงเลือกใช้ Next.js และ Vercel เช่นเดิม

sla-old

หน้าตาเว็บไซต์เวอร์ชันแรกที่ลองทำ เลียนแบบโครงสร้างเดิม มี Slideshow แสดงกรมธรรม์ด้านล่าง

แต่สุดท้ายผมก็ไม่ได้ทำต่อ โปรเจกต์นี้ถูกทิ้งร้างไว้บน Git นานข้ามปี โดยมีเพียง 3 Commits เท่านั้น

sla-old-commit

โปรเจกต์ที่ถูกดองเค็มตั้งแต่ปี 2024 โดยไม่มีการอัปเดตใด ๆ

ยกเครื่องใหม่ด้วย Astro

จุดเปลี่ยนเกิดขึ้นในช่วงปิดเทอมก่อนขึ้นปี 3 ผมมีเวลาว่างและอยากลองทำ Blog ส่วนตัวขึ้นมาบ้าง แรงบันดาลใจมาจากเว็บไซต์ Suksit.com ของพี่คนนึง ซึ่งเขียนเกี่ยวกับ Cybersecurity, Coding และ Lifestyle ได้น่าติดตาม

ผมจึงเริ่มค้นหาข้อมูลเกี่ยวกับ Static Site Generator (SSG) และพบตัวเลือกที่น่าสนใจคือ:

ผมตัดสินใจเลือก Astro เนื่องจากได้รับคำแนะนำมากมายใน Reddit และตอบโจทย์ความต้องการของผมได้ดีที่สุด:

  • ทำงานแบบ Client-side โดยปกติ แต่สามารถทำ Server-side Rendering (SSR) ได้ง่าย
  • Documentation อ่านง่าย มี Tutorial สอนเป็นขั้นตอน
  • มี Template ให้เลือกใช้งานจำนวนมาก
  • รองรับการเขียนเนื้อหาด้วย Markdown เป็นหลัก ทำให้ไม่ต้องยุ่งกับ CSS หรือ React component มากนักหากไม่จำเป็น

Apiratchai.com

แต่เหนือสิ่งอื่นใด การที่คนโค้ดดิ้งกากมาก ๆ แบบผมจะขึ้น site ได้ มันน่าจะเป็นการมาถึงของ Claude code, Antigravity เสียมากกว่า (lol) มันเหมือนกับการที่ Almight เดินมาตบบ่าแล้วพูดว่า “มิโดริยะ นายก็เป็นได้นะ developer สาย ก๊อป-วาง-tab น่ะ”

almight

และนี่คือที่มาของ Blog แห่งนี้ Apiratchai.netlify.app (เนื่องจาก Domain Name มีราคาสูง ผมจึงยังใช้ Subdomain ของ Netlify อยู่)

ผมตั้งเป้าหมายให้เว็บไซต์นี้มีคุณสมบัติดังนี้:

  • เรียบง่ายและโหลดไว แบบ Suksit.com
  • รองรับ Dark/Light Mode
  • หน้ารวมโพสต์ต้องมีรูปภาพปก (Thumbnail)
  • มีระบบ Tag สำหรับค้นหาเนื้อหา

และฟีเจอร์เพิ่มเติมเพื่อการเรียนรู้ (เผื่อนำไปใช้กับเว็บของแม่):

  • ระบบ Search สำหรับค้นหาบทความจำนวนมาก
  • ระบบ AI Chatbot
  • หน้า Dashboard สำหรับแสดงข้อมูลอื่น ๆ

ai-chat

แชทบอทแบบจำกัดจำเขี่ย

ที่บอกว่า “จำกัดจำเขี่ย” เพราะว่าข้อมูลที่ AI ตัวนี้จะนำมาตอบมันคือไฟล์ ๆ นึงชื่อ ‘knowledge_base.yaml’

นั่นหมายความว่าระบบนี้เป็น Text-based ทั้งหมด ไม่มี Vector DB หรือ Database ภายนอกมาเกี่ยวข้อง ซึ่งข้อดีคือตรวจสอบความถูกต้องได้ง่าย หาก AI เกิดอาการหลอน (Hallucinate)

yaml

แม้บทความจะยาว แต่ขนาดไฟล์ยังเล็กพอที่จะไม่เกิน Context Window ของ AI

ทำเว็บให้แม่

หลังจากทดลองทำเว็บส่วนตัวสำเร็จ ผมจึงเริ่มทำเว็บไซต์ให้แม่จริงจัง โดยตั้งเป้าหมายไว้ดังนี้:

  1. UX/UI ต้องใช้งานง่าย (เนื่องจากผมไม่ถนัดเรื่อง Design)
  2. มี AI Chatbot แบบ RAG Model ที่สามารถดึงข้อมูลจากไฟล์ PDF กรมธรรม์ประกันชีวิตได้จริง
  3. แยกโครงสร้าง ระหว่าง “Product” (กรมธรรม์) และ “Blog Post” (บทความทั่วไป)

ผมเริ่มต้นด้วยการ Init Astro Project ใหม่ และย้าย Component ทั้งหมดจาก Next.js โปรเจกต์เดิมมาจัด Layout ปัญหาที่พบคือ:

  • โครงสร้าง Framework ต่างกัน: ต้องรื้อแก้การ Import และ Path ต่าง ๆ ทั้งหมด
  • CSS เละเทะ: โค้ดเก่าเขียน CSS แบบ Inline ไว้เยอะมาก ไม่มี Global CSS ทำให้การปรับแก้ Theme ทำได้ยากและไม่สวยงาม
  • Code Structure แย่: ขาดการแยก Component ที่ดี มีการยัด HTML ก้อนใหญ่ลงในไฟล์เดียว
  • Dependency: ผมพยายามลดการใช้ React Library ภายนอก และหลีกเลี่ยง Tailwind CSS เพื่อเขียนเองให้ได้มากที่สุด จะได้เข้าใจโครงสร้างเว็บตัวเองจริง ๆ

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

do-everything

รับบท Project Manager คอยสั่งงาน AI

และผลงานที่ได้ก็คือการถอดด้ามจาก Next.js มาที่ Astro สำเร็จ คือไอ้การที่ UI ยังกากอยู่เนี่ยเป็นการยืนยันว่าเรา port มาสำเร็จแบบสำเร็จจริง ๆ 55465465 ฟีล Witcher 3 พอร์ตไปลง Nintendo Switch นั่นแหละน่า input กำหนด output เสมอ

fonladda-frontend

เว็บไซต์ใหม่ที่ไส้ในถูกเปลี่ยนเป็น Astro เรียบร้อยแล้ว

AI RAG Chatbot

ความท้าทายถัดมาคือการสร้าง AI Chatbot ที่สามารถตอบคำถามจากเอกสาร PDF ได้ ซึ่งต้องใช้เทคนิค RAG (Retrieval Augmented Generation)

โดยปกติเมื่อพูดถึง Database เรามักนึกถึง SQL หรือ NoSQL ที่เก็บข้อมูลเป็นตารางหรือ JSON แต่การทำ Similarity Search (ค้นหาความหมายที่ใกล้เคียงกัน) จำเป็นต้องใช้ Vector Database

แต่ว่าเราจะทำ similarity search ไม่ได้เลย ถ้าเราใช้ database เดิม ๆ แบบนั้น (หรือเปล่าฟะ หรือมันทำได้ idk) ผมก็เลยไปค้นหาข้อมูลเพิ่ม จาก ไอ้ริว เพื่อนที่มันเคยทำมินิโปรเจคที่ใช้ RAG ดึงข้อมูลจาก vector DB มันแนะนำว่าให้ใช้ Qdrant เก็บ เพราะว่าฟรี ได้ storage ประมาณนึง ไม่เต็มง่ายหรอก

qdrant-node

เออเว้ย ให้ RAM ให้ storage ตั้งเยอะ ฟรีอีกต่างหาก

ข้อมูลใน Qdrant มาจาก 3 แหล่งหลัก:

  1. เนื้อหา Manual ที่ผมเขียนลงในไฟล์ Markdown
  2. ข้อมูลส่วนตัวของแม่ (Personal Data) จากไฟล์ personal_data.json
  3. เนื้อหาจากไฟล์ PDF กรมธรรม์ประกันต่าง ๆ

qdrant-collection

ข้อมูลจาก PDF 12 หน้า ใช้พื้นที่จัดเก็บไปเพียงเล็กน้อยเท่านั้น

ครั้งแรกกับ Vector DB

กระบวนการแปลง PDF เป็นข้อมูลสำหรับ AI ไม่ใช่เรื่องง่าย โดยเฉพาะภาษาไทยที่มีปัญหาสระลอย

  1. วิธีแรกที่ผมคิดน้อยไปหน่อยคือผมใช้ Library pdf-parse เพื่อดึงข้อความจาก PDF (คือมันเร็วมากเพราะเป็นโค้ดล้วน แต่มีปัญหาสระลอย)
  2. มันมีปัญหานิดหน่อยกับสระภาษาไทย สระมันลอย แต่ผมให้ AI แก้โดยการเขียน helper function คอยแก้ เปลี่ยน unicode ซึ่งอันนี้ผมคิดว่าเหมาะสมให้เป็นงานของ AI นะ มันเป็น language model ให้มัน parse จาก PDF ทุกไฟล์ -> ใส่ thai-fn -> อ่านซ้ำจนกว่าจะถูก

(หลักการทำงานคือ PDF parse อะไรมา AI ก็แค่อ่าน brute force ไปเลย เช่นคือว่า “วิทยาศาสตร์” library มัน parse มาผิดเป็น “ว็ทยาศาสตรื” แต่ AI ที่เป็น LLM มันเดาออกแน่นอนว่าต้นฉบับคืออะไร)

thai-fn

Function ที่ AI ช่วยเขียนเพื่อแก้ปัญหาสระลอย

thai-fn-success

ตรวจสอบแล้วว่าแก้ไขได้ถูกต้องกว่า 99%
  1. หลังจากมัน parse text ออกมาได้แล้ว ผมก็ใช้ ai ตัวเดียวกันกับทุก feature ในก* ตอน Build Phase: ผมรันสคริปต์ build-knowledge.ts ให้ดึงบทความทั้งหมดมาซอยเป็นส่วนละ 800 ตัวอักษร แล้วยิงไปขอเวกเตอร์ความละเอียดสูง 3,072 มิติผ่านโมเดล gemini-embedding-2 ของ Google จากนั้นก็บันทึกเวกเตอร์ทั้งหมดเซฟลงในไฟล์ JSON ดิบๆ ชื่อ post_embeddings.json ในเครื่องเลย
  • ตอนรันจริง (Runtime):
    • ถ้าคนคลุมดำ Ask AI ตัวแอปฝั่งหน้าบ้าน (AIChat.tsx) จะตามไปแกะ Tag Parent รอบๆ เพื่อลอกเอา “ทั้งย่อหน้า” แนบไปเป็นบริบทแรก บอทเลยเข้าใจคำถามชัดเจน
    • ถ้าพิมพ์ถามทั่วไป ตัว Netlify Function จะดึงคำถามผู้ใช้ไปแปลงเวกเตอร์ แล้วนำไปวิ่งสูตร Cosine Similarity หาจุดที่ตรงที่สุดเปรียบเทียบในแรมกับไฟล์ JSON ตรงๆ
    • ความเจ๋งคือมันคำนวณคณิตศาสตร์ในแรมบน Serverless ตรงๆ เลยเร็วมาก ใช้เวลาไม่ถึง 1 มิลลิวินาที โดยไม่ต้องเสียตังค์เช่า DB หรือยิงคิวรีข้ามคลาวด์ไปมาเลยครับ

และถ้าวันไหน API หรือโมเดล Embedding ล่ม ระบบหลังบ้านก็จะ Fallback กลับไปดึงบริบทจากไฟล์ knowledge_base.yaml แบบเดิมมาใช้ตอบแทนทันทีกันเหนียวไว้ให้เรียบร้อยแล้วครับ

รอบหน้าถ้ามีอะไรแปลกๆ มาเล่นอีก เดี๋ยวจะมาอัปเดตต่อใน Playground นี้ละกันนะ 🤣��มก็มีแนวทางในอนาคตในการสร้าง vector db ที่ฉลาดกว่านี้นิดหน่อย ที่พอนึกออก เช่น

  • ใช้ AI ที่ฉลาดมาก ๆ เช่น gemini-pro ในการ parse PDF ให้อยู่ในลักษณะที่ดีพอที่จะแยกตารางกราฟ, แผนผังต่าง ๆ แล้วเขียนลงเป็น text ได้ ผมจะไม่ทำการ parse pdf ตรง ๆ เพราะความหมายของกราฟจะหายหมด
  • ใช้ AI ในการตัดคำดี ๆ ให้เป็นธรรมชาติก่อนแยก chunk เพราะตอนนี้แยกดิบ ๆ ตามจำนวนตัวอักษร
  • ถ้าจำเป็นผมก็เปลี่ยน gemini-flash ที่ใช้ embed-search เป็น gemini-pro มันด้วยซะเลย แค่มันช้ากว่า และเปลือง

TOON Format

นอกจากนี้ ผมยังไปเจอโปรเจคภาษาใหม่ (?) เรียกว่า format ใหม่ละกันที่มันมาแทน JSON, YAML ได้ ซึ่งก็คือ TOON มันก็คือโครงสร้างไฟล์ Object ที่เอาไว้ทำงานประเภทคู่ข้อมูล key:value ได้นั่นแหละ แต่มันสร้างมาเพื่อให้ AI ประหยัด token เวลารับ-ส่งข้อมูลไปประมวลผล

Accuracy | when using gemini-2.5-flash

Gemini 2.5 Flash
→ TOON           ██████████████████░░    87.6%
  CSV            █████████████████░░░    86.2%
  JSON compact   ████████████████░░░░    82.3%
  YAML           ████████████████░░░░    79.4%
  XML            ████████████████░░░░    79.4%
  JSON           ███████████████░░░░░    77.0%

Efficiency Ranking (Accuracy per 1K Tokens) | across 4 LLMs

→ TOON           ████████████████████     73.9%   │  2,744 tokens
  JSON compact   █████████████████░░░     70.7%   │  3,081 tokens
  YAML           ██████████████░░░░░░     69.0%   │  3,719 tokens
  JSON           ███████████░░░░░░░░░     69.7%   │  4,545 tokens
  XML            ██████████░░░░░░░░░░     67.1%   │  5,167 tokens

ตามด้านบนเลย เขาว่างั้นนะ

เอ้อ แล้วก็ลืมบอกเลยว่าผมใช้ gemini-2.5-flash สำหรับทำทุกอย่าง ซึ่งมันก็ได้ 350 request ต่อวันมั้ง โดยประมาณ เพียงพอเล็ก ๆ น้อย ๆ แหละ

agent-test

AI สามารถตอบคำถามทั่วไปได้ดี ใช้เวลาประมวลผลเฉลี่ย 3-5 วินาที

Neon DB และการเก็บ Log

และยังไม่วาย เพื่ออัปเกรด User Experience เรื่อย ๆ ผมก็ได้เปิด neon db (คือ netlify ที่ผมไป deploy เว็บไว้มันแนะนำมาว่า ใช้ง่ายเชื่อมง่าย one click setup) โดยที่ neon db เป็น Postgres SQL นั่นแหละ ใช้คำสั่งได้เหมือนกันหมดเลย

ผมเอาไว้เพื่อให้มัน LOG ตัวคำถาม-คำตอบระหว่าง user - ai agent เพื่อเอาไว้สร้าง “คำถามที่พบบ่อย” หรือเอาไว้ให้แม่ผมคอยมาบรีฟ AI เพื่อเพิ่มคำตอบที่เป็นมนุษย์และมีประสบการณ์

neon-db

หน้าตา LOG หลังบ้านบน NEON DB อย่าสนใจว่ามันซ้ำ ๆ นะ คือผมเทสมัน 5555 เว็บไม่มีคนใช้อื่นนอกจากผม

ปัจจุบันฟีเจอร์หลัก ๆ ถือว่าเสร็จสมบูรณ์ งานที่เหลือคือการ Fine-tune และปรับปรุงข้อมูลให้ถูกต้อง รวมถึงเรื่อง Ethics และความรับผิดชอบหาก AI ให้ข้อมูลที่ผิดพลาด

สามารถเยี่ยมชมเว็บไซต์ (ที่ยังไม่มีโดเมน) ได้ที่: fonladda.netlify.app

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


อัปเดตล่าสุด: 2024-12-03

ขออัปเดตความคืบหน้าเพิ่มเติมของการพัฒนาเว็บไซต์ครับ

ฝั่ง Apiratchai.netlify.app

  • เพิ่มให้ Saab AI สามารถตอบคำถามที่เป็น domain knowledge, common knowledge ได้ คือเดิมทีมันเป็น gemini ที่ทำได้อยู่แล้ว ผมแค่ไป “ห้าม” มันไว้เพราะกลัวคนจะถามไปทั่วมากกว่า แต่ตอนนี้น้องสามารถตอบคำถามที่เกี่ยวข้องกับโพสต์ได้แล้ว แต่ถ้าถามไปทั่ว น้องก็ไม่สามารถตอบได้อยู่ดี

  • ตอนนี้น้องสามารถจดจำแชทได้ 4 ข้อความก่อนหน้า ดังนั้นการถามตอบแชทไปเรื่อย ๆ จะเป็นธรรมชาติมากขึ้น โดยที่ได้ลองเทสดูแล้วพบว่า token ที่เสียไปก็ไม่ได้มากขึ้นเท่าไรในการเก็บเป็น history prompt แบบนี้มันก็มีเด่น

  • อัปเดต personality ให้น้องนิดหน่อย บุคลิกน่าจะสม่ำเสมอมากขึ้น ไม่เปลี่ยนไปมา

saab can now remember chat history

น้อง Saab มีความทรงจำแล้ว ถามตอบต่อเนื่องได้

ฝั่ง Fonladda.netlify.app

  • ตอนนี้การแปลง PDF จะไม่ใช่การใช้ JS เพื่อ parse เป็น text ตรง ๆ แล้ว เพราะถึงจะได้ตัวอักษรที่ถูกต้อง 100 % แต่ก็ไม่สามารถเข้าใจตาราง หรือแผนผังได้อยู่ดี ดังนั้นผมจึงเลือกใช้ multimodal AI แบบพวก gemini-pro ที่สามารถมองเห็นรูปและอ่าน PDF ได้จริง ๆ โดยให้ gemini ถอดคำ,อธิบาย และสรุปความเชื่อมโยงของตาราง,แผนผังออกมาในรูปแบบที่ AI ตัวอื่นเข้าใจได้ แล้วจึง output เป็น text อีกรอบ
    • gemini-pro แม้ว่าจะเปลืองมาก แต่ก็รันแค่ 1 รอบต่อ 1 เอกสารแล้วใช้ยาว ๆ เพราะเก็บเป็น text ไว้ ส่วนตัวที่ทำ embedding, search จะยังเป็น gemini-flash
    • เลือกใช้การ Chunk แบบฉลาดมากขึ้น เลือกตัด chunk ไม่ให้ส่วนที่เกี่ยวข้องกันแยกออกจากกัน
  • แก้ performance, บางรูปภาพให้เป็น lazy load หากไม่ได้อยู่ใน viewport

แผนในอนาคต

สำหรับ Apiratchai.netlify.app น่าจะถูกใช้เป็น playground ส่วนตัวเล่นไปอีกนาน ซึ่งหลาย feature ที่จะใช้ในโปรเจคอื่น จะสามารถพบแอบ ๆ ได้ที่นี่ ฟีลเหมือน RedHat Enterprise Linux (RHEL) ที่จะมี Fedora ปล่อยอัปเดตออกมาก่อนเสมอ (ฮา)

โดยสิ่งที่ผมสนใจตอนนี้ก็จะมีพวก

  • Web assembly (Wasm) การเอาพวกภาษาอื่น ๆ นอกจาก JS มารันบน browser ให้ได้ performance แบบเกือบจะ native
  • Self-hosting: อยากลองเช่า Private server บน cloud แล้ว deploy web เองดู โดยน่าจะได้ฝึกพวก Docker ด้วย เผลอ ๆ จะ CI/CD กับ Github action ให้มัน build dockerfile อีก ตอนนี้กำลังมอง ๆ free tier ของ Oracle ไว้อยู่ (เพิ่งเข้าหุ้น Oracle ไปก่อนไตรมาส 1 week, lol)
  • Mini-projects: ทำโปรเจคขำ ๆ อะไรซักอย่างเพิ่มบนเว็บนี้ เอาไว้ใช้เฉพาะงาน, เฉพาะเทศกาล แต่ไม่รบกวนประสบการณ์การใช้งานส่วนอื่น

AI ตามสั่ง: รีวิวทุก AI ที่ยัดใส่ใน chatbot

ล่าสุดผมเจอปัญหาใหญ่คือเจ้า Gemini Flash (Native Google) ที่ใช้อยู่มันติด Rate Limit (429 Too Many Requests) รายวันบ้าง รายนาทีบ้าง ก็เลยเป็นที่มาของการรื้อระบบ RAGChat ใหม่หมด เปลี่ยนจากการพึ่งพา Google เจ้าเดียว มาเป็นการใช้ API ของมหาลัย (KKU AI API) ที่รวมมิตรโมเดลตัวตึงระดับโลกไว้เพียบ

ไหน ๆ ก็ทำระบบ Model Selector ให้เลือกใช้กันได้แล้ว ผมเลยขอมารีวิว “ความรู้สึกหลังการใช้งาน” ของแต่ละตัวแบบไม่อวย (มั้ง) เผื่อใครอยากรู้ว่าตัวไหนเก่งด้านไหนครับ

1. Gemini 2.5 Flash (Google Native) ตัวนี้จริง ๆ เป็น flash-latest หมายถึง alias ไปโมเดลใหม่ตลอด

  • ความเร็ว: เร็วมาก (Native Speed)
  • จุดเด่น: เป็นตัว Default ของระบบ เพราะมันตอบสนองไวที่สุด เหมาะกับงาน RAG ที่ต้องพ่น text เยอะ ๆ Context Window ใหญ่ยัดข้อมูลได้ไม่อั้น
  • ข้อเสีย: Rate Limit โหดร้าย คือมันแชร์กันหลายโปรเจคจนเหลือนิดเดียวให้ใช้

2. Gemini 2.5 Flash (KKU API) ร่างโคลนคนขอนแก่น

  • ความเร็ว: เร็วพอกัน ถ้าจะมีอะไรช้าคือวิ่งเข้า endpoint kku
  • รีวิว: นี่คือตัว Fallback เบอร์ 1 ของผม ไส้ในมันคือตัวเดียวกับข้างบน แต่วิ่งผ่าน KKU แทนเฉย ๆ ฟีลเดียวกันแหละ

3. DeepSeek V3.1 (KKU) AI เจ๊ก

  • ความเร็ว: เร็วนะว่าไป
  • รีวิว: ตัวนี้เก่งโคตร ๆ โดยเฉพาะเรื่อง Logic & Coding ถ้าถามอะไรที่เป็นเชิงตรรกะ หรือให้เขียนโค้ด ตัวนี้บางทีตอบดีกว่า Gemini อีก แถมราคา (ในตลาดจริง) ก็ถูกกว่า ถือเป็นตัวเลือก “Second Best” ที่น่ากลัวมาก

4. Llama 4 Maverick (KKU) ของ META มั้ยวะอันนี้

  • ความเร็ว: กลาง ๆ เร็วบ้าง
  • รีวิว: ถ้าชอบอ่านอะไรยาว ๆ น้ำเยอะ ๆ เนื้อแน่น ๆ ต้องตัวนี้ น้องจะมีความ “เวิ่นเว้อ” ในทางที่ดี คืออธิบายละเอียดมาก ยกตัวอย่างเก่ง ใครถามสั้น ๆ แต่หวังคำตอบระดับเรียงความส่งครู เลือกตัวนี้ครับ

5. Grok 4.1 Fast (KKU) ตัวตึงสายปั่น แม่งเอย

  • ความเร็ว: เร็วแหละ
  • รีวิว: โมเดลจากค่าย X (Twitter) ของพี่ Elon จุดเด่นคือ… มันถูก (Cheap) เอาจริงๆ คือมันทำงานเร็วใช้ได้เลย เหมาะกับถามอะไรที่ไม่ซีเรียสมาก หรือต้องการคำตอบที่ดูเป็นกันเอง ไม่ทางการจ๋าเหมือนพวก GPT

6. Claude Haiku 4.5 (KKU) ฉายา: นักเล่าเรื่อง

  • ความเร็ว: ปานกลาง
  • รีวิว: ถ้าต้องการคำอธิบายที่อ่านแล้ว “ลื่นไหล” ภาษาเป็นธรรมชาติที่สุด ต้องยกให้ตระกูล Claude แม้ตัวนี้จะเป็นรุ่นเล็ก (Haiku) แต่สกิลการสรุปความและการใช้ภาษาคือที่สุด ใครชอบอ่านบล็อก หรือให้สรุปเนื้อหา ตัวนี้เหมาะ

7. GPT-5 Mini & Qwen 3 มาเอารัย

  • GPT-5 Mini: ตัวนี้ยังรู้สึกเฉย ๆ ความฉลาดตามมาตรฐาน OpenAI แต่พอมันเป็นรุ่น Mini ความรู้สึกมันเลยกั๊ก ๆ ไม่สุดสักทาง สู้ไปใช้ Gemini Flash หรือ DeepSeek สนุกกว่า
  • Qwen 3: ตัวนี้จาก Alibaba ช้าครับ… ช้าแบบสัมผัสได้ แต่ข้อดีคือ “อ่านง่าย” ภาษาเรียบเรียงมาดี แต่อย่างว่าแหละ ในโลกของ Chatbot ถ้าช้า = บาป ควรตกนรกบึงไฟ

บทสรุป: ระบบกันตาย (Fallback System)

จากรายชื่อข้างบน ผมเลยออกแบบ Logic ให้มันทำงานร่วมกันซะเลย:

  1. เปิดด้วย Google Native: เพราะไวสุด ประหยัดโควต้ามหาลัย
  2. ถ้าดับ: สลับไป KKU Gemini ทันที (User แทบไม่รู้ตัว)
  3. ถ้ายังไม่รอด: ไหลไป DeepSeek (ตัวเทพ) -> Llama (ตัวละเอียด) -> Grok (ตัวเร็ว)

พร้อมทำ Status Bar สีเหลือง ๆ เตือนด้านบนด้วยว่า “ตอนนี้ฉันเปลี่ยนร่างอยู่นะ” เพื่อความโปร่งใส (Transparency) สุด ๆ

// AIChat.tsx: ส่วนที่คอยเช็คว่าถ้ามี Log มากกว่า 1 บรรทัด (แปลว่ามีการ Fallback)
// ให้แสดงแถบสีเหลืองแจ้งเตือน User ทันที
{debugLogs.length > 1 && (
    <div className="debug-logs">
        {debugLogs.map((log, i) => (
            <div key={i} className="log-entry">
                {/* ถ้า Log มีคำว่า failed ให้ใส่ไอคอน ⚠️ ถ้าสำเร็จใส่ ✅ */}
                {log.includes('failed') ? '⚠️ ' : '✅ '}
                {log}
            </div>
        ))}
    </div>
)}

ใครอยากลองของก็จิ้มเปลี่ยนเล่นได้ที่มุมซ้ายบนของกล่องแชทนะครับ (ถ้ามันยังไม่พังนะ 🤣)

อัปเดต User Experience

⚠️ [อัปเดตสำคัญ - ฟีเจอร์วิเคราะห์รูปภาพถูกยกเลิกใช้งานแล้ว]
น่าเสียดายที่ปัจจุบันฟีเจอร์การส่งรูปภาพ (Image Asking) ไม่สามารถใช้งานได้อีกต่อไป เนื่องจากเหตุผลหลักสองประการ:

  1. การจำกัดโควต้าของค่ายหลัก: ทาง Google ได้ทำการปิดและปรับเปลี่ยนการใช้งานโมเดล Multimodal ฟรีบนระบบ AI Studio ไปเป็นแบบมีค่าใช้จ่าย
  2. ข้อจำกัดของการส่งผ่าน Proxy: การส่งรูปภาพผ่าน API Fallback ของมหาวิทยาลัย (KKU Proxy) ประสบปัญหาทางเทคนิค เนื่องจากข้อมูล Image Payload ขนาดใหญ่ (Base64) มักจะถูกบีบอัด คัดกรอง หรือตัดออกจากโครงสร้างทราฟฟิกเพื่อประหยัดแบนด์วิธของโครงข่ายหลัก ทำให้โมเดลปลายทางไม่ได้รับรูปภาพจริงและไม่สามารถวิเคราะห์รูปภาพได้

เพื่อลดอาการ Error และเพิ่มความเสถียรสูงสุดในการเปลี่ยนถ่ายโมเดล (Fallback Chain) ระบบบอท Saab จึงได้ทำการปรับลดฟังก์ชันกลับมาเป็นระบบสนทนาด้วยข้อความ (Text-based) 100% เพื่อรับประกันความเร็วระดับมิลลิวินาทีและความลื่นไหลในการใช้งาน

  • อีกระบบคือตอนนี้ AI chatbot เราสามารถย่อ-ขยายขนาดหน้าต่างได้แล้ว ทำให้แชทสนุกขึ้น กดได้ที่ปุ่ม มุมขวาบนหน้าต่างแชทเลย

  • และเพื่อรองรับการใช้งานถาม-ตอบขั้นสุด ต่อไปนี้เราสามารถคลุมดำข้อความที่สนใจจากโพสต์ แล้วกด ask ai ได้ทันที

askai

คลุมดำแล้วกด Ask AI

askAIchat

เราสามารถพิมพ์คำถามต่อได้ทันที โดย AI จะตอบตาม context ที่เราถาม
  • แน่นอนว่าสืบเนื่องจาก feature ก่อนหน้า ทำให้เราก็มีระบบที่ทำให้ reply ข้อความของทั้ง AI และ user เองได้ด้วย ทำให้ AI เข้าใจบริบทมากขึ้นว่าเรากำลังถามย้อนถึงข้อความไหน

AIreply

เห็นได้เลยว่า AI สามารถ reply ข้อความของทั้ง AI และ user เองได้ด้วย ทำให้การใช้งานง่ายขึ้นไปอีก

  • และตอนนี้เรามีระบบ emote, comment รวมถึงปุ่มแชร์แล้ว โดยที่หลังบ้านเก็บไว้บน PostgreSQL ของ NeonDB

comments

ระบบ comment , emote และแชร์

อัปเดตล่าสุด: รื้อระบบ RAG ด้วย Static Vector (2026-05-21)

มีอัปเดตใหญ่ตรงส่วนของบอท Saab บนเว็บนี้ครับ

เดิมทีบอทจะอ่านบริบทจากไฟล์ knowledge_base.yaml ตัวเดียว ซึ่งพอเขียนบล็อกเยอะขึ้นเรื่อยๆ (บางโพสต์ล่อไป 2,000 กว่าบรรทัด) ไฟล์มันก็เริ่มใหญ่เกินไปจนบวม คุยแชททั่วไปทีนึงนี่เปลือง Token มาก แถมถ้าคนใช้คลุมดำถามคำสั้นๆ แค่ไม่กี่คำ บอทก็มักจะหลอนเพราะไม่มีบริบทแวดล้อมแถมไปให้

จะไปเช่า Vector Database แยกมาต่อสำหรับเว็บบล็อกธรรมดาก็เกินเรื่องไปหน่อย (ตัวฟรีแบบพวก Qdrant, Pinecone ก็มักจะระเบิด vector เราทิ้งถ้าไม่ active นาน) แถม Netlify Serverless ที่ไม่อนุญาติให้เรารัน backend เป็น database service ได้

สุดท้ายเลยแก้ปัญหาด้วยการทำ Static Vector Search แทน:

graph TD
    A[เขียนบล็อกใหม่ / Push Git] --> B[Netlify สั่ง Build]
    B --> C[รันสคริปต์ build-knowledge.ts]
    C -->|หั่นบทความเป็นย่อยๆ 800 ตัวอักษร| D[ย่อยบทความ]
    D -->|เรียก Gemini gemini-embedding-2| E[ได้เวกเตอร์ 3,072 มิติ]
    E -->|บันทึกเป็น JSON นิ่งๆ| F[post_embeddings.json]
    F -->|มัดรวมขึ้นระบบ| G[Netlify Serverless]
  1. คำนวณเวกเตอร์ล่วงหน้าตอน Build: ผมเขียนสคริปต์ build-knowledge.ts ขึ้นมาตัวนึง ให้มันลูปดึงเนื้อหาบล็อกทุกอันมาซอยย่อยเป็นส่วนๆ (Chunks) ส่วนละ 800 ตัวอักษร แล้วส่งไปดึงเวกเตอร์ขนาด 3,072 มิติจากโมเดล gemini-embedding-2 ของค่าย Google
  2. เซฟเป็น Static JSON: จากนั้นก็นำเวกเตอร์ของทุก Chunks มาเขียนเซฟเก็บเป็นไฟล์ JSON ดิบๆ ชื่อ post_embeddings.json ในโปรเจกต์เลย (ตอนนี้ไฟล์มีความยาว 5 แสนกว่าบรรทัด ขนาดประมาณ 10MB) แล้วอัปโหลดขึ้นคู่กับโค้ดเว็บตอนดีพลอย

Static vector build knowld

ด้านบนคือเอามายัดใส่ yaml, ด้านล่างคือ embed ลง vector

ประสิทธิภาพตอนรันจริง (Runtime Execution)

พอเอาไฟล์ JSON ขึ้นไประบบ Serverless แล้ว วิธีการทำงานตอนผู้ใช้พิมพ์ถามบอทจะเป็นแบบนี้:

  • ดึงย่อหน้าต้นทาง (สำหรับ Ask AI): เวลาผู้ใช้คลุมดำแล้วกด Ask AI ตัวแอปฝั่งหน้าบ้าน (AIChat.tsx) จะทำการสืบค้นหา DOM ต้นทางขึ้นไปจนเจอ Tag Parent (เช่น <p>, <li>, <code>) เพื่อลอกเอา ทั้งย่อหน้า+บริบทแวดล้อมที่ผู้ใช้เลือกพ่วงไปด้วยเสมอ พร้อมส่งข้อมูลบทความของหน้านั้นไปเป็น Context ชุดแรก บอทเลยตอบคำถามได้ตรงจุดเป๊ะๆ
  • คำนวณ Cosine Similarity ในหน่วยความจำ (สำหรับคำถามทั่วไป): เวลาพิมพ์คุยทั่วไป ระบบหลังบ้านบน Netlify Serverless Function จะแปลงคำถามผู้ใช้เป็นเวกเตอร์ จากนั้นก็โหลดไฟล์ post_embeddings.json ขึ้นมาในแรมชั่วคราว แล้วรันสูตร Cosine Similarity (การหาความใกล้เคียงของเวกเตอร์) เปรียบเทียบหาย่อหน้าที่ตรงที่สุดกับคำถามในแรมของตัวมันเองทันที
    • ผลลัพธ์คือมันเร็วมาก: เนื่องจากการคำนวณเวกเตอร์เกิดขึ้นในแรมของเครื่องตรงๆ โดยไม่ต้องยิง API ข้ามไปหา Database ที่อื่น การประมวลผลจึงเสร็จสิ้นในเวลา ไม่ถึง 1 millisecond (กล่าววาจาโป้ปดสมสู่อาชา) เร็วกว่า Vector DB บนคลาวด์ทั่วไปหลายเท่าตัว แถมได้ RAG ประสิทธิภาพสูงใช้งานฟรี ไม่มีวันหมดอายุ
  • ระบบสำรอง (Fallback): ถ้าคีย์ Google หรือระบบสร้างเวกเตอร์เกิดมีปัญหาขึ้นมา ตัวระบบหลังบ้านจะสลับไปดึง Context แบบเดิมจากไฟล์ knowledge_base.yaml ให้ทันที แชทบอทเลยไม่มีวันค้างหรือตอบผิดพลาด

อัปเดตล่าสุด: เปลี่ยนไปค้นหาด้วยเวกเตอร์ในเครื่อง (2026-07-31)

Static vector แบบข้างบนอยู่ได้ไม่นานก็โดนรื้ออีก เพราะยังติดปัญหาอยู่สามอย่าง คือยังต้องจ่ายค่า API ของ Google ตอน build ไฟล์เวกเตอร์สิบกว่าเมกะต้องโหลดขึ้นแรมทุกครั้งที่มีคนถาม และตอนลองใช้วิธีนับคำก็ใช้กับภาษาไทยไม่ดี เพราะไทยไม่มีช่องว่างระหว่างคำ ทำให้จับคู่ได้แค่ตัวอักษรที่เหมือนกัน แต่ความหมายคนละเรื่อง

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

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

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