← Phase 1 Phase 1 • Day 7 Milestone

Local SE Agent Masterclass

MacBook M3 16GB • Complete Implementation Standard
📍 ส่วนที่ 1: หมุดตั้งโจทย์ & วัตถุประสงค์หลัก (Title Connection)

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 1: Setup & Ollama API ✓ Day 2: JSON Mode & Structured Output ✓ Day 3: Tool Calling & Dispatcher ✓ Day 4: File System Reader ✓ Day 5: Maven & Test Runner ✓ Day 6: Autonomous ReAct Loop Day 7: Modular Engine & CLI 🏆
📂 ส่วนที่ 2: ผังโครงสร้างโปรเจกต์จริง & ตำแหน่งไฟล์ (Project File Structure)

เมื่อสิ้นสุด Day 7 โฟลเดอร์โปรเจกต์ se_agent/ ของเราจะได้รับการ Refactor จนกลายเป็นระบบซอฟต์แวร์แบบ Production-ready ที่สมบูรณ์แบบ 100%:

se_agent/
├── requirements.txt                       # ollama>=0.4.0
├── main.py                            # [ไฟล์ใหม่ Day 7] Entrypoint CLI รับอาร์กิวเมนต์ --task, --interactive, --model
├── core/
│   ├── __init__.py                         # [อัปเดต Day 7] Export Clean API: OllamaLLMClient, ToolDispatcher, ReActAgentEngine
│   ├── client.py                          # Ollama Client Wrapper (Day 1-2)
│   ├── parser.py                          # Robust JSON Parser & Markdown Cleaner (Day 2)
│   ├── dispatcher.py                      # Dynamic Tool Registry & Execution Gateway (Day 3)
│   ├── engine.py                          # ReAct Agent State Machine & Guardrail (Day 6)
│   └── tools/
│       ├── __init__.py                     # [อัปเดต Day 7] Export Tools: read_file, run_maven_test
│       ├── file_reader.py                  # Safe File Reader Tool (Day 4)
│       └── test_runner.py                  # Subprocess Maven Runner Tool (Day 5)
├── sample_maven_project/                  # โปรเจกต์ Java ตัวอย่างที่มี Unit Test
│   ├── pom.xml
│   ├── src/main/java/Calculator.java
│   └── src/test/java/CalculatorTest.java
└── tests_archived/                        # รวมไฟล์ทดสอบชั่วคราว Day 1-6 เข้าโฟลเดอร์ให้เป็นระเบียบ
   ├── test_connection.py
   ├── test_structured_output.py
   ├── test_tool_dispatch.py
   ├── test_file_reader.py
   ├── test_maven_runner.py
   └── test_react_loop.py
📍 main.py ทำหน้าที่อะไร?

ทำหน้าที่เป็น Application CLI Entrypoint ใช้ไลบรารีมาตรฐาน argparse เพื่อรับคำสั่งจากผู้ใช้ใน Terminal, จัดเตรียม Dependency Injection (ผูก Client + Dispatcher + Tools เข้ากับ Engine) และรองรับทั้งโหมด --task (รันครั้งเดียวจบ) และ --interactive (คุยโต้ตอบต่อเนื่อง)

📍 core/__init__.py & tools/__init__.py ทำหน้าที่อะไร?

ทำหน้าที่สร้าง Public API Boundary ให้กับ Package เพื่อให้โค้ดภายนอกสามารถเรียก from core import ReActAgentEngine หรือ from core.tools import read_file ได้โดยตรง โดยไม่ต้องรู้โครงสร้างไฟล์ภายในลึกๆ

🏛️ ส่วนที่ 3: สถาปัตยกรรม CLI & Modular Orchestration (Mental Model)
💻
1. CLI Arguments
`python main.py --task "..."`
➔
🔌
2. Dependency Wiring
จดทะเบียน Tool ลง Dispatcher
➔
🔄
3. Engine Execution
ReAct Loop อัตโนมัติ
ทำไมการทำ Modular Packaging ถึงเป็นทักษะชี้ชะตาของ Software Engineer?
❌ โค้ดดิบแบบสคริปต์เดี่ยว (Monolithic Scripting):

สคริปต์ที่รวมทุกอย่างในไฟล์เดียว 1,000 บรรทัดจะยากต่อการทดสอบ (Testability ต่ำ), ไม่สามารถนำฟังก์ชันไป Reusable ในโปรเจกต์อื่นได้ และเมื่อมีทีมงานมาช่วย จะเกิด Git Merge Conflict ตลอดเวลา

✅ โครงสร้าง Modular แบบ Clean Architecture:

แยกความรับผิดชอบชัดเจน (Client ดูแลเน็ตเวิร์ก, Parser ดูแลไวยากรณ์, Tools ดูแล OS, Engine ดูแลลูป) และมี main.py เป็นจุดประกอบร่าง (Composition Root) ทำให้เพิ่ม Tool ใหม่หรือเปลี่ยนโมเดลได้ทันทีโดยไม่แตะต้องโค้ดแกนกลาง

💻 ส่วนที่ 4: ซอร์สโค้ดจริง & คำอธิบายทีละบรรทัด (Line-by-Line Implementation)
📄 1. core/__init__.py และ core/tools/__init__.py (สร้าง Public API Exports) จัดการ Package Expose ให้เรียกใช้งานได้สะดวก
# 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"
]
📄 2. main.py (Entrypoint CLI ประจำเครื่องของคุณ) รวมร่างทุกอวัยวะ สั่งงานผ่าน Terminal สะดวกสบาย
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 แดงเถือกน่าตกใจ

⚡ ส่วนที่ 5: ปฏิบัติการจริงบน Terminal Mac M3 (Step-by-Step Lab)

เปิด Terminal บน MacBook M3 แล้วทำตาม 5 ขั้นตอนนี้เพื่อจัดระเบียบโปรเจกต์และรัน Personal CLI ใหม่ของคุณทันที:

ขั้นตอนที่ 1: เข้าโฟลเดอร์โปรเจกต์และเปิด Virtual Environment
cd ~/se_agent
source .venv/bin/activate
ขั้นตอนที่ 2: จัดระเบียบไฟล์ทดสอบเก่าและสร้าง main.py
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
ขั้นตอนที่ 3: วางโค้ดลงในแต่ละไฟล์

- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 1.1) ไปวางใน core/tools/__init__.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 1.2) ไปวางใน core/__init__.py
- นำโค้ดใน ส่วนที่ 4 (ไฟล์ที่ 2) ไปวางใน main.py

ขั้นตอนที่ 4: ทดสอบเรียกดูเมนูช่วยเหลือ CLI
python main.py --help

คุณจะเห็นหน้าจอช่วยเหลือของ CLI แสดงคำอธิบาย --task, --interactive, --model สวยงามเหมือนโปรแกรมสากล

ขั้นตอนที่ 5 (Final Test): สั่งงานจริงผ่าน CLI Argument
python main.py --task "ช่วยตรวจโปรเจกต์ sample_maven_project รันเทส ส่องดูโค้ดที่มีบั๊ก แล้วสรุปวิธีแก้ให้ฟัง"

🎉 ผลลัพธ์: CLI จะสั่ง Agent วนลูป คิด ➔ รัน mvn test ➔ อ่าน Calculator.java ➔ สรุปคำตอบแก้บั๊ก return price - discount; และพิมพ์รายงานในหน้าจอ Terminal ของ Mac M3 อย่างสมบูรณ์แบบ 100%!

📝 ส่วนที่ 6: ชุดควิซประเมินความรู้เชิงวิศวกรรม 15 ข้อ (ตอบปุ๊บรู้ผลทันที)

ข้อที่ 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 ต่อไปครับ!