Tech Blog

AI ไปถึงแนวคิดการออกแบบเดียวกับที่ผมมี

RAG Gemini AI my-rag-brain CRAG

AI ไปถึงแนวคิดการออกแบบเดียวกับที่ผมมี

วันที่กลับมาหลังหายไปหนึ่งเดือน

ผมเปิด Gemini เป็นครั้งแรกในรอบนาน

มีเรื่องที่ค้างคาใจมาก่อนหน้านี้ ขึ้นอยู่กับคำถาม Gemini บางครั้งพูดเรื่องผิดๆ ด้วยความมั่นใจเต็มเปี่ยม — ที่เรียกว่าฮาลูซิเนชัน แต่พอกลับมาใช้หลังจากหายไปหนึ่งเดือน มันหายไปแล้ว

คนส่วนใหญ่คงไม่สังเกตความเปลี่ยนแปลงนี้ แค่รู้สึกว่า “ช่วงนี้ดีขึ้นนะ” แล้วก็ผ่านไป

อาจเป็นโรคอาชีพของวิศวกรระบบ แต่ผมสนใจช่วงเวลาที่ข้อบกพร่องที่รู้จักได้รับการแก้ไขมากกว่าตอนที่ระบบทำงานปกติ ฮาลูซิเนชันคือ “บั๊กที่รู้จักอยู่แล้ว” ถ้ามันถูกแก้ไข ผมต้องรู้ว่าอะไรเปลี่ยนไป


ตรวจสอบการออกแบบ

ผมถาม Gemini ตรงๆ ว่าอะไรเปลี่ยนไป

คำอธิบายที่ได้รับคือ: การรวม RAG (Retrieval-Augmented Generation) ซึ่งค้นหาข้อมูลล่าสุดก่อนสร้างคำตอบ เข้ากับ Grounding ที่อ้างอิงแหล่งที่มาอย่างชัดเจน ช่วยลดการเดาโดยไม่มีหลักฐาน

นั่นคือแนวคิดการออกแบบเดียวกับ my-rag-brain ของผมเอง

อยากรู้มากขึ้น ผมถามเพิ่มเรื่องแหล่งที่มา คำตอบที่ได้คือ:

ผมดึงข้อมูลและอัปเดตอยู่เสมอ เนื่องจากรวมถึง Qiita ด้วย จึงสมเหตุสมผลที่จะกล่าวว่ามีความเป็นไปได้ที่ข้อมูลของคุณได้กลายเป็นส่วนหนึ่งของผม

บทความที่ผมเคยเขียนใน Qiita อาจวนเวียนมาเป็นส่วนหนึ่งของ Gemini ยืนยันไม่ได้ แต่เป็นเรื่องที่น่าสนใจ


นำการออกแบบของ Gemini มาใส่ใน my-rag-brain

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

CRAG (Corrective RAG)

เพิ่มเกตประเมินคุณภาพผลลัพธ์การค้นหาจาก ChromaDB ทันทีหลังดึงข้อมูล chunk ที่มีระยะทางโคไซน์เกิน 0.55 จะถูกตัดสินว่า “poor” และไม่ให้ผ่าน

POOR_MATCH_THRESHOLD = 0.55

_best_dist = min(_type_min_dist.values()) if _type_min_dist else None
_poor_quality = _best_dist is not None and _best_dist > POOR_MATCH_THRESHOLD

เมื่อตัดสินว่า poor จะมีกลไก fallback ที่ลบ type filter แล้วค้นหาข้ามประเภท เพื่อช่วยกรณีที่เนื้อหาที่ถูกต้องถูกบันทึกไว้ใน type อื่น

Adaptive Retrieval Depth

การค้นหาทุกคำถามด้วยความลึกเท่ากันนั้นไม่มีประสิทธิภาพ จึงเพิ่มการให้คะแนนความซับซ้อนเพื่อสลับระหว่าง top 5 / 7 / 10 แบบไดนามิก

def _calc_adaptive_top(query: str, user_top: int) -> tuple[int, str]:
    score = 0
    if len(query) > 60:     score += 2
    elif len(query) > 30:   score += 1
    # เพิ่มคะแนนจาก marker เช่น "และ" "เปรียบเทียบ" "ครั้งก่อน"
    if score >= 3: return 10, f"wide(score={score})"
    elif score >= 1: return 7, f"mid(score={score})"
    return 5, ""

Tiny-Critic RAG

ถ้า CRAG คือเกตคุณภาพ Tiny-Critic คือตัวกรองความเกี่ยวข้อง โมเดลขนาดเล็กนี้ตัดสินแบบไบนารีว่า chunk ที่ผ่านเกตคุณภาพมาแล้วนั้น เกี่ยวข้องกับคำถามจริงๆ หรือไม่ และลบอันที่ไม่เกี่ยวออก รายละเอียดการฝึกอยู่ในบทความแยก

ปฏิเสธ Self-RAG

Self-RAG คือสถาปัตยกรรมที่ประเมินคุณภาพผลลัพธ์เองและค้นหาซ้ำหากจำเป็น แต่ผมไม่ใช้ ในโครงสร้างที่ Claude Code เรียกผ่าน MCP server ตัว Claude Code เองทำหน้าที่เกตอยู่แล้ว จึงซ้ำซ้อน

ไปป์ไลน์ทั้งหมดที่นำมาใช้:

graph TD
    A[คำถาม] --> B[Adaptive Retrieval Depth<br/>กำหนด top 5 / 7 / 10 ตามคะแนนความซับซ้อน]
    B --> C[ChromaDB Search<br/>ดึงผู้สมัครด้วยความคล้ายคลึงเวกเตอร์]
    C --> D{CRAG Quality Gate<br/>dist > 0.55?}
    D -->|ผ่าน| E[Tiny-Critic<br/>ลบ chunk ที่ไม่เกี่ยวข้อง]
    D -->|Poor| F[CRAG Fallback<br/>ค้นหาซ้ำโดยไม่มี type filter]
    F --> E
    E --> G[สร้างคำตอบ]

ทำไมถึงเขียนเรื่องนี้

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

ครั้งนี้ ผมพบแนวคิดการออกแบบซ่อนอยู่ในสัญญาณรบกวนจากพฤติกรรมที่เปลี่ยนไปของ AI แม้แต่ AI ก็ไม่ใช่ข้อยกเว้น


บทความที่เกี่ยวข้อง

ส่งข้อความได้ตามสบาย

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