วิธีที่เราจัดการ Sandbox สำหรับ Agent ในระดับสเกลใหญ่

@LegitSeanSmith
อังกฤษ2 สัปดาห์ที่ผ่านมา · 03 ก.ค. 2569
319K
79
11
4
73

TL;DR

Sean Smith ตำแหน่ง CTO ของ Adapt อธิบายรายละเอียดเกี่ยวกับการเปลี่ยนโครงสร้างพื้นฐานจาก gVisor มาเป็น Firecracker microVM ซึ่งช่วยให้สามารถสร้าง Sandbox ที่แยกส่วนกันได้นับพันรายการและบูตได้ในเวลาไม่ถึงหนึ่งวินาทีสำหรับการประมวลผลโค้ดของ AI Agent

เราตัดสินใจเดิมพันตั้งแต่เนิ่นๆ ว่าจะมอบพลังให้ LLM สามารถรันโค้ดอะไรก็ได้ตามต้องการ บทความนี้จะเล่าถึงเหตุผลที่เราเลือกเดิมพันเช่นนั้น และสิ่งที่เราต้องทำเพื่อรันแซนด์บ็อกซ์ (sandbox) เหล่านี้นับพันรายการพร้อมกัน โดยสามารถเปิดและปิดการทำงานได้รวดเร็วทันทีที่ผู้ใช้เริ่มและจบการสนทนากับเอเจนต์

ทุกการสนทนาที่ผู้ใช้มีกับเอเจนต์ Adapt จะมีคอมพิวเตอร์ส่วนตัวรองรับ ไม่ใช่แค่คอนเทนเนอร์ที่ถูกล็อกไว้บนเซิร์ฟเวอร์ที่ใช้ร่วมกัน แต่เป็น VM แบบแยกส่วนที่โมเดลสามารถทำอะไรก็ได้ตามใจชอบ ไม่ว่าจะเป็นการติดตั้งซอฟต์แวร์ เขียนและรันโปรแกรม ท่องเว็บ หรือเชื่อมต่อกับ API เราเรียกสิ่งเหล่านี้ว่าแซนด์บ็อกซ์ ซึ่งเป็นหนึ่งในองค์ประกอบพื้นฐานสำคัญที่ Adapt สร้างขึ้นมา

การควบคุมที่สมบูรณ์แบบ

LLM คืออัจฉริยะด้านการเขียนโค้ด และงานหลักของผมคือการสร้างสภาพแวดล้อมการพัฒนาที่สมบูรณ์แบบเพื่อให้พวกมันได้ทำงาน

วิธีทั่วไปในการเชื่อมต่อ AI เข้ากับโลกภายนอกคือการสร้างการเชื่อมต่อ (integration) ขึ้นมาเอง เช่น ตัวเชื่อมต่อสำหรับ GitHub, HubSpot, Stripe หรือรอให้แต่ละบริการเปิดตัว MCP server วิธีนี้ไม่สามารถขยายผลได้จริง และผมก็ไม่ได้อยากเขียนโค้ดเชื่อมต่อระบบไปวันๆ

ดังนั้น แทนที่จะลงมือทำเอง เราจึงปล่อยให้โมเดลเป็นคนจัดการ บริการใดก็ตามที่มี API สามารถเข้าถึงได้จาก Adapt เพราะเรามอบทุกสิ่งที่ LLM จำเป็นต้องใช้ในการเขียนสคริปต์หรือโปรแกรมเพื่อสื่อสารกับ API นั้นๆ นี่คือส่วนสำคัญของสิ่งที่เราหมายถึงเมื่อเรียก Adapt ว่าเป็น "ปัญญาประดิษฐ์แนวราบ" (horizontal intelligence) เพราะมันไม่ได้ถูกจำกัดอยู่แค่เครื่องมือชุดใดชุดหนึ่ง แต่มันสามารถสร้างเครื่องมือที่จำเป็นขึ้นมาได้ทันที

หัวใจสำคัญของเรื่องนี้คือการให้ LLM เข้าถึงแซนด์บ็อกซ์ได้อย่างเต็มที่ แทนที่จะให้โมเดลใช้แค่ชุดภาษาและเครื่องมือ CLI แบบคงที่ที่มีสิทธิ์เข้าถึงระบบไฟล์จำกัด เรากลับให้สิทธิ์เข้าถึงทุกอย่างอย่างสมบูรณ์ มันทำงานในฐานะ root และในขณะที่แซนด์บ็อกซ์ของเรามาพร้อมกับรันไทม์ทั่วไปอย่าง Node และ Python แต่ถ้า SDK ที่ดีที่สุดสำหรับ API ของบริการบางอย่างเขียนด้วย Go ล่ะ? โมเดลก็สามารถติดตั้งและรันมันได้ทันที

Sean Smith - inline image

ถ้า LLM จำเป็นต้องเขียนโปรแกรมด้วยภาษา Go ก็แค่ติดตั้ง Go แล้วรันได้เลย

ดังนั้น หากเราอนุญาตให้โมเดลติดตั้งอะไรก็ได้ที่ต้องการและรันโค้ดที่ไม่มีมนุษย์คนไหนตรวจสอบ เราจะรักษาความปลอดภัยได้อย่างไร? โชคดีที่เราไม่ใช่คนกลุ่มแรกที่ต้องรันโค้ดที่ไม่น่าเชื่อถือ มีรันไทม์ที่ปลอดภัยและเป็นที่นิยมมากสองตัวสำหรับงานนี้โดยเฉพาะ คือ gVisor และ Firecracker ซึ่งการเดินทางของเราที่ผ่านมาทำให้เราคุ้นเคยกับทั้งสองตัวนี้เป็นอย่างดี

จาก gVisor สู่ Firecracker

ก้าวแรกของเราในการสร้างแซนด์บ็อกซ์ที่ปลอดภัยสำหรับ LLM คือแนวทางที่ "ง่าย" ที่สุด นั่นคือการรันแซนด์บ็อกซ์แต่ละตัวด้วย gVisor บน GKE (Google Kubernetes Engine) โดยใช้ GKE Sandbox เราใช้งานบริการอื่นๆ ทั้งหมดบน GKE อยู่แล้ว ดังนั้นนี่จึงเป็นขั้นตอนที่เป็นธรรมชาติสำหรับเรา

gVisor ทำหน้าที่คั่นกลางระหว่างคอนเทนเนอร์กับเคอร์เนลของโฮสต์ แทนที่จะปล่อยให้โปรแกรมเรียกใช้ระบบ (system call) ตรงไปยังเคอร์เนล Linux จริงๆ ซึ่งเป็นสิ่งที่คุณไม่ต้องการให้โค้ดที่ไม่น่าเชื่อถือเข้าไปยุ่ง gVisor จะคอยดักจับการเรียกเหล่านั้นในเคอร์เนลระดับผู้ใช้ (user-space kernel) ของตัวเองและจัดการให้ คุณจะได้รับความสะดวกสบายเหมือนคอนเทนเนอร์ทั่วไปแต่มีพื้นที่เสี่ยงต่อการโจมตี (attack surface) ที่น้อยลงมาก และ GKE Sandbox ก็รวมทุกอย่างนี้ไว้ให้แล้ว คุณเพียงแค่ปรับใช้ Pods (คอนเทนเนอร์) และพวกมันก็จะรันภายใต้ gVisor ได้อย่างราบรื่นโดยที่เราแทบไม่ต้องตั้งค่าโครงสร้างพื้นฐานอะไรเลย

และในช่วงแรกมันก็ทำงานได้ดีมาก เรากำหนดให้แซนด์บ็อกซ์ "พื้นฐาน" เป็นอิมเมจ Docker และปล่อยให้ GKE ขยายขนาดตามจำนวนแซนด์บ็อกซ์ที่เราต้องการในแต่ละช่วงเวลา การอัปเดตซอฟต์แวร์ที่แซนด์บ็อกซ์ใช้งานก็ทำได้ง่ายๆ เพียงแค่อัปเดต Dockerfile และปรับเลขเวอร์ชันในไฟล์ manifest

Sean Smith - inline image

แซนด์บ็อกซ์ Pods หลายร้อยรายการที่รันอยู่ภายใต้ GKE Sandbox

แต่สิ่งที่เป็นนามธรรม (abstraction) ซึ่งทำให้ gVisor ใช้งานง่าย กลับเป็นสิ่งที่เราต้องต่อสู้มาตลอด เนื่องจาก gVisor จำลองการทำงานของ Linux syscall ในระดับ user space ทำให้บางอย่างทำงานไม่เหมือนกับบนเคอร์เนลจริง และเวิร์กโหลดที่โมเดลของเราคิดขึ้นมานั้นคาดเดาไม่ได้สุดๆ การดักจับที่ช่วยเรื่องความปลอดภัยกลับกลายเป็นภาระเมื่อต้องเจอกับงานที่เน้น syscall และ I/O หนักๆ และการพึ่งพา GKE ในตลอดวงจรชีวิตของมัน หมายความว่าส่วนที่เราต้องการควบคุมมากที่สุด เช่น เวลาในการบูต, ความหนาแน่นในการจัดวาง, ระบบเครือข่าย และความถี่ในการรีไซเคิลเครื่อง กลับเป็นส่วนที่เราควบคุมได้น้อยที่สุด ข้อความ OutOfcpu ที่ปรากฏขึ้นมาคือสิ่งที่คุณจะเริ่มเห็นเมื่อคุณใช้งานตัวจัดตารางงาน (scheduler) ของคนอื่นหนักเกินกว่าที่มันจะรับไหว

นั่นคือสิ่งที่ผลักดันให้เราหันมาใช้ Firecracker

Firecracker microVM คือเครื่องเสมือน (virtual machine) จริงๆ ที่มีเคอร์เนลของตัวเอง รันด้วยการจำลองฮาร์ดแวร์ แต่ถูกปรับแต่งให้บูตได้ในเสี้ยววินาทีโดยใช้ทรัพยากรเพียงไม่กี่เมกะไบต์ นี่เป็นเทคโนโลยีเดียวกับที่ AWS ใช้เพื่อรองรับเวิร์กโหลดจำนวนมหาศาลของ Lambda และ Fargate บนฮาร์ดแวร์ที่ใช้ร่วมกัน มันมอบขอบเขตการแยกส่วนที่แข็งแกร่งกว่าเคอร์เนลที่ใช้ร่วมกัน บูตได้เร็วพอที่จะรู้สึกว่าทันที และมีขนาดเล็กพอที่จะอัดแน่นหลายเครื่องลงบนโฮสต์เดียวได้

ข้อแลกเปลี่ยนคือ Firecracker ให้ VM มาให้คุณ แต่ไม่ได้ให้อะไรอย่างอื่นมาด้วย ไม่มีเลเยอร์แบบ GKE ที่คอยจัดการตารางงาน ระบบเครือข่าย และวงจรชีวิต เราจึงสร้างมันขึ้นมาเองและเรียกมันว่า orc

rootfs ก็เป็นเพียงแค่อิมเมจ

สิ่งหนึ่งที่เราไม่อยากสูญเสียไปในการย้ายออกจากคอนเทนเนอร์คือการกำหนดแซนด์บ็อกซ์ให้เป็น Dockerfile ธรรมดาๆ คอนเทนเนอร์ทำให้เรื่องนี้เป็นเรื่องง่าย แต่ VM โดยทั่วไปทำไม่ได้ เนื่องจาก microVM จะบูตจากระบบไฟล์รูท (root filesystem) ไม่ใช่ OCI image

ดังนั้น orc จึงเข้ามาเชื่อมช่องว่างนี้ เมื่อได้รับคำสั่งให้สร้าง VM มันจะนำอิมเมจ Docker/OCI ปกติมาสร้างเป็นระบบไฟล์รูทของ VM ในทันที พร้อมกับแคชผลลัพธ์ไว้เพื่อให้การบูตอิมเมจเดิมในครั้งถัดไปทำได้อย่างรวดเร็ว แซนด์บ็อกซ์พื้นฐานของเรายังคงเป็นเพียง Dockerfile และ orc จะเปลี่ยนมันให้เป็น rootfs ที่บูตได้เมื่อมีการร้องขอ

นั่นทำให้เวิร์กโฟลว์ของเราเหมือนกับสมัยที่ใช้ GKE คือแก้ไข Dockerfile แล้วส่งแซนด์บ็อกซ์ใหม่ไป ในขณะที่เบื้องหลังรันอยู่บน VM จริงๆ และมันยังเปิดประตูสู่โอกาสใหม่ๆ ที่เราเพิ่งจะเริ่มก้าวเข้าไป เนื่องจาก OCI image ใดๆ ก็สามารถกลายเป็น microVM ได้ เราจึงสามารถบูตแซนด์บ็อกซ์จากอิมเมจอื่นที่ไม่ใช่อิมเมจเริ่มต้นได้ อยากได้ VM ที่มี Postgres และ pgvector ติดตั้งมาให้เลยใช่ไหม? แค่ชี้ orc ไปที่อิมเมจนั้น คุณก็จะได้มันมาเป็นเครื่องแยกส่วนของตัวเอง แซนด์บ็อกซ์จึงไม่ได้เป็นเพียงสภาพแวดล้อมคงที่ชุดเดียวอีกต่อไป แต่กลายเป็น "อิมเมจอะไรก็ตามที่งานนั้นต้องการ โดยบูตขึ้นมาเป็น VM ของตัวเอง"

การรันงานในระดับสเกล

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

ตัวเลขสองตัวนี้เป็นตัวกำหนดทุกอย่าง: เราเตรียมแซนด์บ็อกซ์ได้เร็วแค่ไหน และเราใส่แซนด์บ็อกซ์ลงบนโฮสต์ได้มากเท่าไหร่

ความหน่วงในการเริ่มต้น (Startup latency): Firecracker microVM บูตได้ในเวลาไม่กี่ร้อยมิลลิวินาที ซึ่งเร็วพอที่เราไม่จำเป็นต้องเตรียมแซนด์บ็อกซ์สำรองไว้ล่วงหน้า (warm pool) เลย ซึ่งเป็นหนึ่งในข้อดีที่เงียบเชียบที่สุดของการเปลี่ยนมาใช้ระบบนี้ ภายใต้ GKE เราคงต้องสำรองทรัพยากรไว้เพื่อลดเวลาในการเริ่มต้น แต่ด้วย orc แซนด์บ็อกซ์ใหม่จะพร้อมใช้งานก่อนที่คุณจะทันสังเกตเห็น ดังนั้นเราจึงสร้างมันขึ้นมาตามความต้องการเมื่อแชทเริ่ม และทำลายทิ้งเมื่อแชทจบ ไม่ต้องมีพูลที่ไม่ได้ใช้งานให้คอยดูแลหรือเสียเงินจ่ายอีกต่อไป

ความหนาแน่น (Density): เนื่องจาก microVM แต่ละตัวมีขนาดเล็กมาก เราจึงสามารถอัดแน่นพวกมันไว้บนโฮสต์จริงเครื่องเดียวได้จำนวนมาก เรากำหนดขนาด CPU และหน่วยความจำของแซนด์บ็อกซ์แต่ละตัวตามความต้องการใช้งานจริง แทนที่จะจัดสรรทรัพยากรเกินความจำเป็น ซึ่งเป็นสิ่งที่ช่วยให้เรารันแซนด์บ็อกซ์นับพันได้อย่างคุ้มค่า

orc เองถูกออกแบบมาให้มีขนาดเล็กโดยเจตนา มันคือระนาบควบคุม (control plane) ที่สื่อสารผ่าน API ง่ายๆ คือสร้าง VM ด้วย N vCPUs และ M เมกะไบต์ของหน่วยความจำจากอิมเมจที่กำหนด, ส่งคำสั่งเข้าไปในนั้น, อ่านและเขียนไฟล์ภายใน, ติดป้ายกำกับ (tag) เพื่อให้เราค้นหาได้ในภายหลัง และลบทิ้งเมื่อเสร็จงาน แขกรับเชิญ (guest) แต่ละตัวจะรันกระบวนการ init เล็กๆ เป็น PID 1 และมีเครือข่ายแยกส่วนของตัวเอง นั่นคือทั้งหมดของมัน ความมหัศจรรย์ไม่ได้อยู่ที่เคล็ดลับฉลาดๆ ใดๆ แต่อยู่ที่องค์ประกอบพื้นฐานเหล่านี้มีความเรียบง่ายและรวดเร็วพอที่จะรันกองทัพแซนด์บ็อกซ์ได้

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

บันทึกในคลิกเดียว

อ่านบทความไวรัลเชิงลึกด้วย AI ใน YouMind

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

สำรวจ YouMind
สำหรับครีเอเตอร์

เปลี่ยน Markdown ของคุณให้เป็นบทความ 𝕏 ที่สะอาดตา

เวลาคุณเผยแพร่งานเขียนยาวของตัวเอง การจัดรูปแบบรูปภาพ ตาราง และบล็อกโค้ดให้เข้ากับ 𝕏 นั้นน่าปวดหัว YouMind เปลี่ยนร่าง Markdown ทั้งฉบับให้เป็นบทความ 𝕏 ที่สะอาดตาและพร้อมโพสต์ทันที

ลอง Markdown เป็น 𝕏

แพตเทิร์นให้ถอดรหัสเพิ่มเติม

บทความไวรัลล่าสุด

สำรวจบทความไวรัลเพิ่มเติม