#การสร้าง AI Agent ด้วย LangGraph

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