ทำให้รอบการทำงานของเอเจนต์ที่มีรูปจำนวนมากอยู่ในหน่วยความจำ
รอบการทำงานของเอเจนต์ในโปรดักชันหน่วยความจำหมดที่ 952 MB ขณะที่กำลังตัดสินค้าออกจากสแกนแคตตาล็อกหกไฟล์และดูภาพที่ตัดแล้ว ไม่ได้มีการรั่วไหลเพียงจุดเดียว แต่มีสามจุด การเรียกโมเดลแต่ละครั้งส่งรูปของรอบนั้นซ้ำด้วยขนาดเต็ม ไลบรารีที่เราใช้เรียกโมเดลเก็บคำขอทุกคำขอไว้จนรอบจบ และระบบ tracing ของเฟรมเวิร์กเอเจนต์เก็บอินพุตของทุกการเรียก รวมถึงรูป ตลอดทั้งรอบ เราแก้ทั้งสามจุด: รูปถูกย่อก่อนส่ง และรูปของแต่ละการเรียกจะถูกปล่อยทันทีที่การเรียกนั้นเสร็จ
เอเจนต์เห็นรูปอย่างไร
เมื่อคุณสร้างบน Mythex เอเจนต์จะทำงานภายในแซนด์บ็อกซ์บนคลาวด์ของโปรเจกต์คุณ มันดูรูปได้ เช่น ภาพหน้าจอที่คุณแนบ mockup ไฟล์ในโปรเจกต์ หรือรูปที่มันเพิ่งสร้างเอง โดยส่งรูปไปยังโมเดลพร้อมกับการสนทนา
รอบ คือทุกอย่างที่เอเจนต์ทำเพื่อตอบข้อความหนึ่งจากคุณ ภายในรอบ เอเจนต์เรียกโมเดลหลายครั้ง: คิด เรียกเครื่องมือ ดูผลลัพธ์ แล้วคิดอีกครั้ง การเรียกแต่ละครั้งจะส่งการสนทนาที่ผ่านมาทั้งหมด ซึ่งรวมถึงรูปของรอบนั้นที่เข้ารหัสเป็นข้อความ (base64 ทำให้มีขนาดใหญ่ขึ้นประมาณหนึ่งในสาม)
รอบที่ล่มกำลังทำสิ่งที่สมเหตุสมผล มันรับสแกนแคตตาล็อกหกไฟล์ ขนาดราว 2.4 MB ต่อไฟล์ ตัดสินค้าแต่ละชิ้นออกมา และอ่านภาพที่ตัดแล้วกลับมาเพื่อตรวจ นั่นคือการเรียกโมเดลจำนวนมาก โดยแต่ละครั้งมีรูปขนาดใหญ่หลายรูป
สาเหตุที่หนึ่ง: ทุกการเรียกส่งรูปขนาดเต็มซ้ำ
การเรียกโมเดลแต่ละครั้งสร้างเนื้อหาคำขอใหม่ด้วยรูปของรอบที่ขนาดเต็ม ราว 20 MB ของ base64 ต่อการเรียก เนื้อหาถูกสร้างเป็นสตริงเดียวในหน่วยความจำของเอเจนต์ ดังนั้นทุกการเรียกจึงจัดสรรอีกราว 20 MB
การส่งขนาดเต็มไม่ได้ให้ประโยชน์อะไรเลย โมเดลจะย่อรูปลงเหลือราว 1,300 พิกเซลด้านยาวเองอยู่แล้ว พิกเซลที่เกินมีแต่จะเปลืองหน่วยความจำและเวลาอัปโหลด
วิธีแก้: ย่อครั้งเดียว ส่งเล็ก
- รูปที่ใหญ่กว่า 400 KB ตอนนี้จะถูกแปลงเป็น JPEG ที่มีด้านยาวไม่เกิน 1,568 พิกเซล ก่อนส่ง รูปที่เล็กกว่าจะส่งตามเดิม
- การแปลงใช้ ImageMagick ซึ่งมาพร้อมทุกแซนด์บ็อกซ์ของ Mythex และทำงานหนึ่งครั้งต่อไฟล์ รอบที่แสดงสแกนเดียวกันในทุกการเรียกจะใช้สำเนาที่ย่อแล้วซ้ำ แคชนี้ใช้คีย์เป็นพาธ ขนาด และเวลาแก้ไขของไฟล์ ไฟล์ที่ถูกแก้ไขจะถูกแปลงใหม่ และแคชมีขีดจำกัดขนาดของตัวเอง เพื่อไม่ให้มันกลายเป็นสิ่งที่ทำให้หน่วยความจำเต็ม
- งบประมาณรูปสำหรับการเรียกโมเดลหนึ่งครั้งลดจาก 16 MB เป็น 6 MB
หกสแกนเดียวกันตอนนี้ใช้ 4.4 MB ต่อการเรียก แทนที่จะเป็น 19.7 MB
สาเหตุที่สอง: ไคลเอนต์โมเดลไม่ปล่อยคำขอ
ขนาดของแต่ละคำขอเป็นเพียงส่วนหนึ่ง หน่วยความจำยังเพิ่มขึ้นเรื่อย ๆ ตามการเรียกที่สะสม ในสภาพแวดล้อม staging ของเรา รอบแบบเดียวกันถือหน่วยความจำที่ยังใช้งานอยู่ 369 MB แม้หลังจาก garbage collection เต็มรูปแบบแล้ว ซึ่งคือจุดที่ทุกอย่างที่เข้าถึงไม่ได้จะถูกคืน การสุ่มตัวอย่าง heap ระบุว่าการเติบโตทั้งหมดอยู่ในส่วนของไลบรารีไคลเอนต์โมเดลที่เข้ารหัสเนื้อหาคำขอ
สาเหตุคือ abort listener เมื่อคุณกดหยุด abort signal ของรอบจะทำงาน และทุกคำขอที่ฟังอยู่จะยกเลิก เราส่ง signal ระดับรอบเดียวนั้นไปให้ทุกคำขอ ไลบรารีไคลเอนต์จะเพิ่มตัวฟัง “abort” ไปยัง signal ที่ได้รับ และไม่เคยลบออกเลย
ดังนั้นทุกคำขอของรอบจะยังถูกอ้างอิงจาก signal ของรอบตลอดเวลาที่รอบยังดำเนินอยู่ คือ listener ซึ่งถือ controller ซึ่งถือคำขอ ซึ่งถือเนื้อหาคำขอทั้งหมดพร้อมรูป
วิธีแก้: หนึ่ง signal ต่อคำขอ
ตอนนี้แต่ละคำขอได้ signal ของตัวเอง ซึ่งจะเชื่อมกับ signal ของรอบเฉพาะตอนที่คำขอนั้นกำลังทำงาน สำหรับการตอบกลับแบบสตรีม คือจนกว่าชิ้นสุดท้ายจะถูกอ่าน และจะถูกยกเลิกการเชื่อมทันทีหลังจากนั้น การกดหยุดยังคงไปถึงคำขอที่กำลังทำงานอยู่ แต่ signal ของรอบจะถือ listener เพียงหนึ่งตัวในแต่ละครั้ง
การทดสอบครอบคลุมทั้งสามส่วนนั้น:
- หลังจาก 12 การเรียก จะไม่มี listener เหลือบน signal ของรอบ ก่อนแก้ไขมี 12
- ตลอด 15 คำขอขนาดคำขอละ 8 MB หน่วยความจำคงที่ ก่อนแก้ไขมันเพิ่มขึ้น 128 MB
- การกดหยุดยังคงยกเลิกคำขอที่กำลังสตรีมอยู่ได้ตรงกลาง
สาเหตุที่สาม: tracing เก็บอินพุตของทุกการเรียก
Snapshot ของ heap แสดงผู้ถือรายที่สาม คือประมาณ 6 MB ที่เก็บต่อการเรียกโมเดลหนึ่งครั้ง อันนี้มาจากการสตรีมเหตุการณ์ของเฟรมเวิร์กเอเจนต์ มันบันทึกทุกการเรียกโมเดลในรอบเป็นลูกของรอบพร้อมอินพุต จนกว่ารอบจะจบ รูปทุกรูปที่แนบกับทุกการเรียกจึงค้างอยู่ในหน่วยความจำไปด้วย
วิธีแก้: ล้างรูปเมื่อการเรียกคืนค่า
รูปจะแนบอยู่กับข้อความที่สร้างขึ้นสำหรับการเรียกโมเดลเฉพาะเท่านั้น และไม่เคยถูกเก็บไว้ในสถานะการสนทนาที่บันทึกไว้ ดังนั้นเมื่อการเรียกคืนค่าแล้ว จึงปลอดภัยที่จะล้างข้อมูลรูปในข้อความเหล่านั้น trace ยังคงเก็บบันทึกข้อความของการเรียก ส่วนพิกเซลจะหายไป
มีรายละเอียดหนึ่งที่สำคัญ: ต้องล้างรูปในที่เดิม เพราะ tracer อาจเข้าถึงข้อมูลรูปเดียวกันผ่านการอ้างอิงมากกว่าหนึ่งทางบนข้อความ การเปลี่ยนเป็นลิสต์ว่างใหม่จะทิ้งต้นฉบับ พร้อมพิกเซลทั้งหมด ไว้ให้เข้าถึงได้อยู่ สถานะการสนทนาที่บันทึกไว้จะไม่ถูกแก้ไขเลย
การทดสอบรันกราฟของเอเจนต์ภายใต้การสตรีมเหตุการณ์เดียวกันโดยมีรูป 4 MB ในทุกการเรียก ถ้าไม่มีการแก้ไข หน่วยความจำเพิ่มขึ้น 77 MB ตลอด 16 การเรียก เมื่อมีการแก้ไข หน่วยความจำคงที่
สิ่งที่เราได้เรียนรู้
- ปัญหาหน่วยความจำอาจซ้อนกันเป็นชั้น สาเหตุแยกกันสามอย่างต่างเก็บรูปไว้นานเกินไป และทั้งสามต้องถูกกำจัดก่อนที่รอบจะอยู่ในหน่วยความจำได้
- สิ่งที่คุณส่งให้ไลบรารีอาจถูกเก็บไว้ signal และ trace ต่างมีอายุยืนกว่าการเรียกที่ได้รับมา เมื่อหน่วยความจำเพิ่มขึ้นต่อการเรียก ให้ดูว่าการเรียกไปแตะต้องอ็อบเจกต์อายุยาวอะไรบ้าง
- อย่าส่งสิ่งที่อีกฝ่ายจะทิ้งอยู่แล้ว พิกเซลที่โมเดลจะทิ้งอยู่ดีทำให้เราเสียหน่วยความจำทุกการเรียก
- ทดสอบหน่วยความจำโดยตรง การแก้แต่ละอย่างมาพร้อมการทดสอบที่วัดการเติบโตของ heap หรือจำนวน listener ดังนั้นการถดถอยจะปรากฏเป็นตัวเลข ไม่ใช่การล่ม
นี่เป็นเรื่องหน่วยความจำที่สองจากเอเจนต์ในเวลาไม่นาน เรื่องแรกคือป้ายความคืบหน้าที่ส่งซ้ำ 41,000 ครั้ง หากคุณอยากรู้เพิ่มเติมว่าเอเจนต์คืออะไรและทำงานอย่างไร ดูคู่มือ AI agents และเอกสารแชท