Tool: write_file — เขียนไฟล์แบบ atomic ในขอบเขตที่ล็อกไว้เท่านั้น

บทความ Tool: read_file พูดถึงการอ่านไฟล์ บทความนี้ต่อด้วย tool ที่ตรงข้ามกัน — write_file
Tool นี้คืออะไร
เขียนข้อความลงไฟล์ตาม path ที่ระบุ — ถ้าไฟล์มีอยู่แล้วจะปฏิเสธไม่เขียนทับโดยดีฟอลต์ ต้องส่ง overwrite="true" มาชัดเจนถึงจะเขียนทับได้ เป็นเบรกความปลอดภัยง่ายๆ ที่กันไฟล์สำคัญถูกเขียนทับโดยไม่ตั้งใจ
Agent เรียก tool นี้เมื่อไร
ใช้เมื่อผู้ใช้ขอให้บันทึก/สร้าง/เขียนไฟล์ผลลัพธ์ลงดิสก์จริง — ต่างจาก read_file/rag_search ที่แค่ดึงข้อมูลเข้ามาให้โมเดลอ่าน write_file เป็น tool เดียวใน 6 ตัวของ AGENT LITE ที่เปลี่ยนแปลงสถานะจริงบนเครื่อง (สร้าง/แก้ไฟล์) จึงมีข้อจำกัดขอบเขตที่เข้มงวดกว่า tool อื่น
ขอบเขตการเขียน: จำกัดอยู่ใต้ workspect เท่านั้น
จุดออกแบบที่สำคัญที่สุดของ tool นี้: เขียนได้เฉพาะใต้ config.BASH_ALLOWED_ROOT (โฟลเดอร์ workspect/ ของโปรเจกต์เอง) เท่านั้น — ไม่ใช่ที่ไหนในเครื่องก็ได้ ทุก path ที่ส่งมาต้อง resolve แล้วอยู่ใต้ path นี้จริงๆ (เช็คด้วย Path.relative_to() ซึ่ง raise error ทันทีถ้า path หลุดออกนอกขอบเขต) ถ้าไม่ผ่านจะได้ error กลับไปตรงๆ ไม่มีการเขียนเกิดขึ้นเลย
หมายความว่า agent เขียนทับซอร์สโค้ดของตัวเองไม่ได้ และเขียนไฟล์ในโฟลเดอร์อื่นของผู้ใช้ (Desktop, Documents) ไม่ได้เลย — ไม่ว่าโมเดลจะพยายามส่ง path แบบไหนมาก็ตาม การจำกัดขอบเขตนี้ทำที่ระดับโค้ด ไม่ใช่ระดับ prompt ที่ขอให้โมเดล “อย่าเขียนนอก workspect” (ซึ่งพึ่งพาไม่ได้กับโมเดลขนาดเล็ก)
เขียนแบบ atomic — กันไฟล์เสียหายครึ่งๆ กลางๆ
ถ้าเขียนไฟล์ตรงๆ (เปิดไฟล์ปลายทางแล้วเขียนทับทีละ byte) แล้ว process ถูกขัดจังหวะกลางทาง (crash, timeout, ปิดโปรแกรม) ไฟล์ปลายทางจะเหลือข้อมูลครึ่งๆ กลางๆ เสียหายถาวร — write_file จึงใช้เทคนิคเขียนลงไฟล์ชั่วคราวก่อน แล้วค่อย rename ทับไฟล์จริงทีเดียว (os.replace):
1. เขียนเนื้อหาทั้งหมดลง <path>.tmp
2. flush() + fsync() — บังคับให้ OS เขียนลง disk จริง ไม่ใช่ค้างอยู่ใน buffer
3. os.replace(tmp, path) — เปลี่ยนชื่อไฟล์ทีเดียว (atomic ในระดับ filesystem)
os.replace เป็นการดำเนินการที่ atomic ในระดับ filesystem — ไม่มีสถานะกึ่งกลางที่ไฟล์ปลายทางเสียหายบางส่วน ผลลัพธ์มีแค่ 2 แบบ: เขียนสำเร็จสมบูรณ์ หรือไฟล์เดิมยังอยู่ครบเหมือนไม่มีอะไรเกิดขึ้น ไม่มีกรณีที่สาม
ถ้าเกิด exception ระหว่างทาง ระบบจะพยายามลบไฟล์ .tmp ทิ้งด้วย (cleanup) ไม่ปล่อยให้ไฟล์ชั่วคราวค้างอยู่
Feedback ที่ให้กลับมีรายละเอียดพอให้ตรวจสอบได้
ผลลัพธ์ที่คืนกลับไม่ใช่แค่ “เขียนสำเร็จ” เฉยๆ แต่บอกขนาดไฟล์ใหม่ (จำนวนตัวอักษร, จำนวนบรรทัด) และถ้าเป็นการเขียนทับ จะบอกขนาดไฟล์เดิมก่อนถูกแทนที่ด้วย — ให้ทั้งโมเดลและผู้ใช้ที่อ่าน log เห็นชัดว่าเกิดอะไรขึ้นจริง ไม่ต้องเดา
ทำไม tool นี้ถูกตัดทอนจากเวอร์ชันเดิมของ MAX
คอมเมนต์ในซอร์สระบุว่า write_file เวอร์ชันนี้ตัดความสามารถบางอย่างออกจากเวอร์ชันต้นทาง (ENDEAVOR_LOCAL_AGENT_MAX_VLM) โดยตั้งใจ: ไม่มีการเขียนแบบ chunk 256KB ทีละส่วน (ไม่จำเป็นที่ขนาดไฟล์ที่ agent นี้เขียนจริง) และไม่มีพฤติกรรม “สร้างไฟล์ .edited คู่กันไว้” สำหรับไฟล์ภายนอกที่มีอยู่ก่อน — Lite เลือกความเรียบง่ายและขอบเขตที่แคบกว่า เพราะเป้าหมายต่างกัน (agent สำหรับงานทั่วไปบนเครื่องเล็ก ไม่ใช่ agent แก้โค้ดโปรเจกต์ใหญ่)
ทำไมเรื่องนี้ถึงสำคัญ
write_file เป็นตัวอย่างที่ดีของหลักการ “ให้ความสามารถน้อยที่สุดที่จำเป็น” (least privilege) — แทนที่จะให้ agent เขียนไฟล์ได้ทุกที่แล้วหวังว่าโมเดลจะระวังเอง ระบบล็อกขอบเขตไว้แน่นตั้งแต่ระดับโค้ด ทำให้ต่อให้โมเดลพลาดหรือถูกชักจูงผิดทาง (เช่นจากเนื้อหาที่อ่านมาจากไฟล์อื่น) ความเสียหายที่เกิดได้จริงก็ยังจำกัดอยู่แค่ในโฟลเดอร์ที่ตั้งใจให้เขียนได้เท่านั้น
ความคิดเห็น
กำลังโหลดความคิดเห็น...