การพัฒนา Multi-Agent System ด้วย LangGraph นั้นมีความซับซ้อนอย่างยิ่ง โดยเฉพาะเมื่อต้องจัดการกับ เงื่อนไขการตัดสินใจ (Conditional Branching) และ การวนซ้ำ (Loop Control) ที่ถูกต้อง ในบทความนี้ผมจะแบ่งปันประสบการณ์ตรงจากการแก้ไขข้อผิดพลาดที่เกิดขึ้นจริงในโปรเจกต์

สถานการณ์ข้อผิดพลาดจริง: "TypeError: state is not a StateGraph"

ในการพัฒนาระบบ Customer Support Agent ผมเจอข้อผิดพลาดที่ทำให้แอปพลิเคชันล่มทั้งระบบ:

TypeError: state is not a StateGraph, cannot add conditional edges from a state graph to another state graph

ปัญหานี้เกิดจากการที่ผมเข้าใจผิดว่าสามารถเพิ่ม add_conditional_edges ได้โดยตรงจาก subgraph ไปยัง graph หลัก ซึ่งในความเป็นจริงต้องผ่าน END state หรือใช้ compile ก่อน

พื้นฐาน Conditional Branching ใน LangGraph

การสร้างเงื่อนไขใน LangGraph ทำได้โดยการกำหนด route function ที่คืนค่าชื่อ edge ที่ต้องการ:

from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from typing import TypedDict, Literal

กำหนด State Schema

class AgentState(TypedDict): messages: list intent: str confidence: float

สร้าง route function สำหรับตัดสินใจ

def route_intent(state: AgentState) -> Literal["handle_complaint", "handle_inquiry", "escalate"]: """ ฟังก์ชันสำหรับตัดสินใจเส้นทางตาม intent """ intent = state.get("intent", "") confidence = state.get("confidence", 0.0) if confidence < 0.5: return "escalate" elif "complaint" in intent.lower(): return "handle_complaint" elif "inquiry" in intent.lower(): return "handle_inquiry" else: return "escalate"

สร้าง Graph

workflow = StateGraph(AgentState)

เพิ่ม node

workflow.add_node("analyze", analyze_intent) workflow.add_node("handle_complaint", handle_complaint_node) workflow.add_node("handle_inquiry", handle_inquiry_node) workflow.add_node("escalate", escalate_node)

กำหนด entry point

workflow.set_entry_point("analyze")

เพิ่ม conditional edge

workflow.add_conditional_edges( "analyze", route_intent, { "handle_complaint": "handle_complaint", "handle_inquiry": "handle_inquiry", "escalate": "escalate" } )

compile graph

app = workflow.compile(checkpointer=MemorySaver())

การใช้ Loop Control ใน LangGraph

ในบางกรณีเราต้องการให้ Agent ทำงานวนซ้ำจนกว่าจะได้ผลลัพธ์ที่ต้องการ ผมใช้ loop ด้วย iteration counter ซึ่งช่วยแก้ปัญหา infinite loop ได้:

from langgraph.graph import StateGraph, START, END
from typing import Annotated
import operator

Extended State สำหรับ loop control

class LoopState(TypedDict): messages: list iteration: int status: str def should_continue(state: LoopState) -> Literal["refine", END]: """ ตรวจสอบว่าควรจบการทำงานหรือทำต่อ """ iteration = state.get("iteration", 0) messages = state.get("messages", []) # จำกัดการวนซ้ำสูงสุด 5 ครั้ง if iteration >= 5: return END # ตรวจสอบว่าผลลัพธ์น่าเชื่อถือหรือไม่ if messages and len(messages) > 3: last_message = messages[-1].get("content", "") if len(last_message) > 100: # มีเนื้อหาเพียงพอ return END return "refine"

สร้าง workflow พร้อม loop

loop_graph = StateGraph(LoopState) loop_graph.add_node("initial", initial_process) loop_graph.add_node("refine", refine_process) loop_graph.add_node("finalize", finalize_process) loop_graph.add_edge(START, "initial") loop_graph.add_edge("initial", "refine") loop_graph.add_conditional_edges( "refine", should_continue, { "refine": "refine", END: "finalize" } ) loop_graph.add_edge("finalize", END) loop_app = loop_graph.compile()

การรวม Conditional Routing กับ Tool Calling

ในการใช้งานจริงผมต้องการให้ Agent เลือกใช้ Tool ตามสถานการณ์ โดยใช้ HolySheep AI สำหรับ LLM API ที่มีความเร็วสูงและราคาประหยัด สมัครที่นี่ เพื่อเริ่มต้นใช้งาน:

import httpx
from langchain_core.messages import HumanMessage, AIMessage
from langchain_core.tools import tool

กำหนด Tools สำหรับ Agent

@tool def search_knowledge_base(query: str) -> str: """ค้นหาข้อมูลในฐานความรู้""" # implementation return f"Found: {query} in KB" @tool def calculate_price(items: list) -> str: """คำนวณราคาสินค้า""" total = sum(items) return f"Total: ${total}"

Function สำหรับเรียก HolySheep AI

def call_holysheep(prompt: str, system: str = None) -> str: """เรียก HolySheep AI API สำหรับ LLM inference""" from openai import OpenAI client = OpenAI( api_key="YOUR_HOLYSHEEP_API_KEY", base_url="https://api.holysheep.ai/v1" ) messages = [] if system: messages.append({"role": "system", "content": system}) messages.append({"role": "user", "content": prompt}) response = client.chat.completions.create( model="gpt-4.1", messages=messages, temperature=0.7, max_tokens=500 ) return response.choices[0].message.content

Route function สำหรับเลือกใช้ tool

def route_based_on_intent(state: AgentState) -> Literal["search_knowledge_base", "calculate_price", "general_response"]: """เลือกเส้นทางตามประเภทของคำถาม""" last_message = state["messages"][-1].content.lower() if "ราคา" in last_message or "price" in last_message or "คำนวณ" in last_message: return "calculate_price" elif "ค้นหา" in last_message or "search" in last_message or "หา" in last_message: return "search_knowledge_base" else: return "general_response"

การจัดการ Error Handling ใน Conditional Flow

สิ่งสำคัญคือการจัดการข้อผิดพลาดในแต่ละ branch ผมใช้ try-except block และ retry pattern:

from tenacity import retry, stop_after_attempt, wait_exponential

class ConditionalWorkflow:
    def __init__(self):
        self.graph = self._build_graph()
    
    def _build_graph(self):
        workflow = StateGraph(AgentState)
        
        # เพิ่ม nodes พร้อม error handling
        workflow.add_node("process", self._safe_process)
        workflow.add_node("fallback", self._fallback_handler)
        workflow.add_node("retry", self._retry_node)
        
        workflow.set_entry_point("process")
        
        # Conditional edges พร้อม error state
        workflow.add_conditional_edges(
            "process",
            self._determine_outcome,
            {
                "success": END,
                "error": "fallback",
                "retry": "retry"
            }
        )
        
        workflow.add_conditional_edges(
            "retry",
            self._check_retry_status,
            {
                "continue": "process",
                "max_reached": END
            }
        )
        
        return workflow.compile(checkpointer=MemorySaver())
    
    @retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
    def _safe_process(self, state: AgentState):
        try:
            # ลองเรียก HolySheep AI
            result = call_holysheep(state["messages"][-1].content)
            return {"messages": state["messages"] + [AIMessage(content=result)]}
        except httpx.TimeoutException:
            raise  # ให้ retry decorator จัดการ
        except Exception as e:
            return {"status": "error", "error": str(e)}
    
    def _fallback_handler(self, state: AgentState):
        return {
            "messages": state["messages"] + [
                AIMessage(content="ขออภัย เกิดข้อผิดพลาด กรุณาลองใหม่อีกครั้ง")
            ]
        }
    
    def _determine_outcome(self, state: AgentState):
        if state.get("status") == "error":
            return "error"
        elif state.get("retry_needed"):
            return "retry"
        return "success"

ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข

1. KeyError: 'state' เมื่อใช้ Conditional Edge

สาเหตุ: ฟังก์ชัน route คืนค่า key ที่ไม่ตรงกับที่กำหนดใน edge mapping

# ❌ วิธีที่ผิด - key ไม่ตรงกัน
def bad_route(state):
    if state["value"] > 10:
        return "high"  # ไม่มี "high" ใน mapping
    
workflow.add_conditional_edges(
    "node1",
    bad_route,
    {
        "low": "node2",    # รอ "low" แต่ได้ "high"
        "medium": "node3"  # รอ "medium" แต่ได้ "high"
    }
)

✅ วิธีที่ถูกต้อง - การันตีว่าทุกเส้นทางมี key ตรงกัน

def good_route(state): value = state["value"] if value > 10: return "high" elif value > 5: return "medium" else: return "low" # ครอบคลุมทุกกรณี workflow.add_conditional_edges( "node1", good_route, { "low": "node2", "medium": "node3", "high": "node4" } )

2. Infinite Loop เมื่อใช้ Conditional Edge กลับไปยังตัวเอง

สาเหตุ: ไม่มี condition ที่จะหยุดการวนซ้ำ

# ❌ วิธีที่ผิด - จะวนไม่รู้จบถ้า condition เป็น True ตลอด
def infinite_route(state):
    if state["needs_refinement"]:  # ถ้าเป็น True ตลอด = loop ตลอดไป
        return "refine"
    return END

✅ วิธีที่ถูกต้อง - เพิ่ม iteration counter และ maximum limit

def safe_loop_route(state: AgentState): iteration = state.get("iteration", 0) # จำกัดจำนวนครั้งสูงสุด if iteration >= 5: return END # ตรวจสอบเงื่อนไขอื่นๆ if state["needs_refinement"] and iteration < 5: return "refine" return END

อย่าลืม increment iteration ใน node

def refine_node(state: AgentState): current_iteration = state.get("iteration", 0) return {"iteration": current_iteration + 1}

3. AttributeError: 'dict' object has no attribute 'messages'

สาเหตุ: State เป็น dict ไม่ใช่ object ต้องเข้าถึงด้วย bracket notation

# ❌ วิธีที่ผิด - ใช้ dot notation กับ dict
def bad_node(state: AgentState):
    messages = state.messages  # AttributeError!
    return {"result": messages[-1]}

✅ วิธีที่ถูกต้อง - ใช้ bracket notation

def good_node(state: AgentState): messages = state.get("messages", []) # หรือ state["messages"] if not messages: return {"result": "No messages"} return {"result": messages[-1].content if hasattr(messages[-1], 'content') else str(messages[-1])}

หรือใช้ type annotation แบบถูกต้อง

class ProperState(TypedDict): messages: list data: dict def proper_node(state: ProperState) -> ProperState: messages = state["messages"] # TypedDict อนุญาตให้ใช้ bracket return {"data": {"count": len(messages)}}

4. ConnectionError: Timeout เมื่อเรียก External API

สาเหตุ: ไม่มี timeout configuration หรือ retry mechanism

# ❌ วิธีที่ผิด - ไม่มี timeout
def unsafe_api_call(prompt: str) -> str:
    client = OpenAI(
        api_key="YOUR_HOLYSHEEP_API_KEY",
        base_url="https://api.holysheep.ai/v1"
    )
    response = client.chat.completions.create(
        model