บทนำ: ทำไม Agent ถึงต้องมี State Machine

ในระบบ Multi-Agent ของ CrewAI แต่ละ Agent ไม่ได้ทำงานแบบ Linear หรือเรียบง่ายตลอดเวลา Agent ต้องรับมือกับสถานการณ์ที่ซับซ้อน เช่น การรอข้อมูลเพิ่มเติม การตัดสินใจแตกแขนง หรือการจัดการข้อผิดพลาดที่เกิดขึ้นระหว่างทาง การใช้ State Machine ช่วยให้เรากำหนดสถานะที่ชัดเจน ทำให้ Agent ทำงานได้อย่างมีประสิทธิภาพและควบคุมได้ดีขึ้น

กรณีศึกษา: บริษัทสตาร์ทอัพ AI ในกรุงเทพฯ

บริบทธุรกิจ

ทีมพัฒนา AI จากสตาร์ทอัพแห่งหนึ่งในกรุงเทพฯ สร้างระบบ Customer Support Agent ที่ทำงานอัตโนมัติ โดยมี Agent ย่อย 5 ตัวทำหน้าที่ต่างกัน ตั้งแต่รับข้อความลูกค้า วิเคราะห์ความต้องการ ค้นหาข้อมูลในฐานข้อมูล ไปจนถึงตอบกลับและติดตามผล ระบบเดิมใช้ OpenAI เป็นหลักแต่ประสบปัญหา Latency สูงและค่าใช้จ่ายที่พุ่งสูงขึ้นอย่างต่อเนื่อง

จุดเจ็บปวดของระบบเดิม

ระบบเดิมมีปัญหาหลายอย่างที่สะสมมานาน ค่าใช้จ่ายด้าน API พุ่งไปถึง $4,200 ต่อเดือน ซึ่งสูงเกินไปสำหรับธุรกิจขนาดกลาง นอกจากนี้ Latency เฉลี่ยอยู่ที่ 420ms ทำให้ลูกค้ารู้สึกว่าการตอบกลับช้าเกินไป โดยเฉพาะเมื่อต้องรอ Response จากหลาย Agent ติดต่อกัน ทำให้ทั้ง Flow ใช้เวลานานเกินไป

การย้ายมาใช้ HolySheep AI

ทีมตัดสินใจย้ายมาใช้ HolySheep AI เพราะมีอัตรา $1=¥1 ทำให้ประหยัดได้มากกว่า 85% เมื่อเทียบกับราคาเดิม และมี Latency ต่ำกว่า 50ms ซึ่งต่ำกว่าระบบเดิมอย่างเห็นได้ชัด การย้ายระบบเริ่มจากการเปลี่ยน base_url จาก OpenAI เป็น https://api.holysheep.ai/v1 จากนั้นทำการหมุนคีย์ API ใหม่และเริ่มทยอย Deploy แบบ Canary ทีละ Agent เพื่อไม่ให้ระบบหยุดชะงัก

ผลลัพธ์หลัง 30 วัน

หลังจากย้ายระบบมาใช้ HolySheep AI ได้ 30 วัน ตัวชี้วัดดีขึ้นอย่างเห็นได้ชัด Latency เฉลี่ยลดลงจาก 420ms เหลือ 180ms คิดเป็นการปรับปรุงมากกว่า 57% และค่าใช้จ่ายรายเดือนลดลงจาก $4,200 เหลือเพียง $680 ซึ่งเป็นการประหยัดเกือบ 84% ทีมพัฒนาสามารถนำเงินที่ประหยัดได้ไปลงทุนพัฒนาฟีเจอร์ใหม่ๆ แทน

พื้นฐาน State Machine ใน CrewAI

State Machine เป็นแนวคิดที่ใช้จัดการสถานะของ Agent โดยกำหนดว่า Agent จะอยู่สถานะไหน จากสถานะหนึ่งไปสถานะไหนได้บ้าง และต้องทำเงื่อนไขอะไรถึงจะเปลี่ยนสถานะได้ ใน CrewAI เราสามารถกำหนด State และ Transition ได้ด้วยตัวเอง

สถานะหลักของ Agent

สถานะแรกคือ IDLE ซึ่งเป็นสถานะเริ่มต้นเมื่อ Agent พร้อมทำงาน จากนั้นเมื่อได้รับ Task Agent จะเปลี่ยนเป็น PROCESSING เพื่อทำงาน เมื่อทำงานเสร็จจะเปลี่ยนเป็น COMPLETED หรือถ้ามีปัญหาจะเปลี่ยนเป็น FAILED ซึ่งต้องมีการจัดการ Retry หรือ Escalation ต่อไป นอกจากนี้ยังมีสถานะ WAITING สำหรับรอข้อมูลจาก Agent อื่นหรือผู้ใช้

การกำหนด Transition Rules

การเปลี่ยนสถานะต้องมีเงื่อนไขที่ชัดเจน เช่น จาก IDLE ไป PROCESSING ต้องมี Task ที่รออยู่ จาก PROCESSING ไป COMPLETED ต้องได้ผลลัพธ์ที่ถูกต้อง จาก PROCESSING ไป FAILED ต้องเกิด Error หรือ Timeout ส่วนจาก FAILED ไป PROCESSING ใหม่ต้องผ่านการ Retry Logic ที่กำหนดไว้
from enum import Enum
from typing import Optional, Callable
from dataclasses import dataclass, field

class AgentState(Enum):
    """สถานะหลักของ Agent"""
    IDLE = "idle"
    PROCESSING = "processing"
    WAITING = "waiting"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class Transition:
    """กฎการเปลี่ยนสถานะ"""
    from_state: AgentState
    to_state: AgentState
    condition: Callable[[], bool]
    action: Optional[Callable[[], None]] = None

class AgentStateMachine:
    """State Machine สำหรับจัดการวงจรชีวิตของ Agent"""
    
    def __init__(self, agent_id: str):
        self.agent_id = agent_id
        self.current_state = AgentState.IDLE
        self.transitions: list[Transition] = []
        self.state_history: list[tuple[str, AgentState]] = []
        self._register_default_transitions()
    
    def _register_default_transitions(self):
        """ลงทะเบียน Transition พื้นฐาน"""
        # IDLE -> PROCESSING: เมื่อมี Task รอ
        self.add_transition(
            AgentState.IDLE,
            AgentState.PROCESSING,
            lambda: self.has_pending_task()
        )
        
        # PROCESSING -> COMPLETED: เมื่อทำงานสำเร็จ
        self.add_transition(
            AgentState.PROCESSING,
            AgentState.COMPLETED,
            lambda: self.task_succeeded()
        )
        
        # PROCESSING -> FAILED: เมื่อเกิด Error
        self.add_transition(
            AgentState.PROCESSING,
            AgentState.FAILED,
            lambda: self.task_failed()
        )
        
        # FAILED -> PROCESSING: เมื่อถึงเวลา Retry
        self.add_transition(
            AgentState.FAILED,
            AgentState.PROCESSING,
            lambda: self.should_retry()
        )
        
        # PROCESSING -> WAITING: เมื่อต้องรอข้อมูล
        self.add_transition(
            AgentState.PROCESSING,
            AgentState.WAITING,
            lambda: self.needs_more_info()
        )
        
        # WAITING -> PROCESSING: เมื่อได้รับข้อมูลที่ต้องการ
        self.add_transition(
            AgentState.WAITING,
            AgentState.PROCESSING,
            lambda: self.info_received()
        )
    
    def add_transition(
        self,
        from_state: AgentState,
        to_state: AgentState,
        condition: Callable[[], bool],
        action: Optional[Callable[[], None]] = None
    ):
        """เพิ่มกฎการเปลี่ยนสถานะ"""
        self.transitions.append(Transition(
            from_state=from_state,
            to_state=to_state,
            condition=condition,
            action=action
        ))
    
    def try_transition(self) -> bool:
        """พยายามเปลี่ยนสถานะ"""
        for transition in self.transitions:
            if (transition.from_state == self.current_state and 
                transition.condition()):
                self._execute_transition(transition)
                return True
        return False
    
    def _execute_transition(self, transition: Transition):
        """ดำเนินการเปลี่ยนสถานะ"""
        if transition.action:
            transition.action()
        
        self.state_history.append((self.agent_id, self.current_state))
        self.current_state = transition.to_state
        print(f"[{self.agent_id}] State: {self.current_state.value}")
    
    def has_pending_task(self) -> bool:
        """ตรวจสอบว่ามี Task รอหรือไม่"""
        return getattr(self, '_has_task', False)
    
    def task_succeeded(self) -> bool:
        """ตรวจสอบว่าทำงานสำเร็จหรือไม่"""
        return getattr(self, '_task_result', None) == 'success'
    
    def task_failed(self) -> bool:
        """ตรวจสอบว่าทำงานล้มเหลวหรือไม่"""
        return getattr(self, '_task_result', None) == 'failed'
    
    def should_retry(self) -> bool:
        """ตรวจสอบว่าควร Retry หรือไม่"""
        retry_count = getattr(self, '_retry_count', 0)
        return retry_count < 3
    
    def needs_more_info(self) -> bool:
        """ตรวจสอบว่าต้องรอข้อมูลเพิ่มหรือไม่"""
        return getattr(self, '_waiting_for_info', False)
    
    def info_received(self) -> bool:
        """ตรวจสอบว่าได้รับข้อมูลแล้วหรือยัง"""
        return getattr(self, '_info_ready', False)
    
    def update_context(self, **kwargs):
        """อัปเดต Context สำหรับตรวจสอบเงื่อนไข"""
        for key, value in kwargs.items():
            setattr(self, f'_{key}', value)

การผสมผสาน CrewAI กับ State Machine

เมื่อนำ State Machine มารวมกับ CrewAI เราจะได้ระบบที่มีทั้งความยืดหยุ่นของ Multi-Agent และความควบคุมของ State Machine ตัวอย่างนี้แสดงการสร้าง Customer Support Crew ที่ใช้ State Machine จัดการการไหลของงานระหว่าง Agent ต่างๆ
import os
from crewai import Agent, Task, Crew
from langchain_openai import ChatOpenAI
from dataclasses import dataclass
from typing import Optional, Dict, Any
import time

ตั้งค่า HolySheep AI เป็น LLM provider

os.environ["OPENAI_API_BASE"] = "https://api.holysheep.ai/v1" os.environ["OPENAI_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"

สร้าง LLM instance

llm = ChatOpenAI( model="gpt-4.1", temperature=0.7, api_key=os.environ["OPENAI_API_KEY"], base_url=os.environ["OPENAI_API_BASE"] )

กำหนดราคา (2026 pricing)

PRICING = { "gpt-4.1": 8.00, # $8 per 1M tokens "claude-sonnet-4.5": 15.00, # $15 per 1M tokens "gemini-2.5-flash": 2.50, # $2.50 per 1M tokens "deepseek-v3.2": 0.42 # $0.42 per 1M tokens } @dataclass class TicketContext: """Context สำหรับ Ticket ที่กำลังดำเนินการ""" ticket_id: str customer_message: str customer_tier: str # regular, premium, vip sentiment: Optional[str] = None resolved: bool = False escalation_needed: bool = False assigned_agent: Optional[str] = None history: list = None def __post_init__(self): if self.history is None: self.history = [] class CustomerSupportStateMachine(AgentStateMachine): """State Machine สำหรับระบบ Customer Support""" def __init__(self, agent_id: str): super().__init__(agent_id) self.ticket_context: Optional[TicketContext] = None self.processing_start: Optional[float] = None def assign_ticket(self, ticket: TicketContext): """มอบหมาย Ticket ให้ Agent""" self.ticket_context = ticket self.update_context(_has_task=True) self.processing_start = time.time() def mark_resolved(self): """ทำเครื่องหมายว่า Ticket ถูกแก้ไขแล้ว""" self.ticket_context.resolved = True self.update_context(_task_result='success') def mark_failed(self, reason: str): """ทำเครื่องหมายว่า Ticket แก้ไขไม่ได้""" self.ticket_context.history.append({ "state": "failed", "reason": reason, "timestamp": time.time() }) self.update_context(_task_result='failed') self._retry_count = getattr(self, '_retry_count', 0) + 1 def escalate(self): """Escalate ไปยัง Agent ระดับสูงกว่า""" self.ticket_context.escalation_needed = True self.ticket_context.assigned_agent = "senior_agent" def get_processing_time(self) -> float: """คำนวณเวลาที่ใช้ประมวลผล (มิลลิวินาที)""" if self.processing_start: return (time.time() - self.processing_start) * 1000 return 0

สร้าง Agents

router_agent = Agent( role="Ticket Router", goal="วิเคราะห์ Ticket และจัดส่งไปยัง Agent ที่เหมาะสม", backstory="คุณคือผู้เชี่ยวชาญในการจัดการ Ticket สนับสนุนลูกค้า", llm=llm, verbose=True ) support_agent = Agent( role="Support Agent", goal="แก้ไขปัญหาของลูกค้าอย่างรวดเร็วและมีประสิทธิภาพ", backstory="คุณคือ Agent สนับสนุนลูกค้าที่มีประสบการณ์", llm=llm, verbose=True ) senior_agent = Agent( role="Senior Support Agent", goal="จัดการปัญหาที่ซับซ้อนและต้องการความเชี่ยวชาญสูง", backstory="คุณคือผู้เชี่ยวชาญอาวุโสในการแก้ไขปัญหายาก", llm=llm, verbose=True ) def create_support_crew(ticket: TicketContext) -> tuple[Crew, CustomerSupportStateMachine]: """สร้าง Crew และ State Machine สำหรับ Ticket""" # สร้าง State Machine state_machine = CustomerSupportStateMachine("main_support") state_machine.assign_ticket(ticket) # กำหนด Task ตามสถานะ if ticket.escalation_needed: # Task สำหรับ VIP หรือปัญหาซับซ้อน tasks = [ Task( description=f"แก้ไขปัญหาของลูกค้า VIP: {ticket.customer_message}", agent=senior_agent, expected_output="รายงานการแก้ไขปัญหาพร้อมขั้นตอนที่ดำเนินการ" ) ] else: # Task ปกติ tasks = [ Task( description=f"วิเคราะห์และแก้ไข: {ticket.customer_message}", agent=support_agent, expected_output="คำตอบที่ชัดเจนสำหรับลูกค้า" ) ] # สร้าง Crew crew = Crew( agents=[senior_agent if ticket.escalation_needed else support_agent], tasks=tasks, verbose=True ) return crew, state_machine def run_ticket_workflow(ticket: TicketContext) -> Dict[str, Any]: """Run Workflow แบบเต็มสำหรับ Ticket""" print(f"[Workflow] เริ่มดำเนินการ Ticket: {ticket.ticket_id}") # ขั้นตอนที่ 1: Routing router_state = CustomerSupportStateMachine("router") router_state.assign_ticket(ticket) while router_state.current_state != AgentState.COMPLETED: router_state.try_transition() if router_state.current_state == AgentState.PROCESSING: # ใช้ Router Agent วิเคราะห์ Ticket routing_result = router_agent.execute_task( f"วิเคราะห์ Ticket: {ticket.customer_message}" ) # กำหนดว่าต้อง Escalate หรือไม่ if ticket.customer_tier == "vip" or "refund" in routing_result.lower(): ticket.escalation_needed = True router_state.escalate() router_state.mark_resolved() # ขั้นตอนที่ 2: Support crew, support_state = create_support_crew(ticket) while support_state.current_state != AgentState.COMPLETED: support_state.try_transition() if support_state.current_state == AgentState.PROCESSING: result = crew.kickoff() # ตรวจสอบผลลัพธ์ if result and "error" not in str(result).lower(): support_state.mark_resolved() else: support_state.mark_failed(str(result)) return { "ticket_id": ticket.ticket_id, "status": "resolved" if support_state.ticket_context.resolved else "failed", "processing_time_ms": support_state.get_processing_time(), "escalated": ticket.escalation_needed, "agent": ticket.assigned_agent }

ทดสอบ Workflow

if __name__ == "__main__": test_ticket = TicketContext( ticket_id="TKT-001", customer_message="ฉันต้องการขอเงินคืนสำหรับการสั่งซื้อครั้งที่แล้ว", customer_tier="vip" ) result = run_ticket_workflow(test_ticket) print(f"ผลลัพธ์: {result}")

การติดตาม Lifecycle และ Metrics

การจัดการ State Machine ที่ดีต้องมีการติดตาม Lifecycle ของ Agent อย่างต่อเนื่อง เพื่อให้เราสามารถวิเคราะห์ประสิทธิภาพและหาโอกาสในการปรับปรุงได้ ระบบติดตามที่ดีควรเก็บข้อมูลสถานะ ระยะเวลาในแต่ละสถานะ และการเปลี่ยนแปลงต่างๆ

การสร้าง Lifecycle Tracker

from datetime import datetime
from typing import Dict, List, Optional
from collections import defaultdict
import threading

@dataclass
class StateTransition:
    """บันทึกการเปลี่ยนสถานะ"""
    timestamp: float
    agent_id: str
    from_state: str
    to_state: str
    duration_ms: float  # เวลาที่อยู่ในสถานะก่อนหน้า

@dataclass
class LifecycleMetrics:
    """Metrics สำหรับ Lifecycle ของ Agent"""
    total_transitions: int
    state_distribution: Dict[str, int]
    avg_time_per_state: Dict[str, float]
    failed_count: int
    retry_count: int
    escalation_count: int

class LifecycleTracker:
    """Tracker สำหรับติดตาม Lifecycle ของ Agent ทั้งหมด"""
    
    def __init__(self):
        self.transitions: List[StateTransition] = []
        self.state_timers: Dict[str, float] = {}  # agent_id -> last timestamp
        self.current_states: Dict[str, str] = {}
        self._lock = threading.Lock()
        self._callbacks: List[callable] = []
    
    def on_state_change(self, callback: callable):
        """ลงทะเบียน callback สำหรับเมื่อสถานะเปลี่ยน"""
        self._callbacks.append(callback)
    
    def record_transition(
        self,
        agent_id: str,
        from_state: str,
        to_state: str
    ):
        """บันทึกการเปลี่ยนสถานะ"""
        with self._lock:
            current_time = time.time()
            duration_ms = 0
            
            if agent_id in self.state_timers:
                duration_ms = (current_time - self.state_timers[agent_id]) * 1000
            
            transition = StateTransition(
                timestamp=current_time,
                agent_id=agent_id,
                from_state=from_state,
                to_state=to_state,
                duration_ms=duration_ms
            )
            
            self.transitions.append(transition)
            self.state_timers[agent_id] = current_time
            self.current_states[agent_id] = to_state
            
            # เรียก callbacks
            for callback in self._callbacks:
                callback(agent_id, from_state, to_state)
    
    def get_agent_history(self, agent_id: str) -> List[StateTransition]:
        """ดึงประวัติการเปลี่ยนสถานะของ Agent"""
        with self._lock:
            return [t for t in self.transitions if t.agent_id == agent_id]
    
    def get_current_state(self, agent_id: str) -> Optional[str]:
        """ดึงสถานะปัจจุบันของ Agent"""
        return self.current_states.get(agent_id)
    
    def get_metrics(self, time_window_seconds: Optional[float] = None) -> LifecycleMetrics:
        """คำนวณ Metrics รวม"""
        with self._lock:
            transitions = self.transitions
            
            if time_window_seconds:
                cutoff = time.time() - time_window_seconds
                transitions = [t for t in transitions if t.timestamp >= cutoff]
            
            # กระจายตัวของสถานะ
            state_counts = defaultdict(int)
            state_times: Dict[str, List[float]] = defaultdict(list)
            
            for t in transitions:
                state_counts[t.to_state] += 1
                state_times[t.to_state].append(t.duration_ms)
            
            # คำนวณเวลาเฉลี่ยต่อสถานะ
            avg_times = {
                state: sum(times) / len(times) if times else 0
                for state, times in state_times.items()
            }
            
            # นับ Failed และ Retry
            failed = sum(1 for t in transitions if t.to_state == "failed")
            escalations = sum(1 for t in transitions if "escalation" in str(t).lower())
            
            return LifecycleMetrics(
                total_transitions=len(transitions),
                state_distribution=dict(state_counts),
                avg_time_per_state=avg_times,
                failed_count=failed,
                retry_count=0,  # ต้อง track แยก
                escalation_count=escalations
            )
    
    def get_agent_metrics(self, agent_id: str) -> Dict[str, Any]:
        """ดึง Metrics ของ Agent เฉพาะ"""
        history = self.get_agent_history(agent_id)
        
        if not history:
            return {"error": "No history found"}
        
        state_counts = defaultdict(int)
        for t in history:
            state_counts[t.to_state] += 1
        
        return {
            "agent_id": agent_id,
            "total_transitions": len(history),
            "current_state": self.get_current_state(agent_id),
            "state_distribution": dict(state_counts),
            "last_transition": history[-1].timestamp if history else None,
            "total_time_ms": sum(t.duration_ms for t in history)
        }

Integration กับ State Machine

class TrackedStateMachine(AgentStateMachine): """State Machine ที่เชื่อมต่อกับ Lifecycle Tracker""" def __init__(self, agent_id: str, tracker: LifecycleTracker): super().__init__(agent_id) self.tracker = tracker self._previous_state = None def try_transition(self) -> bool: """ดำเนินการเปลี่ยนสถานะพร