Tech Blog

สิ่งที่ Gemini มีแต่ผมไม่มี — รู้ตัวก็ต่อเมื่อใช้งานจริง

RAG Gemini Grounding my-rag-brain Python

สิ่งที่ Gemini มีแต่ผมไม่มี — รู้ตัวก็ต่อเมื่อใช้งานจริง

สิ่งที่รู้ได้เมื่อลองรันจริงเท่านั้น

เริ่มใช้งาน my-rag-brain จริงๆ แล้วพบสิ่งที่ไม่สะดวก

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

ไม่มี file path ไม่มีเบาะแสนอกจากวันที่


นี่คือช่องว่างการออกแบบเทียบกับ Gemini

ตรวจสอบ spec ของ Gemini

Gemini มี RAG Engine, MemoryCorpus และ Sources: Previous chats ไม่เพียงแค่ค้นหาบทสนทนาเก่า แต่ยังส่งคืนบทสนทนาเองในฐานะแหล่งที่มาได้ “ข้อมูลนี้มาจากไหน” ถูกเก็บรักษาไว้เสมอผ่าน Grounding

นั่นคือสิ่งที่ขาดหายไปในระบบของผม

vault (entry ที่กลั่นกรองแล้วเช่น lesson และ conclusion) ขาดการเชื่อมต่อกับไฟล์บทสนทนาต้นฉบับ การกลั่นกรองบีบอัดข้อมูลแต่ตัดแหล่งที่มาออก


นำ source_origin มาใช้งาน

การเปลี่ยนแปลงครอบคลุม 4 ไฟล์ ก่อนอื่นสรุปบทบาทของแต่ละไฟล์:

ไฟล์บทบาท
store.pyชั้นเขียนข้อมูลลง ChromaDB จัดการ metadata ของ chunk ทั้งหมด
note_writer.pyจุดเข้าสำหรับการบันทึก ถูกเรียกจาก MCP server และ CLI เรียก store.py
distill_v2.pypipeline ที่เลื่อนโน้ตดิบใน diary ไปเป็น Lesson/Conclusion
server.pyตัว MCP server หลัก มี logic การค้นหาและแสดงผลสำหรับ search_memory

흐름การเขียนคือ distill_v2.py → note_writer.py → store.py → ChromaDB และการอ่านคือ server.py → ChromaDB

① store.py — เพิ่ม field ใน ChromaDB metadata

เพิ่ม source_origin ใน metadata ของทุก chunk ที่เขียนลง ChromaDB

meta = {
    "source_type":   source_type,
    "source_file":   str(path),
    "source_ref":    source_ref,
    "source_origin": source_origin,  # บันทึก path ของไฟล์ต้นทาง
    "title":         title,
    "date":          date_str,
    ...
}

② note_writer.py — เพิ่ม argument ให้ฟังก์ชันเขียน

เพิ่ม source_origin เป็น argument ใน write_note() และส่งต่อไปยัง store.py

def write_note(text: str, source_type: str, ref: str = "",
               priority: str = "", source_origin: str = ""):
    ...
    ingest_file(str(target_file), source_type, ..., source_origin=source_origin)

③ distill_v2.py — ส่ง path ไฟล์ต้นทางตอนกลั่นกรอง

เมื่อเขียนเนื้อหา diary ที่กลั่นกรองแล้วลงใน vault จะส่ง path ไฟล์ diary ต้นทางเป็น source_origin ทำให้บันทึกได้ว่าบทเรียนนั้นมาจากบทสนทนาวันใด

write_note(
    text=body,
    source_type=note_type,
    ref=f"distill_v2:{source_date}:{time_str}",
    source_origin=str(md_path),  # เช่น data/diary/2026-05-27.md
)

④ server.py — แสดงแหล่งที่มาในผลการค้นหา

เมื่อประกอบผลลัพธ์ search_memory ถ้ามี source_origin จะแสดงท้ายผลลัพธ์

source_origin = meta.get("source_origin", "")
...
if source_origin:
    lines.append(f"📄 ที่มา: {Path(source_origin).name}")

ตอนนี้ผลการค้นหาแสดง 📄 ที่มา: 2026-05-27.md ท้ายรายการ มีเส้นทางกลับไปยังบทสนทนาต้นฉบับแล้ว


ทำไม Grounding ถึงสำคัญ

มีบันทึกอยู่แล้ว แต่ถ้ากลับไปดูบริบทของบันทึกไม่ได้ ความรู้ก็กลายเป็นแค่ป้ายกำกับที่โดดเดี่ยว

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

Gemini ไม่ปล่อยแหล่งที่มาทิ้ง ระบบของผมก็ควรเป็นแบบนั้น


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

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

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