Bỏ qua

Bài 6: Multi-Agent Systems

Tổng quan

Một agent với quá nhiều tool và trách nhiệm trở nên khó kiểm soát - system prompt phình to, model dễ nhầm lẫn giữa các domain. Bài này trình bày các pattern chia nhỏ công việc cho nhiều agent chuyên biệt, so sánh framework hỗ trợ multi-agent, và thực hành xây dựng hệ thống multi-agent với LangGraph - nền tảng trực tiếp cho capstone.


1. Patterns

graph TD
    P[Multi-Agent Patterns] --> SEQ[Sequential<br/>Agent A → B → C]
    P --> HIER[Hierarchical<br/>Supervisor điều phối Workers]
    P --> COLLAB[Collaborative<br/>Nhiều agent cùng bàn bạc]
    P --> SWARM[Swarm<br/>Agent tự chuyển giao ngang hàng]

Sequential

Agent chạy theo thứ tự cố định, output của agent trước là input của agent sau.

graph LR
    IN[Input] --> A1[Researcher] --> A2[Analyzer] --> A3[Writer] --> OUT[Output]

Phù hợp khi: Quy trình có thứ tự rõ ràng, không cần agent quay lại bước trước (vd: research → phân tích → viết báo cáo).

Hierarchical (Supervisor)

Một Supervisor agent nhận task, quyết định giao cho Worker agent nào, tổng hợp kết quả - Worker không giao tiếp trực tiếp với nhau.

graph TD
    SUP[Supervisor] --> W1[Worker: Research]
    SUP --> W2[Worker: Code]
    SUP --> W3[Worker: Writing]
    W1 --> SUP
    W2 --> SUP
    W3 --> SUP

Phù hợp khi: Task cần định tuyến động (không biết trước cần Worker nào), cần kiểm soát tập trung về thứ tự và điều phối.

Collaborative

Nhiều agent cùng thảo luận, phản biện lẫn nhau trước khi chốt kết quả - không có thứ bậc cố định.

graph LR
    A1[Agent A: Đề xuất] <--> A2[Agent B: Phản biện]
    A2 <--> A3[Agent C: Tổng hợp]
    A3 <--> A1

Phù hợp khi: Task cần nhiều góc nhìn để giảm sai sót (vd: 1 agent viết code, 1 agent review, lặp lại đến khi đạt chất lượng).

Swarm

Agent tự quyết định chuyển giao (handoff) trực tiếp cho agent khác dựa trên ngữ cảnh, không qua Supervisor trung tâm.

graph LR
    A1[Agent Sales] -->|handoff khi cần kỹ thuật| A2[Agent Support]
    A2 -->|handoff khi cần thanh toán| A3[Agent Billing]
    A3 -->|handoff khi user muốn mua thêm| A1

Phù hợp khi: Ngữ cảnh hội thoại chuyển hướng linh hoạt, không có "trung tâm điều phối" tự nhiên (vd: hệ thống customer service đa domain).

So sánh 4 pattern

Pattern Kiểm soát Độ phức tạp giao tiếp Phù hợp
Sequential Cao (thứ tự cố định) Thấp Quy trình tuyến tính, biết trước các bước
Hierarchical Cao (qua Supervisor) Trung bình Cần định tuyến động, nhiều domain worker
Collaborative Trung bình Cao (nhiều vòng qua lại) Cần chất lượng cao qua phản biện
Swarm Thấp (phi tập trung) Cao (handoff động) Hội thoại chuyển domain linh hoạt

Capstone dùng Hierarchical

Kiến trúc Planner → Researcher/Analyzer/Writer trong capstone (Bài 7) là pattern Hierarchical: Planner đóng vai Supervisor, điều phối 3 Worker chuyên biệt theo kế hoạch đã lập.


2. Framework Comparison

Framework Cách biểu diễn multi-agent Điểm mạnh Điểm yếu
LangGraph Mỗi agent là 1 subgraph/node, giao tiếp qua shared state Kiểm soát chi tiết luồng dữ liệu, dễ debug, tích hợp checkpointer/HITL đã học Cần tự code cấu trúc điều phối
CrewAI Định nghĩa "Crew" gồm nhiều "Agent" có role/goal, "Task" được gán cho agent API cấp cao, khai báo nhanh (role, goal, backstory) Ít linh hoạt khi cần logic điều phối phức tạp/rẽ nhánh
OpenAI Agents SDK "Handoffs" giữa các Agent, mỗi Agent có instructions + tools riêng Đơn giản cho pattern Swarm, tích hợp chặt OpenAI models Gắn với hệ sinh thái OpenAI, ít kiểm soát state chi tiết
AutoGen Agent giao tiếp qua conversation (message passing), có GroupChat Mạnh cho Collaborative pattern, nhiều agent "thảo luận" Khó kiểm soát chính xác luồng như graph tường minh
# So sánh cú pháp: cùng 1 ý tưởng (Supervisor điều phối Researcher + Writer)

# --- CrewAI ---
researcher = Agent(role="Researcher", goal="Tìm thông tin chính xác")
writer = Agent(role="Writer", goal="Viết báo cáo dễ hiểu")
crew = Crew(agents=[researcher, writer], tasks=[research_task, write_task], process=Process.sequential)

# --- OpenAI Agents SDK ---
researcher = Agent(name="Researcher", instructions="Tìm thông tin", handoffs=[])
writer = Agent(name="Writer", instructions="Viết báo cáo", handoffs=[])
triage = Agent(name="Supervisor", handoffs=[researcher, writer])

Vì sao capstone vẫn dùng LangGraph cho multi-agent

LangGraph biểu diễn multi-agent như subgraph lồng trong graph lớn hơn - state, checkpointer, HITL, và tool execution loop đã học ở Bài 2-4 dùng lại được nguyên vẹn cho từng agent con. Điều này tránh phải học lại 1 mental model mới hoàn toàn như khi chuyển sang CrewAI/AutoGen.


3. Hands-on: Multi-agent với LangGraph

Ví dụ Supervisor điều phối 2 Worker: Researcher (tìm thông tin) và Writer (viết tóm tắt) - đơn giản hoá của kiến trúc capstone.

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.2)

# ====== SHARED STATE ======
class SupervisorState(TypedDict):
    messages: Annotated[list, add_messages]
    task: str
    research_notes: str
    next_agent: str

# ====== TOOLS cho Researcher ======
@tool
def search_web(query: str) -> str:
    """Tìm kiếm thông tin trên web."""
    return f"Kết quả tìm kiếm cho '{query}': [thông tin mô phỏng]"

# ====== SUPERVISOR NODE ======
SUPERVISOR_PROMPT = """Bạn điều phối 2 agent: 'researcher' (tìm thông tin) và 'writer' (viết báo cáo từ thông tin đã có).
Nhiệm vụ: {task}
Ghi chú nghiên cứu hiện có: {notes}

Quyết định agent tiếp theo cần chạy. Nếu đã đủ thông tin để viết, chọn 'writer'.
Nếu đã có báo cáo hoàn chỉnh, chọn 'done'.
Chỉ trả về 1 từ: researcher, writer, hoặc done."""

def supervisor_node(state: SupervisorState) -> dict:
    decision = llm.invoke(SUPERVISOR_PROMPT.format(
        task=state["task"], notes=state.get("research_notes", "Chưa có")
    )).content.strip().lower()
    return {"next_agent": decision}

def route_supervisor(state: SupervisorState) -> str:
    return state["next_agent"] if state["next_agent"] in ["researcher", "writer"] else END

# ====== RESEARCHER SUBGRAPH ======
def researcher_node(state: SupervisorState) -> dict:
    llm_with_tools = llm.bind_tools([search_web])
    response = llm_with_tools.invoke(
        f"Nghiên cứu thông tin cho nhiệm vụ: {state['task']}"
    )
    if response.tool_calls:
        results = [search_web.invoke(c["args"]) for c in response.tool_calls]
        notes = "\n".join(results)
    else:
        notes = response.content
    return {"research_notes": state.get("research_notes", "") + "\n" + notes}

# ====== WRITER NODE ======
def writer_node(state: SupervisorState) -> dict:
    report = llm.invoke(
        f"Viết báo cáo ngắn gọn cho nhiệm vụ '{state['task']}' dựa trên:\n{state['research_notes']}"
    )
    return {"messages": [report]}

# ====== BUILD GRAPH ======
graph = StateGraph(SupervisorState)
graph.add_node("supervisor", supervisor_node)
graph.add_node("researcher", researcher_node)
graph.add_node("writer", writer_node)

graph.add_edge(START, "supervisor")
graph.add_conditional_edges(
    "supervisor", route_supervisor,
    {"researcher": "researcher", "writer": "writer", END: END},
)
graph.add_edge("researcher", "supervisor")  # Quay lại Supervisor sau khi research xong
graph.add_edge("writer", "supervisor")      # Quay lại Supervisor để xác nhận done

app = graph.compile()

# ====== CHẠY ======
# Luôn set recursion_limit - vòng Supervisor -> Worker -> Supervisor
# có thể chạy vô hạn nếu Supervisor không bao giờ quyết định "done"
result = app.invoke(
    {
        "messages": [],
        "task": "Tóm tắt xu hướng AI Agent tại Việt Nam năm 2026",
        "research_notes": "",
        "next_agent": "",
    },
    config={"recursion_limit": 10},
)
print(result["messages"][-1].content)

Giới hạn số vòng Supervisor ↔ Worker

Giống ReAct loop (Bài 1-2), vòng lặp Supervisor → Worker → Supervisor có thể chạy vô hạn nếu Supervisor không bao giờ quyết định done. Luôn set recursion_limit và cân nhắc thêm đếm số vòng tối đa trong state.


Tóm tắt

graph TD
    Q{Cấu trúc task?} --> A[Thứ tự cố định, biết trước các bước]
    Q --> B[Cần định tuyến động giữa các domain worker]
    Q --> C[Cần nhiều góc nhìn phản biện lẫn nhau]
    Q --> D[Hội thoại chuyển domain linh hoạt, phi tập trung]
    A --> SEQ[Sequential]
    B --> HIER[Hierarchical - Supervisor]
    C --> COLLAB[Collaborative]
    D --> SWARM[Swarm]

    HIER --> FW{Framework?}
    FW --> LG[LangGraph - tái dùng state/HITL đã học]
    FW --> CREW[CrewAI - khai báo nhanh role/goal]
    FW --> OAI[OpenAI Agents SDK - handoffs đơn giản]
Pattern Ghi nhớ
Sequential A → B → C cố định, không quay lại
Hierarchical Supervisor điều phối, Worker không giao tiếp trực tiếp
Collaborative Nhiều agent phản biện qua lại để tăng chất lượng
Swarm Handoff ngang hàng, phi tập trung
LangGraph multi-agent Subgraph + shared state, tái dùng checkpointer/HITL
CrewAI Nhanh cho prototype, khai báo role/goal cấp cao
Giới hạn vòng lặp Bắt buộc với mọi Supervisor loop, tránh chạy vô hạn