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 |