第 3 篇:让 Agent 学会分工,LangGraph 构建多 Agent系统

30 阅读9分钟

系列简介:从零搭建一个多 Agent AI 助手,覆盖原理、实现、部署全链路。不讲空话,每篇都有可运行的代码。
项目地址github.com/CodeMomentY…
本篇目标:用 LangGraph 搭建一个多 Agent 协作系统,支持意图识别、动态路由、串行/并行执行。

IMG_7499.JPG

前言

  大家好,我是一名前端工程师。都说前端“已死”,那与其担心被 AI 替代,不如打入敌人内部,于是我开始折腾 Agent 开发。

  折腾下来发现,Agent 的核心不是算法,而是“工程能力”(怎么设计架构、怎么串联服务、怎么把 LLM 的能力落地成产品)。这些恰好是我们擅长的事。

  这个系列记录我从零搭建多 Agent 系统的完整过程。只聊技术知识和设计思路,代码交给 AI 写。如果你也想从应用层切入 AI,希望这个系列对你有帮助。

读完本篇你将学到:

  • 为什么需要多 Agent,单 Agent 的瓶颈在哪;
  • LangGraph 是什么,为什么选它;
  • 多 Agent 架构设计:Dispatcher + 专业 Agent;
  • 串行和并行两种执行模式的实现;

背景与动机

  上两篇我们用 30 行代码实现了单 Agent,也了解了三种范式。但当你想让 Agent 同时具备“查天气”、“写文章”、“闲聊”这些能力时,那么问题来了:

  全塞到一个 Agent 里,Prompt 会越来越臃肿。 你得在一个 System Prompt 里描述所有工具、所有规则、所有场景。LLM 看到一大坨指令,容易搞混,该调工具的时候不调,不该调的时候乱调。

  这就像一个人同时当厨师、服务员、收银员——忙得过来,但质量堪忧。

  解决方案很自然:分工。让不同的 Agent 各管一摊,再加一个“调度员”决定把任务交给谁。

  但分工带来了新问题:多个 Agent 之间怎么传递数据执行顺序怎么控制什么条件走哪条路?如果还用前两篇的原生写法(手写 for 循环 + if-else),代码会迅速变成一团意大利面。

  这就是为什么我们需要一个框架来管理流程——不是因为原生写法不行,而是当节点多了、路由复杂了,框架能帮你把“流程控制”这件事做得更清晰。

核心概念

单 Agent vs 多 Agent

  我们看一下两者对比:

单 Agent多 Agent
Prompt一个巨大的 System Prompt每个 Agent 一个精简 Prompt
职责什么都干各司其职
调试出错不知道哪个环节的问题哪个 Agent 出错一目了然
扩展加功能就改 Prompt(越改越乱)加功能就加一个新 Agent
速度还行多了 Dispatcher 的开销

设计多 Agent 架构

  架构图:

image.png

三个专业 Agent:

  • tools 路径:需要调工具的任务(查天气、获取网页、查语雀文档),内部是 ReAct 循环;
  • writer 路径:写作类任务(邮件、文案、翻译),纯 LLM 生成,temperature 调高一点增加创造力;
  • chat 路径:闲聊和知识问答,纯 LLM 对话,会从知识库检索相关内容(RAG);

使用 LangGraph 来构建

  为什么选 LangGraph 而不是 LangChain?很多人分不清 LangChain 和 LangGraph 的关系。简单来说:

  • LangChain:工具箱。提供了调 LLM、解析输出、连接向量库等基础能力,像是一堆零件。
  • LangGraph:施工图。在 LangChain 的零件基础上,定义了"谁先执行、谁后执行、什么条件走哪条路"的流程控制。

感兴趣的可以看看这篇文章:www.datacamp.com/tutorial/la…

  LangChain 本身也能做 Agent(AgentExecutor),但它的问题是:流程是黑盒的。你把工具和 Prompt 丢进去,它内部自己循环,你很难控制“什么时候该停”、“中间结果怎么传递”、“多个 Agent 怎么协作”。

  LangGraph 把流程显式化了——你画一张图,节点是什么、边怎么连、条件是什么,一目了然。对于多 Agent 协作这种需要精确控制流程的场景,LangGraph 比 LangChain 的 AgentExecutor 好用得多。

LangChain AgentExecutorLangGraph
流程控制黑盒,框架内部循环白盒,你画图定义
多 Agent不原生支持天然支持(多节点)
条件路由难实现一行代码
状态管理手动传递自动维护
调试难(看不到中间过程)易(逐节点 stream)
适合场景简单单 Agent复杂多 Agent 协作

  其实在项目里,LangChain 的东西还是在用的(消息格式、工具定义、OpenAI SDK),只是不用它的 AgentExecutor,改用 LangGraph 来编排流程。

动手实现

LangGraph 基础用法

  很多小伙伴不熟悉 LangGraph,所以在搭建多 Agent 之前,先看一个最简单的 LangGraph 示例,理解它的三个核心概念:State(状态)、Node(节点)、Edge(边)

  其实就是把代码调度更具体化了,像是在画“流程图”。

from langgraph.graph import StateGraph, END
from typing_extensions import TypedDict

# 1. 定义状态:所有节点共享的数据
class MyState(TypedDict):
    message: str
    count: int

# 2. 定义节点:就是普通函数,接收 state,返回要更新的字段
def say_hello(state):
    return {"message": f"你好!你已经来了 {state['count']} 次"}

def add_count(state):
    return {"count": state["count"] + 1}

# 3. 构建图:注册节点 + 连接边
graph = StateGraph(MyState)
graph.add_node("counter", add_count)
graph.add_node("greeter", say_hello)

graph.set_entry_point("counter")          # 入口
graph.add_edge("counter", "greeter")      # counter → greeter
graph.add_edge("greeter", END)            # greeter → 结束

app = graph.compile()

# 4. 运行
result = app.invoke({"message": "", "count": 0})
print(result)  # {"message": "你好!你已经来了 1 次", "count": 1}

  就这么简单:定义状态 → 写几个函数 → 用边连起来 → Compile → Invoke(调用)

  LangGraph 真正强大的地方在于条件边——根据状态动态决定下一步走哪:

# 条件边:根据 count 决定走哪条路
def should_continue(state):
    if state["count"] >= 3:
        return "done"
    return "again"

graph.add_conditional_edges("greeter", should_continue, {
    "again": "counter",   # count < 3 → 回到 counter(循环)
    "done": END,          # count >= 3 → 结束
})

  有了条件边,就能实现循环、分支、路由——这正是多 Agent 系统需要的能力。

Step 1:定义状态

  LangGraph 的核心是 State——所有节点共享的数据结构。每个节点读取 state、处理、写回 state。

from typing import Annotated, Sequence
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage
import operator

class AgentState(TypedDict):
    """所有节点共享的状态"""

    # 对话历史(自动追加)
    messages: Annotated[Sequence[BaseMessage], operator.add]

    # 意图列表(dispatcher 填写)
    intents: list[str]

    # 执行模式:sequential 或 parallel
    mode: str

    # 当前执行到第几个意图
    current_step: int

  关键设计:messagesoperator.add 标注,意味着每个节点返回的消息会追加到列表里,而不是覆盖,这样对话历史自动累积。

Step 2:实现 Dispatcher(意图分类)

  Dispatcher 是整个系统的入口,负责判断用户想干什么:

DISPATCHER_PROMPT = """你是一个意图分类器。

分类规则:
- tools:需要调用工具的请求(查天气、获取网页、查文档)
- writer:写作类(写邮件、文案、翻译)
- chat:其他(闲聊、知识问答)

如果需要多个步骤,按顺序列出。
回复格式:意图1,意图2|模式(sequential 或 parallel)

示例:
- "上海天气" → tools|sequential
- "查天气然后写文案" → tools,writer|sequential
- "翻译hello,顺便查天气" → tools,writer|parallel
"""

def dispatcher_node(state):
    last_user_msg = ...  # 取最后一条用户消息
    response = invoke_llm([SystemMessage(DISPATCHER_PROMPT), ...])

    # 解析:intents + mode
    intents, mode = parse_response(response)

    return {"intents": intents, "mode": mode, "current_step": 0}

  Dispatcher 的输出决定了后续走哪条路。注意它支持多意图。比如:“查天气然后写文案”会被拆成 ["tools", "writer"],串行执行。

Step 3:构建 LangGraph 图

  这是最关键的部分——把所有节点和路由规则组装成一张图,我们先看流程设计:

image.png

  最终代码实现:

from langgraph.graph import StateGraph, END

def build_graph():
    graph = StateGraph(AgentState)

    # 注册节点
    graph.add_node("dispatcher", dispatcher_node)
    graph.add_node("mode_router", lambda state: {})  # 路由跳板
    graph.add_node("step_router", lambda state: {})  # 串行步骤跳板
    graph.add_node("router", router_node)            # 工具决策
    graph.add_node("tool_executor", tool_executor_node)
    graph.add_node("writer_agent", writer_agent_node)
    graph.add_node("chat_agent", chat_agent_node)
    graph.add_node("advance_step", advance_step)     # 推进到下一步
    graph.add_node("parallel_executor", parallel_executor_node)

    # 入口
    graph.set_entry_point("dispatcher")

    # dispatcher → mode_router
    graph.add_edge("dispatcher", "mode_router")

    # 根据 mode 分流
    graph.add_conditional_edges("mode_router", route_mode, {
        "sequential": "step_router",
        "parallel": "parallel_executor",
    })

    # 串行:根据当前 intent 路由
    graph.add_conditional_edges("step_router", route_current_step, {
        "tools": "router",
        "writer": "writer_agent",
        "chat": "chat_agent",
        "done": END,
    })

    # tools 路径:ReAct 循环
    graph.add_conditional_edges("router", should_use_tools, {
        "tools": "tool_executor",
        "next": "advance_step",
    })
    graph.add_edge("tool_executor", "router")

    # writer/chat 完成后推进
    graph.add_edge("writer_agent", "advance_step")
    graph.add_edge("chat_agent", "advance_step")

    # 检查是否还有下一步
    graph.add_conditional_edges("advance_step", has_next_step, {
        "continue": "step_router",
        "done": END,
    })

    # 并行路径
    graph.add_edge("parallel_executor", END)

    return graph.compile()

Step 4:串行 vs 并行

  串行:有依赖关系的任务。“查天气然后写文案”——writer 需要天气结果才能写。

image.png

  并行:互不依赖的任务。“翻译hello world,顺便查天气”——两个任务没关系。

image.png

  并行模式用 ThreadPoolExecutor 同时执行多个 Agent,最后让 LLM 把结果整合成一个连贯的回答。

Step 5:验证效果

# 单意图
curl -X POST /api/chat -d '{"message": "你好"}'
# → dispatcher: chat → chat_agent → "你好!有什么可以帮你的?"

# 单意图 + 工具
curl -X POST /api/chat -d '{"message": "上海天气"}'
# → dispatcher: tools → router → get_weather → router → "上海晴天24°C"

# 多意图串行
curl -X POST /api/chat -d '{"message": "查上海天气,写个朋友圈文案"}'
# → dispatcher: [tools, writer] sequential
# → tools(查天气)→ writer(基于天气写文案)→ 最终回复

# 多意图并行
curl -X POST /api/chat -d '{"message": "翻译hello world,顺便查北京天气"}'
# → dispatcher: [tools, writer] parallel
# → 并发执行 → 整合回复

刨根问底

序号问题
1️⃣Q:Dispatcher 的意图分类准确吗?模糊场景怎么办?
A:不一定准。"帮我写个出行计划"到底是 tools 还是 writer?靠 Prompt 里的规则和示例来约束。模糊时兜底到 chat(最安全的路径)。后续可以加 few-shot 示例提升准确率。
2️⃣Q:并行模式怎么整合多个 Agent 的结果?
A:用 LLM 整合。把多个 Agent 的输出拼在一起,让 LLM 生成一个连贯的最终回答。类似于"你有两段信息,请合并成一段自然的回复"。
3️⃣Q:LangGraph 和直接写 Python 函数调用有什么区别?
A:小项目没区别。但当图变复杂(10+ 节点、多种条件路由、需要流式输出)时,LangGraph 的状态管理和条件边比手写 if-else 清晰得多。而且它内置了 stream 模式,后面做 SSE 推送时直接用。

本篇小结

  • 多 Agent 的核心是分工——Dispatcher 做路由,专业 Agent 各管一摊;
  • LangGraph 用来描述 Agent 的执行流程,节点是函数,边是条件;
  • 支持串行(有依赖)和并行(无依赖)两种多意图执行模式;
  • dispatcher 的意图分类质量决定了整个系统的上限;

写在最后

  多 Agent 架构看起来复杂,但核心思想很朴素:把大问题拆成小问题,让专业的人做专业的事。这和微服务架构的理念一模一样——单体应用拆成多个服务,每个服务职责单一,通过 API 协作。

  如果你做过前端的组件化拆分,多 Agent 的设计思路你一定不陌生。

  下一篇预告:核心服务架构搭好了,接下来让它真正能完整跑起来——Web 界面 + SSE 流式输出,把 Agent 从命令行变成一个能用的产品。