LangGraph เป็น framework ใน ecosystem ของ LangChain สำหรับสร้าง stateful AI agents และ agentic workflows ที่มีขั้นตอนการทำงานซับซ้อนกว่าการเรียก Large Language Model (LLM) เพียงครั้งเดียว

แนวคิดสำคัญของ LangGraph คือการมองระบบ Agent เป็น กราฟ (Graph) ซึ่งประกอบด้วย

  • State — ข้อมูลหรือบริบทที่ไหลอยู่ในระบบ
  • Node — ขั้นตอนการประมวลผล เช่น เรียก LLM หรือเรียก Tool
  • Edge — เส้นทางเชื่อมระหว่าง Node
  • Conditional Edge — เส้นทางที่เลือกตามเงื่อนไข
  • Cycle — การวนกลับไปทำงานซ้ำ เช่น LLM → Tool → LLM
  • Checkpoint / Persistence — การบันทึกสถานะเพื่อให้ Agent ทำงานต่อจากจุดเดิม
  • Human-in-the-loop — แทรกมนุษย์เข้ามาตรวจสอบหรืออนุมัติบางขั้นตอนได้

LangGraph จึงเหมาะกับงานที่ต้องการควบคุม workflow ของ Agent อย่างชัดเจน เช่น

  • AI Assistant ที่เรียก API หรือฐานข้อมูล
  • Research Agent
  • Coding Agent
  • RAG Agent
  • Customer Support Agent
  • Workflow Automation
  • Multi-Agent System
  • Agent ที่ต้องมี Memory
  • Agent ที่ต้องขออนุมัติจากมนุษย์ก่อนทำบาง Action

#1. LangGraph ต่างจากการเรียก LLM ปกติอย่างไร

การเรียก LLM แบบปกติอาจมีโครงสร้างประมาณนี้

User
  |
  v
LLM
  |
  v
Answer

แต่ Agent ต้องสามารถคิดว่าจะทำอะไรต่อ เช่น

User
  |
  v
LLM
  |
  +------ ไม่ต้องใช้ Tool ------> Answer
  |
  +------ ต้องใช้ Tool
             |
             v
            Tool
             |
             v
            LLM
             |
             +---- อาจเรียก Tool เพิ่ม
             |
             +---- Answer

LangGraph ช่วยให้เราเขียน workflow ลักษณะนี้ออกมาเป็น Graph โดยตรง

flowchart LR
    START --> LLM
    LLM -->|tool_calls| TOOL
    TOOL --> LLM
    LLM -->|no tool call| END

จุดเด่นสำคัญคือ เราควบคุมวงจรการทำงานของ Agent ได้อย่าง explicit


#2. สถาปัตยกรรมของ Agent

ตัวอย่างในบทความนี้จะสร้าง Agent ที่สามารถเลือกใช้เครื่องมือคำนวณได้ 3 ตัว

add(a, b)
multiply(a, b)
divide(a, b)

Workflow:

flowchart TD
    S([START])
    L[LLM Node]
    T[Tool Node]
    E([END])

    S --> L
    L -->|มี tool_calls| T
    T --> L
    L -->|ไม่มี tool_calls| E

หลักการคือ

  1. รับคำถามจากผู้ใช้
  2. ส่งข้อความเข้า LLM
  3. LLM วิเคราะห์ว่าต้องใช้ Tool หรือไม่
  4. ถ้าต้องใช้ Tool ให้ไปที่ tool_node
  5. นำผลลัพธ์จาก Tool กลับไปให้ LLM
  6. LLM อาจเรียก Tool เพิ่ม หรือสร้างคำตอบสุดท้าย
  7. เมื่อไม่มี tool_calls แล้ว Graph จบการทำงาน

#3. เตรียม Environment

บทความนี้ใช้ Python

แนะนำ Python 3.11 ขึ้นไป

สร้าง project

mkdir langgraph-agent
cd langgraph-agent

สร้าง virtual environment ด้วย uv

uv venv

ติดตั้ง package

uv add langgraph langchain langchain-anthropic python-dotenv

หรือใช้ pip

pip install -U langgraph langchain langchain-anthropic python-dotenv

สร้างไฟล์ .env

ANTHROPIC_API_KEY=your_api_key

ไม่ควร hard-code API Key ลงใน source code หรือ commit ไฟล์ .env ขึ้น Git


#4. สร้าง Tools

สร้างไฟล์ main.py

from langchain.tools import tool


@tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b


@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b


@tool
def divide(a: int, b: int) -> float:
    """Divide a by b."""
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b

Decorator

@tool

ทำให้ Python function สามารถถูก expose เป็น Tool ให้ LLM เรียกใช้งานได้

คำอธิบายใน docstring มีความสำคัญ เพราะโมเดลจะใช้ข้อมูลนี้เพื่อพิจารณาว่า Tool ทำหน้าที่อะไร


#5. สร้าง Chat Model และ Bind Tools

เพิ่ม code

from langchain.chat_models import init_chat_model


model = init_chat_model(
    "claude-sonnet-4-6",
    temperature=0,
)

tools = [add, multiply, divide]

tools_by_name = {
    tool.name: tool
    for tool in tools
}

model_with_tools = model.bind_tools(tools)

คำสั่ง

model.bind_tools(tools)

ทำให้ LLM ทราบว่ามี Tool อะไรให้เลือกใช้

ตัว LLM จะไม่ได้ execute function โดยตรง แต่จะส่ง tool_calls ออกมา เช่น

{
  "name": "multiply",
  "args": {
    "a": 25,
    "b": 4
  }
}

จากนั้น Graph ของเราจะเป็นผู้รับคำสั่งดังกล่าวไป execute จริง


#6. สร้าง State

หัวใจของ LangGraph คือ State

import operator

from langchain.messages import AnyMessage
from typing_extensions import Annotated, TypedDict


class AgentState(TypedDict):
    messages: Annotated[list[AnyMessage], operator.add]
    llm_calls: int

messages ใช้เก็บ conversation เช่น

HumanMessage
AIMessage
ToolMessage
AIMessage

ส่วน

Annotated[list[AnyMessage], operator.add]

กำหนด reducer ให้ LangGraph append ข้อความใหม่ต่อจากของเดิม แทนการเขียนทับ list ทั้งหมด


#7. สร้าง LLM Node

Node แรกทำหน้าที่เรียกโมเดล

from langchain.messages import SystemMessage


def llm_call(state: AgentState):
    response = model_with_tools.invoke(
        [
            SystemMessage(
                content=(
                    "You are a helpful AI assistant. "
                    "Use tools when calculation is required."
                )
            )
        ]
        + state["messages"]
    )

    return {
        "messages": [response],
        "llm_calls": state.get("llm_calls", 0) + 1,
    }

Node รับ

state

แล้วคืนค่าบางส่วนของ State ที่ต้องการ update


#8. สร้าง Tool Node

เมื่อ LLM ต้องการเรียก Tool เราจะอ่าน tool_calls

from langchain.messages import ToolMessage


def tool_node(state: AgentState):
    results = []

    last_message = state["messages"][-1]

    for tool_call in last_message.tool_calls:
        tool = tools_by_name[tool_call["name"]]

        observation = tool.invoke(
            tool_call["args"]
        )

        results.append(
            ToolMessage(
                content=str(observation),
                tool_call_id=tool_call["id"],
            )
        )

    return {
        "messages": results
    }

Workflow ณ จุดนี้จะเป็น

LLM
 |
 | tool_calls
 v
Tool Node
 |
 | ToolMessage
 v
LLM

#9. สร้าง Conditional Routing

Agent ต้องรู้ว่าเมื่อไรควรไป Tool และเมื่อไรควรจบ

from typing import Literal
from langgraph.graph import END


def should_continue(
    state: AgentState,
) -> Literal["tool_node", END]:

    last_message = state["messages"][-1]

    if last_message.tool_calls:
        return "tool_node"

    return END

ถ้า LLM ส่ง tool_calls

LLM → Tool

แต่ถ้าไม่มี

LLM → END

นี่คือส่วนที่ทำให้ workflow กลายเป็น Agent Loop


#10. สร้าง StateGraph

Import LangGraph

from langgraph.graph import StateGraph, START, END

สร้าง Graph

builder = StateGraph(AgentState)

เพิ่ม Node

builder.add_node(
    "llm_call",
    llm_call,
)

builder.add_node(
    "tool_node",
    tool_node,
)

กำหนดจุดเริ่มต้น

builder.add_edge(
    START,
    "llm_call",
)

เพิ่ม Conditional Edge

builder.add_conditional_edges(
    "llm_call",
    should_continue,
    ["tool_node", END],
)

หลัง Tool ทำงานเสร็จ ให้วนกลับไปหา LLM

builder.add_edge(
    "tool_node",
    "llm_call",
)

Compile

agent = builder.compile()

กราฟสมบูรณ์จึงเป็น

START
  |
  v
LLM
  |
  +------ ไม่มี Tool ------> END
  |
  +------ มี Tool
             |
             v
            TOOL
             |
             +-------------> LLM

#11. Code ฉบับสมบูรณ์

import operator
from typing import Literal

from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain.messages import (
    AnyMessage,
    HumanMessage,
    SystemMessage,
    ToolMessage,
)
from langchain.tools import tool
from langgraph.graph import END, START, StateGraph
from typing_extensions import Annotated, TypedDict


load_dotenv()


# -------------------------
# Tools
# -------------------------

@tool
def add(a: int, b: int) -> int:
    """Add two integers."""
    return a + b


@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b


@tool
def divide(a: int, b: int) -> float:
    """Divide a by b."""
    if b == 0:
        raise ValueError("Cannot divide by zero")

    return a / b


tools = [
    add,
    multiply,
    divide,
]

tools_by_name = {
    tool.name: tool
    for tool in tools
}


# -------------------------
# Model
# -------------------------

model = init_chat_model(
    "claude-sonnet-4-6",
    temperature=0,
)

model_with_tools = model.bind_tools(tools)


# -------------------------
# State
# -------------------------

class AgentState(TypedDict):
    messages: Annotated[
        list[AnyMessage],
        operator.add,
    ]
    llm_calls: int


# -------------------------
# LLM Node
# -------------------------

def llm_call(state: AgentState):
    response = model_with_tools.invoke(
        [
            SystemMessage(
                content=(
                    "You are a helpful AI assistant. "
                    "Use tools when arithmetic is required."
                )
            )
        ]
        + state["messages"]
    )

    return {
        "messages": [response],
        "llm_calls": state.get(
            "llm_calls",
            0,
        ) + 1,
    }


# -------------------------
# Tool Node
# -------------------------

def tool_node(state: AgentState):
    results = []

    last_message = state["messages"][-1]

    for tool_call in last_message.tool_calls:
        tool = tools_by_name[
            tool_call["name"]
        ]

        observation = tool.invoke(
            tool_call["args"]
        )

        results.append(
            ToolMessage(
                content=str(observation),
                tool_call_id=tool_call["id"],
            )
        )

    return {
        "messages": results
    }


# -------------------------
# Router
# -------------------------

def should_continue(
    state: AgentState,
) -> Literal["tool_node", END]:

    last_message = state["messages"][-1]

    if last_message.tool_calls:
        return "tool_node"

    return END


# -------------------------
# Build Graph
# -------------------------

builder = StateGraph(AgentState)

builder.add_node(
    "llm_call",
    llm_call,
)

builder.add_node(
    "tool_node",
    tool_node,
)

builder.add_edge(
    START,
    "llm_call",
)

builder.add_conditional_edges(
    "llm_call",
    should_continue,
    ["tool_node", END],
)

builder.add_edge(
    "tool_node",
    "llm_call",
)

agent = builder.compile()


# -------------------------
# Run
# -------------------------

result = agent.invoke(
    {
        "messages": [
            HumanMessage(
                content=(
                    "คำนวณ 25 คูณ 8 "
                    "แล้วนำผลลัพธ์หารด้วย 4"
                )
            )
        ],
        "llm_calls": 0,
    }
)

print(
    result["messages"][-1].content
)

print(
    "LLM calls:",
    result["llm_calls"],
)

รันโปรแกรม

uv run python main.py

หรือ

python main.py

#12. Agent ทำงานอย่างไรเมื่อ Run จริง

สมมติผู้ใช้ถาม

คำนวณ 25 คูณ 8 แล้วนำผลลัพธ์หารด้วย 4

Agent อาจทำงานดังนี้

Human
  |
  v
LLM
  |
  | multiply(25, 8)
  v
Tool
  |
  | 200
  v
LLM
  |
  | divide(200, 4)
  v
Tool
  |
  | 50
  v
LLM
  |
  v
"คำตอบคือ 50"

สังเกตว่าเราไม่ได้เขียนว่า

multiply(...)
divide(...)

ตามลำดับแบบ hard-code

แต่ LLM เป็นผู้เลือกเองว่าจะใช้ Tool ใด

นี่คือความแตกต่างสำคัญระหว่าง Workflow ธรรมดา กับ AI Agent


#13. Visualize Graph

LangGraph สามารถสร้างภาพของ Graph ได้

from IPython.display import Image, display


display(
    Image(
        agent
        .get_graph(xray=True)
        .draw_mermaid_png()
    )
)

เหมาะสำหรับใช้ตรวจสอบว่า workflow เชื่อมต่อกันถูกต้องหรือไม่


#14. เพิ่ม Memory ด้วย Checkpointer

Agent ในตัวอย่างก่อนหน้ายังไม่ได้เก็บ state ข้ามการ invoke

สามารถเพิ่ม checkpointer ได้ เช่น In-Memory Checkpointer สำหรับ development

from langgraph.checkpoint.memory import InMemorySaver


checkpointer = InMemorySaver()

agent = builder.compile(
    checkpointer=checkpointer
)

จากนั้นกำหนด thread_id

config = {
    "configurable": {
        "thread_id": "user-001"
    }
}

เรียกครั้งแรก

agent.invoke(
    {
        "messages": [
            HumanMessage(
                content="ฉันมีเงิน 100 บาท"
            )
        ],
        "llm_calls": 0,
    },
    config=config,
)

เรียกครั้งถัดไปด้วย thread_id เดิม

result = agent.invoke(
    {
        "messages": [
            HumanMessage(
                content="เพิ่มอีก 50 บาท รวมเป็นเท่าไร"
            )
        ],
        "llm_calls": 0,
    },
    config=config,
)

แนวคิด thread_id มีประโยชน์กับ

  • Chatbot
  • Personal Assistant
  • Customer Support
  • Long-running Workflow
  • Human approval flow

สำหรับ production ควรเลือก persistent checkpointer ที่เหมาะสมกับระบบ เช่น database-backed checkpoint storage แทนการเก็บเฉพาะใน process memory


#15. Human-in-the-loop

หนึ่งในจุดเด่นของ LangGraph คือ workflow สามารถหยุดรอมนุษย์ได้

ตัวอย่างงานที่เหมาะสม

AI สร้างคำสั่งโอนเงิน
        |
        v
Human Approval
        |
     Approve?
      /    \
    Yes     No
    |        |
 Execute   Cancel

แนวทางนี้สำคัญเมื่อ Agent มีสิทธิ์ทำ Action ที่มีผลจริง เช่น

  • ส่ง Email
  • Deploy Production
  • ลบข้อมูล
  • อนุมัติเอกสาร
  • สร้าง Purchase Order
  • เรียก Payment API

หลักการออกแบบที่ดีคือ

ให้ AI มี autonomy เฉพาะส่วนที่มีความเสี่ยงต่ำ และใส่ approval gate ก่อน action ที่มีผลกระทบสูง


#16. จาก Single Agent ไป Multi-Agent

เมื่อระบบซับซ้อนขึ้น เราสามารถสร้างหลาย Agent และเชื่อมเป็น Graph

ตัวอย่าง

flowchart TD
    U[User]
    S[Supervisor]
    R[Research Agent]
    C[Coding Agent]
    D[Database Agent]
    A[Answer Agent]

    U --> S
    S --> R
    S --> C
    S --> D

    R --> A
    C --> A
    D --> A

    A --> U

แต่ละ Agent มีหน้าที่เฉพาะ

Agent หน้าที่
Supervisor วิเคราะห์คำขอและ routing
Research Agent ค้นหาและสรุปข้อมูล
Coding Agent วิเคราะห์หรือสร้าง code
Database Agent Query ข้อมูล
Answer Agent รวมผลและสร้างคำตอบ

LangGraph เหมาะกับ architecture ลักษณะนี้เพราะสามารถกำหนด

  • state ร่วม
  • routing
  • loop
  • parallel branches
  • retry
  • checkpoint
  • human approval

ได้ในระดับ Graph


#17. แนวทางจัดโครงสร้าง Project

เมื่อ project เริ่มใหญ่ ไม่ควรรวมทุกอย่างไว้ใน main.py

โครงสร้างตัวอย่าง

langgraph-agent/
│
├── .env
├── pyproject.toml
│
├── app/
│   ├── __init__.py
│   │
│   ├── graph.py
│   ├── state.py
│   ├── models.py
│   │
│   ├── nodes/
│   │   ├── __init__.py
│   │   ├── llm_node.py
│   │   └── tool_node.py
│   │
│   └── tools/
│       ├── __init__.py
│       ├── calculator.py
│       ├── database.py
│       └── search.py
│
└── tests/
    ├── test_tools.py
    └── test_graph.py

แบ่ง responsibility ออกจากกันชัดเจน จะช่วยให้

  • test ง่าย
  • เปลี่ยน model ง่าย
  • เพิ่ม tool ง่าย
  • เพิ่ม node ง่าย
  • ดูแล routing ง่าย
  • รองรับ Multi-Agent ในอนาคต

#18. การทดสอบ Agent

Agent ไม่ควรทดสอบเฉพาะ final answer

ควรแยกทดสอบอย่างน้อย 4 ระดับ

#18.1 Tool Test

def test_add():
    assert add.invoke(
        {
            "a": 2,
            "b": 3,
        }
    ) == 5

#18.2 Node Test

ทดสอบว่าแต่ละ Node update state ถูกต้อง

#18.3 Routing Test

ตรวจสอบว่า

มี tool_calls

ต้อง route ไป Tool

และ

ไม่มี tool_calls

ต้องไป END

#18.4 End-to-End Agent Test

ทดสอบ workflow จริงตั้งแต่

User Input
   ↓
LLM
   ↓
Tool
   ↓
LLM
   ↓
Final Answer

สำหรับ production ควรเก็บ trace และวัด metric เช่น

  • Task Success Rate
  • Tool Selection Accuracy
  • Tool Execution Error Rate
  • Number of LLM Calls
  • Latency
  • Token Usage
  • Cost
  • Hallucination Rate

#19. LangGraph เหมาะกับงานแบบไหน

LangGraph เหมาะมากเมื่อระบบมีคุณสมบัติต่อไปนี้

#ต้องมีหลายขั้นตอน

เช่น

วิเคราะห์ → ค้นข้อมูล → ตรวจสอบ → สรุป

#ต้องมี Loop

เช่น

LLM → Tool → LLM → Tool → LLM

#ต้องเก็บ State

เช่น Chatbot หรือ workflow ที่ใช้เวลานาน

#ต้องมี Routing

เช่น

Question
  |
  +--> SQL Agent
  |
  +--> RAG Agent
  |
  +--> Web Search Agent

#ต้องมี Human Approval

เช่น Agent ที่มีสิทธิ์ execute action สำคัญ

#ต้องสร้าง Multi-Agent System

เช่น Supervisor + Specialized Agents


#20. เมื่อไรไม่จำเป็นต้องใช้ LangGraph

ถ้าระบบเป็นเพียง

Prompt → LLM → Answer

การใช้ LangGraph อาจซับซ้อนเกินความจำเป็น

ในกรณีที่ workflow เป็น sequence ตายตัว เช่น

Step A
  ↓
Step B
  ↓
Step C

โดยไม่มี routing, state, tool loop หรือ decision making การใช้ function ธรรมดาหรือ workflow engine ที่ง่ายกว่าอาจเพียงพอ


#21. Best Practices

#1. ให้ Tool ทำงานเฉพาะเจาะจง

ไม่ควรสร้าง Tool ที่ทำทุกอย่าง

ไม่ดี

do_everything()

ดีกว่า

search_documents()
get_customer()
create_ticket()
calculate_total()

#2. Tool Description ต้องชัดเจน

LLM ใช้ชื่อ Tool, argument schema และ description ในการตัดสินใจเลือก Tool


#3. จำกัด Loop

ควรมีแนวทางป้องกัน Agent วนไม่จบ เช่น

  • recursion limit
  • maximum tool calls
  • timeout
  • retry policy

#4. Validate Tool Input

อย่าเชื่อ argument จาก LLM โดยอัตโนมัติ โดยเฉพาะ Tool ที่ทำ action จริง


#5. แยก Read Tool และ Write Tool

ตัวอย่าง

Read
- search_order
- get_customer
- query_database

Write
- update_order
- delete_customer
- send_payment

Write Tool ควรมี security control สูงกว่า


#6. เก็บ Trace

Agent system มี behavior แบบ dynamic จึงควร trace

Input
 ↓
Node
 ↓
Model
 ↓
Tool call
 ↓
Tool result
 ↓
Routing
 ↓
Output

การดูเฉพาะ final answer ทำให้ debug ยาก


#7. ใส่ Human Approval ใน High-Risk Action

โดยเฉพาะงาน

  • Payment
  • Deployment
  • Delete
  • External Communication
  • Permission Change

#22. LangGraph กับ LangChain Agent

โดยภาพรวม

LangChain Agent
      |
      | ใช้งานง่ายกว่า
      | abstraction สูงกว่า
      v
Prebuilt Agent Architecture

LangGraph
      |
      | ควบคุม workflow มากกว่า
      | abstraction ต่ำกว่า
      v
Custom Agent Architecture

LangChain agents เองสามารถใช้ LangGraph เป็น execution foundation ได้

ดังนั้นไม่ได้หมายความว่าต้องเลือกอย่างใดอย่างหนึ่งเสมอไป

เลือกตามระดับ control ที่ต้องการ


#23. สรุป

LangGraph ทำให้การสร้าง Agent เปลี่ยนจากแนวคิด

Prompt → LLM → Answer

ไปเป็น

State
  ↓
Node
  ↓
Decision
  ↓
Tool
  ↓
State Update
  ↓
Loop / Route
  ↓
Final Answer

หัวใจสำคัญที่ควรรู้มี 5 เรื่อง

  1. State — ข้อมูลกลางของ Agent
  2. Node — ขั้นตอนประมวลผล
  3. Edge — เส้นทางการทำงาน
  4. Conditional Edge — การตัดสินใจและ Routing
  5. Cycle — ทำให้ LLM เรียก Tool และกลับมาคิดต่อได้

เมื่อเข้าใจ 5 แนวคิดนี้แล้ว สามารถต่อยอดจาก Single Agent ไปสู่

RAG Agent
    ↓
Tool-Using Agent
    ↓
Agent with Memory
    ↓
Human-in-the-loop Agent
    ↓
Multi-Agent System

ได้อย่างเป็นระบบ


#References