ตัดสินใจว่าจะ "สำรองข้อมูล" แต่ไม่เคยทำจริง จนทำลาย Vector DB ความจำไปสองครั้ง
บทนำ
ผมมี vector DB ส่วนตัวที่ดูแลอยู่ ทุกวันผมจะโยนบันทึกการทำงานและการตัดสินใจต่างๆ ลงไป แล้ว AI assistant ก็จะดึงมันกลับมาด้วยการค้นหาเชิงความหมาย เหมือนเป็นความจำระยะยาวที่ฝากไว้ภายนอกตัวเอง แนวคิดการออกแบบเบื้องหลังนี้ผมเขียนไว้ใน คืนที่ AI แก้ไขฉัน 5 ครั้ง - ปรัชญาการออกแบบเพื่อให้ RAG ส่วนตัวเป็นเพื่อนคุณเป็นเวลา 5 ปี บทความนี้ไม่ใช่เรื่องการออกแบบนั้น แต่เป็นเรื่องอุบัติเหตุที่เกิดขึ้นระหว่างการใช้งานจริง
ขอวางข้อสมมติฐานสองข้อไว้ก่อน ถ้าไม่มีสองข้อนี้ ทั้งความหมายของอุบัติเหตุและความตื่นตระหนกในตอนนั้นจะสื่อออกมาไม่ได้
ข้อสมมติฐานที่ 1: เนื้อหาข้อมูลเก็บไว้ในมือตัวเอง ไม่ฝากให้บุคคลที่สาม เนื้อหาเป็นบันทึกส่วนตัว ดังนั้นนโยบายคือไม่ส่งออกไปข้างนอกตามสภาพเดิม โค้ด (กลไก) อยู่ภายใต้ version control แต่ตัวบันทึกเอง — คือข้อมูล — ไม่ได้ซิงค์กับภายนอกเลย นโยบายนี้ถูกต้อง แต่ในตอนนั้นผมทำมันแบบ “อยู่ในมือตัวเองทั้งหมด” เพียงอย่างเดียว จึงไม่มีตาข่ายนิรภัยแบบที่คลาวด์หรือประวัติ Git จะ “ย้อนกลับไปสถานะก่อนหน้าโดยอัตโนมัติ” ให้กับข้อมูลนี้ได้เลย จะย้อนกลับได้หรือไม่ขึ้นอยู่กับว่าตัวผมเองได้สร้างสิ่งที่สำรองไว้จริงหรือเปล่าเท่านั้น อาจฟังดูเวอร์ แต่ข้อมูลนี้คือความทรงจำของตัวเองที่ฝากไว้ภายนอก ไม่มีอะไรมาแทนได้
ข้อสมมติฐานที่ 2: DB นี้ดูแลร่วมกับ AI coding assistant งานย้ายข้อมูลหรือจัดระเบียบประจำวันส่วนใหญ่ให้ assistant เป็นคนลงมือทำ ดังนั้นผมจำเป็นต้องออกแบบและปรับแต่ง (tuning) “ว่า assistant จะทำงานอย่างไร” ไว้ล่วงหน้า อุบัติเหตุครั้งนี้เกิดขึ้นเพราะการปรับแต่งนั้นยังไม่เพียงพอ
DB นั้นพังไปสองครั้ง ทั้งสองครั้งพังด้วยวิธีเดียวกัน และเป็นวิธีที่ป้องกันได้ทั้งคู่
เกิดอะไรขึ้น
งานที่ทำคือการย้ายข้อมูลไปยังที่เก็บอีกแห่งหนึ่ง เป็นการดำเนินการแบบทำลายล้าง (destructive operation) ตามตำรา ถ้าล้มเหลวระหว่างทาง ข้อมูลต้นฉบับอาจพังได้
ในขั้นตอนการทำงาน ผมได้ตัดสินใจไว้แล้วว่า “สำรองข้อมูลก่อนดำเนินการแบบทำลายล้าง” การตัดสินใจนี้ถูกต้องเท่าที่มันไปถึง แต่ ณ จุดนี้มันเป็นเพียง กฎการดำเนินงานที่เขียนไว้เฉยๆ (คำแนะนำ) ยังไม่มีด่านตรวจเชิงกลไกที่จะยืนยันว่ามันถูกปฏิบัติตามจริงหรือไม่ AI assistant พูดว่า “จะสำรองข้อมูลนะ” ก่อนเริ่มการย้ายข้อมูล แต่กลับดำเนินการย้ายข้อมูลต่อไปโดยไม่เคยสร้างสิ่งที่สำรองไว้จริงเลย มีกฎอยู่ แต่ไม่มีกลไกบังคับให้ปฏิบัติตาม
ทันทีที่รันการย้ายข้อมูลเสร็จ การค้นหาก็หยุดคืนผลลัพธ์ ไม่ว่าจะค้นอะไรก็ได้ผลลัพธ์เป็นศูนย์ และในที่สุด DB เองก็หยุดตอบสนองด้วย error ตอนนั้นเลือดจางลงทันที ตามข้อสมมติฐานที่ 1 ความทรงจำนี้ไม่ได้อยู่บนคลาวด์ และผมก็ยังไม่ได้สร้างสิ่งที่กู้คืนได้ไว้เลย มีอยู่หลายนาทีที่ความคิด “บันทึกหลายเดือนหายไปทั้งหมดตอนนี้เลยหรือเปล่า” ผุดขึ้นมาในหัว พูดตรงๆ ว่าตอนนั้นค่อนข้างตื่นตระหนกเลย
ลองวาดขั้นตอนเป็นไดอะแกรม จะเห็นชัดว่าอุบัติเหตุเกิดขึ้นตรงไหน
ก่อนดำเนินการแบบทำลายล้าง (การย้ายข้อมูล) ทันที
│
├─▶ สร้างสิ่งที่สำรองไว้จริง ─▶ ตรวจสอบตำแหน่งที่เก็บ ─▶ รันการย้ายข้อมูล
│ │
│ ├─ สำเร็จ ─▶ เสร็จสิ้น
│ └─ ล้มเหลว ─▶ กู้คืนจากสิ่งที่สำรองไว้ ─▶ กู้คืน (ใช้เวลาสั้น)
│
└─▶ "ตั้งใจจะทำ" แต่ไม่ได้สร้างสิ่งที่สำรองไว้จริง ─▶ รันการย้ายข้อมูล ─▶ การค้นหาพังทั้งหมด
│
└─▶ ไม่มีสิ่งที่กู้คืนได้ ─▶ กู้คืนด้วยมือ (หลายชั่วโมง)
▲
(ครั้งที่สองก็ไปตามเส้นทางด้านล่างเดิมนี้เช่นกัน)┘
มันพังอย่างไร และกู้คืนอย่างไร
ระหว่างที่ยังตื่นตระหนก ผมตรวจสอบเนื้อหาข้างในแล้วก็โล่งใจขึ้นมานิดหนึ่ง สิ่งที่พังมีแค่ดัชนีค้นหาเท่านั้น ตัวบันทึกเองยังปลอดภัยดี
DB นี้มีดัชนีแบบ HNSW เพื่อให้การค้นหาเชิงความหมายเร็วขึ้น เป็นโครงสร้างกราฟที่ไล่ตามเวกเตอร์ที่ใกล้เคียงกันเพื่อเข้าใกล้เป้าหมาย เนื่องจากมีกระบวนการที่รันคู่ขนานกันจับ DB เดียวกันแยกกัน กราฟนี้จึงกลายเป็นสถานะที่ไม่สอดคล้องกันบนดิสก์ ทำให้การค้นหาพังทั้งหมด ในขณะเดียวกัน ตัวบันทึก (เอกสารพร้อม metadata) ยังคงอยู่ครบถ้วนในที่เก็บอีกแห่งหนึ่ง สิ่งที่พังคือ “กลไกในการดึงข้อมูลออกมา” ไม่ใช่ “ตัวความทรงจำเอง”
พอเข้าใจแบบนี้แล้ว การกู้คืนก็ทำได้อย่างใจเย็น ขั้นตอนเป็นดังนี้
การค้นหาพังทั้งหมด (มีแค่ดัชนีที่เสียหาย / ตัวบันทึกยังปลอดภัย)
│
├─▶ ① สำรองไดเรกทอรี DB ทั้งหมดก่อน (เพื่อไม่ให้สูญเสียเพิ่มอีก)
├─▶ ② ส่งออกเอกสารและ metadata ทั้งหมดด้วย SQL ดิบ ─▶ ตรวจสอบจำนวน (ยืนยันว่าไม่ขาดหายแม้แต่รายการเดียว)
├─▶ ③ ลบดัชนี (collection) ที่พังแล้วสร้างใหม่
└─▶ ④ ทำ embedding ใหม่ทั้งหมดแล้วนำเข้าใหม่ ─▶ จำนวนตรงกัน การค้นหากลับมาใช้งานได้
ครั้งแรก ผมกู้คืนบันทึกทั้งหมด (ตอนนั้นมี 3,707 รายการ) ได้โดยไม่ขาดหายแม้แต่รายการเดียว แต่การสร้าง embedding ใหม่ใช้เวลาประมาณ 2 ชั่วโมง 19 นาที ข้อมูลปลอดภัยก็จริง แต่เวลาที่เสียไปไม่กลับคืนมา
แล้วอีกไม่กี่วันต่อมา ครั้งที่สองก็มาถึง เป็นการย้ายข้อมูลแบบทำลายล้างอีกงานหนึ่ง แต่พังด้วยวิธีเดียวกัน ผมกู้คืนทั้งหมด (ตอนนี้เพิ่มเป็น 4,732 รายการ) ด้วยขั้นตอนเดิม แต่ความรู้สึก “อีกแล้วเหรอ” กับความเหนื่อยล้าที่ต้องเสียเวลาไปอีกหลายชั่วโมงนั้นหนักกว่าครั้งแรกมาก การเกิดซ้ำสองครั้งไม่ใช่เรื่องบังเอิญ ผมต้องยอมรับว่ามันเป็นปัญหาเชิงโครงสร้าง
ทำไมครั้งที่สองถึงเกิดขึ้น
ผมแบ่งสาเหตุออกเป็นสองชั้น คือตัวกระตุ้นที่เห็นบนผิว และสาเหตุที่แท้จริงที่ซ่อนอยู่ข้างใต้
สาเหตุบนผิว: หลายโปรเซสแตะ DB โดยตรงพร้อมกัน
Vector DB นี้เดิมทีทำงานแบบ “โปรแกรมเปิดไฟล์ DB โดยตรง” สะดวกก็จริง แต่มีกับดัก ถ้าหลายโปรเซสต่างเปิดไฟล์ DB เดียวกันแล้วเขียนแยกกัน ดัชนีภายในอาจกลายเป็นสถานะที่ขัดแย้งกันได้ กระบวนการย้ายข้อมูลกับกระบวนการนำเข้าอัตโนมัติที่ทำงานอยู่เบื้องหลัง ต่างจับสิ่งเดียวกันแยกจากกัน การแย่งกันนี้เองที่เป็นตัวกระตุ้นให้ดัชนีเสียหาย
ครั้งแรก ผมหยุดอยู่แค่ตรงนี้ พอสร้างดัชนีใหม่แล้วการค้นหากลับมาใช้งานได้ ผมก็ถือว่า “แก้แล้ว” โดยไม่ได้ไล่ต่อว่า “แล้วอะไรกันแน่ที่ทำให้ดัชนีพัง” โครงสร้างเดิมจึงยังอยู่ และไม่กี่วันต่อมาก็มาสะดุดที่จุดเดิมอีกครั้ง
สาเหตุที่ซ่อนอยู่: ไม่มี “กลไกบังคับ” ให้ทำตามสิ่งที่ตัดสินใจไว้
อีกสาเหตุหนึ่งที่ลึกกว่านั้นคือนี่ การตัดสินใจว่า “จะสำรองข้อมูล” ไม่ได้ถูกแยกออกจากข้อเท็จจริงที่ว่า “ได้สำรองข้อมูลแล้ว” และยิ่งไปกว่านั้น ไม่มีด่านตรวจที่บังคับให้แยกสองสิ่งนี้ออกจากกัน การเขียนกฎไว้เป็นแค่คำแนะนำ โดยไม่มีด่านตรวจ ณ จุดที่ลงมือทำจริงเพื่อยืนยันว่าปฏิบัติตามแล้วหรือไม่ กฎนั้นก็อาจถูกข้ามไปได้เฉยๆ
โดยเฉพาะในงานที่ให้ AI assistant เป็นคนลงมือทำ ช่องว่างระหว่าง “สิ่งที่พูดไว้” กับ “สิ่งที่ทำจริง” ขยายกว้างได้ง่าย คำประกาศว่า “จะสำรองข้อมูลนะ” กับข้อเท็จจริงที่ว่ามีสิ่งที่สำรองไว้อยู่จริง เป็นคนละเรื่องกัน เวลาลงมือทำเองสองสิ่งนี้เชื่อมกันอยู่ในหัวแบบคลุมเครือโดยไม่รู้ตัว แต่พอเปลี่ยนคนที่ฝากงานให้ทำ ความจริงที่ว่ามันไม่ได้เชื่อมกันเลยก็ปรากฏออกมา ครั้งนี้ผมถูกจุดนั้นเล่นงานพอดี
สิ่งที่ปรับปรุงใหม่
1. รวมทางเข้า DB ให้เหลือทางเดียว
ผมเลิกวิธี “โปรแกรมเปิดไฟล์โดยตรง” แล้วเปลี่ยนเป็นแบบที่โปรเซสประจำเพียงตัวเดียวครอบครอง DB ไว้ ส่วนกระบวนการอื่นๆ จะเชื่อมต่อเข้ามาอ่านเขียนผ่านโปรเซสนั้น เมื่อทางเข้าสำหรับการเขียนเหลือเพียงหนึ่งเดียวทางกายภาพ ก็ไม่มีช่องว่างให้หลายโปรเซสจับสิ่งเดียวกันแยกกันแล้วทำให้ดัชนีขัดแย้งกันได้อีกต่อไป
【ก่อน】แต่ละโปรเซสเปิดไฟล์ DB โดยตรง (อาจเกิดการแย่งกัน)
กระบวนการย้ายข้อมูล ─┐
├─▶ ไฟล์ DB เดียวกัน ─▶ ดัชนีอาจขัดแย้งกัน
กระบวนการนำเข้าอัตโนมัติ ─┘ ▲
└─ อีกโปรเซสเขียนพร้อมกัน
【หลัง】โปรเซสประจำครอบครอง DB ไว้ ส่วนอื่นเชื่อมต่อเข้ามา (รวมทางเข้าเป็นหนึ่งเดียว)
กระบวนการย้ายข้อมูล ─┐
├─▶ โปรเซสประจำ (ครอบครอง DB) ─▶ DB
กระบวนการนำเข้าอัตโนมัติ ─┘ │
ซ้อนกลไก mutual exclusion หลายชั้น
ทำให้การเขียนพร้อมกันเรียงลำดับกัน
2. ป้องกันการเขียนแย่งกันด้วยหลายชั้น
หลังจากรวมทางเข้าให้เหลือทางเดียวแล้ว ผมยังซ้อนกลไก mutual exclusion หลายชั้นเพื่อไม่ให้กระบวนการที่ไม่อยากให้รันพร้อมกันมาซ้อนทับกัน แม้จะหลุดผ่านชั้นหนึ่งไปได้ ก็ยังมีอีกชั้นคอยหยุดไว้ การออกแบบเพื่อความปลอดภัยที่ดี ไม่ใช่กำแพงสมบูรณ์แบบเพียงชั้นเดียว แต่คือการรับด้วยหลายชั้น
3. ยกระดับ “สิ่งที่ตัดสินใจไว้” จากคำแนะนำให้เป็นการบังคับ
อันนี้แหละที่ได้ผลที่สุด กฎที่ว่า “ต้องมีสิ่งที่สำรองไว้จริงและตรวจสอบตำแหน่งที่เก็บก่อนดำเนินการแบบทำลายล้าง” ผมตัดสินใจไว้แล้ว แต่บทเรียนจากครั้งนี้คือ “แค่เขียนกฎไว้ไม่พอ” ถ้าไม่มีด่านตรวจยืนยันว่าปฏิบัติตามแล้ว คำแนะนำก็จะถูกข้ามไปสักวันหนึ่ง
ผมจึงสร้างด่านตรวจเข้าไปในการทำงานของ assistant นั่นคือ ในขณะที่กำลังจะดำเนินการแบบทำลายล้าง ให้ตรวจสอบด้วยกลไกว่ามีสิ่งที่สำรองไว้จริงหรือไม่ ถ้าไม่มีก็ให้หยุดตรงนั้นทันที พูดง่ายๆ คือยกระดับคำแนะนำให้กลายเป็นการบังคับ
คำขอดำเนินการแบบทำลายล้าง
│
├─[ตอนที่ยังเป็นแค่คำแนะนำ]─▶ กฎ "มีอยู่" แต่ไม่มีการตรวจสอบ ─▶ ผ่านไปได้แม้ไม่มีสิ่งที่สำรองไว้ ─▶ พัง
│
└─[หลังวางด่านตรวจ]─▶ ตรวจสอบด้วยกลไกว่ามีสิ่งที่สำรองไว้จริงหรือไม่
│
├─ มีสิ่งที่สำรองไว้ ─▶ อนุญาตให้ดำเนินการแบบทำลายล้าง
└─ ไม่มีสิ่งที่สำรองไว้ ─▶ หยุดตรงนี้ (ไม่ให้ไปต่อ)
นี่คือการปรับแต่ง (tuning) เพื่อให้ AI assistant ทำงานได้อย่างปลอดภัยโดยแท้จริง ไม่ว่าจะเป็นคนหรือ AI การออกแบบที่พึ่งพา “ความระมัดระวัง” จะรั่วไหลที่จุดใดจุดหนึ่งในหน้างานที่ยุ่งเสมอ เส้นที่ห้ามข้ามเด็ดขาดต้องปกป้องด้วยด่านตรวจ ไม่ใช่ด้วยความใส่ใจ
หลังแก้ไขแล้ว ผมไม่ได้ตรวจแค่ว่าทำงานปกติได้หรือไม่ แต่ตรวจว่า “ถ้าลองทำการดำเนินการที่อาจทำให้พัง จะทนได้หรือไม่” ทั้งจากกรณีปกติและกรณีผิดปกติ เพราะการทำงานปกติได้แสดงแค่ว่า “ทำงานถูกต้อง” ไม่ได้แสดงว่า “ไม่มีวันพัง”
ช่องโหว่ที่ยังเหลืออยู่: สิ่งที่สำรองไว้ในเครื่องอาจหายไปพร้อมกับตัวเครื่องเอง
การจัดการสามอย่างนี้หยุดการเกิดซ้ำของวิธีพังแบบนี้ได้ แต่ยังมีช่องโหว่ที่ยังปิดไม่ได้ ถึงจะสร้างสิ่งที่สำรองไว้ในเครื่องตัวเองแล้วก็ตาม ถ้าตัวเครื่องนั้น — ดิสก์หรือตัวอุปกรณ์ — หายไป มันก็จะหายไปพร้อมกันด้วย ตามข้อสมมติฐานที่ 1 ความทรงจำนี้ไม่ได้อยู่บนคลาวด์ การมีอยู่แค่ในเครื่องหมายความว่า ความเสียหายจากฮาร์ดแวร์ การโจรกรรม หรือภัยพิบัติเพียงครั้งเดียว ก็อาจทำให้ทั้งข้อมูลและสิ่งที่สำรองไว้หายไปพร้อมกันได้
ตรงนี้ปัญหาแรกก็ย้อนกลับมาอีกครั้ง “ไม่อยากฝากเนื้อหาให้บุคคลที่สาม แต่ถ้ามีแค่ในเครื่องก็ไม่ทนทานพอ” — สองอย่างนี้อยู่ร่วมกันได้ แค่เข้ารหัสในเครื่องก่อน แล้วส่งเฉพาะก้อนที่เข้ารหัสแล้วไปเก็บระยะไกล ที่ฝากไม่สามารถอ่านเนื้อหาได้เลย (กุญแจอยู่ในเครื่องเท่านั้น) แต่ถ้าดิสก์ในเครื่องตายก็ยังกู้คืนจากระยะไกลได้
ในเครื่อง (ตัวเองเท่านั้น) ระยะไกล (คลาวด์สตอเรจ)
DB แบบข้อความธรรมดา ─▶ เข้ารหัสในเครื่อง ─▶ ส่งเฉพาะก้อนที่เข้ารหัสแล้ว ─▶ เก็บไว้
│ │
กุญแจอยู่ในเครื่องเท่านั้น ที่ฝากไม่สามารถอ่านเนื้อหาได้
│
├─ เป้าหมาย 1: ดิสก์ตายก็กู้คืนจากระยะไกลได้ (ความทนทาน)
└─ เป้าหมาย 2: ไม่ส่งเนื้อหาให้บุคคลที่สาม (รักษาข้อสมมติฐานที่ 1)
การออกแบบที่ตัดสินใจไว้คือ restic เครื่องมือมาตรฐานที่เข้ารหัสฝั่งไคลเอนต์ เพื่อทำ incremental backup แบบเข้ารหัส และ ปลายทางคือ AWS S3 ในบัญชีของตัวเอง มีสามประเด็นสำคัญ
- การเข้ารหัสเสร็จสิ้นในเครื่อง restic เข้ารหัสในเครื่องก่อนส่ง ดังนั้นมีแค่ก้อนที่เข้ารหัสแล้วเท่านั้นที่ไปถึงปลายทาง กุญแจ (passphrase ที่แข็งแรง) เก็บไว้ทั้งในเครื่องและสำรองแบบออฟไลน์คู่กัน ไม่ส่งทั้งข้อความธรรมดาและกุญแจไปให้ปลายทางเลย ข้อสมมติฐานที่ 1 (ไม่ส่งเนื้อหาออกไปข้างนอก) จึงยังคงอยู่
- แยกปลายทางด้วยสิทธิ์ขั้นต่ำสุด ใช้ credential เฉพาะสำหรับการสำรองข้อมูล ที่อนุญาตแค่อ่านเขียนที่เก็บนั้นแห่งเดียว และปิดกั้นการเข้าถึงสาธารณะทั้งหมด เพื่อจำกัดความเสียหายจากกุญแจหรือสิทธิ์รั่วไหลไว้ในจุดเดียว
- การขจัดข้อมูลซ้ำช่วยลดขนาดที่บวม วิธีเดิมที่เรียบง่ายของผมคือคัดลอกทั้งหมดใหม่ทุกครั้งที่สำรองข้อมูล ทำให้ข้อมูลจริงแค่ไม่กี่ร้อย MB แต่สิ่งที่สำรองไว้บวมไปถึงหลาย GB restic จะใช้ส่วนที่ไม่เปลี่ยนแปลงซ้ำ ดังนั้นไม่ว่าจะเก็บประวัติกี่รุ่นก็ตาม ขนาดรวมจะหยุดอยู่ใกล้เคียงกับขนาดข้อมูลจริง
ยังไม่ได้ลงมือทำจริง — ตรงนี้เป็นการบ้านต่อไป แต่ จะใช้อะไรและป้องกันอย่างไร ผมตัดสินใจไว้แล้ว รวมสี่อย่างนี้เข้าด้วยกัน คือในเครื่อง ระยะไกล การเข้ารหัส และสิทธิ์ขั้นต่ำสุด แล้วการไม่ส่งเนื้อหาออกไปกับความทนทานในระยะไกลก็จะอยู่ร่วมกันได้
สิ่งที่เปลี่ยนเพื่อป้องกันครั้งที่สาม
“สำรองข้อมูล” ไม่ใช่การเสร็จสิ้น ให้นิยามเงื่อนไขของ “เสร็จสิ้น” ด้วยการมีอยู่ของผลลัพธ์จริง ไม่ใช่การตัดสินใจ สำหรับการสำรองข้อมูล นั่นหมายถึง มีสิ่งที่กู้คืนได้อยู่ตรงนั้นจริงๆ ไม่ว่าจะเตรียมที่หลบภัยระยะไกลไว้ด้วยหรือไม่ก็ตาม การสร้างสิ่งที่กู้คืนได้ในเครื่องก่อนดำเนินการแบบทำลายล้างทันที คือปราการด่านแรก
เมื่อพัง ให้แยก “ตัวบันทึก” กับ “ดัชนี” ออกจากกันก่อน ครั้งนี้ ไม่มีอะไรสูญหายจริงๆ เลย สิ่งที่พังมีแค่ดัชนีที่สร้างใหม่ได้เท่านั้น แม้อยู่ท่ามกลางความตื่นตระหนก ถ้าแยกสิ่งที่สูญหายจริงกับสิ่งที่สร้างใหม่ได้ออกจากกัน การกู้คืนก็จะดำเนินไปอย่างใจเย็น ดังนั้นผมจะรักษาโครงสร้างที่แยกตัวบันทึกกับดัชนีออกจากกัน ไว้เป็นประกันสุดท้าย
เส้นที่อยากปกป้อง ให้ปกป้องด้วยการบังคับ ไม่ใช่คำแนะนำ “ความระมัดระวัง” ปกป้องได้แค่ในสภาวะปกติเท่านั้น ตราบใดที่ยังให้ AI assistant ลงมือทำงานจริง ช่องว่างระหว่าง “พูดไว้” กับ “ทำจริง” ต้องปิดด้วยด่านตรวจ ไม่ใช่ด้วยความใส่ใจ นั่นแหละคือการปรับแต่งให้ assistant ทำงานได้อย่างปลอดภัย
หลายชั่วโมงที่เสียไปสองครั้งไม่กลับคืนมา แต่ความทรงจำที่ไม่มีอะไรมาแทนได้ไม่เคยสูญหายแม้แต่ครั้งเดียว เพราะตัวบันทึกแยกออกจากดัชนีและยังอยู่ครบ ด่านตรวจที่ยืนยันก่อนดำเนินการแบบทำลายล้างว่า “สิ่งที่กู้คืนได้อยู่ตรงนั้นจริงหรือเปล่า” ต้องมีอยู่เป็นกลไก ไม่ใช่แค่ความตั้งใจ นี่คือสิ่งที่ผมตัดสินใจไว้ เพื่อไม่ให้สะดุดที่จุดเดิมอีกครั้ง
บทความที่เกี่ยวข้อง
- เหตุผลเชิงลึกว่าทำไม vector DB นี้ถึงพังไปสองครั้ง (หลายโปรเซสเปิดไฟล์โดยตรง) และวิธีแก้เชิงโครงสร้าง อยู่ใน ทำลาย DB แบบ “เปิดไฟล์โดยตรง” ไปสองครั้งจากการแตะด้วยหลายโปรเซส บทความนี้คือฝั่งการป้องกัน (สำรองข้อมูล) ของอุบัติเหตุเดียวกันนั้น
- เรื่องการปรับปรุงความแม่นยำในการค้นหาของ vector DB เดียวกันนี้ด้วยการวัดผลแทนความรู้สึก อยู่ใน RAG ที่ใช้แค่การค้นหาด้วยเวกเตอร์ “ลืมตอนที่สำคัญที่สุด”
- บทเรียนจากอุบัติเหตุนี้ “ยกระดับคำแนะนำให้กลายเป็นการบังคับ” ที่นำไปสร้างเป็นกลไกจริงฝั่ง AI assistant อยู่ใน สร้างกลไกที่ไม่ปล่อยให้ AI Assistant “ทำไปเลย”
- กับดักอีกแบบหนึ่งที่เจอฝั่งการออกแบบการค้นหา/แนะนำด้วย embedding เดียวกัน (แคตตาล็อกต่างประเภทปนกัน) อยู่ใน กับดักที่การค้นหาด้วยเวกเตอร์ปล่อยให้แคตตาล็อกต่างประเภทหลุดเข้ามา
- ตัวปรัชญาการออกแบบเองเขียนไว้ใน คืนที่ AI แก้ไขฉัน 5 ครั้ง - ปรัชญาการออกแบบเพื่อให้ RAG ส่วนตัวเป็นเพื่อนคุณเป็นเวลา 5 ปี
- บันทึกอุบัติเหตุตัวอักษรเพี้ยนจากการแก้ไขข้อความจำนวนมากอีกครั้งหนึ่ง อยู่ใน อุบัติเหตุ Encoding จากการแก้ไขข้อความจำนวนมาก