- โครงสร้างหลักของ AI coding assistant ไม่ใช่เวทมนตร์ที่ซับซ้อน แต่ประกอบด้วย โค้ด Python แบบเรียบง่าย ราว 200 บรรทัด
- ระบบทำงานบนพื้นฐานของ ลูปการสนทนากับ LLM โดยเมื่อ LLM ขอเรียกใช้เครื่องมือ โค้ดฝั่งโลคัลจะเป็นผู้รันเครื่องมือนั้นและส่งผลลัพธ์กลับไป
- เครื่องมือพื้นฐานที่จำเป็นมี 3 อย่างคือ อ่านไฟล์ (read), แสดงรายการไฟล์ (list) และ แก้ไขไฟล์ (edit) ซึ่งเพียงพอสำหรับการสำรวจโปรเจกต์และแก้ไขโค้ด
- LLM จะตัดสินใจเองว่าจะเรียกใช้เครื่องมือใด เมื่อไร โดยอิงจาก signature และคำอธิบาย (docstring) ของเครื่องมือ
- โครงสร้างนี้เหมือนกับแกนหลักของผลิตภัณฑ์เชิงพาณิชย์อย่าง Claude Code และแสดงให้เห็นว่า แม้โครงสร้างจะเรียบง่าย ก็สามารถสร้าง coding agent ที่ทรงพลังได้
แนวคิดพื้นฐานของ coding agent
- coding agent คือ ระบบที่ทำงานผ่านการสนทนากับ LLM รับคำสั่งจากผู้ใช้แล้วลงมือทำงานกับไฟล์จริงผ่านการเรียกใช้เครื่องมือ
- ผู้ใช้ป้อนคำขออย่าง “สร้างไฟล์ใหม่ที่มีฟังก์ชัน hello world”
- LLM ตอบกลับเป็นการเรียกใช้เครื่องมือในรูปแบบ JSON
- โปรแกรมรันเครื่องมือนั้นและส่งผลลัพธ์กลับไปให้ LLM อีกครั้ง
- LLM ไม่ได้เข้าถึงระบบไฟล์โดยตรง แต่ทำได้เพียง ส่งคำขอ และให้โค้ดฝั่งโลคัลเป็นผู้จัดการงานจริง
เครื่องมือที่ต้องมี 3 อย่าง
- read_file: อ่านเนื้อหาทั้งหมดของไฟล์ที่ระบุแล้วส่งกลับ
- list_files: ส่งกลับรายการไฟล์และโฟลเดอร์ภายในไดเรกทอรี
- edit_file: แทนที่สตริงเดิมด้วยสตริงใหม่ หรือถ้า
old_str ว่างอยู่ ก็จะสร้างไฟล์ใหม่
- หากไม่พบสตริงที่จะนำมาแทนที่ จะส่งกลับว่า “old_str not found”
- เพียงมีเครื่องมือ 3 อย่างนี้ ก็สามารถ สร้าง แก้ไข และสำรวจไฟล์ ได้แล้ว
การลงทะเบียนเครื่องมือและการเชื่อมกับ LLM
- เครื่องมือทั้งหมดถูกลงทะเบียนไว้ใน TOOL_REGISTRY ด้วยชื่อและฟังก์ชัน ทำให้ LLM สามารถเรียกใช้ได้
- ระบบจะดึง docstring และ signature ของแต่ละเครื่องมือส่งต่อให้ LLM
- system prompt จะบอก LLM อย่างชัดเจนเกี่ยวกับ “รายการเครื่องมือที่ใช้ได้” และ “รูปแบบการเรียกใช้”
- การเรียกใช้เครื่องมือถูกจำกัดให้อยู่ในรูปแบบ
'tool: TOOL_NAME({JSON_ARGS})'
- ผลลัพธ์จากการรันเครื่องมือจะถูกส่งกลับไปยัง LLM ในรูปแบบ
tool_result(...)
การพาร์สการเรียกใช้เครื่องมือและการจัดการคำตอบของ LLM
- ระบบจะค้นหาบรรทัดที่ขึ้นต้นด้วย
tool: จากคำตอบของ LLM เพื่อ ดึงชื่อเครื่องมือและอาร์กิวเมนต์ (JSON)
- หลังจากรันแต่ละเครื่องมือแล้ว ผลลัพธ์จะถูก serialize เป็น JSON และเพิ่มเข้าไปในประวัติการสนทนา
- ฟังก์ชัน execute_llm_call มีหน้าที่เรียก LLM API และส่งกลับข้อความตอบกลับ
- run_coding_agent_loop รับอินพุตจากผู้ใช้และรักษาลูปการสนทนากับ LLM เอาไว้
- ลูปภายในจะทำซ้ำไปเรื่อย ๆ จนกว่า LLM จะไม่ร้องขอการเรียกใช้เครื่องมืออีก
ตัวอย่างการทำงานและความสามารถในการขยาย
- ตัวอย่างบทสนทนา:
- “สร้างไฟล์ hello.py และเขียน hello world ให้หน่อย” → เรียก
edit_file เพื่อสร้างไฟล์ใหม่
- “เพิ่มฟังก์ชันคูณเลขสองตัวลงใน hello.py” → เรียก
read_file ก่อน แล้วตามด้วย edit_file
- สามารถสร้าง coding assistant ที่ใช้งานได้ครบถ้วนด้วยโค้ดเพียงประมาณ 200 บรรทัด
- ผลิตภัณฑ์เชิงพาณิชย์จะต่อยอดจากจุดนี้ด้วย การจัดการข้อผิดพลาด, การตอบกลับแบบสตรีม, การจัดการคอนเท็กซ์, เครื่องมือเพิ่มเติม และขั้นตอนการอนุมัติ
- แต่โครงสร้างแกนกลางยังเหมือนเดิม คือ ลูปเรียบง่ายที่ LLM เป็นผู้ตัดสินใจ และโค้ดเป็นผู้ลงมือทำ
การลงมือทำและการต่อยอด
- ซอร์สโค้ดทั้งหมดมีความยาวราว 200 บรรทัด และสามารถขยายได้ด้วย การเปลี่ยนผู้ให้บริการ LLM หรือเพิ่มเครื่องมือใหม่
- แม้โครงสร้างจะเรียบง่าย ก็ยังสามารถสร้าง ต้นแบบ AI coding agent ที่ทรงพลัง ได้ด้วยตัวเอง
1 ความคิดเห็น
ความเห็นจาก Hacker News
สิ่งที่ฉันอยากเพิ่มคือ planning
หัวใจของการใช้เครื่องมืออย่างมีประสิทธิภาพคือการตระหนักว่าเครื่องมือเหล่านี้ทำงานอยู่บน รายการ TODO แบบไดนามิก
โหมด Plan ทำหน้าที่บูตสแตรปว่ารายการนั้นถูกตั้งต้นอย่างไร และแต่ละรายการจะถูกรันเมื่อใด
การโต้ตอบกับผู้ใช้ทำงานในลักษณะของการจัดลำดับรายการนั้นใหม่
เดือนก่อนฉันทดลองดูว่า Claude Code แก้โจทย์ CTF ได้ดีแค่ไหน แล้วพบว่าถ้าปิดเครื่องมือ TodoList และ planning ประสิทธิภาพจะตกลง 1~2 ระดับ
วิดีโอที่เกี่ยวข้องดูได้ที่ Breaking Bots: Cheating at Blue Team CTFs with AI Speed Runs
สิ่งที่น่าสนใจคือหลายคนมัวแต่โฟกัสว่า “จะใช้ plan mode หรือไม่” แต่จริง ๆ แล้วรายการ TODO เปิดใช้งานอยู่ตลอดเวลา
และเวลาเห็นบทความที่ลดทอน “การจัดการคอนเท็กซ์อย่างชาญฉลาด” ให้เหลือแค่รายการ TODO ธรรมดา ๆ ก็ชวนให้ขำเหมือนกัน
หลายคนพยายามทำเองแล้วเสียเวลาไปเป็นปี เพราะ ผลการประเมินที่พังในสภาพแวดล้อม production
จะเพิ่มสิ่งนี้เข้าไปเป็น reasoning token เฉย ๆ ก็ได้ แต่ในทางปฏิบัติการทำเป็น เครื่องมือเก็บข้อมูลแบบ explicit single-key จะคาดเดาได้และมีประสิทธิภาพกว่ามาก
ฉันคิดว่าแนวทางเรียบง่ายแบบนี้น่าจะใช้ได้กับไอเดียเครื่องมืออื่น ๆ ที่เก็บโครงสร้างทางภาษาเช่นกัน
ตอนทดสอบ Codex ฉันใช้เวลาประมาณ 10 นาทีสรุปสเปกแล้วแบ่งออกเป็นรายการเปลี่ยนแปลง จากนั้นให้มันบันทึกลงไฟล์ และสั่งให้ทบทวน-แก้ไขแผนหลังจบแต่ละการเปลี่ยนแปลง
วิธีนี้ทำให้ LLM โฟกัสกับ งานสั้น ๆ ที่ยึดตามเป้าหมาย ได้ โดยไม่ต้องป้อนพรอมป์ตต่อเนื่องตลอดเวลา
โดยหลักการแล้วให้ผลคล้ายกับการมีซับเอเจนต์
บางทีก็กำหนด todo สุดท้ายว่า “ให้ทบทวนงานทั้งหมดอีกครั้งและตรวจคุณภาพด้วย linter เป็นต้น”
ตอนบีบอัดคอนเท็กซ์ มันยังถูกใช้เป็น ตัวแทนแบบกระชับ ของเซสชันด้วย
แก่นของเอเจนต์เขียนโค้ดจริง ๆ แล้วก็คือ ลูปกับโครงสร้างการเรียกใช้เครื่องมือ ที่เรียบง่าย
แต่ถ้าจะเขียนบทความชื่อประมาณ “The Emperor Has No Clothes: How to Code Claude Code in 200 Lines of Code” ก็ควรอ้างถึง How to Build an Agent ของ Thorsten Ball ด้วย
เพราะบทความนั้นเป็นตัวแรก ๆ ที่เสนอไอเดียว่า “แก่นแท้ของเอเจนต์นั้นเรียบง่าย”
แน่นอนว่าในความเป็นจริงยังต้องมี TODO และ scaffolding อีกหลายอย่าง และตัว Claude Code เองก็มีการตั้งค่าที่ซับซ้อน ปลั๊กอิน และฟีเจอร์ UI จำนวนมาก
ถึงอย่างนั้น ถ้ามีแค่ลูปขั้นต่ำ ก็ยังบูตสแตรปให้มันขยายความสามารถเองได้
ถ้าอยากดูการทำงานภายใน สามารถใช้ claude-trace เพื่อติดตามการโต้ตอบระหว่าง LLM กับการเรียกใช้เครื่องมือได้
นอกจากลูปง่าย ๆ แล้ว ยังมีองค์ประกอบซับซ้อนอีกมาก เช่น uuid threading, การประมวลผล message queue, snapshot ของการเปลี่ยนไฟล์, sidechain ของซับเอเจนต์ เป็นต้น
เพราะงั้น “200 บรรทัด” จึงถูกในเชิงแนวคิด แต่ถ้าจะให้ถึงระดับ production จริง ๆ ซับซ้อนกว่านั้นมาก
Codex ยังไม่มีฟีเจอร์ queueing แต่ก็ยังทรงพลังอยู่ดี
ฉันสร้างแอป macOS ชื่อ Contextify เพื่อมอนิเตอร์ transcript ของ CLI จาก Claude Code และ Codex แบบเรียลไทม์ และใช้ฟีเจอร์ Total Recall เพื่อ query ประวัติการสนทนาได้
โมเดล Claude ถูกฝึกด้วย สคีมาเครื่องมือ str replace ของตัวเอง
การเขียนไฟล์ทั้งก้อนใหม่ไม่มีประสิทธิภาพ ดังนั้นการแก้ไขเฉพาะบางส่วนจึงเป็นหัวใจสำคัญ
ในความเป็นจริงยังมีองค์ประกอบมากกว่านั้น
ตัวอย่างเช่น บางครั้งเอเจนต์เกิด early stopping แล้วจบงานไปเอง
แม้แต่ reasoning model รุ่นล่าสุดก็ยังแก้ไม่ได้
Claude Code แก้ปัญหานี้ด้วยการ inject TODO เข้าไปในทุกพรอมป์ตเพื่อเตือนว่ายังมีงานอะไรเหลืออยู่
ในรีโพสาธารณะของ HolmesGPT มี benchmark ทดลองหลายแบบ
ตอนแรกแนวคิดที่ว่า “แค่บอก LLM ถึงรายการเครื่องมือและรูปแบบการเรียกใช้ก็พอ” ทำให้ฉันช็อกมาก
เพราะ LLM ก็แค่สร้างข้อความ แล้วมันจะเรียกใช้เครื่องมือได้อย่างไร แต่พอเข้าใจว่า มันมีแค่นั้นจริง ๆ ก็รู้สึกเหมือนเวทมนตร์
ช่วงวันหยุดฉันลองสร้างเอเจนต์เขียนโค้ดบน Opus ที่ใช้ Prolog DSL (เกิน 200 บรรทัด)
น่าแปลกที่มันใช้งานได้ดีแทบจะทันที
ดูเหมือนว่าโมเดลเจเนอเรชันล่าสุดจะมาถึงจุดที่ ความสำคัญของ agent harness ลดลงแล้ว
ดูการทดลองที่เกี่ยวข้องได้ในโพสต์นี้
เมื่อ 1 ปีก่อน บทความนี้ถือว่าแม่นยำพอสมควร แต่ตอนนี้ harness พัฒนาไปมากแล้ว จนโมเดลลูปแบบง่าย อธิบายการทำงานจริงของ Claude Code ได้ยาก
แม้เป็นเอเจนต์เรียบง่าย ถ้าใช้โมเดลเดียวกัน ความต่างด้านประสิทธิภาพก็ไม่ได้มาก
คล้ายกับบทเรียน “สร้าง DB เอง” ที่แสดง B-tree พื้นฐานให้ดู
Subagent, MCP และ Skills อยู่ในระดับกลาง ๆ ส่วนการปรับคอนเท็กซ์ให้เหมาะสมมีนัยสำคัญเฉพาะกับการรันระยะยาวเท่านั้น
codex-cliจะช่วยให้วิเคราะห์โมเดลได้ดีขึ้นฉันสร้าง agent loop สำหรับงานองค์กรด้วยตัวเอง และตอนนี้ประมวลผลมากกว่า 1 พันล้านโทเคนต่อเดือน
ลูปแบบง่ายคือแกนหลักก็จริง แต่ในสภาพแวดล้อมจริงมีรายละเอียดมากมายที่ทำให้ความซับซ้อนพุ่งสูงมาก
ตัวอย่างเช่น ถ้าผู้ใช้ส่งข้อความเข้ามาระหว่างทาง จะจัดการลูปอย่างไร จะซิงก์ อินพุตแบบ webhook จาก Slack หรือบริการคล้ายกันอย่างไร
ยังมีเรื่อง approval, guardrail, การจัดการ task แบบ asynchronous ฯลฯ ให้ต้องคิดอีกมาก
ฉันกำลังคิดอยู่ว่าจะเอาประสบการณ์เหล่านี้มาเขียนเป็นบล็อกดีไหม
บทความที่น่าอ่านอ้างอิงได้มี You Should Write An Agent และ How To Build An Agent
ทีม SWE-bench ของเราเปิดซอร์สเอเจนต์ขนาด 100 บรรทัดออกมาแล้ว
มันคือ mini-swe-agent ซึ่งได้รับความนิยมทั้งในแวดวงวิชาการและอุตสาหกรรม
เป็นจุดเริ่มต้นที่ดีสำหรับการเรียนรู้โลกของเอเจนต์
ในปี 2023 เคยมีบทความชื่อ “reimplement LangChain in 100 lines”
ฉันดู บทความนั้น แล้วลองทำตามจริง และเอาไปใช้ในหลายโปรเจกต์