从零到一搞懂 AutoGen:多智能体协作框架完整拆解

环境:Windows 11 + Python 3.14 + autogen-agentchat 0.7.5(文中所有 API 均在本地实测验证)。


目录


一、AutoGen 是什么

AutoGen 是微软开源的多智能体(Multi-Agent)框架,核心思想一句话:

把多个”有人设的 LLM”放进一个聊天室里,让它们通过对话协作完成复杂任务。

版本警告(重要):AutoGen 有两个完全不同的时代——

版本 包名 API 风格
0.2 及以前 autogen/pyautogen ConversableAgent+initiate_chat,老式单体 API
0.4 及以后(当前) autogen-agentchat 等分层包 完全重写,异步、事件驱动、分层架构

网上大量教程还是 0.2 时代的写法,和现在的代码完全不兼容。认准 autogen-agentchat 这个包名。本文基于 0.7.5。

生态备注:微软后来推出了整合 AutoGen 与 Semantic Kernel 的 Agent Framework,但 AutoGen 本身仍在独立维护,0.7.x 是当前学习的主流版本。


二、三层架构:先建立地图

安装时会发现 AutoGen 不是一个包,而是一组包——这是 0.4+ 重写的核心设计:

1
2
3
4
5
6
7
8
9
10
11
12
13
┌──────────────────────────────────────────────────────┐
│ autogen-agentchat 高层「对话式」API │
│ AssistantAgent / UserProxyAgent / Team / 终止条件 │
│ → 日常开发 99% 在这一层 │
├──────────────────────────────────────────────────────┤
│ autogen-core 底层引擎 │
│ 事件驱动 actor 模型:消息、运行时、模型抽象 │
│ → 只有深度定制(自定义 Agent)才需要碰 │
├──────────────────────────────────────────────────────┤
│ autogen-ext 插件层 │
│ OpenAIChatCompletionClient、代码执行器等 │
│ → 换模型/换工具实现时,动的是这一层 │
└──────────────────────────────────────────────────────┘

心智模型:agentchat 是剧本和演员,core 是舞台机械,ext 是外接设备。

这个分层带来的直接好处:模型层可替换。本文所有案例跑在 DeepSeek 上,只是换了个 base_url,上层代码零改动。


三、环境搭建(含真实踩坑)

1
pip install autogen-agentchat "autogen-ext[openai]" python-dotenv

三个包的分工:

包 提供什么
autogen-agentchat 智能体、团队、终止条件(框架主体)
autogen-ext[openai] OpenAIChatCompletionClient(连接 OpenAI 兼容 API)
python-dotenv 读取.env 配置文件

⚠️ 教材 requirements.txt 的坑:教程里写的是 asyncio 和 dotenv,两个都是错的——asyncio 是标准库不该装,dotenv 是另一个同名错误包,正确的是 python-dotenv。

.env 配置示例(DeepSeek):

1
2
3
LLM_MODEL_ID="deepseek-flash"
LLM_API_KEY="sk-xxxx"
LLM_BASE_URL="https://api.deepseek.com"

四、核心概念逐个击破

4.1 模型客户端 ModelClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_core.models import ModelInfo

client = OpenAIChatCompletionClient(
model="deepseek-flash",
api_key="sk-xxxx",
base_url="https://api.deepseek.com",
model_info=ModelInfo(
vision=False,
function_calling=True, # 要用工具,这项必须是 True
json_output=False,
structured_output=False,
family="unknown",
),
)

两个关键认知:

  1. 它名叫 OpenAI,实际是”OpenAI 协议兼容客户端”。base_url 指向谁就连谁——DeepSeek、Moonshot、各种国产模型都提供 OpenAI 兼容接口,客户端零改动。

  2. model_info 是能力声明。AutoGen 内置了 GPT 系列的能力表,遇到不认识的模型名必须手工声明能力。少了它会直接报:

    1
    ValueError: model_info is required when model name is not a valid OpenAI model

    这是我踩的第一个坑。框架需要知道模型能力(会不会 function calling、支不支持 JSON 输出)来决定如何构造请求。

4.2 消息与事件体系

AutoGen 0.7.5 里一切皆为消息,这是理解框架的钥匙。运行时会看到这些类型:

类型 含义
TextMessage 一条普通对话消息(agent 的发言)
ThoughtEvent 模型的思考过程(推理模型会产出)
ToolCallRequestEvent LLM 请求调用工具(只是”请求”!)
ToolCallExecutionEvent 框架真正执行完工具的结果
ToolCallSummaryMessage 工具结果汇总
SelectSpeakerEvent SelectorGroupChat 的选择过程
StopMessage 团队终止时的收尾消息

洞察:所谓”多智能体协作”,本质是一份共享聊天记录 + 轮流让不同人设的 LLM 续写它。所有 agent 看到同样的历史,区别只在各自的 system_message。

4.3 智能体 Agent

0.7.5 内置的智能体全家桶:

Agent 一句话说明
AssistantAgent 主力:LLM + system_message(人设)+ 可选工具
UserProxyAgent 人类在环路(human-in-the-loop),轮到他时等你键盘输入
SocietyOfMindAgent 把一个团队封装成一个”超级智能体”,可嵌套进更大的团队
CodeExecutorAgent 从消息里提取代码块并真正执行(需配代码执行器,如 Docker)
MessageFilterAgent 给别的 agent 加”消息过滤器”,控制它能看到哪些上下文
BaseChatAgent 基类,自定义 agent 时继承它

最常用的两个:

AssistantAgent——本质就是 system_message + 模型,没有任何隐藏逻辑:

1
2
3
4
5
AssistantAgent(
name="ProductManager",
model_client=client,
system_message="你是一位经验丰富的产品经理……",
)

核心思想:用提示词分工,而不是用代码分工。“产品经理”和”工程师”的差别 100% 来自那段人设文本。

UserProxyAgent——它不接 LLM,就是你的麦克风:

1
UserProxyAgent(name="UserProxy", description="代表用户提需求、做测试")

默认的 input_func 会从控制台读输入,所以运行时轮到他,程序会停下来等你打字。也可以通过 input_func 参数换成自动回复逻辑,实现全自动测试。

4.4 团队 Team:五种协作模式

这是 AutoGen 最精华的部分。0.7.5 内置五种团队:

团队 发言顺序由谁决定 适用场景
RoundRobinGroupChat 代码写死:按 participants 顺序轮流 流程固定的流水线(需求 → 开发 → 审查)
SelectorGroupChat 选择器 LLM 每轮动态选 流程不确定、需按需调度
Swarm agent 之间通过 **handoff(交接)**传递 客服式”转接”场景
MagenticOneGroupChat 一个**总指挥(Orchestrator)**维护任务清单并调度 复杂的开放式任务
GraphFlow 有向图(DAG),可带条件分支 需要严格审批流/分支逻辑的工作流

RoundRobinGroupChat(最简单):

1
2
3
4
5
team = RoundRobinGroupChat(
participants=[pm, engineer, reviewer, user_proxy], # 顺序即流程
termination_condition=termination,
max_turns=20,
)

SelectorGroupChat(动态路由):

1
2
3
4
5
6
7
team = SelectorGroupChat(
participants=[pm, engineer, reviewer],
model_client=client, # ⚠️ 选择器本身是个 LLM,必须传客户端
selector_prompt=自定义提示词, # 可选,含 {roles} {participants} {history} 占位符
termination_condition=...,
# allow_repeated_speaker=False # 默认同一角色不能连续发言
)

Selector 的三个关键点:

  1. 每轮 2 次 LLM 调用(选择器 1 次 + 发言者 1 次),成本约为 RoundRobin 两倍;
  2. 选择器只看各 agent 的 description——description 写不好,选人就会乱;
  3. RoundRobin 里”请工程师接手”只是对话内容,在 Selector 里才真正起了路由作用。

4.5 终止条件 Termination

框架自己不知道”任务完成了没有”,只认机械条件。0.7.5 的全家桶:

终止条件 触发时机
TextMentionTermination("TERMINATE") 某条消息提到指定文本
MaxMessageTermination(12) 总消息数达到上限
TokenUsageTermination(...) token 用量超标(成本控制)
TimeoutTermination(...) 超时
SourceMatchTermination("UserProxy") 指定角色发过言
FunctionCallTermination(...) 某个工具被调用
HandoffTermination(...) 发生指定交接
StopMessageTermination() 出现 StopMessage
ExternalTermination() 由外部代码手动叫停

最实用的技巧——用 | 和 & 组合条件:

1
2
termination = TextMentionTermination("TERMINATE") | MaxMessageTermination(12)
# 语义:有人说 TERMINATE 【或】满 12 条消息 → 停(后者是保险丝)

血泪建议:永远带一个兜底条件(消息数或 token 上限)。否则没人说 TERMINATE 时,团队会空转烧钱到天荒地老。

4.6 工具 Tools

核心认知:LLM 从来不”执行”工具,它只是”请求”执行。真正跑代码的是框架。

写一个工具只需三个要件:

1
2
3
def calculate(expression: str) -> str:          # ① 类型注解(框架靠它生成 JSON Schema)
"""计算一个数学表达式,例如 '123 * 456'。""" # ② docstring(LLM 靠它决定何时用、怎么用)
return str(eval(expression, {"__builtins__": {}}, {})) # ③ 返回值塞回对话

装到智能体上,不需要任何装饰器:

1
2
3
4
5
6
agent = AssistantAgent(
name="Analyst",
model_client=client,
tools=[calculate, get_bitcoin_price], # 直接传普通函数
system_message="需要数据或计算时必须调用工具,禁止自己编造数字。",
)

运行时的四步循环(真实输出):

1
2
3
4
① 任务下发,框架把工具 Schema 一并发给 LLM
② ToolCallRequestEvent: LLM 返回 FunctionCall(name='calculate', arguments='{"expression": "12345 * 678"}')
③ ToolCallExecutionEvent: 框架本地执行,得到 "8369910"
④ ToolCallSummaryMessage: 结果回到对话

三个容易忽略的点:

  • model_info 里 function_calling=True 是工具的前提;
  • docstring 是写给 LLM 的使用说明书,它的好坏直接决定模型会不会正确调用——它是提示词的一部分,不是注释;
  • 想让 LLM 用”人话”总结工具结果,传 reflect_on_tool_use=True(多一次 LLM 调用);max_tool_iterations 控制连续调工具的轮数上限。

4.7 运行与可视化:run / run_stream / Console

1
2
3
4
5
6
7
8
9
# 方式一:流式 + 漂亮的控制台输出(学习时首选)
result = await Console(team.run_stream(task=任务))

# 方式二:自己消费事件流(观察框架内部,收获巨大)
async for event in team.run_stream(task=任务):
print(type(event).__name__)

# 方式三:一次性等结果
result = await team.run(task=任务)

整个框架是异步的,入口固定是:

1
asyncio.run(main())

五、实战拆解:软件开发团队案例

教程第六章的案例:产品经理 → 工程师 → 审查员 → 用户代理,协作开发一个比特币价格应用。

运行时的消息流时序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
task(开发需求)放入共享记录
│
▼
RoundRobin 轮到 ProductManager
他的上下文 = 自己的 system_message + [task]
→ 生成需求分析 → 追加到共享记录(所有人可见)
→ 终止检查:含 TERMINATE?没有,继续
▼
轮到 Engineer
上下文 = 自己的 system_message + [task, PM的分析]
→ 写出完整代码 → 追加
▼
轮到 CodeReviewer(能看到以上全部内容)
→ 输出审查意见 → 追加
▼
轮到 UserProxy → 控制台等你打字
你输入"测试通过 TERMINATE" → 追加
终止检查:命中 ✅ → 结束
(否则回到 PM 开始第二轮,直到 max_turns 保险丝烧断)

三个最容易误解的点:

  1. “请工程师开始实现”不指挥路由——RoundRobin 顺序是写死的。这句话的真正作用是让下一个发言者知道自己该干嘛(因为所有人共享聊天记录)。
  2. “完成”是靠文本宣告的,不是框架判断的。没人说 TERMINATE 就一直转。
  3. 四个 agent 是同一个模型。多智能体 ≠ 多个模型,而是多个视角。

六、两个进阶实验(附真实运行结果)

我写了 learning_selector_tools.py,包含两个可独立运行的实验。

实验一:Tools

给单个 agent 装 calculate 和 get_bitcoin_price 两个工具,问它两个问题。真实输出:

1
2
3
4
5
ToolCallRequestEvent (PriceAnalyst)
FunctionCall(name='get_bitcoin_price', arguments='{}')
FunctionCall(name='calculate', arguments='{"expression": "12345 * 678"}')
ToolCallExecutionEvent
calculate → '8369910' ✓ 算对了

LLM 一次并行请求了两个工具,框架执行后回填——工具循环完整走通。

实验二:SelectorGroupChat

三个角色(PM / Engineer / Reviewer)完成”写一个 is_prime 函数”。真实流程:

1
2
PM 分析需求 → 选择器选 Engineer → 写完代码说"请 Reviewer 审查"
→ 选择器选 Reviewer → 审查后说"通过,TERMINATE" → 团队精准停止 ✅

全程没有写死顺序,全由选择器读对话后决定——和 RoundRobin 形成鲜明对比。


七、踩坑记录

坑 现象 解决
非 OpenAI 模型缺 model_info ValueError: model_info is required... 显式传ModelInfo(...)
教材依赖写错 pip install asyncio dotenv 装了错误的包 正确是python-dotenv,asyncio 是标准库
IDEA 用了全局解释器 ModuleNotFoundError: No module named 'dotenv' 运行配置里把 Python interpreter 换成装了依赖的解释器
解释器路径填错位置 SyntaxError: Non-UTF-8 code...(把 python.exe 当脚本读了) 解释器只能填在 Python interpreter 栏,不能填进 Script path
CoinGecko 连不通 工具返回超时错误 国内网络问题,换数据源或配代理;工具函数务必 try/except 优雅降级
UserProxy 卡住 程序像”死”了 不是死,是在等你键盘输入

八、进阶主题速览

学有余力后,按这个顺序探索:

主题 入口 说明
结构化输出 AssistantAgent(output_content_type=你的Pydantic模型) 让 agent 返回强类型数据而非自由文本
交接 Handoff AssistantAgent(handoffs=[...]) agent 主动把任务转给别的 agent(Swarm 的基础)
记忆 Memory AssistantAgent(memory=[...]) 给 agent 接长期记忆(RAG 等)
工作台 Workbench AssistantAgent(workbench=...) 比 tools 更强大的工具集管理
消息过滤 MessageFilterAgent 控制 agent 能看到哪些上下文,省 token
团队即智能体 SocietyOfMindAgent 团队套团队,构建层级化系统
自定义 Agent 继承autogen_core 的基类 深入底层事件驱动模型
图工作流 GraphFlow+DiGraphBuilder 严格的有向图流程,可带条件分支

九、学习路线建议

1
2
3
4
5
Day 1  跑通 RoundRobin 案例 → 理解共享聊天记录 + 人设分工
Day 2 给 agent 加工具 → 理解"LLM 只请求不执行"的四步循环
Day 3 换 SelectorGroupChat → 理解动态路由与 description 的作用
Day 4 自写事件循环(方式二)→ 看清框架内部的每一条消息
Day 5+ 按兴趣深入:Swarm / MagenticOne / GraphFlow / 自定义 Agent

最重要的一句话总结:

多智能体框架的本质,不是魔法,而是”一份共享聊天记录 + 若干人设 + 一套发言规则 + 一套停止规则”。 AutoGen 做的,就是把这四件事变成了可组合的积木。


参考资料

  • AutoGen 官方文档:https://microsoft.github.io/autogen/
  • 《Hello-Agents》教程第六章(本文案例来源)
  • 本文验证环境:autogen-agentchat / autogen-core / autogen-ext 0.7.5,Python 3.14,DeepSeek API

写于学习《Hello-Agents》第六章期间。文中所有代码与运行结果均为本地实测。