teach-me-ai / sources / HO-1
⟵ กลับหน้าแทร็กเสริม
HO-1 · Hands-On AI Engineering

AI Agents — จาก agent loop เดี่ยว ถึงทีม multi-agent

⏱️ ~25 นาที 🎯 ถอด pattern ของ agent: loop, tool, memory, single vs multi-agent, orchestration แล้ว map ไป NestJS/Go 📦 ต้นทาง: ai_agents/ (34 โปรเจกต์)

หมวด AI Agents ใหญ่สุดในคลัง (34 ตัว) เพราะมันคือหัวใจของแอป AI ยุคนี้ บทนี้จะไม่ให้คุณจำชื่อทั้ง 34 ตัว แต่จะถอด pattern ร่วม ที่ทุกตัวใช้ — พอเข้าใจ pattern แล้ว เปิดตัวไหนก็อ่านออกทันที และที่สำคัญ: เอาไปเขียนเองบน NestJS/Go ได้ (ควรเรียน HO-0 มาก่อนเพื่อรู้วิธีเดินอ่านโปรเจกต์)

agent ต่างจาก "เรียก LLM ครั้งเดียว" ยังไง

การเรียก LLM ธรรมดา = ส่ง prompt เข้าไป 1 ครั้ง ได้คำตอบ 1 ครั้ง จบ แต่ agent คือการให้ LLM วนคิด-ทำ-ดูผล หลายรอบ โดยมันตัดสินใจเองว่าจะทำอะไรต่อ จนกว่างานจะเสร็จ หัวใจคือ loop ที่เรียกว่า think → act → observe

ผลยังไม่พอ → วนคิดใหม่ (พร้อม context ที่เพิ่มขึ้น) Task โจทย์จากผู้ใช้ 1. Think LLM ตัดสินใจ 2. Act เรียก tool 3. Observe ดูผลลัพธ์ Final answer เมื่อ "จบ"
agent = LLM + tools + loop — วนคิด-ทำ-ดูผล จนกว่าจะได้คำตอบ (ต่างจากเรียก LLM ครั้งเดียวจบ)

โปรเจกต์ smolagents_code_agent ในคลังคือตัวอย่างชัดที่สุด — README มันบอกตรง ๆ ว่า รับโจทย์ภาษาคนแล้ว "reasons through it with a think-act-observe loop" โดยแต่ละรอบมันจะ เขียนโค้ด Python แล้วรันเอง (มี tool คือ DuckDuckGo ค้นเว็บ + Wikipedia) และ stream ทุก step ให้ดูสด ๆ นี่คือ agent loop เปลือย ๆ ที่เห็นทุกจังหวะ

กายวิภาคของ agent: 5 ชิ้นส่วน

ชิ้นส่วนหน้าที่ในโลก NestJS/Go มองเป็น
LLM (สมอง)ตัดสินใจว่าจะทำอะไรต่อservice ที่เรียก API ภายนอก
Toolsสิ่งที่ agent เรียกได้ (ค้นเว็บ, query DB, เรียก API)ชุด method/function ที่ลงทะเบียนไว้ (tool registry)
Memoryจำสิ่งที่ทำมาแล้วในรอบก่อน ๆstate object / เก็บลง Redis-Mongo
Loopวน think-act-observewhile loop ใน service
Stopping conditionเงื่อนไขหยุด (จบงาน / ครบจำนวนรอบ)เงื่อน breakสำคัญมาก กันวนไม่รู้จบ

แบบที่ 1 · Single-agent + tools (พร้อม MCP)

agent เดี่ยว 1 ตัว แต่มี tool ให้เรียกหลายอย่าง โปรเจกต์ github_intelligence_agent เป็นเคสน่าสนใจ: มันต่อ MCP (Model Context Protocol) ของ GitHub เพื่อคุยกับ GitHub API ทั้งหมดผ่านภาษาคน

จุดที่ควรถอดเป็น pattern คือปัญหา "tool เยอะเกินจน context ล้น" — GitHub MCP มี tool กว่า 40 ตัว ถ้าโหลด schema ทุกตัวเข้า context ตั้งแต่แรกจะเปลืองมหาศาล README เลยเล่าว่ามันใช้ SearchableToolset ที่ "searches for relevant tools by keyword and loads only what it needs" — โหลดเฉพาะ tool ที่เกี่ยวกับงานตอนนั้น

💡

pattern พกพา: "อย่ายัด tool ทุกตัวให้ LLM เห็นพร้อมกัน" — ถ้าระบบคุณมี tool เยอะ (เช่น 50 endpoint ใน NestJS) ให้ทำ tool discovery เลือกเฉพาะที่เกี่ยว ก่อนส่งให้ LLM ตัดสินใจ ประหยัด token และแม่นขึ้น — เรื่อง tool schema จะลงลึกใน Lv3 · Tool use

แบบที่ 2 · Multi-agent (ทีมที่แบ่งหน้าที่)

แทนที่จะมี agent เก่งรอบด้าน 1 ตัว ใช้หลาย agent ที่ เชี่ยวชาญคนละอย่าง ส่งงานต่อกันเป็นทอด ๆ โปรเจกต์ multi_agent_coding_assistant เป็นตัวอย่างที่สะอาดมาก: 4 stage ทำงานเรียงกัน

1. Planner วางแผน (ไม่เขียนโค้ด) 2. Coder ร่างโค้ดตามแผน 3. Reviewer หาบั๊ก/แนะนำ 4. Coder (final) แก้ตาม review โค้ดสุดท้าย พร้อมใช้
Planner → Coder → Reviewer → Coder — เลียนแบบทีมจริง: คนวางแผน คนเขียน คนรีวิว

สิ่งที่หลายคนเข้าใจผิดคือคิดว่า multi-agent ต้องมีเฟรมเวิร์กวิเศษ แต่พอเปิดโค้ดจริง (agents.py) จะเห็นว่ามันคือ ฟังก์ชันธรรมดา 4 ตัว ที่ต่างกันแค่ "system prompt" (บทบาท) แล้วส่ง output ของตัวก่อนเป็น input ตัวถัดไป:

# agents.py (ย่อจากของจริง) — แต่ละ agent = system prompt + llm.invoke()
def run_planner(llm, task):
    messages = [SystemMessage(PLANNER_SYSTEM_PROMPT), HumanMessage(f"Coding task: {task}")]
    return llm.invoke(messages).content

# flow ใน app.py: ร้อยต่อกันเป็นทอด ๆ ด้วยโค้ดธรรมดา
plan   = run_planner(llm, task)
draft  = run_coder(llm, plan)
review = run_reviewer(llm, draft)
final  = run_final_coder(llm, plan, draft, review)

pattern พกพา: "multi-agent" ส่วนใหญ่ = บทบาทเฉพาะทาง (system prompt ต่างกัน) + context แยกกัน + ร้อยต่อด้วยโค้ดธรรมดา — ไม่ต้องมีเวทมนตร์ ใน NestJS ก็คือ service หลายตัว (PlannerService, CoderService, ReviewerService) ที่ orchestrator เรียกต่อกัน

แบบที่ 3 · Self-reflection loop (agent ตรวจงานตัวเอง)

ระดับที่ฉลาดขึ้น: agent ที่ เกรดผลงานตัวเองก่อนส่ง ถ้ายังไม่ดีพอก็วนแก้ โปรเจกต์ agentic_rag_system (ใช้ LangGraph) ทำ RAG ที่ไม่ตอบทันที แต่วน retrieve → grade → (rewrite ถ้าไม่ผ่าน) → generate

retrieve ดึง context grade_retrieval context พอตอบไหม? VERDICT: YES / ครบ 3 รอบ generate ตอบแบบ grounded END VERDICT: NO rewrite_query แก้คำค้นให้ดีขึ้น วน retrieve ใหม่
LangGraph = state machine: node (retrieve/grade/rewrite/generate) + conditional edge — มี MAX_ITERATIONS = 3 กันวนไม่รู้จบ

หัวใจที่ควรจำคือมันคือ state machine ชัด ๆ — โค้ดจริงประกาศ node แล้วต่อ edge โดยมี "เส้นทางแบบมีเงื่อนไข" (conditional edge) และตัวนับรอบกันลูปค้าง:

# rag_graph.py (ย่อจากของจริง) — นิยาม node + edge เหมือนวาด state machine
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade_retrieval", grade_retrieval)
workflow.add_edge("retrieve", "grade_retrieval")
workflow.add_conditional_edges("grade_retrieval", should_continue)  # แยกทาง YES/NO
workflow.add_edge("rewrite_query", "retrieve")              # วนกลับ

def should_continue(state):
    if state["iterations"] >= MAX_ITERATIONS: return "generate"  # กันวนไม่จบ
    return "generate" if verdict == "YES" else "rewrite"

เฟรมเวิร์ก orchestration — เลือกตัวไหน?

ในคลังคุณจะเจอชื่อพวกนี้เต็มไปหมด จริง ๆ ทุกตัวทำเรื่องเดียวกัน: "ช่วยร้อย agent loop / หลาย agent เข้าด้วยกัน" ต่างกันที่สไตล์ — จำไว้พอให้เลือกถูก ไม่ต้องท่อง:

เฟรมเวิร์กสไตล์เหมาะเมื่อ
LangChainต่อ component เป็น chainflow เส้นตรง เรียบง่าย (เหมือน multi_agent_coding_assistant)
LangGraphstate machine (node + edge)มี loop/เงื่อนไข/ย้อนกลับ (เหมือน agentic RAG)
CrewAI / AG2"ทีม" ที่มี role คุยกันหลาย agent เจรจา/แบ่งงานเอง
Agnoagent + tool + memory เบา ๆอยาก setup เร็ว น้ำหนักเบา
smolagentscode-first (เขียน+รันโค้ดทุก step)งานที่แก้ด้วยการรันโค้ดได้

อย่าเพิ่งรีบหยิบเฟรมเวิร์ก: สำหรับ flow ตรง ๆ 3-4 step (แบบ Planner→Coder→Reviewer) คุณเขียน loop + เรียก LLM เองด้วย SDK ตรง ๆ ก็พอ — เฟรมเวิร์กมีค่าเมื่อ flow ซับซ้อน (loop เยอะ, conditional, หลาย agent คุยกัน)

map ลงงาน NestJS/Go ของคุณ

นี่คือของจริงที่เอาไปใช้ได้: agent loop = service ที่มี while loop + tool registry + state ถ้าคุณเคยเขียน job processor (BullMQ) หรือ state machine (XState) มาก่อน คุณเข้าใจมันอยู่แล้ว

// NestJS — agent loop เปลือย ๆ (pseudo) หน้าตาคุ้นมาก
async runAgent(task: string) {
  const state = { task, history: [], iterations: 0 };
  while (state.iterations++ < MAX_ITERATIONS) {   // stopping condition กันวนไม่จบ
    const decision = await this.llm.decide(state);  // think
    if (decision.done) return decision.answer;
    const result = await this.tools.run(decision.tool, decision.args); // act
    state.history.push({ decision, result });          // observe → memory
  }
}
  • Tools = method ใน service ที่คุณลงทะเบียนไว้ (query Mongo, เรียก REST, ส่งอีเมล) พร้อม schema อธิบายให้ LLM รู้ว่าเรียกยังไง
  • Multi-agent = orchestrator service เรียก sub-service เฉพาะทางต่อกัน (หรือใช้ queue ส่งงานเป็นทอด)
  • Self-reflection = state machine — ถ้าคุ้น XState/BullMQ flow อยู่แล้ว map ตรง ๆ ได้เลย
⚠️

3 กับดักของ agent ที่ต้องระวังในงานจริง: (1) วนไม่รู้จบ — ต้องมี MAX_ITERATIONS เสมอ (สังเกตว่า agentic RAG hard-code ไว้ 3); (2) ค่าใช้จ่ายบาน — ทุกรอบ loop = เรียก LLM = จ่ายเงิน + ช้า ยิ่ง multi-agent ยิ่งคูณ; (3) tool ทำงานอันตราย — ถ้า tool ลบข้อมูล/จ่ายเงินได้ ต้องมี guard/confirm ก่อนให้ agent เรียกเอง

🎯 แบบฝึกหัด
  1. เปิด sources/Hands-On-AI-Engineering/ai_agents/multi_agent_coding_assistant/agents.py แล้วหาให้เจอว่ามี agent (system prompt) ทั้งหมดกี่ตัว และแต่ละตัวห้าม/ต้องทำอะไร (ใบ้: มองหาคำว่า SYSTEM_PROMPT)
  2. เปิด ai_agents/agentic_rag_system/rag_graph.py หา MAX_ITERATIONS แล้วอธิบายด้วยคำพูดตัวเองว่า "ถ้าไม่มีบรรทัดนี้จะเกิดอะไรขึ้น"
  3. นึกงานจริงในโปรเจกต์คุณที่เหมาะกับ agent (เช่น "อ่านอีเมลลูกค้า → จัดหมวด → ร่างตอบ") แล้วเขียนว่ามันควรเป็นแบบไหน: single-agent+tools, multi-agent, หรือ self-reflection loop และ tool ที่ต้องมีคืออะไรบ้าง

เมื่อทำเสร็จ พิมพ์ /quiz HO-1 เพื่อทดสอบความเข้าใจ