วิธีสร้าง Coding Agent
(ghuntley.com)- ณ ปี 2025 การสร้าง Coding Agent ด้วยตัวเองเป็นหนึ่งในโปรเจกต์ที่ดีที่สุดที่นักพัฒนารายบุคคลควรลองทำ
- เอเจนต์สามารถทำงานได้ด้วยเพียง โค้ด 300 บรรทัด และ ลูปโทเค็นของ LLM และการลองสร้างสิ่งนี้จะเปิดโอกาสให้เปลี่ยนจากผู้บริโภคมาเป็น ผู้ผลิต AI
- องค์ประกอบพื้นฐานคือเครื่องมืออย่าง การอ่านไฟล์, การแสดงรายการไฟล์, การรัน Bash, การแก้ไขไฟล์, การค้นหาโค้ด ซึ่งช่วยให้สร้างความสามารถด้านอัตโนมัติได้จริง
- ในการเลือกโมเดล โมเดลเชิงเอเจนต์ อย่าง Claude Sonnet, Kimi K2 เหมาะสมกว่า และหากจำเป็นก็สามารถเชื่อมต่อโมเดลแบบออราเคิลอย่าง GPT เป็นเครื่องมือเพื่อทำการตรวจสอบในระดับสูงได้
- ในความเป็นจริง ผลิตภัณฑ์เชิงพาณิชย์อย่าง Amp, Cursor, Claude Code, GitHub Copilot ก็ใช้โครงสร้างคล้ายกัน
ภาพรวมเวิร์กช็อป
- เวิร์กช็อปฟรีที่จัดโดย Geoffrey Huntley ซึ่งสอนทั้งวิธีและหลักการของการสร้าง Coding Agent ด้วยตนเองโดยเน้นการลงมือปฏิบัติ
- เปรียบเทียบโครงสร้างและหลักการกับผู้ช่วย AI เชิงพาณิชย์ที่มีอยู่แล้ว เช่น Roo code, Cline, Amp, Cursor, Windsurf, OpenCode พร้อมเปิดโอกาสให้ได้ลองทำเอง
- จากประสบการณ์การสร้างจริง ผู้เข้าร่วมสามารถเติบโตจากผู้ใช้ AI ทั่วไปไปเป็น นักพัฒนาที่ใช้ AI เพื่อสร้างเครื่องมืออัตโนมัติด้วยตนเอง
- โครงสร้างหลักคือการใช้ลูปโทเค็นของ LLM ภายในโค้ดราว 300 บรรทัดเพื่อสร้างความสามารถแบบเอเจนต์
- มีการเพิ่มความสามารถระดับ primitive ของแต่ละเครื่องมือ (อ่าน, แสดงรายการไฟล์, รัน, แก้ไข, ค้นหาโค้ด) และเผยแพร่ตัวอย่างการทำงานจริงพร้อมโค้ดไว้ที่ GitHub repository
เอเจนต์คืออะไร
- ช่วงหลังมานี้คำว่า "เอเจนต์" ถูกใช้กันอย่างกว้างขวาง แต่ความหมายที่แท้จริงและ หลักการทำงานภายใน ยังไม่ชัดเจนนัก
- เมื่ออุปสรรคในการเริ่มสร้างเอเจนต์ลดลง จึงเป็นไปได้ที่จะก้าวข้ามจากผู้บริโภค AI ไปสู่ ผู้ผลิตที่ขับเคลื่อนระบบอัตโนมัติในการทำงาน
- ณ ปี 2025 หลักการสร้างเอเจนต์ ได้กลายเป็นความรู้จำเป็นพอ ๆ กับแนวคิดพื้นฐานของฐานข้อมูลอย่าง Primary key
- บริษัทอย่าง Canva เริ่ม สนับสนุนการใช้ AI ในกระบวนการสัมภาษณ์แล้ว และความสามารถด้านระบบอัตโนมัติด้วย AI ก็กลายเป็นองค์ประกอบสำคัญของการจ้างงาน
- จากนี้ไป สาเหตุที่ตามไม่ทันไม่ใช่เพราะ AI แต่เป็นเพราะไม่ยอมพัฒนาตัวเองและเรียนรู้เครื่องมือใหม่
หลักการสำคัญของ Coding Agent
- Coding Agent ประกอบขึ้นจากเพียง โค้ด 300 บรรทัด และ ลูปโทเค็นของ LLM โดยอาศัยการป้อนโทเค็นแบบวนซ้ำเพื่อทำงาน
- แนวคิดเรื่อง การทำงานพร้อมกัน (concurrent work) มีความสำคัญ
- ตัวอย่าง: แม้อยู่ในระหว่างประชุม Zoom เอเจนต์ก็ยังสามารถทำงานแบบขนานต่อไปได้ ช่วยเพิ่มประสิทธิภาพการทำงานอย่างมาก
- ไม่ใช่ LLM ทุกตัวจะมีความเป็นเอเจนต์
- 'ความปลอดภัยสูง' (เช่น Anthropic, OpenAI)
- 'ความปลอดภัยต่ำ' (เช่น Grok)
- 'ออราเคิล' (เหมาะกับการสรุปและการคิดขั้นสูง)
- 'เชิงเอเจนต์' (เอนเอียงไปทางการลงมือทำ, วนซ้ำเร็ว, เรียกใช้เครื่องมือเก่ง)
- นักพัฒนาควรเข้าใจ ลักษณะเฉพาะของแต่ละโมเดล และเลือกโมเดลให้เหมาะกับเป้าหมาย
- การจัดสรรคอนเท็กซ์วินโดว์แบบมากเกินไปส่งผลให้ประสิทธิภาพลดลง และควรจำไว้ว่า "ยิ่งใส่น้อย ผลลัพธ์ยิ่งดี"
- การลงทะเบียนเครื่องมือ MCP มากเกินไป นำไปสู่ประสิทธิภาพที่ลดลง
- กฎคือ “Less is more” → ควรวางเครื่องมือและข้อมูลในบริบทเท่าที่จำเป็นเพื่อให้ได้ประสิทธิภาพสูงสุด
ลำดับขั้นของการสร้าง Coding Agent
-
1. การลงทะเบียนเครื่องมือและการเรียกใช้ฟังก์ชัน
- ตัวอย่างเช่น ลงทะเบียน เครื่องมือตรวจสอบสภาพอากาศ ให้กับ LLM เพื่อให้ LLM ตอบสนองในรูปแบบการเรียกฟังก์ชันได้เมื่อเจอสถานการณ์ที่เหมาะสม
- MCP(Model Context Protocol) มีลักษณะคล้าย "แบนเนอร์ข้อมูลของฟังก์ชัน" และเพียงลงทะเบียนคำอธิบายของฟังก์ชันก็สามารถเรียกใช้อัตโนมัติได้
-
2. ความสามารถหลักของเครื่องมือ primitive แต่ละชนิด
- อ่านไฟล์(ReadFile): เมื่อส่งพาธเข้ามา จะอ่านเนื้อหาไฟล์เข้ามาเป็นคอนเท็กซ์
- แสดงรายการไฟล์(ListFiles): แสดงรายการไฟล์และโฟลเดอร์ในไดเรกทอรี
- รันคำสั่ง(Bash): ให้ LLM รันคำสั่งเชลล์ของระบบและส่งผลลัพธ์กลับมา
- แก้ไขไฟล์(Edit): ทำให้การสร้างหรือแก้ไขไฟล์ที่ระบุเป็นไปโดยอัตโนมัติ
- ค้นหาโค้ด(CodeSearch): ค้นหาได้อย่างรวดเร็วทั่วทั้งโค้ดเบสตามแพตเทิร์น คีย์เวิร์ด หรือชื่อฟังก์ชัน (ใช้ ripgrep)
-
3. ตัวอย่างและลำดับผลลัพธ์
- เมื่อนำแต่ละเครื่องมือมารวมเข้ากับ LLM ก็สามารถทำงานต่อเนื่องโดยอัตโนมัติได้ด้วยเพียงพรอมป์ต์ภาษาธรรมชาติ เช่น สร้างโค้ด FizzBuzz → ตรวจสอบการรัน, สำรวจไดเรกทอรี → วิเคราะห์เนื้อหา เป็นต้น
- ฟังก์ชันของเครื่องมือจะถูกเรียกตามลำดับให้สอดคล้องกับอินพุตของผู้ใช้หรือสถานการณ์ และวนซ้ำภายในลูปพร้อมส่งคืนผลลัพธ์
- ลำดับการทำงานหลักของเอเจนต์คือ: อินพุตผู้ใช้ → ตัดสินใจว่าจะเรียกเครื่องมือหรือไม่ → รันเครื่องมือ → ใส่ผลลัพธ์เข้าไปในคอนเท็กซ์ → ทำซ้ำ
ความสามารถในการขยายและโอเพนซอร์ส
- ปัจจุบัน Coding Agent ส่วนใหญ่ทำงานบนพื้นฐานของ เครื่องมือโอเพนซอร์สที่มีอยู่แล้ว เช่น ripgrep
- บน GitHub มีโปรเจกต์เอเจนต์ที่เรียบง่ายแต่ทรงพลังอย่าง SST Open Code, mini-swe-agent ซึ่งสร้างด้วยโค้ดเพียง 100 บรรทัด ทำให้สามารถใช้เป็นข้อมูลอ้างอิงด้านประสิทธิภาพและโครงสร้างได้
- นักพัฒนาควรทำความเข้าใจหลักการและการใช้งานผ่านการสร้างด้วยตนเอง มากกว่ามัวแต่เปรียบเทียบผลิตภัณฑ์ที่มีอยู่
- เมื่อนำไปใช้กับงานจริงและระบบอัตโนมัติ การสร้างเอเจนต์ใช้เองและขยายการใช้งานภายในองค์กรจะกลายเป็นความสามารถในการแข่งขัน
บทสรุปและข้อคิด
- Coding Agent ไม่ใช่เทคโนโลยีที่ซับซ้อน แต่ประกอบขึ้นจาก โครงสร้างลูปที่เรียบง่ายและการผสมผสานเครื่องมือ
- หัวใจสำคัญของการสร้าง Coding Agent คือ ความเข้าใจในโครงสร้างและความสามารถในการลงมือทำอย่างรวดเร็ว และประสบการณ์จากการสร้างเองช่วยให้รับมือกับการเปลี่ยนแปลงของเทคโนโลยี AI ได้อย่างกระตือรือร้น
- สิ่งสำคัญในตอนนี้ไม่ใช่ AI เพียงอย่างเดียว แต่คือ การพัฒนาตัวเองอย่างต่อเนื่องและการลงทุนกับความสามารถในการสร้างเครื่องมือ ซึ่งเป็นกลยุทธ์การเติบโตส่วนบุคคลที่สำคัญที่สุด ณ เวลานี้
- “สิ่งที่คุกคามไม่ใช่ AI ที่มาแย่งงานของคุณ แต่คือ เพื่อนร่วมงานของคุณที่ติดอาวุธด้วยเอเจนต์ ทำระบบอัตโนมัติ และทำงานได้เร็วกว่าเดิม”
4 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ทีม Princeton SWE-bench ของพวกเราเคยสร้างเอเจนต์ที่ทำผลงานได้ดีบน SWE-bench ด้วยโค้ดราว 100 บรรทัด ถ้าสนใจก็น่าลองดู mini-swe-agent
น่าทึ่งที่โครงสร้างมันเรียบง่ายได้ขนาดนี้จริง ๆ ขอบคุณที่แชร์ข้อมูลแบบนี้
โค้ดทั้งหมดจริง ๆ แล้วทำงานอยู่บนพรอมป์ต์เหล่านี้ ซอร์สโค้ดพรอมป์ต์พื้นฐานของเอเจนต์
ในตัวอย่างพรอมป์ต์ของเอเจนต์ ส่วนที่ว่า “1. ค้นหาและอ่านไฟล์ที่เกี่ยวข้องในโค้ดเบส 2. สร้างสคริปต์เพื่อทำให้บั๊กเกิดซ้ำ 3. แก้บั๊ก 4. ตรวจสอบการแก้ด้วยสคริปต์ 5. ทดสอบ edge case” มีประโยชน์มาก
ผมเองก็ใช้พรอมป์ต์ลักษณะคล้ายกันในลูปดีบัก
แนวทางแบบ “วิเคราะห์โค้ดเบสแล้วลิสต์สาเหตุที่เป็นไปได้ จัดอันดับตามความน่าจะเป็น แล้วตรวจสอบสมมติฐานด้วยสคริปต์หรือ debug logging” ช่วยรูทีนการแก้ปัญหาของผมได้มาก
การใช้ LLM แก้ปัญหานั้นง่ายมากเมื่อปัญหาอยู่ครบในไฟล์เดียว
แต่ในโค้ดเบสทั่วไป ไฟล์และบริบทมักกระจายกันไปหลายจุด เลยไม่ง่ายที่จะทำความเข้าใจให้ตรงกับเจตนาเชิงสถาปัตยกรรมและการจัดระเบียบที่นักพัฒนาวางไว้
ขอชื่นชมว่าเป็นความพยายามที่ยอดเยี่ยม แต่ก็น่าเสียดายที่มีเครื่องมือไม่มากนัก
โค้ดส่วนใหญ่เป็นกรอบงานของเอเจนต์ และโค้ดที่ปรับเฉพาะเพื่อ SWE จริง ๆ มีไม่มากอย่างที่คิด
ผมก็เคยทำ SWE agent เล่น ๆ เหมือนกัน ลองดู autocode ได้
เพิ่มไว้ในแหล่งอ้างอิงเพื่อเป็นการขอบคุณ
มี “คู่มือวิธีสร้างเอเจนต์” ที่คล้ายกันมาก เขียนโดย Thorsten Ball อยู่ใน AmpCode How To Guide ด้วย
โดยรวมแล้ว Amp ก็น่าสนใจไม่น้อย
ตอนนี้มันไม่ใช่บริการลับอีกต่อไปแล้ว แต่ก็ดีใจที่เครื่องมือเกี่ยวกับ agent coding ถูกเปิดเผยออกมาเรื่อย ๆ
คิดว่าในอนาคตซอฟต์แวร์หลากหลายแบบจะมีโมเดลเอเจนต์ลักษณะนี้ติดมาเป็นค่าเริ่มต้น
อันนี้อ่านสบายตากว่าเยอะ ขอบคุณมาก
มีการพูดถึงด้วยว่าผู้เขียนทำงานอยู่ที่ Amp
Ghuntley ก็ทำงานที่ Amp เช่นกัน
เขาว่าภาพหนึ่งภาพมีค่าพอ ๆ กับคำพูด 1000 คำ แต่ในเอกสารนี้รู้สึกเหมือนภาพต่าง ๆ ถูกลดค่าลง 99.6%
เลยสงสัยว่านี่คืออะไรกันแน่
ข้อความคือคำพูดจริงจากการบรรยายที่ถอดออกมาเป็นตัวอักษร
สงสัยว่ามีใครช่วยยืนยันเรื่องวิธีใช้เครื่องมือได้ไหม
ผมเข้าใจว่า Claude, ChatGPT ฯลฯ ให้ “tools” ผ่าน API และเมื่อมีคำขอเรียกใช้เครื่องมือเข้ามา ฝั่งผู้ตอบก็จะรันเครื่องมือนั้นจริงแล้วส่งผลลัพธ์กลับมา
แต่ในเมื่อโมเดลจริง ๆ เป็นระบบที่อิงตัวอักษรอย่างเคร่งครัด เลยสงสัยว่า API แปลงคำตอบของโมเดลให้กลายเป็นหลายโครงสร้างได้อย่างไร
เดาว่าในขั้นตอน fine-tuning น่าจะมีตัวอย่างที่ใส่การเรียกใช้เครื่องมือบางอย่างในรูปแบบบล็อกพิเศษ เพื่อให้โมเดลเข้าใจ แล้วฝั่งเซิร์ฟเวอร์ของ Claude/ChatGPT ก็ตีความมัน
เลยอยากรู้ว่ามีเอกสารเกี่ยวกับเรื่องนี้ไหม หรือมีข้อมูลเกี่ยวกับ special token ที่ใช้ภายในหรือเปล่า และเขาป้องกันไม่ให้ผู้ใช้โจมตี “โทเค็นที่มีหน้าที่เชิงความหมาย” นี้อย่างไร
มีเอกสารการติดตั้งใช้งานที่ Anthropic เผยแพร่ไว้
Anthropic Tool Use Documentation
จากตรงนี้จะเห็นได้ชัดว่าโมเดลจริง ๆ ไม่ได้ทำงานกับ “ข้อความ” แต่ทำงานในระดับโทเค็น
คล้ายกับที่คอมไพเลอร์พาร์สซอร์สโค้ดออกมาเป็นลำดับของ “โทเค็น” เช่น คีย์เวิร์ด วงเล็บ และโครงสร้างต่าง ๆ
ในเอาต์พุตจึงอาจมีทั้งคำจริงและเมทาดาทาปะปนกันได้
ในเชิงแนวคิด คุณเข้าใจถูกแล้ว
อินเทอร์เฟซที่แท้จริงกับ LLM มีเพียง “โทเค็น” เท่านั้น และไม่มีการแยกช่องทางควบคุมออกจากช่องทางข้อมูล
ที่ชั้น API ของโมเดลจะมีการแทรกคำสั่งสำหรับการเรียกใช้เครื่องมือและรายการเครื่องมือที่ใช้ได้ลงในพรอมป์ต์ พร้อมคำอธิบายของแต่ละตัว
เมื่อจำเป็นต้องเรียกใช้เครื่องมือ โมเดลจะใส่บล็อกพิเศษในคำตอบของมัน (มี special token และระบุชื่อเครื่องมือกับพารามิเตอร์) แล้วชั้น API จะดึงส่วนนี้ออกมาแปลงเป็น JSON
ผลการทำงานของเครื่องมือก็ถูกเข้ารหัสและแทรกกลับเข้าไปด้วย special token เช่นกัน
ชั้น API จะป้องกันไม่ให้ข้อมูลจากผู้ใช้ฉีดโทเค็นประเภทนี้เข้าไปเอง
โมเดลรุ่นใหม่ระดับ SoTA ถูก fine-tune เรื่องการเรียกใช้เครื่องมือมาอย่างมาก ทั้งในแบบเรียกเครื่องมือทั่วไปและกรณีเฉพาะเครื่องมือบางตัว (เช่น โมเดล Claude Sonnet ที่ถูกจูนให้เข้ากับเครื่องมือของ Claude Code)
มันน่าทึ่งพอสมควรที่ทุกอย่างทำงานได้ดีขนาดนี้ เพราะในการเรียกใช้เครื่องมือ fine-tuning มีบทบาทสำคัญมากจริง ๆ
ถ้าไม่มี fine-tuning มันก็ยังพอทำงานได้ แต่ความสำเร็จจะลดลงอย่างมาก
ผมคิดว่าข้อสันนิษฐานที่ว่า “ถูก fine-tune ให้ส่งตัวอย่างที่ต้องเรียกใช้เครื่องมือกลับมาในรูปแบบบล็อกพิเศษ” นั้นถูกต้อง
มันถูกฝึกให้ตอบในรูปแบบการเรียกใช้เครื่องมือเมื่อไม่รู้คำตอบดีพอหรือเมื่อได้รับคำสั่งให้ทำเช่นนั้น
มีทั้งการฝึกจากตัวอย่างการเรียกใช้เครื่องมือที่ทำตามฟอร์แมตนั้น และการฝึกที่ปรับเฉพาะกับเครื่องมือบางตัว
ตัวอย่างเช่น gpt-oss มีแนวโน้มจะใช้เครื่องมือค้นหาแม้ไม่มีการพูดถึงเลย
ในเอกสารของ Anthropic ยังแยกรายการเครื่องมือที่คุ้นเคยไว้ด้วย (เช่น
text_editor,bash) และมีความเป็นไปได้สูงว่าเครื่องมือเหล่านี้ถูกฝึกให้เข้าใจวิธีใช้อย่างลึกพอสมควรด้วยในทางปฏิบัติโครงสร้างพวกนี้แตกง่ายพอสมควร และทั้งหมดอาศัยสัญญาณระดับต่ำอย่าง “special token หรือ sequence ของโทเค็น”
คำพูดที่ว่า “ถ้าโยนโทเค็นเข้าไปในลูปเรื่อย ๆ เอเจนต์ก็จะเกิดขึ้น” ถ้าลองแทนคำว่า “โทเค็น” ด้วย “เงิน” จะรู้สึกว่าเป็นการเสียดสีโลกความจริงได้ดีมาก
สรุปคือแค่เผาเงินต่อเนื่อง เดี๋ยวเอเจนต์ก็เกิดขึ้นเอง
โมเดลแบบรันในเครื่องก็กำลังดีขึ้นเรื่อย ๆ
ตอนนี้ถ้าต้องการผลลัพธ์ที่ดีที่สุดยังคงต้องใช้โทเค็น (=เงิน) แต่อนาคตอาจเปลี่ยนไปได้มาก
พอมีแต่รูปแบบนี้มันอ่านยากมาก
ให้ความรู้สึกเหมือนกำลังดูตัวจำลองการเลื่อนหน้าจอ
สงสัยว่าทำไมต้องมีเครื่องมืออื่นนอกจาก bash
พวกการดูรายการไฟล์ การค้นหาและสำรวจ repo หรือการแก้เนื้อหาไฟล์ ใช้แค่ bash อย่างเดียวก็ทำได้ไม่ใช่หรือ
หรือว่านี่เป็นกรณีแบบที่แสดงในตัวอย่าง mini-swe-agent
ถ้ามองในเชิงเทคนิค ใช้แค่ bash ตัวเดียวก็ทำงานได้หลากหลายมาก และผมเองก็เคยสำเร็จแบบนั้นจริง
สิ่งที่น่าสนใจคือยิ่งจำกัดเครื่องมือ เอเจนต์ยิ่งหาทางแก้แบบสร้างสรรค์มากขึ้น
แต่ถ้าให้เครื่องมือหลากหลายที่ถูกฝึกไว้แล้ว โมเดลจะรู้วิธีใช้ของแต่ละตัวดีอยู่แล้ว ทำให้ใช้โทเค็นได้คุ้มกว่าและอัตราความสำเร็จโดยรวมสูงขึ้น
ถ้าใช้แต่ bash มันก็มักหลงกับพวก bashism การจัดการอาร์กิวเมนต์ หรือการจัดการช่องว่างบ่อย ๆ
การมีเครื่องมือแยกต่างหากง่ายกว่าการยัดทุกอย่างลงใน bash ตัวเดียวมาก
ถ้าจะทำทุกอย่างผ่าน bash คุณต้องสร้างระบบแยกว่าคำสั่งไหนปลอดภัยจนรันได้ทันที (เช่น ดูรายการไฟล์) และคำสั่งไหนเสี่ยงจนต้องขออนุมัติผู้ใช้ก่อน
ถ้ามีเครื่องมือสำหรับดูรายการไฟล์แยกออกมา ก็ยังป้องกันไม่ให้ไฟล์นอกไดเรกทอรีโปรเจกต์ถูกเปิดเผยได้ด้วย
ในทางปฏิบัติ ใช้แค่เครื่องมือ bash กับ Edit ก็เพียงพอให้ coding agent ทำงานได้แล้ว (Edit ไม่ถึงกับบังคับ แต่ถ้าไม่มีจะไม่มีประสิทธิภาพมาก)
แต่ในส่วนค้นหาโค้ดอาจจะลำบากหน่อย
เช่น อาจแก้ได้ด้วยการปรับพรอมป์ต์ให้ใช้ ripgrep ผ่าน bash
ทำไมต้องมี IDE? ทั้งที่ทุกอย่างทำได้จากเชลล์อยู่แล้ว ทำไมถึงยังใช้?
UI (อินเทอร์เฟซ) มีหน้าที่มอบข้อมูลและแอ็กชันที่จำเป็นในจังหวะที่ต้องใช้พอดี
สำหรับคำถามว่าทำไมถึงมีอย่างอื่นนอกจาก bash tool ผมคิดว่าน่าจะเป็นเพราะเริ่มต้นด้วยเครื่องมือให้น้อยที่สุดก่อน แล้วค่อยเพิ่ม bash ทีหลังได้
แทนที่จะอธิบายยืดยาวว่า “วิธีสร้างเอเจนต์” ผมอยากเห็นโปรเจกต์ที่เอเจนต์สร้างขึ้นจริงมากกว่า
มีใครอธิบายได้ไหมว่าแกนอย่าง Oracle, Agent, high safety, low safety หมายถึงอะไร
ผมลองด้วยตัวเองบนโมเดล on-device ของ edge และ chrome (phi4-mini, gemini nano) แล้ว และแปลกใจที่มันทำงานได้ดีทีเดียวเมื่อเทียบกับขนาดโมเดล
กรณีทดลอง how to build an agent on device
โคตรฮาเลย 555555 ตอนแรกก็งงว่าหมายถึงอะไร แต่พอกดลิงก์เข้าไปแล้วก็เข้าใจแจ่มแจ้งเลย
ภาพตัวอย่างของโพสต์อื่น ๆ ในบล็อกก็ห่วยเหมือนกัน ดูแล้วไม่อยากกดเข้าไปเลย
55555555555555555555555