Multi-Agent - Xây dựng "Biệt đội Avengers" cho Jarvis bằng LangGraph

AI Hunter

Member
Bây giờ chúng ta sẽ viết code để tạo ra... nhân viên cho Jarvis.
Hệ thống sẽ hoạt động như sau:
  1. User: "Hãy tìm hiểu giá Bitcoin hiện tại và viết một bài nhận định ngắn."
  2. Supervisor (Sếp): Đọc lệnh. Thấy cần tra cứu -> Giao cho Researcher.
  3. Researcher (Lính 1): Dùng Tool Search Google -> Trả về kết quả giá Bitcoin.
  4. Supervisor: Có số liệu rồi -> Giao cho Writer viết bài.
  5. Writer (Lính 2): Viết bài văn dựa trên số liệu -> Gửi lại Sếp.
  6. Supervisor: Kiểm tra xong -> Trả lời User.
Multi-Agent - Xây dựng Biệt đội Avengers cho Jarvis bằng LangGraph.jpg

1. Chuẩn bị vũ khí​


Chúng ta sẽ dùng thư viện LangGraph (của LangChain) để vẽ sơ đồ làm việc này. Ngoài ra, cần một công cụ Search. Mình đề xuất Tavily API (chuyên cho AI Search, có gói Free cực ngon).

Mở file backend/requirements.txt thêm:
Mã:
langgraph
tavily-python

Đăng ký tài khoản tại https://tavily.com/ để lấy API Key (Miễn phí 1000 search/tháng).

2. Định nghĩa các Nhân viên (Agents)​


Tạo một file mới backend/agent_graph.py. Đây là nơi "phân vai" cho các AI.

Python:
import os
from typing import Annotated, List, TypedDict, Union
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from langchain_ollama import ChatOllama
from langgraph.graph import StateGraph, END, START
import operator

# 1. Cấu hình Model (Nên dùng model thông minh xíu, ví dụ llama3.1 hoặc qwen2.5)
llm = ChatOllama(model="llama3.1", base_url=os.getenv("OLLAMA_URL", "http://localhost:11434"))

# 2. Công cụ Search
tavily_tool = TavilySearchResults(max_results=3)

# 3. Định nghĩa Trạng thái (State) của cuộc họp
# Đây là "cái bảng trắng" giữa phòng họp, ai cũng đọc và ghi vào đây được.
class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], operator.add]
    next_step: str

# --- ĐỊNH NGHĨA CÁC AGENT ---

# Agent 1: Researcher (Nhà nghiên cứu)
def researcher_node(state: AgentState):
    print("🕵️ Researcher đang làm việc...")
    messages = state['messages']
    last_message = messages[-1]
   
    # Dùng Tool để search (Giả lập logic gọi tool)
    # Trong thực tế LangGraph có bind_tools, nhưng để đơn giản ta gọi trực tiếp
    search_result = tavily_tool.invoke(last_message.content)
   
    # Trả về kết quả dưới dạng tin nhắn
    content = f"Dữ liệu tìm được:\n{search_result}"
    return {"messages": [HumanMessage(content=content, name="Researcher")]}

# Agent 2: Writer (Nhà văn)
def writer_node(state: AgentState):
    print("✍️ Writer đang viết bài...")
    messages = state['messages']
   
    # Writer đọc hết tin nhắn trước đó (yêu cầu của sếp + dữ liệu của Researcher)
    prompt = "Dựa vào các dữ liệu trên, hãy viết một báo cáo ngắn gọn, súc tích."
    response = llm.invoke(messages + [HumanMessage(content=prompt, name="Supervisor")])
   
    return {"messages": [response]}

# Agent 3: Supervisor (Sếp - Router)
# Đây là phần khó nhất: LLM phải quyết định bước tiếp theo là gì.
def supervisor_node(state: AgentState):
    messages = state['messages']
    last_message = messages[-1]
   
    # Prompt để Sếp định hướng
    system_prompt = """
    Bạn là Supervisor quản lý quy trình.
    - Nếu người dùng hỏi thông tin cần tra cứu mới nhất -> Chọn 'Researcher'.
    - Nếu đã có dữ liệu từ Researcher -> Chọn 'Writer' để tổng hợp.
    - Nếu Writer đã viết xong hoặc câu hỏi đơn giản -> Chọn 'FINISH'.
   
    Chỉ trả về đúng 1 từ: Researcher, Writer, hoặc FINISH.
    """
   
    # Gọi LLM để xin chỉ đạo
    decision = llm.invoke([SystemMessage(content=system_prompt)] + messages).content.strip()
   
    print(f"👨‍✈️ Sếp quyết định: {decision}")
   
    if "Researcher" in decision: return {"next_step": "Researcher"}
    if "Writer" in decision: return {"next_step": "Writer"}
    return {"next_step": "FINISH"}

# --- XÂY DỰNG ĐỒ THỊ (GRAPH) ---

workflow = StateGraph(AgentState)

# Thêm các nút (Phòng ban)
workflow.add_node("Supervisor", supervisor_node)
workflow.add_node("Researcher", researcher_node)
workflow.add_node("Writer", writer_node)

# Điểm bắt đầu -> Vào phòng Sếp trước
workflow.add_edge(START, "Supervisor")

# Sếp chỉ đâu đánh đó (Conditional Edge)
workflow.add_conditional_edges(
    "Supervisor",
    lambda x: x['next_step'], # Hàm lấy quyết định
    {
        "Researcher": "Researcher",
        "Writer": "Writer",
        "FINISH": END
    }
)

# Làm xong thì quay lại báo cáo Sếp
workflow.add_edge("Researcher", "Supervisor")
workflow.add_edge("Writer", "Supervisor")

# Biên dịch thành cỗ máy chạy được
jarvis_brain = workflow.compile()

3. Tích hợp vào Server API​


Mở file backend/server.py, chúng ta tạo một endpoint mới chuyên xử lý các tác vụ phức tạp.

Python:
from agent_graph import jarvis_brain
from langchain_core.messages import HumanMessage

# ... (Code cũ)

class ComplexRequest(BaseMessage):
    message: str

@app.post("/agent/chat")
async def agent_endpoint(req: ChatRequest):
    print(f"🚀 Kích hoạt chế độ Multi-Agent cho: {req.message}")
   
    # Tạo input ban đầu
    initial_state = {"messages": [HumanMessage(content=req.message, name="User")]}
   
    # Chạy đồ thị (LangGraph)
    # Recursion_limit là số vòng lặp tối đa để tránh loop vô tận
    final_state = jarvis_brain.invoke(initial_state, config={"recursion_limit": 10})
   
    # Lấy tin nhắn cuối cùng (Kết quả của Writer hoặc Sếp)
    final_response = final_state["messages"][-1].content
   
    return {"answer": final_response}

Lưu ý: Đừng quên thêm biến môi trường TAVILY_API_KEY vào file docker-compose.yml nhé!

4. Chạy thử nghiệm​


Bước 1: Rebuild Docker.
Mã:
docker-compose up --build

Bước 2: Thử thách Jarvis.
Hãy dùng Postman hoặc sửa Frontend để gọi vào endpoint /agent/chat.

Câu hỏi: "Tìm kiếm thông tin về vụ phóng tàu Starship mới nhất của SpaceX và tóm tắt thành 3 gạch đầu dòng."

Quy trình bạn sẽ thấy trong Log Terminal:
  1. 👨‍✈️ Sếp quyết định: Researcher (Vì thấy cần tìm tin mới).
  2. 🕵️ Researcher đang làm việc... (Gọi API Tavily search Google).
  3. 👨‍✈️ Sếp quyết định: Writer (Vì đã có dữ liệu thô, cần tóm tắt).
  4. ✍️ Writer đang viết bài... (LLM tổng hợp lại).
  5. 👨‍✈️ Sếp quyết định: FINISH.

Kết quả trả về: Một đoạn văn bản cực kỳ chính xác, cập nhật thông tin mới nhất mà model gốc (Llama 3 training từ quá khứ) không thể biết được.

Tổng kết​


Bạn vừa nâng cấp Jarvis lên tầm Enterprise.
Từ giờ, không có nhiệm vụ nào là quá khó, chỉ cần bạn định nghĩa thêm Agent (ví dụ: Agent chuyên Code Python để vẽ biểu đồ, Agent chuyên check mail...).

Hệ thống Backend đã quá mạnh. Nhưng nhìn lại thì giao diện (Chainlit) vẫn hơi... chán.
Đã đến lúc lột xác vẻ bề ngoài cho xứng tầm với trí tuệ bên trong. Chúng ta sẽ rời bỏ Chainlit để tự tay code một giao diện ReactJS đậm chất Sci-fi.
 
Back
Top