这篇是智能体开发系列的6.2节,主题是微软开源的AutoGen框架。作为大模型开发工程师,我最近半年测试过的agent框架不下五种,但真正在项目里留下来当主力工具的,AutoGen算一个。它解决的并不是"把大模型API包一层"这种简单问题,而是多个AI角色之间如何通过对话完成协作——你让一个Agent写代码,另一个Agent审查代码,还有一个代理负责执行和反馈,这种场景用AutoGen非常顺手。这篇文章我会从设计原理讲到实际代码,再讲我在生产环境中踩过的坑,适合想系统了解AutoGen、或者正在做多智能体方案选型的开发者阅读。
1. 从"单模型调用"到"多智能体对话":AutoGen要解的问题
1.1 为什么单Agent不够用
大模型应用开发刚起步的时候,大家写的大多是"单轮调用"程序:用户抛一个问题,拼一个prompt,丢给GPT或者国产大模型,拿到结果就完事。这种模式处理翻译、摘要、文案改写确实够了,但一旦碰到需要分步骤完成的任务,立刻捉襟见肘。比如"帮我把这个Excel里所有重复的客户记录找出来,然后生成一份去重报告",模型不是不能做,而是它需要在一次回答里同时完成"理解数据结构、写数据处理代码、检查代码能不能跑、解释结果"这一整套动作,prompt稍微复杂一点,模型就开始顾此失彼。
更麻烦的是,很多任务天然需要不同视角的介入。写代码的人容易忽略边界条件,审查的人专门挑毛病,如果让同一个模型既写代码又检查自己的代码,它往往"自己看自己什么都好"。我早期用一个Agent做代码生成加自查,连续几次都在同样的逻辑漏洞上翻车,后来改成两个独立角色的对话式Agent,问题立刻少了很多。这其实就是多智能体对话最朴素的动机:把一个人格拆成多个专业角色,让它们各自专注一件事,再通过消息机制协作。
单Agent不够用还有一个原因,是上下文窗口的物理限制。一个复杂的业务任务,中间过程会产生大量中间态——读取的数据、生成的中期报告、执行后的错误日志。如果这些全部堆在同一个对话里,token消耗涨得飞快,轮次一多,模型还会把早期的指令逐渐"遗忘"。多Agent结构把任务分阶段拆开,每个Agent只维护自己相关的上下文,反倒让整体更可控。
1.2 AutoGen的设计哲学:对话即编排
既然需要多角色分工,那自然要选一个编排方案。市面上常见思路有两种:一种是流程驱动,像写程序一样定义好节点和跳转条件,Agent只是流程里的一个执行单元;另一种就是AutoGen主打的对话驱动,不给任务规定死路径,而是让多个Agent在对话中动态决定下一步怎么做。
对话即编排,这个理念一开始我也有点怀疑——让模型自由对话,会不会跑偏?后来在项目里用下来的感受是:对话本身就是一种天然适合LLM的协议。模型最擅长的事情就是用自然语言沟通,与其去定义复杂的流程图状态机,不如给Agent设定角色和目标,让它们"商量着来"。AutoGen把编排逻辑藏在了对话轮次的背后,开发者只需要关心角色怎么设计、对话怎么收敛,心智负担小很多。
这个设计跟人很像。一个研发团队做需求,不会每一步都走严格的审批流,而是产品、开发、测试在一个群聊里来回讨论,聊到信息对齐再动手。AutoGen的GroupChat就是这个思路的工程化实现——多个Agent在一个会话里轮流发言,谁该说话、说什么,由会话管理器动态裁决。你会发现,它更接近现实中的协作形态,而不是一张冷冰冰的流程图。
2. AutoGen的组件体系:这几个类搞明白,框架就懂了一半
2.1 一切可对话的智能体,都从ConversableAgent派生
AutoGen整个框架的核心抽象就一个——ConversableAgent(可对话智能体)。从名字也能看出来,这个框架里所有参与协作的成员,本质都是"能收发消息的对象"。它不是一个大而全的框架,更像一套基础通信协议,你往里面塞什么角色,它就变成什么角色。
创建ConversableAgent时,最关键的几个参数是:
- name:Agent在对话中的名称,会被模型看到,所以尽量起有角色感的名字,比如"coder"、"reviewer"。
- system_message:角色设定。这里要写得具体,直接决定Agent在会话里的行为方式。
- llm_config:大模型配置。如果设为
False,这个Agent就不依赖LLM,只靠代码逻辑响应,比如负责执行代码和接收人工输入的代理。 - human_input_mode:什么时候需要人工输入。可选值有
NEVER、TERMINATE、ALWAYS,生产环境通常设成NEVER,靠终止条件自动收尾。
你不需要继承这个类去写复杂逻辑,大多数场景下直接实例化、设定参数就够了。AutoGen把灵活性和复杂度都收敛在这一个类里,这一点和很多动辄让你写十几行配置的框架不太一样。
2.2 两个开箱即用的角色:AssistantAgent与UserProxyAgent
如果没有现成的角色,AutoGen默认给你两个:AssistantAgent和UserProxyAgent。这俩是框架里出场率最高的角色,理解它们的分工,基本就理解了整个框架的协作方式。
AssistantAgent是由LLM驱动的助手,它负责生成回复内容,可以是方案、代码、分析结论。它不执行代码,只动嘴。UserProxyAgent则是用户的代理,它的特点是可以执行代码、可以请求人工输入。这两者天然形成一条工作链:UserProxyAgent把任务交给AssistantAgent,AssistantAgent回复一段代码,UserProxyAgent把代码在本地或Docker里运行,再把运行结果(成功或报错)作为新消息发给AssistantAgent,AssistantAgent根据结果修正代码。这个循环反复进行,直到任务完成。
我第一次跑通这个循环的时候,最大的感受是:它把一个"人用ChatGPT写代码再手动跑"的过程,完全自动化了。人不需要在每一轮都出现,只在关键节点把关就行。默认情况下UserProxyAgent的human_input_mode是TERMINATE,意思是它只在需要判断要不要结束时才问人,平时自动执行。
2.3 群聊模式:GroupChat与GroupChatManager
双Agent对话只覆盖"一个问、一个答"的场景,真实业务往往需要更多角色参与,这时候就用GroupChat。GroupChat维护一个会话列表和多个Agent,GroupChatManager负责调度——每一轮决定由哪个Agent发言。
管理器怎么决定谁发言?默认是让LLM根据当前的对话上下文,从Agent列表中挑一个"最该说话的人"。这个机制很有意思,它让发言顺序完全由内容驱动。比如三个Agent在讨论一个架构方案,前几轮是架构师在输出,后面实施工程师发现方案有遗漏,插进来补充,管理器会自动把话语权交给它。不需要开发者预判每次发言的轮次。
GroupChat有一个max_round参数,限制总对话轮数,防止讨论无休止进行。这个参数在开发阶段一定要设,而且要设得小一点,否则模型兴致上来能聊几十轮给你看。
3. 第一步实操:安装、模型配置和最小对话
3.1 安装与版本选择:先看清你找到的资料是哪代API
安装很简单,一条命令:
pip install pyautogen但这个坑我必须提前说:AutoGen在0.4版本经历了一次非常大的核心重构,0.4及之后的版本换成基于异步事件驱动的架构,包名、API用法和0.2系列几乎不兼容。你现在去网上搜资料,会看到两种完全不同的写法,搜到0.2的教程硬套0.4的代码,基本跑不通。
我的建议是:如果你是新手,先装0.2系列,因为网上存量教程、Stack Overflow问题、社区示例绝大多数都是0.2的写法,遇到问题更容易百度到答案;如果你已经熟悉框架想用到极致,再去看0.4的异步机制。本文代码基于0.2系列,Python版本建议3.9以上。
3.2 配置模型:不只是OpenAI,兼容接口都能接
AutoGen的模型配置通过config_list传入,它的设计很聪明:支持同时配置多个模型,base_url允许自定义,所以任何兼容OpenAI接口协议的服务都能无缝接入。
下面这段配置,用DeepSeek的API就能跑通,不需要OpenAI的Key:
llm_config = { "config_list": [ { "model": "deepseek-chat", "api_key": "YOUR_API_KEY", "base_url": "https://api.deepseek.com/v1", } ] }如果你用Ollama跑本地模型,base_url改成http://localhost:11434/v1,model改成你本地拉取的名字(比如qwen2.5:14b)就行。本地模型的优势是数据不出内网,对隐私要求高的项目很合适,代价是响应速度和推理能力跟云端API有差距。
有一点要提醒:不同模型对多Agent对话的"角色扮演"能力差异很大。我实测下来,指令遵循能力比较强的模型(比如GPT-4系列、DeepSeek的V3系列)在多Agent场景表现稳定;偏轻量的模型在角色多、任务复杂时,偶尔会答非所问。如果预算允许,生产环境尽量给LLM驱动的Agent配高端模型,省下调试prompt的功夫。
3.3 最小双Agent对话:代码示例与运行解读
装好环境、配好模型之后,写一个最小对话只需要十几行代码:
from autogen import AssistantAgent, UserProxyAgent llm_config = { "config_list": [ { "model": "deepseek-chat", "api_key": "YOUR_API_KEY", "base_url": "https://api.deepseek.com/v1", } ] } assistant = AssistantAgent( name="assistant", llm_config=llm_config, system_message="你是一个Python专家,负责编写高质量代码。代码用markdown代码块输出。", ) user_proxy = UserProxyAgent( name="user_proxy", human_input_mode="TERMINATE", max_consecutive_auto_reply=5, code_execution_config={ "work_dir": "coding", "use_docker": False, }, ) user_proxy.initiate_chat( assistant, message="写一个Python脚本,计算斐波那契数列前20项并打印结果。", )看运行日志,你会发现一个很有意思的过程:
user_proxy把用户问题发给assistant。assistant生成一段Python代码,用markdown代码块包起来。user_proxy检测到代码块,自动在coding目录下保存并在本地执行。- 执行成功,
user_proxy把输出结果(斐波那契数列)发回给assistant。 assistant判断任务已完成,给出最终总结。- 因为设置了
human_input_mode="TERMINATE",user_proxy会询问是否继续,如果没有额外指令就结束会话。
整个过程中我只在第一步输入了任务,后面完全是自动的。看起来很简单,但注意几个细节:assistant输出代码必须用markdown代码块,user_proxy才会识别并执行;max_consecutive_auto_reply=5限制了自动回复次数,防止模型停不下来;use_docker=False表示在本地直接执行,开发环境图省事可以这样,生产环境建议改成Docker。
3.4 控制会话走向:人工介入模式与终止条件
human_input_mode和is_termination_msg是两个很容易被忽略但极其重要的参数。
human_input_mode的取值逻辑:
- NEVER:Agent之间自动对话,永不询问人类。适合流水线批处理,比如定时任务里跑数据分析。
- TERMINATE:Agent自动对话,但每次要结束时问一下人类。适合半自动化场景,人有最终决定权。
- ALWAYS:每轮都询问人类。调试阶段用,看得清楚每步在干什么,但很累赘。
生产环境我强烈建议用NEVER配合is_termination_msg精确控制结束。is_termination_msg是一个回调函数,判断某条消息是否代表"任务结束":
def is_termination_msg(msg): content = msg.get("content", "") return "TERMINATE" in content配合这个函数,你需要在assistant的system_message里明确告诉它:"任务完成时,最后一条消息以TERMINATE作为结尾。"模型确实会照做。这样做的好处是结束时机由业务内容决定,而不是靠死板的轮数上限,同时保留轮数上限作为兜底,双保险。
4. 实战进阶:用GroupChat搭一个数据处理流水线
4.1 场景设计与角色分工
双Agent解决"生成并执行代码"足够,但我想演示一个更贴近真实开发的多角色场景。假设要做这样一件事:分析一个销售CSV文件,找出销售额异常下降的区域,生成一份带图表的可视化报告。
这个任务单靠一个Agent,又要写分析、又要保证代码质量、又要注意图表美观,很容易翻车。我拆成四个角色:
- pm(产品经理):明确任务目标,拆解需求,协调各方。
- coder(程序员):编写数据处理和可视化的Python代码。
- reviewer(审查员):审视代码的逻辑漏洞、边界情况,给出修改意见。
- executor(执行代理):实际运行代码,反馈运行日志,可以由
UserProxyAgent充当。
这四个角色通过GroupChat协作,让对话自由推进。角色之间的互动会产生真实感很强的讨论:coder写代码,reviewer挑毛病,pm在旁边看到讨论偏离需求时拉回来,executor把运行结果实时反馈,整个流程比硬编码的流水线灵活得多。
4.2 核心代码实现
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager llm_config = { "config_list": [ { "model": "deepseek-chat", "api_key": "YOUR_API_KEY", "base_url": "https://api.deepseek.com/v1", } ] } pm = AssistantAgent( name="pm", llm_config=llm_config, system_message="你是一个产品经理,负责拆解需求、控制讨论方向。" "当发现讨论跑偏时,把话题拉回任务目标。", ) coder = AssistantAgent( name="coder", llm_config=llm_config, system_message="你是资深Python开发,负责编写数据分析代码。" "代码必须用markdown代码块输出。", ) reviewer = AssistantAgent( name="reviewer", llm_config=llm_config, system_message="你是代码审查专家,只关注逻辑漏洞、边界条件和代码质量。" "发现问题就给修改建议,问题未解决不要通过。", ) executor = UserProxyAgent( name="executor", human_input_mode="NEVER", code_execution_config={ "work_dir": "analysis", "use_docker": False, }, is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""), ) group_chat = GroupChat( agents=[pm, coder, reviewer, executor], messages=[], max_round=12, ) manager = GroupChatManager( groupchat=group_chat, llm_config=llm_config, ) pm.initiate_chat( manager, message="分析sales.csv中各个区域的销售额趋势,找出异常下降的区域," "并生成一个可视化图表保存为trend.png。", )4.3 运行效果与调参心得
这个配置跑起来后,我观察到几个典型现象:
第一,reviewer真的会拦代码。有一次coder生成的代码里,读取CSV时没有处理空值,reviewer直接说"这里会有KeyError风险,请修改后再提交"。coder修改后再输出,reviewer确认通过,executor执行成功。这个"写代码-审查-修改-执行-再审查"的闭环,在单Agent模式下很难自动形成,因为模型不会自己主动否定自己。
第二,pm的控制力取决于system_message。如果pm的角色设定写得太弱,它基本上就是个传话筒,讨论跑偏也不管;写清楚"发现跑偏要拉回",它在关键节点会主动发言纠正方向。所以每个角色的system_message值得花时间打磨,这不是走形式,是整个系统行为和输出质量的主要决定因素之一。
第三,max_round要按任务复杂度设。我一开始设成20,结果任务已经完成了,几个Agent还在客套地说"感谢配合""合作愉快",白白消耗token。后来设成12,同时在coder和reviewer的system_message里都强调"输出最终结果时以TERMINATE结尾",整个群聊能干净利落地收尾。
第四,如果发现GroupChat的对话质量不稳定,优先检查管理器的模型选择。GroupChatManager默认也用llm_config来调度说话人,调度模型的能力直接影响对话质量。预算允许的话,给管理器配一个强模型,Agent们可以配稍弱一点的模型,这样性价比会好很多。
5. 生产环境踩坑:我总结的四个高频问题
5.1 版本升级带来的API不兼容
这个坑我前面提过,但值得再展开。AutoGen 0.4把from autogen import ConversableAgent这一套顶层API改成了from autogen_agentchat import ...的包结构,还引入了异步运行环境。如果你的项目已经基于0.2跑了一堆代码,千万别脑子一热升级0.4,迁移成本远比你想象得高。我的做法是开发新项目时单独建虚拟环境试用0.4,老项目继续用0.2锁版本。
锁定版本的正确姿势:
pip install "pyautogen==0.2.*"5.2 模型生成代码的执行安全
AutoGen的能力核心在于能自动执行代码,但这也带来安全风险。模型生成的代码未必安全——它可能包含删除文件的命令、访问外网的请求,或者在错误的工作目录里乱写文件。开发环境里我踩过一次:Agent在一个临时目录里生成了个递归删除脚本,幸好作用在指定工作目录下,没有造成更大影响,但也让我意识到不能把use_docker=False当成默认配置。
生产环境的执行安全,我建议至少做到三层:
- 用Docker隔离:
use_docker=True,每个Agent执行代码都在独立容器环境里,炸了不连累宿主机。 - 专用工作目录:给
work_dir指定一个空目录,并且在每次任务结束后清理。 - 配置超时和交互审核:
max_consecutive_auto_reply设一个合理上限,关键任务的执行结果必须经过人工确认再进入下一轮。
5.3 无限对话与token成本失控
多Agent对话最大的隐形成本是token消耗。每一次消息传递,模型都要重新阅读整个对话历史来生成回复,会话轮次越多,历史越长,单次调用的token成本急剧上升。我见过一个任务最多烧掉几十块钱的token,最后整个对话因为循环逻辑错误完全跑偏了,产出结果却没多少可用。
控制成本主要靠三个手段:
max_consecutive_auto_reply限制单个Agent的连续自回复次数,避免它自己跟自己较劲。is_termination_msg明确收尾信号,防止模型进入"不断优化"的循环。- 对话记录定期裁剪,对超长会话做截断或摘要,只保留关键上下文。
另外,建议对接模型服务时开启token用量统计,把每次调用的usage字段汇总保存。这样谁能知道每个Agent烧了多少token,优化方向也不会靠猜。
5.4 中文模型对角色指令的理解差异
如果你用的是国产模型,要特别注意一点:它们对多Agent system_message的遵循程度,跟GPT-4的差距可能超出预期。最典型的表现是,让Agent"以审查者的身份提意见",结果它把自己当成被审查者,开始自夸;或者让Agent"任务完成时输出TERMINATE",它输出的是"好的,任务已完成"。这类问题在主流的国产API上我都遇到过。
解决办法有两个方向:一是强化system_message,把期望的输出格式写得跟模板一样具体,比如"你的回复必须以'TERMINATE'作为最后一行,且不得包含其他解释";二是对话过程中加规则校验,在is_termination_msg之外,用代码检查返回内容是否满足约定格式,不满足就触发重试或终止。我倾向于两者都做,毕竟模型的输出稳定性和网络波动一样,属于不可控因素。
6. AutoGen和LangGraph、MetaGPT、CrewAI怎么选
6.1 四类框架的设计取向对比
我在项目调研阶段画过一张对比表,至今还在用:
| 框架 | 核心抽象 | 编排方式 | 上手难度 | 最适合的场景 |
|---|---|---|---|---|
| AutoGen | 可对话Agent | 对话驱动,动态轮转 | 中 | 多智能体自由协商、代码生成闭环 |
| LangGraph | 图节点与状态 | 图结构,显式流程 | 中高 | 固定流程、可预测的工程管线 |
| MetaGPT | 角色与SOP | 标准作业流程 | 中 | 软件公司全流程模拟 |
| CrewAI | 角色与任务 | 任务顺序/层级执行 | 低 | 快速原型、任务型Agent团队 |
AutoGen的核心优势是对话驱动,适合任务边界模糊、需要动态协商的场景;LangGraph的优势是显式的状态图,适合每一步都可预定的任务,对调试和分支控制更友好;MetaGPT偏重软件工程规范,内置了文档、设计、编码、测试的完整流程;CrewAI胜在简单直接,定义好角色和任务就能跑。
6.2 我的选型经验
选框架,先回答三个问题:我的流程是固定的还是需要Agent自己探索?我的Agent之间是协作还是上下级指派?我对实时可观测性的要求有多高?
我做自动化数据清洗时用了AutoGen,因为清洗规则在不同数据集上差别很大,需要Agent在对话中自己判断该做什么清洗动作,LangGraph那种预设流程图反而显得死板。但我做一个定时采集的Pipeline时选了LangGraph,因为流程就是固定的"抓取-解析-入库-告警",状态机表达比自由对话可控得多,出了问题也好定位在哪一步。
如果你想要的是"快速看到一个能跑的多Agent Demo",CrewAI上手确实快,写几个类就能跑起来,但深入之后会发现可定制性不如AutoGen。如果你做的是偏研发流程的助手,比如自动生成代码、自动评审、自动改bug那一套,AutoGen的代码执行闭环是几个框架里最顺手的。
注意:框架没有绝对的好坏,只有和场景匹配度的差异。我的建议是不要一开始就纠结"哪个最强",找几个标准任务(比如"生成代码并执行"、"多角色头脑风暴"、"固定流程信息抽取")分别跑一遍,看谁的调试成本和稳定性符合要求。
最后再分享一个我的使用习惯:AutoGen的代码示例看起来简单,但真正让它稳定的,是后面的约束项——迭代轮数、终止条件、生成参数、上下文清理。我会把会话历史和中间结果定时导出,同时对每轮Agent调用开启token统计,这样即使线上出了问题,也能快速定位是哪个环节失控。多Agent协作不是越自由越好,它需要边界,而边界就是你在配置里写下的那些硬约束。