Danh sách bài viết

Bài 31: Multi-agent system intuition

Bài cuối Module 5 tập trung vào tư duy: vì sao cần nhiều agent, khi nào một agent đủ rồi, 3 pattern phổ biến (Supervisor, Network, Hierarchical), cách agent chia sẻ state, minh họa Supervisor pattern bằng LangGraph 0.2, và các pitfall khi đưa multi-agent vào thực tế.

27/05/2026
1 lượt xem
1

Mục tiêu bài học

Sau bài này bạn sẽ:

  • Giải thích được tại sao task phức tạp cần nhiều agent chuyên biệt thay vì một agent duy nhất.
  • Nhận ra khi nào multi-agent là over-engineering và khi nào thực sự cần thiết.
  • Phân biệt 3 pattern: Supervisor, Network, Hierarchical — ưu nhược từng cái.
  • Hiểu shared state và filtered state, cách LangGraph quản lý state qua nhiều agent.
  • Đọc và viết được Supervisor pattern cơ bản với LangGraph 0.2.
  • Biết langgraph-supervisor, langgraph-swarm, CrewAI, AutoGen là gì và dùng khi nào.
2

Vì sao cần multi-agent

Context limit của single agent

Một single agent phải bao tất cả trong một prompt: system prompt mô tả vai trò + tool list + toàn bộ lịch sử hội thoại + instruction cho từng bước. Khi task phức tạp, context window bị nhồi nhét đến mức LLM bắt đầu "quên" các instruction ở giữa (lost in the middle problem).

Hạn chế thứ hai: một agent có một set tool. Nếu task cần 20 tool khác nhau, LLM phải lựa chọn từ danh sách dài — khả năng chọn nhầm tăng theo số lượng tool.

Task phức tạp có nhiều giai đoạn rõ rệt

Ví dụ: yêu cầu "nghiên cứu về LangGraph rồi viết bài blog kỹ thuật" gồm ít nhất:

  1. Research: tìm kiếm, đọc tài liệu, tổng hợp fact.
  2. Outline: cấu trúc bài.
  3. Write: viết nháp theo outline + fact đã có.
  4. Review: kiểm tra kỹ thuật, tính nhất quán.
  5. Edit: sửa theo feedback của reviewer.

Mỗi giai đoạn có "vai trò" khác nhau: researcher cần tool tìm kiếm, writer cần context từ research output, reviewer không cần tool nào nhưng cần system prompt khắt khe hơn. Tách thành nhiều agent cho phép mỗi agent có system prompt và tool set phù hợp với vai trò của nó.

Phân chia trách nhiệm

Khi một agent làm sai, với single agent rất khó xác định sai ở bước nào. Với multi-agent, mỗi agent có output riêng — debug dễ hơn: "writer agent viết sai vì research agent cung cấp fact sai" hay "writer agent có system prompt chưa đủ chi tiết".

Mở rộng cũng dễ hơn: thêm "fact-check agent" vào giữa research và write mà không cần sửa toàn bộ hệ thống.

Parallel execution

Một số task có thể chạy song song: agent A nghiên cứu phần kỹ thuật trong khi agent B nghiên cứu phần thị trường. Sau đó agent tổng hợp gộp kết quả. Single agent phải làm tuần tự.

3

Khi nào không nên dùng multi-agent

Multi-agent có overhead thực sự. Trước khi thiết kế hệ thống nhiều agent, cần kiểm tra xem có thực sự cần hay không.

Task đơn giản — single agent đủ

Nếu task chỉ cần 1-2 tool và fit trong một context window, single agent rẻ hơn và nhanh hơn. Thêm agent thứ hai vào để "có vẻ professional" là lãng phí: latency tăng, cost tăng, debug phức tạp hơn.

Chain prompt cố định không cần agent

Nếu flow là chuỗi bước cố định, không có điều kiện phân nhánh, không cần LLM quyết định bước tiếp theo — đây là chain, không phải multi-agent. LCEL chain với | pipe operator trong LangChain đủ dùng và đơn giản hơn nhiều.

# Flow cố định: research → summarize → format
# Không cần multi-agent — dùng chain
chain = research_prompt | llm | summarize_prompt | llm | format_prompt | llm
result = chain.invoke({"query": "LangGraph overview"})

Multi-agent thực sự cần thiết khi LLM phải quyết định bước tiếp theo tùy theo kết quả hiện tại — tức là có planning và routing động.

Overhead của multi-agent

  • Latency cộng dồn: mỗi agent step là ít nhất một LLM call. 5 agent × 2 LLM call/agent = 10 LLM calls tuần tự. Nếu mỗi call mất 1 giây, total latency tối thiểu 10 giây.
  • Cost cộng dồn: mỗi agent có context riêng. Supervisor thấy toàn bộ message history. Tổng token tăng nhanh.
  • Debug phức tạp hơn: trace một lỗi qua nhiều agent khó hơn trace trong single agent. Cần observability tốt.
4

Pattern 1 — Supervisor (orchestrator + workers)

Pattern phổ biến nhất. Một supervisor agent đóng vai orchestrator: nhận task, quyết định gọi worker nào, nhận output từ worker, rồi quyết định bước tiếp theo.

Kiến trúc

           ┌─────────────────────────────────────┐
  input ──►│           SUPERVISOR                 │◄── output
           │  (LLM quyết định worker tiếp theo)   │
           └──────┬──────────┬──────────┬─────────┘
                  │          │          │
                  ▼          ▼          ▼
           ┌──────────┐ ┌──────────┐ ┌──────────┐
           │RESEARCHER│ │  WRITER  │ │ REVIEWER │
           │(search   │ │(no tool, │ │(no tool, │
           │ tool)    │ │ writer   │ │ critic   │
           │          │ │ prompt)  │ │ prompt)  │
           └──────────┘ └──────────┘ └──────────┘
                  │          │          │
                  └──────────┴──────────┘
                         result về supervisor

Luồng hoạt động

  1. User gửi task cho supervisor.
  2. Supervisor (LLM) quyết định: gọi researcher, writer, reviewer, hay FINISH.
  3. Worker được gọi chạy, trả kết quả về supervisor.
  4. Supervisor nhận kết quả, quyết định bước tiếp theo.
  5. Lặp lại đến khi supervisor quyết định FINISH.

Trong LangGraph

Supervisor là một node, mỗi worker là một node. Supervisor node có conditional edges trỏ đến các worker node. Sau khi worker chạy xong, edge trỏ về supervisor để supervisor quyết định tiếp.

Điểm mạnh / điểm yếu

  • Mạnh: có planning rõ ràng, supervisor kiểm soát toàn bộ flow, dễ debug (biết supervisor quyết định gì mỗi bước).
  • Yếu: supervisor là bottleneck — mọi quyết định đều phải qua nó; nếu task nhiều bước, supervisor call LLM nhiều lần, cost cao.
5

Pattern 2 — Network (peer-to-peer handoff)

Không có supervisor cố định. Mỗi agent có thể chuyển control (handoff) sang agent khác bằng cách gọi một "handoff tool". Agent quyết định ai nên xử lý tiếp dựa trên ngữ cảnh hiện tại.

Kiến trúc

  ┌───────────────────────────────────────────┐
  │                                           │
  │  AGENT_A ◄──────────────────► AGENT_B    │
  │     │                              │      │
  │     │                              │      │
  │     ▼                              ▼      │
  │  AGENT_C ◄──────────────────► AGENT_D    │
  │                                           │
  │  (bất kỳ agent nào cũng có thể handoff   │
  │   sang bất kỳ agent nào khác)            │
  └───────────────────────────────────────────┘

Cơ chế handoff

Mỗi agent được trang bị một tool handoff_to_agent(name). Khi LLM trong agent A quyết định agent B phù hợp hơn, nó gọi tool đó — control chuyển sang agent B. Trong LangGraph 0.2+, dùng Command(goto=...) để jump sang node khác.

Điểm mạnh / điểm yếu

  • Mạnh: flexible, không cần biết trước thứ tự; phù hợp với task collaborative như brainstorming.
  • Yếu: khó kiểm soát — nếu agent A và B cứ handoff qua lại mà không ai kết thúc, sẽ loop vô tận. Cần circuit breaker (recursion limit).
6

Pattern 3 — Hierarchical (team trong team)

Supervisor cấp 1 quản lý nhiều supervisor cấp 2. Mỗi supervisor cấp 2 quản lý team workers của riêng nó. Phù hợp khi project đủ lớn để chia thành nhiều "phòng ban" độc lập.

Kiến trúc

                    ┌──────────────────┐
                    │  TOP SUPERVISOR  │
                    └───────┬──────────┘
                            │
              ┌─────────────┴──────────────┐
              │                            │
    ┌─────────▼────────┐        ┌──────────▼───────┐
    │  TEAM SUPERVISOR │        │  TEAM SUPERVISOR │
    │    (Research)    │        │    (Execution)   │
    └──┬────────┬──────┘        └──┬─────────┬─────┘
       │        │                  │         │
    ┌──▼──┐  ┌──▼──┐           ┌──▼──┐   ┌──▼──┐
    │ Web │  │ Doc │           │Coder│   │Tester│
    │Srch │  │Read │           │     │   │      │
    └─────┘  └─────┘           └─────┘   └──────┘

Khi nào phù hợp

Hierarchical phù hợp nhất cho simulation tổ chức phức tạp: "engineering team" + "marketing team" cùng làm việc trên một dự án, hoặc pipeline đủ lớn để tách thành các sub-pipeline độc lập. Với hầu hết ứng dụng thực tế, Supervisor đơn giản hơn và đủ dùng.

Điểm yếu chính

Latency tăng theo số tầng. Top supervisor gọi team supervisor, team supervisor gọi worker — mỗi tầng thêm ít nhất một LLM call. Cost tăng nhanh theo số tầng.

7

State chia sẻ giữa agents

Khi nhiều agent cùng hoạt động trong một LangGraph, chúng chia sẻ state thông qua TypedDict. Có hai cách tiếp cận:

Shared state đầy đủ

Tất cả agent đọc và ghi vào cùng một state object. Đơn giản nhất để implement — mọi agent đều thấy toàn bộ messages, intermediate results, metadata.

from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage

class TeamState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    current_task: str
    research_result: str
    draft: str
    next: str  # supervisor dùng field này để routing

Ưu điểm: mọi agent đều có đủ context. Nhược điểm: context window mỗi agent chứa toàn bộ messages — tốn token, đặc biệt khi conversation dài.

Filtered state

Mỗi agent chỉ nhận phần state liên quan đến vai trò của nó. Researcher chỉ thấy query, không thấy draft. Writer chỉ thấy research_result + outline. Phức tạp hơn khi implement nhưng giảm token và tránh agent bị nhiễu bởi thông tin không liên quan.

Trong LangGraph, implement bằng cách truyền filtered input khi gọi subgraph hoặc dùng custom node function lọc state trước khi gọi agent:

def call_writer(state: TeamState) -> dict:
    # Chỉ truyền research_result cho writer, không truyền toàn bộ messages
    writer_input = {
        "messages": [
            HumanMessage(content=f"Viết bài dựa trên:\n{state['research_result']}")
        ]
    }
    result = writer_agent.invoke(writer_input)
    return {"draft": result["messages"][-1].content}

Custom reducer

LangGraph cho phép chỉ định reducer cho từng field trong state. Field messages dùng reducer add_messages (append, không replace). Field khác có thể dùng reducer tùy chỉnh — ví dụ chỉ giữ kết quả mới nhất thay vì accumulate.

from typing import Annotated
from langgraph.graph.message import add_messages

def keep_latest(old, new):
    """Reducer: chỉ giữ giá trị mới nhất, không accumulate."""
    return new

class TeamState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]  # append
    research_result: Annotated[str, keep_latest]          # replace
    draft: Annotated[str, keep_latest]                    # replace
    next: Annotated[str, keep_latest]                     # replace
8

Minh họa Supervisor pattern với LangGraph

Code bên dưới là minh họa pattern — đủ để hiểu cơ chế, không phải production-ready. Trong thực tế, langgraph-supervisor package (đề cập ở phần 11) làm sẵn phần lớn boilerplate này.

pip install langgraph langchain-openai
from typing import Annotated, Literal, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

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

# Giả sử đã có search_tool (ví dụ TavilySearch hay DuckDuckGoSearch)
# research_agent: có tool tìm kiếm
research_agent = create_react_agent(
    llm,
    tools=[search_tool],
    prompt="Bạn là researcher. Tìm kiếm thông tin theo yêu cầu và trả về fact chính xác.",
)

# writer_agent: không cần tool, chỉ cần viết
writer_agent = create_react_agent(
    llm,
    tools=[],
    prompt="Bạn là technical writer. Viết bài rõ ràng dựa trên research đã cung cấp.",
)


class TeamState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    next: str  # supervisor quyết định node tiếp theo


MEMBERS = ["research", "writer"]
OPTIONS = MEMBERS + ["FINISH"]

SUPERVISOR_PROMPT = (
    "Bạn là supervisor quản lý team gồm: research, writer. "
    "Dựa trên yêu cầu và kết quả hiện tại, chọn worker tiếp theo. "
    f"Các lựa chọn hợp lệ: {OPTIONS}. "
    "Trả lời ĐÚNG 1 từ trong danh sách đó, không giải thích thêm."
)


def supervisor(state: TeamState) -> dict:
    """Supervisor node: LLM quyết định worker tiếp theo."""
    response = llm.invoke([
        SystemMessage(content=SUPERVISOR_PROMPT),
        *state["messages"],
    ])
    next_step = response.content.strip()
    # Normalize — nếu LLM trả về chữ hoa/thường không khớp
    for opt in OPTIONS:
        if opt.lower() in next_step.lower():
            return {"next": opt}
    return {"next": "FINISH"}  # fallback


def call_research(state: TeamState) -> dict:
    """Gọi research agent, lấy message cuối trả về."""
    result = research_agent.invoke({"messages": state["messages"]})
    last_msg = result["messages"][-1]
    return {"messages": [last_msg]}


def call_writer(state: TeamState) -> dict:
    """Gọi writer agent, lấy message cuối trả về."""
    result = writer_agent.invoke({"messages": state["messages"]})
    last_msg = result["messages"][-1]
    return {"messages": [last_msg]}


def route_supervisor(state: TeamState) -> str:
    """Conditional edge: đọc state['next'] để routing."""
    n = state["next"]
    if n == "research":
        return "research"
    if n == "writer":
        return "writer"
    return END


# Build graph
builder = StateGraph(TeamState)
builder.add_node("supervisor", supervisor)
builder.add_node("research", call_research)
builder.add_node("writer", call_writer)

builder.add_edge(START, "supervisor")
builder.add_conditional_edges("supervisor", route_supervisor)
builder.add_edge("research", "supervisor")
builder.add_edge("writer", "supervisor")

graph = builder.compile()

# Chạy thử
result = graph.invoke({
    "messages": [HumanMessage(content="Nghiên cứu LangGraph rồi viết tóm tắt 200 từ.")]
})
print(result["messages"][-1].content)

Một số điểm cần lưu ý trong code trên:

  • Supervisor gọi LLM mỗi lần để quyết định bước tiếp, nên với task N bước, supervisor gọi LLM N lần. Cost tăng tuyến tính với số bước.
  • route_supervisor đọc field next trong state — pattern thông dụng để supervisor truyền quyết định qua state thay vì return value trực tiếp.
  • Nếu supervisor trả về text không match với OPTIONS, fallback về FINISH thay vì crash — đây là circuit breaker đơn giản.
  • Cần set recursion_limit khi compile để tránh loop vô tận: graph.compile(recursion_limit=20) — mặc định của LangGraph là 25.
9

Pattern Handoff với Command

LangGraph 0.2+ thêm Command object cho phép một node jump trực tiếp sang node khác mà không cần conditional edge. Đây là nền tảng của Network pattern (peer-to-peer handoff).

from langgraph.types import Command
from langchain_core.messages import HumanMessage

def research_agent_node(state: TeamState) -> Command:
    # ... logic LLM ở đây
    result = llm.invoke(state["messages"])

    # Nếu LLM quyết định cần writer xử lý tiếp
    if "cần viết" in result.content.lower():
        return Command(
            goto="writer_agent",
            update={"messages": [result]},
        )

    # Nếu đã đủ thông tin, kết thúc
    return Command(
        goto=END,
        update={"messages": [result]},
    )

def writer_agent_node(state: TeamState) -> Command:
    result = llm.invoke(state["messages"])
    return Command(
        goto=END,
        update={"messages": [result]},
    )

Command(goto=..., update=...) kết hợp hai việc: cập nhật state và điều hướng sang node tiếp theo. Không cần conditional edge tách biệt. Phù hợp cho Network pattern khi routing logic phức tạp và phụ thuộc vào kết quả LLM, không phải vào một field state đơn giản.

Lưu ý: khi dùng Command, node phải được khai báo với builder.add_node đầy đủ, và các node có thể được goto cần có edges hoặc có thể nhận Command — không phải mọi graph setup đều hỗ trợ goto tùy ý.

10

Communication protocol giữa agents

Agent trong một hệ thống multi-agent cần cách truyền thông tin cho nhau. Có ba cách phổ biến:

1. Message passing

Mọi agent append kết quả vào shared messages list. Agent sau đọc message history và biết kết quả của agent trước. Đây là cách đơn giản nhất và phù hợp khi agents cần biết toàn bộ lịch sử.

Nhược điểm: messages accumulate nhanh. Sau 5-10 agent steps, context có thể vài nghìn token chỉ riêng message history.

2. Structured state fields

Thêm field chuyên dụng vào state thay vì chỉ dùng messages:

class TeamState(TypedDict):
    messages: Annotated[list[BaseMessage], add_messages]
    research_result: str    # researcher ghi vào đây
    outline: str            # planner ghi vào đây
    draft: str              # writer ghi vào đây
    review_notes: str       # reviewer ghi vào đây
    current_agent: str      # agent nào đang active

Writer agent chỉ cần đọc research_resultoutline, không phải toàn bộ messages. Reviewer chỉ cần draft. Token usage mỗi agent giảm đáng kể.

3. Tool-based communication

Agent A có tool send_to_agent_B(message: str). Khi LLM trong agent A gọi tool đó, hệ thống route message đến agent B. Thường dùng trong Network pattern khi handoff được biểu diễn qua tool call.

from langchain_core.tools import tool

@tool
def handoff_to_writer(research_summary: str) -> str:
    """Chuyển research summary cho writer agent xử lý."""
    # Trong thực tế: trigger writer node trong LangGraph
    return f"[HANDOFF] research_summary={research_summary}"
11

Prebuilt: langgraph-supervisor và langgraph-swarm

langgraph-supervisor

Package chính thức từ LangChain team, implement Supervisor pattern với boilerplate đã được xử lý sẵn.

pip install langgraph-supervisor
from langgraph_supervisor import create_supervisor
from langgraph.prebuilt import create_react_agent

research_agent = create_react_agent(llm, tools=[search_tool],
                                    name="researcher",
                                    prompt="Bạn là researcher chuyên tìm kiếm thông tin.")
writer_agent   = create_react_agent(llm, tools=[],
                                    name="writer",
                                    prompt="Bạn là writer chuyên viết nội dung kỹ thuật.")

workflow = create_supervisor(
    agents=[research_agent, writer_agent],
    model=llm,
    prompt="Bạn là supervisor quản lý researcher và writer. Phân công task phù hợp.",
)

app = workflow.compile()
result = app.invoke({"messages": [HumanMessage(content="Nghiên cứu LangGraph và viết tóm tắt.")]})

create_supervisor tự xây supervisor node, conditional edges, và routing logic. Agent cần được đặt tên (name=) để supervisor biết gọi agent nào.

langgraph-swarm

Cùng từ LangChain team, implement Network (swarm) pattern với handoff-based routing. Mỗi agent có thể handoff sang agent khác qua transfer_to_{agent_name} tool.

pip install langgraph-swarm
from langgraph_swarm import create_swarm, create_handoff_tool

handoff_to_writer = create_handoff_tool(agent_name="writer",
                                        description="Chuyển sang writer khi đã có đủ research.")
handoff_to_researcher = create_handoff_tool(agent_name="researcher",
                                            description="Cần thêm thông tin thực tế.")

research_agent = create_react_agent(llm, tools=[search_tool, handoff_to_writer], name="researcher")
writer_agent   = create_react_agent(llm, tools=[handoff_to_researcher], name="writer")

app = create_swarm([research_agent, writer_agent], default_active_agent="researcher").compile()

langgraph-swarm phù hợp khi flow không có thứ tự cố định và agents tự quyết định ai xử lý tiếp. Với flow có thứ tự rõ ràng, langgraph-supervisor thường cho kết quả dễ kiểm soát hơn.

12

Khi nào dùng pattern nào

Pattern Dùng khi Tránh khi
Supervisor Task có planning rõ, biết trước các bước, cần orchestrator điều phối và kiểm soát chất lượng Task chỉ 2-3 bước cố định (dùng chain), hoặc khi supervisor overhead quá lớn
Network Task collaborative, không biết trước thứ tự, agents tự thương lượng ai xử lý tiếp Task cần kết quả nhất quán, có deadline latency ngặt (khó predict số bước)
Hierarchical Task đủ lớn để chia thành nhiều nhóm độc lập, simulation tổ chức Hầu hết ứng dụng thực tế — thêm tầng là thêm latency và cost

Trong thực tế, điểm khởi đầu tốt là Supervisor pattern với langgraph-supervisor. Nếu flow không có thứ tự cố định, thử Network. Hierarchical chỉ xem xét khi Supervisor đã chứng minh không đủ scale.

13

Frameworks alternative

LangGraph không phải lựa chọn duy nhất cho multi-agent. Một số framework khác đáng biết:

CrewAI

Abstraction cao hơn LangGraph: định nghĩa agent qua role, goal, backstory; định nghĩa task với expected_output; crew tự orchestrate. Phù hợp khi muốn setup nhanh với ít code hơn. Ít flexible hơn LangGraph trong custom state management và graph structure.

from crewai import Agent, Task, Crew, Process

researcher = Agent(role="Researcher", goal="Tìm kiếm thông tin chính xác",
                   backstory="Chuyên gia nghiên cứu kỹ thuật", tools=[search_tool])
writer = Agent(role="Writer", goal="Viết bài kỹ thuật rõ ràng",
               backstory="Technical writer với 5 năm kinh nghiệm")

task1 = Task(description="Nghiên cứu LangGraph 0.2", agent=researcher,
             expected_output="Danh sách fact chính về LangGraph")
task2 = Task(description="Viết tóm tắt dựa trên research", agent=writer,
             expected_output="Tóm tắt 200 từ")

crew = Crew(agents=[researcher, writer], tasks=[task1, task2], process=Process.sequential)
result = crew.kickoff()

AutoGen (Microsoft)

Framework conversational multi-agent: agents giao tiếp qua hội thoại. Phù hợp cho use case debate, review nhiều vòng. State management ít structured hơn LangGraph.

OpenAI Swarm

Lightweight library experimental từ OpenAI, implement handoff pattern đơn giản. Chưa phải production-stable (marked experimental tính đến 2025). Phù hợp để học concept, không khuyến nghị cho production.

So sánh với LangGraph

LangGraph mạnh ở state management có cấu trúc (TypedDict, custom reducer), checkpoint và persistence, human-in-the-loop. CrewAI và AutoGen có abstraction cao hơn nhưng ít linh hoạt hơn khi cần custom. Nếu đã dùng LangChain ecosystem, LangGraph là lựa chọn tự nhiên nhất.

14

Cost và latency thực tế

Latency

Mỗi agent step là ít nhất một LLM call. Với gpt-4o-mini, một call thường mất 0.5-2 giây tùy context length. Hệ thống 5 agent × 3 steps mỗi agent = 15 LLM calls tuần tự → latency tối thiểu 7-30 giây cho một task.

Giảm latency bằng:

  • Parallel execution khi các agent không phụ thuộc nhau (dùng asyncio.gather hoặc LangGraph parallel nodes).
  • Chọn model nhỏ hơn (gpt-4o-mini thay vì gpt-4o) cho worker agents, giữ model mạnh cho supervisor.
  • Giới hạn số bước tối đa — đặt recursion_limit hợp lý.

Cost

Token cost không tính theo tổng bài toán mà theo từng LLM call. Với Supervisor pattern:

  • Supervisor call N: nhận toàn bộ messages từ step 0 đến step N. Messages accumulate → input tokens tăng tuyến tính theo số bước.
  • Mỗi worker call cũng nhận message history — nếu dùng shared state đầy đủ.

Giảm cost bằng:

  • Filtered state: chỉ truyền phần state cần thiết cho mỗi agent.
  • Summarize message history khi quá dài trước khi đưa vào agent tiếp theo.
  • Cache response cho những query giống nhau (dùng LangChain cache hoặc Redis, đề cập ở bài 50).

Monitoring

Trong production, không thể optimize những gì không đo được. Bật LangSmith để trace từng LLM call trong multi-agent flow, xem token usage per agent per step.

export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY=your_key
export LANGCHAIN_PROJECT=my-multi-agent-project
15

Common pitfalls

1. Multi-agent cho task đơn giản

Task "dịch một đoạn văn" không cần supervisor + worker. Single LLM call đủ rồi. Thêm agent vào chỉ để hệ thống "có vẻ phức tạp" là sai hướng.

2. Infinite loop giữa agents

Network pattern dễ gặp: agent A handoff sang B, B handoff sang A, không ai kết thúc. Giải pháp:

  • Đặt recursion_limit (mặc định 25 trong LangGraph): graph.compile(recursion_limit=15).
  • Thêm điều kiện thoát rõ ràng trong supervisor prompt: "Nếu đã có draft hoàn chỉnh, trả về FINISH ngay."
  • Track số lần mỗi agent được gọi, nếu quá N lần → force FINISH.

3. Agent role không rõ → LLM nhầm vai trò

Nếu system prompt của researcher và writer không đủ phân biệt, LLM có thể làm việc của cả hai vai trò — researcher viết bài thay vì chỉ tìm kiếm, writer tự đi search thay vì dùng research đã có. System prompt phải mô tả rõ: "Bạn CHỈ làm X, không làm Y."

4. State explode — messages grow vô hạn

Mỗi agent append messages vào shared list. Sau 10 agent steps, messages list có thể chứa 20-30 messages với tổng hàng nghìn token. Mỗi agent call tiếp theo phải đọc toàn bộ history đó.

Giải pháp: compress hoặc summarize messages định kỳ. Ví dụ sau mỗi 5 steps, một "summarizer node" tóm tắt messages cũ thành 1 message ngắn, xóa bỏ chi tiết không cần thiết.

5. Không có observability

Multi-agent debug rất khó nếu không có trace. Khi có lỗi, không biết agent nào gây ra, supervisor quyết định gì ở step nào. Bật LangSmith hoặc ít nhất thêm logging mỗi khi supervisor quyết định hoặc worker trả về kết quả.

def supervisor(state: TeamState) -> dict:
    response = llm.invoke([...])
    next_step = response.content.strip()
    print(f"[SUPERVISOR] decision={next_step}, msg_count={len(state['messages'])}")
    return {"next": next_step}

6. Worker agents không check context trước khi làm

Writer agent được gọi khi chưa có research_result. Writer tự bịa fact. Supervisor nên kiểm tra điều kiện trước khi gọi worker: "Chỉ gọi writer khi đã có research output."

16

Tóm tắt

  • Multi-agent giải quyết hai vấn đề: context limit của single agent và phân chia vai trò trong task phức tạp nhiều giai đoạn.
  • Nếu task fit trong một context window và flow cố định, dùng chain — không phải multi-agent.
  • Supervisor: orchestrator điều phối workers, dễ kiểm soát, phù hợp khi có planning rõ.
  • Network: peer-to-peer handoff, flexible, nhưng cần circuit breaker tránh loop.
  • Hierarchical: team trong team, chỉ dùng khi scale thực sự cần.
  • Shared state đầy đủ đơn giản nhất; filtered state giảm token cost khi scale.
  • langgraph-supervisorlanggraph-swarm là prebuilt từ LangChain team, giảm boilerplate đáng kể.
  • Latency = tổng latency từng agent step. Cost = tổng token từng agent call. Cả hai tăng nhanh với số agent và số bước.
  • Các pitfall chính: infinite loop (đặt recursion_limit), state explode (compress messages), agent role mơ hồ (system prompt rõ), không có observability (LangSmith hoặc logging).