AI agents có khả năng lập luận, dùng tools và duy trì trạng thái qua nhiều bước là một trong những ứng dụng thực tế nhất của LLM hiện nay. Trong các framework hiện có, LangGraph nổi bật vì cho phép bạn kiểm soát rõ ràng hành vi agent qua kiến trúc đồ thị.

Tutorial này hướng dẫn bạn xây một AI agent hoàn chỉnh từ đầu với LangGraph. Cuối bài, agent của bạn có thể tìm kiếm web, tính toán và nối nhiều bước lập luận liên tiếp.

LangGraph Là Gì?

LangGraph là thư viện xây dựng trên LangChain, cho phép định nghĩa workflow agent dưới dạng đồ thị có hướng. Mỗi node trong đồ thị là một bước tính toán (gọi LLM, chạy tool, đưa ra quyết định), còn edges định nghĩa luồng điều khiển giữa chúng.

Cách tiếp cận dựa trên đồ thị giải quyết vấn đề cơ bản của agent loop truyền thống: tính khó dự đoán. Thay vì để LLM tự quyết toàn bộ luồng, bạn định nghĩa cấu trúc rõ ràng trong khi để LLM xử lý lập luận bên trong mỗi node.

Yêu Cầu

  • Python 3.10 trở lên
  • OpenAI API key (hoặc bất kỳ LLM provider nào LangChain hỗ trợ)
  • Hiểu cơ bản Python async/await
  • Terminal và code editor

Bước 1: Cài Đặt Dependencies

mkdir langgraph-agent && cd langgraph-agent
python -m venv venv
source venv/bin/activate
pip install langgraph langchain-openai langchain-community tavily-python

Tạo file .env cho API keys:

OPENAI_API_KEY=your-openai-key-here
TAVILY_API_KEY=your-tavily-key-here

Bước 2: Định Nghĩa Agent State

LangGraph agents hoạt động trên một state object dùng chung. State này được truyền qua các nodes và tích lũy thông tin khi agent làm việc:

from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
 
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    tool_calls_count: int

Annotation add_messages báo cho LangGraph thêm messages mới thay vì ghi đè danh sách. tool_calls_count theo dõi số lần agent đã dùng tools — hữu ích để đặt giới hạn an toàn.

Bước 3: Thiết Lập Tools

Định nghĩa các tools agent có thể dùng — ở đây tạo một web search tool và calculator:

from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.tools import tool
 
search_tool = TavilySearchResults(max_results=3)
 
@tool
def calculator(expression: str) -> str:
    """Tính biểu thức toán học. Dùng cú pháp Python."""
    try:
        result = eval(expression, {"__builtins__": {}}, {})
        return str(result)
    except Exception as e:
        return f"Lỗi: {e}"
 
tools = [search_tool, calculator]

Bước 4: Tạo Agent Node

Agent node gọi LLM với lịch sử hội thoại hiện tại và danh sách tools khả dụng:

from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o", temperature=0)
llm_with_tools = llm.bind_tools(tools)
 
def agent_node(state: AgentState) -> dict:
    """Gọi LLM để quyết định bước tiếp theo."""
    messages = state["messages"]
    response = llm_with_tools.invoke(messages)
    return {"messages": [response], "tool_calls_count": state.get("tool_calls_count", 0)}

Bước 5: Tạo Tool Execution Node

Khi LLM quyết định dùng tool, node này thực thi tool được chọn và trả về kết quả:

from langgraph.prebuilt import ToolNode
 
tool_node = ToolNode(tools)

ToolNode có sẵn của LangGraph xử lý việc parse yêu cầu tool call từ LLM, thực thi tool phù hợp và format kết quả thành message.

Bước 6: Định Nghĩa Logic Routing

Router quyết định agent nên tiếp tục dùng tools hay trả về câu trả lời cuối:

from langgraph.graph import END
 
def should_continue(state: AgentState) -> str:
    """Quyết định gọi tools hay kết thúc."""
    last_message = state["messages"][-1]
 
    if last_message.tool_calls:
        # Giới hạn an toàn: dừng sau 5 lần gọi tool
        if state.get("tool_calls_count", 0) >= 5:
            return END
        return "tools"
 
    return END

Bước 7: Lắp Ráp Graph

Kết nối tất cả các mảnh thành graph hoàn chỉnh:

from langgraph.graph import StateGraph
 
workflow = StateGraph(AgentState)
 
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
 
workflow.set_entry_point("agent")
 
workflow.add_conditional_edges("agent", should_continue, {"tools": "tools", END: END})
workflow.add_edge("tools", "agent")
 
agent = workflow.compile()

Luồng là: bắt đầu tại agent node → nếu muốn dùng tools thì sang tool node → quay lại agent node. Lặp đến khi agent trả lời xong.

Bước 8: Chạy Agent

from langchain_core.messages import HumanMessage
 
async def main():
    inputs = {
        "messages": [HumanMessage(content="Dân số Pháp là bao nhiêu và chia cho 7 được bao nhiêu?")],
        "tool_calls_count": 0
    }
 
    async for event in agent.astream(inputs, stream_mode="values"):
        last_msg = event["messages"][-1]
        print(f"[{last_msg.type}]: {last_msg.content[:200]}")
 
import asyncio
asyncio.run(main())

Bước 9: Thêm Memory Cho Multi-Turn

Để agent nhớ các cuộc trò chuyện trước, thêm checkpointer:

from langgraph.checkpoint.memory import MemorySaver
 
memory = MemorySaver()
agent_with_memory = workflow.compile(checkpointer=memory)
 
config = {"configurable": {"thread_id": "user-session-1"}}
 
response = await agent_with_memory.ainvoke(
    {"messages": [HumanMessage(content="Xin chào, tôi tên là Minh")]},
    config=config
)
 
# Lần gọi tiếp theo với cùng thread_id, agent nhớ ngữ cảnh
response = await agent_with_memory.ainvoke(
    {"messages": [HumanMessage(content="Tôi tên gì?")]},
    config=config
)

Tips Thực Tế

Bắt đầu đơn giản, tăng dần độ phức tạp. Bắt đầu với một tool và luồng tuyến tính. Khi hoạt động ổn định rồi mới thêm logic phân nhánh và tools bổ sung.

Luôn đặt giới hạn tool call. Không có giới hạn, agents có thể vào vòng lặp vô hạn. Pattern tool_calls_count ở trên là biện pháp bảo vệ tối thiểu.

Dùng streaming cho UX tốt hơn. Method astream cho phép hiển thị các bước trung gian cho người dùng theo thời gian thực.

Test với input xác định. Khi debug, dùng câu hỏi có đáp án biết trước để xác minh chuỗi lập luận. Đặt temperature=0 khi phát triển.

Theo dõi chi phí token. Mỗi vòng lặp tốn tokens. Query phức tạp có thể trigger 4-5 tool calls, mỗi lần với cả context window.