Chapter 7 — Refactor to Modular Engine & Personal CLI
ทำไมต้องชื่อนี้และเรียนไปเพื่ออะไร?
ตลอด Day 1–6 เราสร้างชิ้นส่วนอวัยวะครบหมดแล้ว: Ollama Client (Day 1), JSON Parser (Day 2), Tool Dispatcher (Day 3), File Reader Tool (Day 4), Maven Test Runner (Day 5), และ Autonomous ReAct Engine (Day 6) แต่ทุกอย่างยังกระจัดกระจายอยู่ตามสคริปต์ทดสอบย่อยๆ เช่น test_tool_dispatch.py หรือ test_react_loop.py
Day 7 คือหมุดหมายฉลองความสำเร็จของ Phase 1: เราจะทำ Modular Refactor รวบรวมทุกโมดูลเข้าเป็น Package สถาปัตยกรรมที่สะอาด และสร้าง Personal CLI (Command Line Interface) ในไฟล์ main.py เพื่อให้คุณสั่งงาน Agent ได้จริงผ่าน Terminal เช่น python main.py --task "..." หรือเข้าสู่ Interactive Shell Mode สั่งให้ AI ตรวจโค้ด รันเทส และแก้ปัญหาได้จากหน้าจอเดียว สะดวกเหมือนมี Senior Software Engineer นั่งประจำการอยู่ข้างๆ คุณบน MacBook M3 16GB!
เมื่อสิ้นสุด Day 7 โฟลเดอร์โปรเจกต์ se_agent/ ของเราจะได้รับการ Refactor จนกลายเป็นระบบซอฟต์แวร์แบบ Production-ready ที่สมบูรณ์แบบ 100%:
ทำหน้าที่เป็น Application CLI Entrypoint ใช้ไลบรารีมาตรฐาน argparse เพื่อรับคำสั่งจากผู้ใช้ใน Terminal, จัดเตรียม Dependency Injection (ผูก Client + Dispatcher + Tools เข้ากับ Engine) และรองรับทั้งโหมด --task (รันครั้งเดียวจบ) และ --interactive (คุยโต้ตอบต่อเนื่อง)
ทำหน้าที่สร้าง Public API Boundary ให้กับ Package เพื่อให้โค้ดภายนอกสามารถเรียก from core import ReActAgentEngine หรือ from core.tools import read_file ได้โดยตรง โดยไม่ต้องรู้โครงสร้างไฟล์ภายในลึกๆ
สคริปต์ที่รวมทุกอย่างในไฟล์เดียว 1,000 บรรทัดจะยากต่อการทดสอบ (Testability ต่ำ), ไม่สามารถนำฟังก์ชันไป Reusable ในโปรเจกต์อื่นได้ และเมื่อมีทีมงานมาช่วย จะเกิด Git Merge Conflict ตลอดเวลา
แยกความรับผิดชอบชัดเจน (Client ดูแลเน็ตเวิร์ก, Parser ดูแลไวยากรณ์, Tools ดูแล OS, Engine ดูแลลูป) และมี main.py เป็นจุดประกอบร่าง (Composition Root) ทำให้เพิ่ม Tool ใหม่หรือเปลี่ยนโมเดลได้ทันทีโดยไม่แตะต้องโค้ดแกนกลาง
# 1.1 ไฟล์ core/tools/__init__.py
"""
Package รวบรวมเครื่องมือ (Tools) ปฏิบัติการของ Agent
"""
from core.tools.file_reader import read_file, FileAccessError
from core.tools.test_runner import run_maven_test, BuildExecutionError
__all__ = ["read_file", "FileAccessError", "run_maven_test", "BuildExecutionError"]
# -------------------------------------------------------------
# 1.2 ไฟล์ core/__init__.py
"""
Core Package ของ Autonomous Software Engineering Agent
"""
from core.client import OllamaLLMClient
from core.parser import StructuredOutputParser, JSONDecodeError
from core.dispatcher import ToolDispatcher, ToolExecutionError
from core.engine import ReActAgentEngine, MaxIterationsReachedError
__all__ = [
"OllamaLLMClient",
"StructuredOutputParser",
"JSONDecodeError",
"ToolDispatcher",
"ToolExecutionError",
"ReActAgentEngine",
"MaxIterationsReachedError"
]
import sys
import argparse
from core import (
OllamaLLMClient,
ToolDispatcher,
ReActAgentEngine,
MaxIterationsReachedError
)
from core.tools import read_file, run_maven_test
DEFAULT_SYSTEM_PROMPT = (
"You are an Autonomous Software Engineering Agent running locally on Apple Silicon Mac.\n"
"You operate in a strict ReAct loop (Reason, Act, Observe).\n"
"You have access to tools to inspect local code and run automated build tests.\n\n"
"Available Tools:\n"
"1. run_maven_test(project_dir: string): Runs Maven unit tests for a Java project.\n"
"2. read_file(filepath: string): Reads file content safely from the workspace.\n\n"
"Response Rules:\n"
"- When you need to take an action, reply strictly with:\n"
"{\n"
' "thought": "Your reasoning why you take this step",\n'
' "tool": "tool_name",\n'
' "arguments": { ... }\n'
"}\n\n"
"- When the task is completely finished, reply strictly with:\n"
"{\n"
' "thought": "Summary of what was achieved",\n'
' "final_answer": "Detailed conclusion and analysis for the human user"\n'
"}\n\n"
"Respond ONLY with valid JSON."
)
def create_agent(model_name: str, max_iterations: int = 6) -> ReActAgentEngine:
"""
Factory Function: จัดเตรียมและประกอบร่าง Agent Component ทั้งหมด
"""
# 1. เชื่อมต่อสมองกล Ollama Daemon
client = OllamaLLMClient(model_name=model_name)
if not client.check_health():
print(f"❌ ไม่พบโมเดล '{model_name}' หรือ Ollama Daemon ไม่ได้เปิดทำงาน")
print("👉 โปรดตรวจสอบคำสั่ง 'ollama list' หรือสั่งรัน 'ollama serve'")
sys.exit(1)
# 2. ติดตั้งและจดทะเบียน Tools ทั้งหมด
dispatcher = ToolDispatcher()
dispatcher.register("read_file", read_file)
dispatcher.register("run_maven_test", run_maven_test)
# 3. ส่งต่อเข้าสู่ ReAct Engine
return ReActAgentEngine(client=client, dispatcher=dispatcher, max_iterations=max_iterations)
def run_single_task(agent: ReActAgentEngine, task: str) -> None:
"""สั่ง Agent ทำงานโจทย์เดี่ยวแบบ One-off Execution"""
print(f"\n🎯 กำลังเริ่มประมวลผลโจทย์: \"{task}\"\n" + "-" * 60)
try:
final_answer = agent.run(user_prompt=task, system_prompt=DEFAULT_SYSTEM_PROMPT)
print("\n" + "=" * 60)
print("🎉 ผลลัพธ์สุดท้ายจาก Autonomous Agent:")
print("=" * 60)
print(final_answer)
print("=" * 60)
except MaxIterationsReachedError as err:
print(f"\n🛑 Guardrail Alert: {err}")
except KeyboardInterrupt:
print("\n⚠️ ผู้ใช้สั่งยกเลิกการทำงานกลางคัน (Ctrl+C)")
except Exception as err:
print(f"\n❌ ข้อผิดพลาดไม่คาดคิด: {err}")
def run_interactive_mode(agent: ReActAgentEngine) -> None:
"""เปิดโหมดโต้ตอบใน Terminal พูดคุยสั่งงานต่อเนื่อง"""
print("\n💬 เข้าสู่โหมด Interactive Agent CLI (พิมพ์ 'exit' หรือ 'quit' เพื่อออก)")
print("-" * 60)
while True:
try:
user_input = input("\n🧑💻 คุณ > ").strip()
if not user_input:
continue
if user_input.lower() in ["exit", "quit", "q"]:
print("👋 ลาก่อนครับ! ขอบคุณที่ใช้งาน Local SE Agent")
break
run_single_task(agent, user_input)
except (KeyboardInterrupt, EOFError):
print("\n👋 ปิดการทำงาน Interactive Mode เรียบร้อย")
break
def main():
parser = argparse.ArgumentParser(
description="Local Autonomous Software Engineering Agent CLI (Mac M3 Optimized)"
)
parser.add_argument(
"-t", "--task",
type=str,
help="ป้อนคำสั่งให้ Agent ลงมือทำ 1 งานแล้วจบ เช่น: --task 'ตรวจบั๊กใน sample_maven_project'"
)
parser.add_argument(
"-i", "--interactive",
action="store_true",
help="เปิดโหมด Interactive Prompt ทำงานต่อเนื่องใน Terminal"
)
parser.add_argument(
"-m", "--model",
type=str,
default="qwen2.5-coder:7b",
help="ระบุชื่อโมเดล Ollama ที่ต้องการใช้งาน (ค่าเริ่มต้น: qwen2.5-coder:7b)"
)
parser.add_argument(
"--max-iterations",
type=int,
default=6,
help="จำนวนรอบสูงสุดที่อนุญาตให้ ReAct Engine วนลูป (ค่าเริ่มต้น: 6)"
)
args = parser.parse_args()
# แสดง Banner ต้อนรับ
print("=" * 60)
print("🤖 Local SE Agent CLI • Phase 1 Production Standard")
print(f"📦 Active Model: {args.model} | Guardrail Limit: {args.max_iterations} steps")
print("=" * 60)
agent = create_agent(model_name=args.model, max_iterations=args.max_iterations)
if args.task:
run_single_task(agent, args.task)
elif args.interactive or len(sys.argv) == 1:
run_interactive_mode(agent)
if __name__ == "__main__":
main()
ชำแหละไวยากรณ์สำคัญใน main.py
argparse.ArgumentParser(...)
ไลบรารีมาตรฐานของ Python ที่แปลงคำสั่ง Terminal ให้มี flag --help, -t/--task, และ -i/--interactive แบบมืออาชีพโดยไม่ต้องลง package เพิ่ม
create_agent(model_name, max_iterations)
รูปแบบ Composition Root / Factory Pattern ที่รวบรวมการตรวจสอบ Health check, สร้าง Dispatcher, ผูก Tools และสร้าง ReAct Engine ไว้ในจุดเดียว ทำให้โค้ดสะอาดและเทสง่าย
except KeyboardInterrupt:
การดักจับสัญญาณ SIGINT (เมื่อผู้ใช้กด Ctrl+C บน Mac) ช่วยให้โปรแกรมหยุดทำงานอย่างสง่างาม แทนที่จะพ่น Traceback แดงเถือกน่าตกใจ
เปิด Terminal บน MacBook M3 แล้วทำตาม 5 ขั้นตอนนี้เพื่อจัดระเบียบโปรเจกต์และรัน Personal CLI ใหม่ของคุณทันที:
cd ~/se_agent
source .venv/bin/activate
mkdir -p tests_archived
# ย้ายไฟล์เทส Day 1-6 ไปเก็บไว้ใน tests_archived เพื่อให้ Root สะอาด
mv test_connection.py test_structured_output.py test_tool_dispatch.py tests_archived/ 2>/dev/null || true
mv test_file_reader.py test_maven_runner.py test_react_loop.py tests_archived/ 2>/dev/null || true
touch main.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 1.1) ไปวางใน core/tools/__init__.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 1.2) ไปวางใน core/__init__.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 2) ไปวางใน main.py
python main.py --help
คุณจะเห็นหน้าจอช่วยเหลือของ CLI แสดงคำอธิบาย --task, --interactive, --model สวยงามเหมือนโปรแกรมสากล
python main.py --task "ช่วยตรวจโปรเจกต์ sample_maven_project รันเทส ส่องดูโค้ดที่มีบั๊ก แล้วสรุปวิธีแก้ให้ฟัง"
🎉 ผลลัพธ์: CLI จะสั่ง Agent วนลูป คิด ➔ รัน mvn test ➔ อ่าน Calculator.java ➔ สรุปคำตอบแก้บั๊ก return price - discount; และพิมพ์รายงานในหน้าจอ Terminal ของ Mac M3 อย่างสมบูรณ์แบบ 100%!
ข้อที่ 1: วัตถุประสงค์หลักของ "Day 7: Refactor to Modular Engine & Personal CLI" คืออะไร?
ข้อที่ 2: บทบาทของไฟล์ `core/__init__.py` ในการทำ Modular Packaging คืออะไร?
ข้อที่ 3: ไลบรารีมาตรฐานใดใน Python ที่ใช้สำหรับสร้าง Command Line Interface (CLI) พร้อมรองรับ Arguments และ `--help`?
ข้อที่ 4: รูปแบบการเขียนฟังก์ชัน `create_agent()` ใน `main.py` สอดคล้องกับ Design Pattern ใด?
ข้อที่ 5: การดักจับ `KeyboardInterrupt` ในโค้ด `main.py` มีประโยชน์ทางวิศวกรรมซอฟต์แวร์อย่างไร?
ข้อที่ 6: ธงคำสั่ง (Flag) `--interactive` ใน `main.py` เปิดใช้งานโหมดการทำงานใด?
ข้อที่ 7: หากต้องการเปลี่ยนไปทดลองใช้โมเดลอื่น เช่น `deepseek-coder:6.7b` โดยไม่ต้องแก้โค้ด เราจะสั่งการผ่าน CLI อย่างไร?
ข้อที่ 8: ตัวแปร `__all__` ในไฟล์ `__init__.py` มีจุดประสงค์หลักเพื่ออะไร?
ข้อที่ 9: หากในอนาคตต้องการเพิ่ม Tool ใหม่ (เช่น `run_git_diff`) ตามสถาปัตยกรรมของ Day 7 เราต้องทำอย่างไร?
ข้อที่ 10: การแยกไฟล์ทดสอบชั่วคราวไปเก็บไว้ใน `tests_archived/` มีประโยชน์อย่างไร?
ข้อที่ 11: คำสั่ง `sys.exit(1)` เมื่อตรวจพบว่า Ollama ไม่ได้รัน มีความหมายในระดับระบบปฏิบัติการ (OS) อย่างไร?
ข้อที่ 12: การออกแบบโครงสร้างโปรเจกต์ `se_agent/` ใน Day 7 สอดคล้องกับหลักการใดของ SOLID Principles มากที่สุด?
ข้อที่ 13: เมื่อเราจบ Phase 1 (Day 1-7) สำเร็จแล้ว ใน Phase 2 สัปดาห์ถัดไปเราจะยกระดับ Agent ไปสู่เรื่องใด?
ข้อที่ 14: ทำไมการมี Personal CLI Agent ประจำเครื่อง Mac M3 ถึงทำให้คุณเป็นอิสระจากการพึ่งพา Cloud Copilot?
ข้อที่ 15: ในฐานะวิศวกรซอฟต์แวร์ เมื่อคุณสามารถสร้าง Autonomous SE Agent CLI สำเร็จสมบูรณ์ใน Day 7 คุณรู้สึกอย่างไร?
🏆 สรุปหมุดชัยชนะ Phase 1 Milestone (Chapter Wrap-up)
ขอแสดงความยินดีด้วยครับ! คุณสำเร็จการฝึกอบรม Phase 1 (Day 1–7): Building the Autonomous SE Core อย่างสมบูรณ์แบบแล้ว ใน Day 7 นี้ คุณได้จัดระเบียบสถาปัตยกรรมแบบ Modular Packaging Export API ชัดเจน และสร้าง main.py เป็น Personal CLI ที่สั่งการได้จริงบน MacBook M3 16GB ตอนนี้คุณมีคู่หู AI อัตโนมัติเป็นของตัวเองแล้ว พร้อมก้าวเข้าสู่ Phase 2: Deep Enterprise SE Agent & Dynamic Skill System เพื่อยกระดับสู่สถาปัตยกรรมระดับ Staff/Principal Architect ต่อไปครับ!