สร้างกลไก "ไม่ให้ทำ" สำหรับผู้ช่วย AI เขียนโค้ด — จากคำแนะนำสู่การบังคับ แล้วพบว่าประตูนั้นเองก็มีช่องโหว่ข้าง
บทนำ
ฉันใช้ผู้ช่วย AI เขียนโค้ดเป็นประจำทุกวัน เมื่อฝ่ายที่รับมอบงานเปลี่ยนไป ปัญหาคือจะทำให้ฝ่ายตรงข้ามปฏิบัติตาม “สิ่งที่อยากให้ระวัง” ของเราได้อย่างไร
คนส่วนใหญ่จะเริ่มด้วย คำแนะนำ ก่อน เขียนไว้ในไฟล์การตั้งค่า (เช่น CLAUDE.md) ว่า “ก่อนการดำเนินการทำลายล้าง ให้สำรองข้อมูลก่อน” นี่คือก้าวแรกที่ถูกต้อง แต่คำแนะนำมีช่องโหว่ ไม่มีประตูตรวจสอบว่าถูกปฏิบัติตามหรือไม่ ในหน้างานที่ยุ่ง คำแนะนำสักวันหนึ่งจะถูกข้ามไปโดยไม่มีใครรู้ตัว
จริงๆ แล้วฉันเคยทำลายฐานข้อมูลความจำของตัวเองด้วยวิธีนั้น (เรื่องราวการทำลายเวกเตอร์ DB สำหรับความจำสองครั้ง) ฉันตัดสินใจแล้วว่า “จะสำรองข้อมูล” แต่กลับดำเนินการย้ายข้อมูลที่ทำลายล้างไปโดยไม่เคยสร้างข้อมูลสำรองจริงเลย คำแนะนำมีอยู่ แต่การบังคับไม่มี
บทความนี้เป็นเรื่องราวของการยกระดับคำแนะนำนั้นให้เป็น การบังคับ และภาคต่อที่พบว่าประตูที่ยกระดับขึ้นมานั้นเองก็มีช่องโหว่ข้างเปิดอยู่
ข้อจำกัดของชั้นคำแนะนำ
กฎที่เขียนไว้ในไฟล์การตั้งค่าเปรียบเสมือน “คำขอร้อง” การจะปฏิบัติตามหรือไม่หลังจากอ่านแล้ว ขึ้นอยู่กับการตัดสินใจในขณะนั้น ไม่มีกลไกที่หยุดในทันทีที่กำลังจะทำการดำเนินการทำลายล้าง แล้วตรวจสอบว่า “มีข้อมูลสำรองจริงหรือไม่”
คำขอดำเนินการทำลายล้าง (ลบ, ย้าย, เขียนทับ …)
│
├─[แค่คำแนะนำ]─▶ การตั้งค่าบอกว่า "สำรองก่อน" ─▶ ไม่มีการตรวจสอบ ─▶ ถูกข้าม ─▶ เกิดอุบัติเหตุ
│
└─[มีชั้นบังคับ]─▶ หยุดก่อนดำเนินการทันที (สิ่งที่บทความนี้จะสร้าง)
สร้างชั้นบังคับ
ผู้ช่วยตัวนี้มี hook (PreToolUse) ที่แทรกก่อนที่เครื่องมือจะทำงานได้ ฉันจึงใส่ประตูไว้ตรงนั้น ตรวจจับรูปแบบคำสั่งทำลายล้างแล้วหยุดการดำเนินการ พร้อม รวบรวมหลักฐานการสำรองข้อมูลด้วยเครื่องจักรและนำเสนอ
หลักฐานคือสิ่งเหล่านี้ เช่น สถานะ Git ของไดเรกทอรีทำงาน (มีการเปลี่ยนแปลงที่ยังไม่ commit หรือไม่ commit ล่าสุดเมื่อไหร่) มีไดเรกทอรีปลายทางสำรองอยู่หรือไม่ วันที่ล่าสุดของมันคือเมื่อไหร่ hook จะตรวจสอบสิ่งเหล่านี้โดยอัตโนมัติแล้วแนบไปกับกล่องยืนยัน
คำขอดำเนินการทำลายล้าง
│
▼
ชั้นบังคับ (hook) หยุดก่อนดำเนินการทันที
│
├─▶ รวบรวมหลักฐานการสำรองข้อมูล
│ ├─ สถานะ Git (ยังไม่ commit / commit ล่าสุด)
│ └─ มีปลายทางสำรองหรือไม่ / วันที่ล่าสุด
│
└─▶ ยืนยันพร้อมหลักฐาน (ask)
├─ มี ─▶ ดำเนินการต่อ
└─ ไม่มี ─▶ หยุด
ตรงนี้มีการตัดสินใจอยู่หนึ่งอย่าง คือจะทำให้ประตูเป็น “ปฏิเสธแบบไม่ต้องถาม (deny)” หรือ “แสดงหลักฐานแล้วให้คนยืนยัน (ask)” ฉันเลือก ask การให้เครื่องปฏิเสธทุกอย่างจะหยุดแม้แต่การดำเนินการที่ถูกต้อง ทำให้หน้างานหยุดชะงัก เครื่องมุ่งเน้นแค่การรวบรวมหลักฐาน การตัดสินใจสุดท้ายให้คนดูหลักฐานแล้วตัดสิน ด้วยวิธีนี้ คำแนะนำที่เคยเป็น “สำรองข้อมูลก่อน” จึงกลายเป็นประตูที่มีหลักฐานแนบมาด้วย
ประตูนั้นเองมีช่องโหว่ข้างเปิดอยู่
พอสร้างประตูเสร็จก็อยากจะวางใจ แต่ประตูที่สร้างขึ้นมาจริงๆ ปิดช่องโหว่ได้หรือไม่ เป็นอีกเรื่องหนึ่ง
หลังจากประกอบชั้นบังคับเสร็จ ฉันอ่านเอกสารทางการของผู้ช่วยในส่วนที่เกี่ยวกับกลไก — hook, agent, สิทธิ์, เส้นทางการดำเนินการคำสั่ง — ทั้งหมดอย่างละเอียด ประเด็นสำคัญที่อ่านได้ ฉันเก็บไว้ในฐานความรู้ของตัวเอง — เวกเตอร์ DB ที่ทำลายไปสองครั้งที่กล่าวถึงตอนต้น (ความจำส่วนตัวที่ค้นหาได้) — ในรูปแบบที่ดึงกลับมาใช้ทีหลังได้ ฉันจัดการว่าอ่านไปถึงไหนแล้วด้วยตารางรายการ เพื่อไม่ให้มีส่วนที่อ่านตกหล่น แล้วฉัน ตรวจสอบการตั้งค่าของตัวเองด้วยความรู้นั้น ผลคือพบช่องโหว่ที่ร้ายแรงที่สุด
hook ของชั้นบังคับตอบสนองแค่กับเครื่องมือเฉพาะอย่างหนึ่ง (shell คือ Bash) เท่านั้น การดำเนินการทำลายล้างแบบเดียวกัน แต่มาจากเส้นทางอื่น — shell อื่น (PowerShell) หรือการแก้ไขไฟล์โดยตรง — hook จะมองว่า “ไม่อยู่ในขอบเขต” แล้วปล่อยผ่านไปเฉยๆ ข้างๆ ประตูที่คิดว่ากำลังป้องกันอยู่ กลับมีช่องโหว่ข้างเปิดอยู่
【ก่อน】ชั้นบังคับมองเห็นแค่ "เครื่องมือเฉพาะอย่างหนึ่ง (Bash)"
คำสั่งอันตราย(Bash) ─▶ hook ตรวจจับได้ ─▶ หยุด ○
เส้นทางอื่น(PowerShell / แก้ไขไฟล์โดยตรง) ─▶ hook ปล่อยผ่าน ─▶ เกิดอุบัติเหตุ × ← ช่องโหว่ข้าง
【หลัง】ปิดทุกเส้นทาง
Bash / PowerShell / แก้ไขไฟล์ ─┐
├─▶ hook ตรวจจับได้ ─▶ หยุด ○
(ไม่ว่าจะมาจากเส้นทางไหน ก็ต้องผ่านประตูนี้) ┘
มาตรการรับมือคือทำให้ hook รองรับทุกเส้นทาง และยังเพิ่มประตูที่บังคับด้วยเครื่องจักรในฝั่งการแก้ไขไฟล์ด้วยว่า “หยุดถ้าจะเขียนทับไฟล์ที่ป้องกันไว้โดยไม่มีการสำรอง” ประตูจะมีความหมายก็ต่อเมื่อวางไว้ที่ ทุกทางเข้า ที่นำไปสู่สิ่งที่ต้องการปกป้องเท่านั้น
เส้นที่คำแนะนำปกป้องไม่ได้
เส้นที่ต้องปกป้อง ให้ปกป้องด้วยการบังคับ ไม่ใช่คำแนะนำ “ระมัดระวัง” ปกป้องได้แค่ในสภาวะปกติเท่านั้น เส้นที่ต้องไม่ถูกข้ามในหน้างานที่ยุ่ง ไม่ได้ถูกปกป้องด้วยความระมัดระวัง แต่ด้วยประตูที่หยุดการดำเนินการ
ทดสอบประตูจากหลายมุมมอง พอสร้างประตูเสร็จแล้ว ให้ตรวจสอบไม่ใช่แค่ “หยุดได้ถูกต้องหรือไม่” แต่ต้องตรวจสอบ “หลบเลี่ยงได้หรือไม่” ด้วย การยืนยันแค่กรณีปกติ (การดำเนินการอันตรายถูกหยุด) เพียงอย่างเดียวจะไม่พบช่องโหว่ข้าง (เส้นทางอื่นปล่อยผ่าน) การที่จะพังหรือไม่ ขึ้นอยู่กับว่าหลบเลี่ยงได้หรือไม่
อ่านกลไกทั้งหมดก่อนที่จะควบคุมมัน ประตูที่สร้างขึ้นจากความเข้าใจบางส่วน จะทิ้งช่องโหว่แบบมองเห็นแค่เครื่องมือประเภทเดียวจากทั้งหมดที่จัดการอยู่ไว้ได้อย่างง่ายดาย ต่อเมื่อเข้าใจจากสเปกว่าอะไรที่อาจกลายเป็นเส้นทางการดำเนินการได้บ้าง ถึงจะนับทางเข้าทั้งหมดได้
คำแนะนำหนึ่งบรรทัดว่า “สำรองข้อมูลก่อน” นั้นใครก็ copy ได้ แต่การสร้างประตูที่หยุดเส้นนั้นไว้จริงๆ ที่ทุกทางเข้าของทุกเส้นทางการดำเนินการ แล้วตรวจสอบว่าไม่มีช่องโหว่ข้างหลงเหลืออยู่ งานนั้น copy ไม่ได้ ต้องสร้างเป็นกลไกด้วยตัวเองและถือครองมันไว้
บทความที่เกี่ยวข้อง
- กรณีจริงที่คำแนะนำเพียงอย่างเดียวปกป้องไม่ได้ และฉันทำลายฐานข้อมูลความจำไปสองครั้ง อยู่ใน เรื่องราวการทำลายเวกเตอร์ DB สำหรับความจำสองครั้ง
- ความพยายามในภาพรวมของการเลี้ยงดูผู้ช่วย AI ให้เป็นคู่หู รวบรวมไว้ใน บันทึกการปฏิบัติงานของผู้ช่วย AI (สารบัญซีรีส์)
- นี่คือ hook เดียวกัน แต่บทความนี้เป็นเรื่องการบังคับที่ไม่ให้ทำการดำเนินการอันตราย ส่วนกลไกการปลุกความทรงจำที่ทำให้ AI นึกถึง “สิ่งที่ควรนึกถึงตอนนี้” ทุกเทิร์น เขียนไว้ใน ฉีด “สิ่งที่ควรนึกถึงตอนนี้” เข้าไปใน AI ทุกเทิร์น — ชั้นการเปิดใช้งานของ RAG ส่วนตัว