Skip to content

บทที่ 9: LangGraph — Multi-Agent Workflows

LangGraph คือไลบรารีสำหรับสร้าง AI workflow แบบมีสถานะ (stateful) ที่ซับซ้อน โดยเหมาะกับงานที่มี:

  • การวนลูป (loops)
  • การแตกแขนงตามเงื่อนไข (conditional branching)
  • การทำงานหลาย agent ร่วมกัน (multi-agent)
  • การหยุดรอมนุษย์อนุมัติ (human-in-the-loop)

คำอธิบาย: แผนภาพด้านล่างสรุปโฟลว์การทำงานให้เห็นภาพรวมของหัวข้อนี้อย่างชัดเจน

graph TD
    LCEL["LangChain LCEL"] --> Linear["Input -> Process -> Output"]

    subgraph LG["LangGraph"]
        A["Node A"] --> B["Node B"]
        B --> C{"Condition"}
        C -->|Path 1| D["Node D"]
        C -->|Path 2| E["Node E"]
        D --> B
        E --> F["End"]
    end

เมื่อไหร่ควรใช้ LangGraph?

Section titled “เมื่อไหร่ควรใช้ LangGraph?”
ใช้ LCELใช้ LangGraph
Pipeline เส้นตรงมี loop/branch
งานต่อเนื่องไม่ซับซ้อนต้องตัดสินใจตามเงื่อนไข
ไม่ต้องเก็บ state ซับซ้อนต้องจัดการ state หลายขั้น
single-agentmulti-agent

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from langgraph.graph import StateGraph
graph = StateGraph(State)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages
class State(TypedDict):
messages: Annotated[list, add_messages] # บทสนทนาสะสม
current_step: str # ขั้นตอนปัจจุบัน
data: dict # ข้อมูลธุรกิจที่ใช้ร่วมกัน

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

def my_node(state: State) -> dict:
return {"current_step": "done"}

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

graph.add_edge("node_a", "node_b")
graph.add_conditional_edges(
"node_a",
router,
{"path_1": "node_b", "path_2": "node_c"}
)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from langgraph.graph import START, END
graph.add_edge(START, "first_node")
graph.add_edge("last_node", END)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
app = graph.compile(checkpointer=checkpointer)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
class State(TypedDict):
messages: Annotated[list, add_messages]
llm = ChatOpenAI(model="gpt-4o-mini")
def chatbot(state: State):
response = llm.invoke(state["messages"])
return {"messages": [response]}
graph = StateGraph(State)
graph.add_node("chatbot", chatbot)
graph.add_edge(START, "chatbot")
graph.add_edge("chatbot", END)
app = graph.compile()
result = app.invoke({
"messages": [("human", "สวัสดี! LangGraph คืออะไร?")]
})
print(result["messages"][-1].content)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_core.messages import ToolMessage
class State(TypedDict):
messages: Annotated[list, add_messages]
needs_tool: bool
def assistant(state: State):
response = llm_with_tools.invoke(state["messages"])
return {
"messages": [response],
"needs_tool": len(response.tool_calls) > 0,
}
def tool_executor(state: State):
last_message = state["messages"][-1]
outputs = []
for tool_call in last_message.tool_calls:
result = tool_map[tool_call["name"]].invoke(tool_call["args"])
outputs.append(ToolMessage(content=str(result), tool_call_id=tool_call["id"]))
return {"messages": outputs}
def router(state: State):
return "tools" if state["needs_tool"] else "end"
graph = StateGraph(State)
graph.add_node("assistant", assistant)
graph.add_node("tools", tool_executor)
graph.add_edge(START, "assistant")
graph.add_conditional_edges("assistant", router, {"tools": "tools", "end": END})
graph.add_edge("tools", "assistant")
app = graph.compile()

คำอธิบาย: แผนภาพด้านล่างสรุปโฟลว์การทำงานให้เห็นภาพรวมของหัวข้อนี้อย่างชัดเจน

graph TD
    START([START]) --> assistant[assistant]
    assistant --> decision{needs_tool?}
    decision -->|yes| tools[tools]
    tools --> assistant
    decision -->|no| END([END])

โครงสร้างที่ใช้บ่อยใน production:

  1. node แรกทำหน้าที่วิเคราะห์งาน
  2. router ตัดสินใจเส้นทาง
  3. ส่งไป worker node ที่เหมาะสม
  4. รวมผลลัพธ์กลับมาตอบ
graph LR
    A[Analyzer] --> B{Route}
    B -->|faq| C[FAQ Worker]
    B -->|search| D[Search Worker]
    B -->|calc| E[Math Worker]
    C --> F[Response]
    D --> F
    E --> F

ให้มนุษย์อนุมัติก่อนขั้นตอนที่มีความเสี่ยง

from typing import TypedDict, Annotated
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_core.messages import AIMessage
checkpointer = MemorySaver()
class State(TypedDict):
messages: Annotated[list, add_messages]
def propose_action(state: State):
response = llm.invoke(state["messages"])
return {"messages": [response]}
def execute_action(state: State):
return {"messages": [AIMessage(content="ดำเนินการเรียบร้อย")]}
graph = StateGraph(State)
graph.add_node("propose", propose_action)
graph.add_node("execute", execute_action)
graph.add_edge(START, "propose")
graph.add_edge("propose", "execute")
graph.add_edge("execute", END)
app = graph.compile(
checkpointer=checkpointer,
interrupt_before=["execute"],
)

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
class State(TypedDict):
messages: Annotated[list, add_messages]
task: str
current_agent: str
result: str
llm = ChatOpenAI(model="gpt-4o-mini")
def researcher(state: State):
prompt = f"คุณเป็นนักวิจัย ค้นข้อมูลเกี่ยวกับ: {state['task']}"
response = llm.invoke([("system", prompt)] + state["messages"])
return {"messages": [response], "current_agent": "researcher"}
def writer(state: State):
prompt = "คุณเป็นนักเขียน เขียนจากข้อมูลที่นักวิจัยรวบรวม"
response = llm.invoke([("system", prompt)] + state["messages"])
return {"messages": [response], "current_agent": "writer"}
def editor(state: State):
prompt = "คุณเป็นบรรณาธิการ ปรับปรุงบทความให้สมบูรณ์"
response = llm.invoke([("system", prompt)] + state["messages"])
return {"messages": [response], "current_agent": "editor", "result": response.content}
graph = StateGraph(State)
graph.add_node("researcher", researcher)
graph.add_node("writer", writer)
graph.add_node("editor", editor)
graph.add_edge(START, "researcher")
graph.add_edge("researcher", "writer")
graph.add_edge("writer", "editor")
graph.add_edge("editor", END)
app = graph.compile()

คำอธิบาย: แผนภาพด้านล่างสรุปโฟลว์การทำงานให้เห็นภาพรวมของหัวข้อนี้อย่างชัดเจน

graph LR
    START([START]) --> R[Researcher]
    R --> W[Writer]
    W --> E[Editor]
    E --> END([END])

สถาปัตยกรรม Multi-Agent ที่ใช้บ่อย

Section titled “สถาปัตยกรรม Multi-Agent ที่ใช้บ่อย”
  1. Planner Agent แยกงานเป็น subtasks
  2. Specialist Agents ทำงานเฉพาะด้าน
  3. Reviewer Agent ตรวจคุณภาพ
  4. Finalizer Agent รวมคำตอบสุดท้าย

คำอธิบาย: โค้ดตัวอย่างด้านล่างแสดงวิธีใช้งานด้วย Python ตามหัวข้อนี้แบบทีละขั้นตอน

for event in app.stream(
{
"messages": [("human", "เขียนบทความเรื่อง AI")],
"task": "AI",
"current_agent": "",
"result": "",
},
stream_mode="values",
):
if "current_agent" in event:
print(f"Agent: {event['current_agent']}")

แนวทางที่ควรทำในระบบจริง:

  1. เก็บ input/output ของแต่ละ node
  2. บันทึก route ที่ตัดสินใจในทุกคำขอ
  3. วัด latency ต่อ node
  4. จำกัดจำนวนรอบ loop สูงสุด

LangSmith ช่วย trace workflow และหาจุดคอขวดได้ง่ายมาก


FeatureLCELLangGraph
Linear chainsเหมาะมากใช้ได้แต่เกินความจำเป็น
Loops/Cyclesจำกัดรองรับ
State managementจำกัดยืดหยุ่น
Multi-agentทำได้ยากออกแบบมาเพื่อสิ่งนี้
Human-in-the-loopจำกัดรองรับ
  1. ออกแบบ State ให้เล็กและชัดเจน
  2. แยก node ให้มีความรับผิดชอบเดียว
  3. เขียน router ให้ deterministic
  4. ใส่ timeout/retry สำหรับ node ที่เรียก API ภายนอก
  5. เขียน test ทั้งระดับ node และระดับ graph
  1. state ใหญ่เกินไปจน graph ช้า
  2. loop ไม่มีเงื่อนไขจบที่ชัดเจน
  3. router พึ่งข้อความอิสระมากเกินไป
  4. ไม่มี error path แยกสำหรับ debug