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-pythonTạo file .env cho API keys:
OPENAI_API_KEY=your-openai-key-here
TAVILY_API_KEY=your-tavily-key-hereBướ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: intAnnotation 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 ENDBướ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.