06 Multi Agent
本文记录的是我用 Vibe Coding 方式学习 Multi Agent 相关技术的 AI (Gemini-3.1-Pro)生成对话。
为什么学:2026 年单 Agent 已不够,角色分工多 Agent 是主流。
核心内容:CrewAI(角色 + 任务 + 流程) / AutoGen(对话式多 Agent) / LangGraph 多 Agent 图。
项目:搭建「研究员 + 写手 + 审稿人」三 Agent 写作流水线。
学习 Multi-Agent 核心概念 (CrewAI 角色、任务、流程)
完成 CrewAI「研究员+写手」双角色协作基础练习
加入「审稿人」角色,完成三 Agent 写作流水线
太棒了!欢迎来到 2026 年的主流战场 —— 多智能体(Multi-Agent)的世界。TODO 挖空的实战脚本。
我们的最终目标是实现 multi_agent_target.md 里的「研究员 + 写手 + 审稿人」三 Agent 写作流水线。为了达到这个目标,今天我们先学习 CrewAI 框架。
接下来我们要怎么做?
- 理解概念:搞懂 CrewAI 的核心机制(角色、任务、流程)。
- 动手填空:在准备好的脚本中填补
# TODO代码,定义出三个角色。 - 跑通流水线:观察三个 Agent 如何协作并产出文章。
🧠 核心概念:为什么需要 Multi-Agent?
在 2024 年,大家都在用单一的 Agent(比如单纯的对话机器人)。但这就像全栈工程师单打独斗,面对复杂需求容易顾此失彼、上下文超载或者产生幻觉(Hallucination)。
到了 2026 年,主流做法是开公司模式(Multi-Agent)。
CrewAI 就是用来“开公司”的框架,它有三个核心概念:
- Agent(角色):公司的员工。你要给它设定
role(职位)、goal(目标)和backstory(背景故事,用于设定语气和专业度)。 - Task(任务):员工要干的活。你要告诉它
description(具体做什么)和expected_output(期望的输出格式)。 - Crew(团队/流程):把员工和任务组装起来的流水线。比如设定是顺序执行(
Process.sequential),研究员干完活再交给写手。
常见误区:很多人以为给 LLM 的 Prompt 越长越好,但在多 Agent 系统中,拆分才是王道。每个 Agent 只专注一件事,产出的质量会产生质的飞跃!
💻 你的第一关:“填空练习”
我已经为你创建了练习文件:crewai_pipeline_practice.py
打开这个文件,你会发现我写好了整个流水线的框架,并且配置好了你之前设置的 DEEPSEEK_API_KEY。但核心位置我留了 5 个 # TODO 需要你来填空:
- TODO 1:为研究员写
backstory(背景故事),告诉大模型它应该用什么态度做研究。 - TODO 2:完成写手的
role和goal(想想一个爆款文章作者的目标是什么?)。 - TODO 3:为研究任务写一段
description(明确提出让它找 3 个关键点)。 - TODO 4:定义写手任务的
expected_output(比如要求输出带有标题的 Markdown)。 - TODO 5:将代码最下方的
topic换成你最近感兴趣的任何话题!
🛠️ 环境准备与运行:
当你填完代码后,请在终端执行以下命令安装必要的依赖:
pip install crewai langchain-openai
然后运行脚本:
python crewai_pipeline_practice.py
你可以先去 crewai_pipeline_practice.py 里摸索一下,尝试把那几个 # TODO 填满。
理解多智能体“角色 + 任务”的基本逻辑了吗?准备好补全代码后运行一下试试吗?遇到任何报错或者不知道怎么填,随时把代码发给我帮你 review!
点击查看代码
import os
# 如果没有安装依赖,请在终端运行: pip install crewai langchain-openai
from crewai import Agent, Task, Crew, Process
from langchain_openai import ChatOpenAI
from crewai import LLM
# ==========================================
# 0. LLM 配置 (使用 Deepseek API 作为核心大脑)
# ==========================================
# 我们利用大模型 OpenAI 兼容接口调用 Deepseek
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
raise ValueError("请先设置 DEEPSEEK_API_KEY 环境变量!")
# 初始化 Deepseek LLM 实例
deepseek_llm = LLM(
model="deepseek-chat",
api_key=api_key,
base_url="https://api.deepseek.com" # Deepseek 的标准兼容 API 地址
)
# ==========================================
# 1. 定义角色 (Agents)
# ==========================================
# 角色一:资深研究员
researcher = Agent(
role='前沿科技研究员',
goal='找出关于 {topic} 的最新发展趋势和核心关键点',
# TODO 1: 为研究员写一段背景故事(backstory),告诉它它的专长是什么,它的工作态度如何。
backstory="""
""",
verbose=True,
allow_delegation=False, # 研究员自己干活,不把工作委派给别人
llm=deepseek_llm
)
# 角色二:爆款写手
writer = Agent(
# TODO 2: 为写手定义 role (角色) 和 goal (目标)
role='',
goal='',
backstory='你是一位擅长将复杂科技概念转化为通俗易懂、引人入胜的爆款文章的资深科技博主。你的语言风趣幽默。',
verbose=True,
allow_delegation=False,
llm=deepseek_llm
)
# 角色三:严苛的审稿人
reviewer = Agent(
role='资深内容主编',
goal='审查草稿,确保内容准确无误、逻辑清晰,并且符合爆款文章的标准',
backstory='你在顶级科技媒体担任主编超过 10 年,对文字有着像素级的强迫症。你不能容忍任何废话和逻辑漏洞。',
verbose=True,
allow_delegation=True, # 审稿人可以将修改任务委派回给写手 (如果需要)
llm=deepseek_llm
)
# ==========================================
# 2. 定义任务 (Tasks)
# ==========================================
# 任务一:研究任务
research_task = Task(
# TODO 3: 描述研究员具体需要执行的任务内容 (description)。提示:让它总结 3 个关键趋势。
description="""
""",
expected_output='一份包含 3 个核心趋势和技术亮点的研究简报。',
agent=researcher
)
# 任务二:写作任务
write_task = Task(
description='基于研究员提供的研究简报,撰写一篇 800 字左右的科普文章。文章需要有吸引人的标题。',
# TODO 4: 描述你期望的输出格式 (expected_output),例如:Markdown 格式,包含标题、引言、正文和结语。
expected_output="""
""",
agent=writer
)
# 任务三:审稿任务
review_task = Task(
description='仔细阅读写手完成的草稿,检查错别字、逻辑连贯性,并给出最终优化后的终稿。',
expected_output='最终可以直接发布的高质量 Markdown 文章。',
agent=reviewer
)
# ==========================================
# 3. 组建团队并启动 (Crew)
# ==========================================
# 将角色和任务组合成一个工作流
writing_crew = Crew(
agents=[researcher, writer, reviewer],
tasks=[research_task, write_task, review_task],
# Process.sequential 表示任务将按顺序依次执行:研究 -> 写作 -> 审稿
process=Process.sequential
)
if __name__ == "__main__":
print("🚀 开始执行多 Agent 写作流水线...")
# 设定我们今天的主题
# TODO 5: 尝试修改 topic 的值,换成你感兴趣的话题
task_inputs = {
'topic': ''
}
# 启动工作流!
result = writing_crew.kickoff(inputs=task_inputs)
print("\n==============================================")
print("✨ 最终产出的文章内容:")
print("==============================================")
print(result)
学习 AutoGen 对话式多 Agent 机制
太棒了!恭喜你成功跑通了 CrewAI 写作流水线!🎉 这说明你已经掌握了“通过定义角色和任务,以流程化的方式串联 Agent”的核心思想。
根据我们在 multi_agent_target.md 里的学习目标,既然你已经拿下了基于“流程驱动”的 CrewAI,接下来我们要进入另一个截然不同的流派:基于“对话驱动”的多智能体框架 —— AutoGen。
下一步计划
- 理解概念:认识 AutoGen 为什么被称为“对话式多 Agent”,以及它与 CrewAI 的核心区别。
- 动手填空:在准备好的脚本中填补
# TODO代码,配置“程序员 + 审查员”的对话组合。 - 跑通对话流:观察这两个 Agent 如何像我们在微信群里一样“互相讨论”并完成编程任务。
🧠 核心概念:AutoGen 是怎么玩的?
如果说 CrewAI 像是一个工厂流水线(任务按顺序从一个人手里传到下一个人手里,结构严谨);
那么 AutoGen 则更像是一个微信群聊(Agents 在群里互相发消息,自由讨论,甚至可以纠正对方的错误)。
AutoGen 的核心机制非常简单但极其强大:
- Agent:不再区分死板的“研究员”或“写手”,它默认提供了两种基础类型:
AssistantAgent:就是干活的 AI,写代码、解答问题。UserProxyAgent:你的代理人(裁判)。它可以代表人类介入对话,也可以自动化地去执行代码。
- 对话 (Conversations):Agent 之间通过互相发送消息来协作。比如 A 写了一段代码,发给 B,B 指出错误,发回给 A,A 再修改... 直到达到某个停止条件。
常见误区:新手刚接触 AutoGen 时,容易把它当成单轮问答。它的精髓在于多轮互相纠错(Reflection)。你可以设定规则,比如“只有当代码审查员回复 APPROVE 时,对话才结束”。
💻 你的第二关:“对话式编程”练习
我已经为你创建了第二个练习文件:autogen_chat_practice.py
请打开这个文件。在这段代码中,我们将模拟一个经典的结对编程场景:“写代码的 Coder” 和 “挑刺的 Reviewer”。
你需要完成以下 3 个 # TODO 挖空:
- TODO 1:为
coder写一段system_message。提示:告诉它它是一个顶级的 Python 开发者,专注于写出优雅、无 Bug 的代码。 - TODO 2:为
reviewer写一段system_message。提示:告诉它负责审查 Coder 写的代码,指出潜在的安全隐患和性能问题。重要规则:如果代码没问题,必须在结尾回复大写的 "APPROVE"(这是我们设定停止对话的暗号)。 - TODO 4:给 Coder 布置一个具体的编程任务
task_description。你可以随便出个题,比如:“写一个 Python 脚本,用多线程下载几个测试图片,并计算总耗时”,或者“写一个冒泡排序算法”。
🛠️ 环境准备与运行:
由于我们要用到 AutoGen 框架,请在终端执行以下命令安装依赖:
pip install pyautogen
然后运行脚本:
python autogen_chat_practice.py
快去 autogen_chat_practice.py 里面填空吧!这回你能看到两个 Agent 是怎么互相“丢代码”和“审查”的。
理解对话式 Agent 的逻辑了吗?填完代码跑通后,你可以故意在 TODO 4 里提一个很难或容易出错的需求,看看 Reviewer 会不会给 Coder 挑刺!如果遇到问题,随时把代码发给我。
点击查看代码
import os
from autogen import AssistantAgent, UserProxyAgent
# ==========================================
# 0. 配置 LLM (使用 Deepseek)
# ==========================================
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
raise ValueError("请先设置 DEEPSEEK_API_KEY 环境变量!")
# AutoGen 的配置格式 (config_list)
config_list = [
{
"model": "deepseek-chat",
"api_key": api_key,
"base_url": "https://api.deepseek.com"
}
]
llm_config = {
"config_list": config_list,
"temperature": 0.7,
}
# ==========================================
# 1. 定义角色 (对话式 Agents)
# ==========================================
# 角色一:Python 程序员 (AssistantAgent 默认就是用来写代码/解答问题的)
coder = AssistantAgent(
name="Coder",
# TODO 1: 为 Coder 写一段 system_message,告诉它它是一个顶级的 Python 开发者,专注于写出优雅、无 Bug 的代码。
system_message="""
""",
llm_config=llm_config,
)
# 角色二:代码审查员
reviewer = AssistantAgent(
name="Reviewer",
# TODO 2: 为 Reviewer 写 system_message。告诉它负责审查 Coder 写的代码,指出潜在的安全隐患和性能问题。如果代码没问题,必须回复 "APPROVE"。
system_message="""
""",
llm_config=llm_config,
)
# 角色三:用户代理 (UserProxyAgent 代表你,或者作为发起任务的裁判)
user_proxy = UserProxyAgent(
name="User_Proxy",
human_input_mode="NEVER", # 设置为 NEVER 表示完全自动化,不需要人类在终端输入
max_consecutive_auto_reply=10, # 最大自动回复次数,防止死循环
is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("APPROVE"), # TODO 3 (已完成): 当有人回复 APPROVE 时,结束对话
code_execution_config={"use_docker": False} # 允许在本地直接执行代码(注意安全)
)
# ==========================================
# 2. 启动对话流 (Chat)
# ==========================================
if __name__ == "__main__":
print("🚀 开始执行 AutoGen 对话式多 Agent 编程...")
# 设定任务
# TODO 4: 给 Coder 布置一个具体的编程任务,比如“写一个 Python 脚本,用多线程下载几个测试图片,并计算总耗时”
task_description = """
"""
# 在 AutoGen 中,我们不是把任务分配给单个 Agent,而是让两个 Agent 围绕这个任务“聊天”
# 我们让 user_proxy 把任务发给 coder
# 但是我们想要 coder 写完后,reviewer 能够看到并审查。
# 我们可以使用 AutoGen 的 GroupChat (群聊) 功能,或者更简单的顺序对话。
# 这里我们演示一种简单的两步对话:
# 第一步:User Proxy 要求 Coder 写代码
print("\n--- 第一阶段:Coder 开始写代码 ---")
user_proxy.initiate_chat(
coder,
message=task_description,
max_turns=1 # 只让 Coder 回复一次
)
# 提取 Coder 的回复代码
coder_reply = user_proxy.last_message(coder)["content"]
# 第二步:User Proxy 把 Coder 的代码发给 Reviewer 审查
print("\n--- 第二阶段:Reviewer 开始审查 ---")
user_proxy.initiate_chat(
reviewer,
message=f"请审查以下代码。如果没问题,请回复 APPROVE。如果有问题,请指出:\n\n{coder_reply}",
max_turns=1
)
学习 LangGraph 状态图构建多 Agent 工作流
非常好!你已经成功跨越了 CrewAI(流程驱动)和 AutoGen(对话驱动)两座大山。
现在,我们要迎接 2026 年多智能体领域的终极 Boss,也是工业界最喜欢用的底层框架 —— LangGraph。
下一步计划:攻克 LangGraph
- 理解概念:为什么有了 CrewAI 和 AutoGen,还需要 LangGraph?它所谓的“状态机”和“图”是什么?
- 动手填空:在准备好的脚本中填补
# TODO,用“节点(Nodes)”和“边(Edges)”画出一个带循环的流程图。 - 跑通工作流:观察它是如何通过状态控制(State)实现精准的循环和条件分支的。
🧠 核心概念:什么是 LangGraph?
如果说 CrewAI 像死板的流水线(单向流动,很难循环),AutoGen 像随意的微信群(很难精确控制谁在什么时候发言,容易跑偏);
那么 LangGraph 就像是一张带交通灯的精确地图(有向图)。
在 LangGraph 中,一切都被抽象为三个核心要素:
- 状态 (State):这是一个全局字典(通常用 TypedDict 定义),像一个“接力棒”。它记录了所有的对话历史和自定义变量。
- 节点 (Nodes):地图上的“站点”。每一个节点就是一个普通的 Python 函数,它接收当前的 State,做点事情(比如调用 LLM),然后返回需要更新的 State。
- 边 (Edges):地图上的“马路”。分为普通边(从 A 必定到 B)和条件边 (Conditional Edges)(到了路口,根据 State 里的某个变量,决定是往左走还是往右走,甚至掉头形成循环)。
为什么它最强? 因为你可以极度精确地控制流程。比如:“如果审稿人说不行,必须退回给写手;如果说行,才能走到结束节点。” 这正是我们接下来要写的代码!
💻 你的第三关:“画一张状态图”练习
我已经为你创建了练习文件:langgraph_practice.py
打开这个文件,我们要用 LangGraph 重新实现一遍“程序员写代码 -> 审查员检查 -> 不行就退回重写”的逻辑。这次你将看到它的控制粒度有多细。
文件中有 4 个 # TODO 挖空,我已经帮你把框架搭好了,你需要重点关注逻辑判断部分:
- TODO 1:在
AgentState中定义一个状态变量revision_needed,用于记录审查员是否认为需要修改。 - TODO 2:在
reviewer_node中,根据 LLM 的回复,判断是否需要修改,并更新到状态里。 - TODO 3:完成路由函数
should_continue,根据状态里的revision_needed决定返回哪条路。 - TODO 4:设定一个有趣的初始任务。
🛠️ 环境准备与运行:
运行前需要安装 LangGraph 及其相关依赖:
pip install langgraph langchain-core langchain-openai
然后运行脚本:
python langgraph_practice.py
你可以先去 langgraph_practice.py 里摸索一下,尝试填补那几个状态流转的核心代码。
理解 LangGraph “状态图”的基本逻辑了吗?准备好补全代码后运行一下试试吗?跑通之后,你可以观察控制台输出,看看它是如何精细地在节点间跳转的!
点击查看代码
import os
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langgraph.graph import StateGraph, END
# ==========================================
# 0. 配置 LLM (使用 Deepseek)
# ==========================================
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
raise ValueError("请先设置 DEEPSEEK_API_KEY 环境变量!")
llm = ChatOpenAI(
model="deepseek-chat",
api_key=api_key,
base_url="https://api.deepseek.com"
)
# ==========================================
# 1. 定义状态 (State)
# ==========================================
# 在 LangGraph 中,一切数据都在节点之间通过一个"状态"字典流动。
# TypedDict 帮我们定义这个字典长什么样。
class AgentState(TypedDict):
# messages 存储对话历史。Annotated 和 operator.add 表示新消息会被追加到列表中,而不是覆盖。
messages: Annotated[Sequence[BaseMessage], operator.add]
# 我们也可以自定义额外的状态字段,比如用来判断是否需要修改
# TODO 1: 添加一个名为 'revision_needed' 的布尔类型字段 (bool),默认不需要。用于记录代码是否需要重写。
# ==========================================
# 2. 定义节点函数 (Nodes)
# ==========================================
# 每一个节点就是一个普通的 Python 函数,接收当前状态,返回更新后的状态。
def coder_node(state: AgentState):
"""程序员节点:负责写代码"""
print("👩💻 Coder 正在思考并编写代码...")
# 取出对话历史中的最后一条消息(通常是需求或者审查意见)
last_message = state["messages"][-1]
# 构造给 LLM 的 prompt
system_prompt = "你是一个顶级程序员。请根据用户的需求或审查者的反馈,提供高质量的Python代码。"
messages_to_llm = [
{"role": "system", "content": system_prompt},
# 我们把之前的对话历史传给它
*[{"role": "user" if isinstance(m, HumanMessage) else "assistant", "content": m.content} for m in state["messages"]]
]
# 调用大模型生成代码
response = llm.invoke(messages_to_llm)
# 返回更新的状态:追加一条 AIMessage
return {"messages": [AIMessage(content=response.content)]}
def reviewer_node(state: AgentState):
"""审查员节点:负责审查代码"""
print("🕵️♂️ Reviewer 正在审查代码...")
# 取出 Coder 刚刚写的代码
last_message = state["messages"][-1].content
system_prompt = """你是一个严格的代码审查员。
请检查以下代码是否有逻辑错误或可以优化的地方。
如果你认为代码没问题,请在回复的最后加上大写的 "APPROVE"。
如果你发现了问题,请详细指出,并不要回复 "APPROVE"。"""
# 让 LLM 进行审查
response = llm.invoke([
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"请审查这段代码:\n\n{last_message}"}
])
review_content = response.content
# 判断是否包含 APPROVE
# TODO 2: 判断审查结果 review_content 中是否包含 "APPROVE"。
# 如果包含,说明不需要修改 (revision_needed=False);否则需要修改 (revision_needed=True)。
needs_revision = None
# 返回更新的状态:追加审查意见,并更新 revision_needed 状态
return {
"messages": [AIMessage(content=f"[审查意见]:\n{review_content}")],
"revision_needed": needs_revision
}
# ==========================================
# 3. 定义条件边 (Conditional Edges)
# ==========================================
# 决定接下来的走向
def should_continue(state: AgentState):
"""这是一个路由函数,决定图的下一步怎么走"""
# TODO 3: 根据 state 中的 'revision_needed' 字段来判断。
# 如果需要修改,返回 "coder"(让它回去重写);如果不需要修改,返回 "end"(结束流程)。
pass
# ==========================================
# 4. 构建状态图 (Graph)
# ==========================================
print("正在构建 LangGraph 状态图...")
# 初始化图,传入我们定义的 State 结构
workflow = StateGraph(AgentState)
# 添加节点:给每个函数起个名字
workflow.add_node("coder", coder_node)
workflow.add_node("reviewer", reviewer_node)
# 设置起点:一开始就去找 Coder
workflow.set_entry_point("coder")
# 设置普通边:Coder 写完代码后,必须交给 Reviewer
workflow.add_edge("coder", "reviewer")
# 设置条件边:Reviewer 审查完后,根据 should_continue 的结果决定去向
# 如果返回 "coder",则回到 coder 节点;如果返回 "end",则走到 END 节点结束。
workflow.add_conditional_edges(
"reviewer",
should_continue,
{
"coder": "coder",
"end": END
}
)
# 编译成可运行的应用
app = workflow.compile()
# ==========================================
# 5. 运行测试
# ==========================================
if __name__ == "__main__":
print("\n🚀 开始运行 LangGraph 循环工作流...")
# 设定初始任务
# TODO 4: 设定一个初始的用户需求。比如:“写一个计算斐波那契数列的Python函数。”
initial_task = "写一个计算斐波那契数列的Python函数,要求使用生成器。"
# 构造初始状态
inputs = {"messages": [HumanMessage(content=initial_task)]}
# 运行工作流。stream 会返回每一步的输出
# 设置 recursion_limit 防止无限循环
for output in app.stream(inputs, {"recursion_limit": 15}):
# 遍历当前步产出的所有节点更新
for node_name, state_update in output.items():
print(f"\n--- 节点 [{node_name}] 执行完毕 ---")
# 打印该节点产生的新消息
if "messages" in state_update:
latest_msg = state_update["messages"][-1].content
# 为了防止输出太长,我们只打印前 200 个字符
print(f"产出摘要: {latest_msg[:200]}...\n")
print("\n🎉 工作流执行结束!")

浙公网安备 33010602011771号