章节引导:单 Agent 的能力终究有限。许多复杂任务,如软件开发、科学研究、数据分析报告撰写,往往需要不同角色、不同能力的“专家”协同完成。本章我们将正式进入激动人心的多 Agent 系统的世界,首先学习由微软研究院推出的 AutoGen 框架。AutoGen 以其灵活的、基于对话的 Agent 协作机制和强大的代码执行能力而备受关注。与 LangGraph 精确控制单 Agent 流程不同,AutoGen 更侧重于模拟多个自主 Agent 如何通过自然语言对话进行交流、分工与协作。我们将深入其核心架构,重点掌握如何安全地让 Agent 执行代码(这是 AutoGen 的一大亮点),如何定制 Agent 行为与对话模式,并通过构建一个代码审查与自动修复系统的实例,亲身体验多 Agent 协作完成复杂任务的威力。
重要提示:相关库与版本
AutoGen 是一个独立的库 (pip install pyautogen
)。本章示例基于 AutoGen 的较新版本,请确保你的环境已安装。同时,与 LLM 的交互通常需要安装相应的 SDK(如openai
)并配置 API Key。
9.1 AutoGen 架构:对话驱动的协作
AutoGen 的核心设计理念是对话驱动。Agent 之间的协作主要通过相互发送和接收消息来实现。理解其架构的关键在于掌握核心 Agent 类型和它们之间的通信及协作机制。
核心 Agent 类型详解
AutoGen 提供了一些内置的 Agent 类,它们都继承自 ConversableAgent
这个定义了基本对话能力的基类:
ConversableAgent
: 所有能参与对话的 Agent 的基类。定义了核心交互方法,如send()
,receive()
,generate_reply()
。可继承以创建自定义 Agent。AssistantAgent
: 最常用的类型之一,代表一个标准的、基于 LLM 的对话 Agent。接收消息历史,调用配置好的 LLM 生成回复。通过设置system_message
来赋予其不同角色和能力。UserProxyAgent
: (极其关键) 代表用户或用户的代理人。核心特点:- 能发起对话: 通常是任务的发起者。
- 能请求人类输入: 可配置在需要时暂停并请求用户指令 (
human_input_mode
)。 - 能执行代码/函数: 其最强大的能力,可以执行 Python 代码块(在沙箱中)或调用预注册的函数。
通信机制
Agent 通过发送和接收消息进行通信(通常是包含 content
和 role
的字典)。
send(message, recipient)
: 发送消息。receive(message, sender)
: 接收消息。generate_reply(messages, sender)
: 生成回复的核心逻辑。
多 Agent 交互核心
AutoGen 支持多种交互模式:
- 两 Agent 对话:
UserProxyAgent
与AssistantAgent
轮流对话,直至满足终止条件。# user_proxy.initiate_chat(assistant, message="Initial task")
GroupChat
与GroupChatManager
: (核心机制) 实现多个 Agent (>=3) 协作的关键。GroupChat
: 定义一个包含多个 Agent 的“聊天室”,维护共享消息历史。GroupChatManager
: 特殊的ConversableAgent
,负责管理GroupChat
中的对话流程,根据speaker_selection_method
选择下一个发言者。"round_robin"
: 轮流。"auto"
: (默认) LLM 决定谁最适合发言(需要 Manager 配置 LLM)。"manual"
: 人类用户选择。- 自定义函数。
- 通常由
UserProxyAgent
向GroupChatManager
发起群聊。
# user_proxy = UserProxyAgent(...) # agent1 = AssistantAgent(name="Coder", ...) # agent2 = AssistantAgent(name="Tester", ...) # groupchat = GroupChat(agents=[user_proxy, agent1, agent2], ...) # manager = GroupChatManager(groupchat=groupchat, llm_config=...) # user_proxy.initiate_chat(manager, message="Task for the group")
9.2 安全的代码执行:UserProxyAgent
与沙箱
AutoGen 的一大特色是 UserProxyAgent
能够执行代码,这赋予了 Agent 强大的自动化能力。
为何需要 Agent 执行代码? 自动化任务、数据分析、工具使用、软件开发等。
UserProxyAgent
的代码执行能力配置:
通过 code_execution_config
参数启用和控制:
from autogen import UserProxyAgent
user_proxy_with_code = UserProxyAgent(
name="Executor",
human_input_mode="NEVER",
code_execution_config={
"work_dir": "coding_sandbox", # 执行目录
"use_docker": True, # <---- 强烈推荐使用 Docker!
# "use_docker": "python:3.11-slim", # 可指定镜像
# "timeout": 120, # 超时时间 (秒)
},
# ... 其他参数 ...
)
核心风险:安全!
警告: 直接在本地执行由 LLM 生成或来自不可信 Agent 的代码是极其危险的行为! 潜在风险包括文件删除、数据泄露、恶意软件安装等。绝不能直接信任!
深度实践:代码执行沙箱
必须使用沙箱 (Sandbox) 环境隔离代码执行:
- Docker 沙箱 (强烈推荐):
- 配置:
code_execution_config={"use_docker": True, ...}
。 - 原理: AutoGen 在隔离的 Docker 容器内执行代码,保护宿主系统。
- 依赖: 可通过自定义镜像或
work_dir
下的requirements.txt
管理。 - 要求: 本地需安装并运行 Docker。
- 配置:
- E2B (e2b.dev) 云沙箱 (推荐):
- 配置: 需安装
e2b
包并配置 API Key(详见 AutoGen/E2B 文档)。 - 原理: 在安全的云端沙箱执行代码。
- 优点: 无需本地 Docker,方便快捷。
- 缺点: 依赖第三方服务,可能有费用。
- 配置: 需安装
- 本地执行 (
use_docker=False
, 不推荐):- 风险: 代码直接在本地执行,
work_dir
隔离有限,极不安全。请勿在生产或处理不可信代码时使用。
- 风险: 代码直接在本地执行,
安全最佳实践:
- 强制使用沙箱: 始终优先 Docker 或 E2B。
- 代码审查/限制: 对关键代码进行审查,或限制可用库/函数。
- 管理沙箱依赖: 确保沙箱环境包含所需库。
- 资源限制: 配置 Docker/E2B 限制 CPU、内存、网络。
- 用户确认: 对高风险操作启用
human_input_mode
进行确认。
安全永远是第一位的!
9.3 定制 Agent 与对话模式
可以通过多种方式定制 AutoGen Agent 的行为和对话流:
1. 修改 Prompt/System Message:
最简单的方式,通过 system_message
赋予 AssistantAgent
不同的角色、指令和个性。
coder = AssistantAgent(
name="PythonCoder",
system_message="You are a senior Python programmer...",
llm_config=llm_config
)
reviewer = AssistantAgent(
name="CodeReviewer",
system_message="You are a meticulous code reviewer...",
llm_config=llm_config
)
2. 继承与重写方法:
通过继承 ConversableAgent
并重写 generate_reply
等方法,实现深度定制逻辑(如加入特定知识、记忆、状态管理)。
3. 注册自定义函数 (UserProxyAgent
):
让 UserProxyAgent
调用预定义的、安全的 Python 函数,而不是执行任意代码。
import autogen
from autogen import UserProxyAgent, AssistantAgent
from dotenv import load_dotenv
import os
load_dotenv()
# 配置 LLM (假设已完成)
config_list = autogen.config_list_from_json(...)
llm_config = {"config_list": config_list, ...}
# 定义安全函数
def get_stock_price(symbol: str) -> float:
"""Gets the (simulated) stock price for a given symbol."""
print(f"--- Called function get_stock_price for {symbol} ---")
if isinstance(symbol, str) and symbol.upper() == "MSFT": return 300.0
if isinstance(symbol, str) and symbol.upper() == "GOOG": return 150.0
return 0.0
# 创建 UserProxyAgent 并注册函数
user_proxy_func = UserProxyAgent(
name="FunctionExecutor",
human_input_mode="NEVER",
# code_execution_config=False, # 显式禁用代码执行
)
user_proxy_func.register_function(
function_map={
"get_stock_price_tool": get_stock_price # 映射工具名到函数
}
)
# 创建 Assistant Agent 并告知它可以使用的工具
assistant_func_caller = AssistantAgent(
name="AssistantFuncCaller",
system_message="You can use tools. Use the get_stock_price_tool for stock prices.",
llm_config={
"config_list": config_list,
"tools": [ # **关键:向 LLM 描述可用工具**
{
"type": "function",
"function": {
"name": "get_stock_price_tool", # 必须与 register_function 中的键匹配
"description": "Gets the current stock price for a given symbol.",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "Stock ticker (e.g., MSFT)"}
},
"required": ["symbol"],
},
},
}
],
}
)
# 发起对话 (示例)
# user_proxy_func.initiate_chat(
# assistant_func_caller,
# message="What's the stock price for MSFT?"
# )
触发机制说明: AssistantAgent
要调用注册的函数,其 llm_config
必须包含 tools
描述。当 LLM 决定使用该工具时,它会生成特定的工具调用请求。UserProxyAgent
收到此请求后,会自动查找 function_map
并执行对应的 Python 函数,将结果返回。
设计复杂对话模式:
- 利用
GroupChatManager
的speaker_selection_method
控制发言顺序。 - 通过精心设计的 Agent
system_message
或内部逻辑引导对话流向。 - 探索 AutoGen 的高级功能或社区扩展以实现更复杂的交互模式。
9.4 动手实验:构建代码审查与自动修复的多 Agent 系统
目标:实践定义不同角色的 Agent,利用 AutoGen 的对话机制进行协作,并安全地执行代码(如运行测试、应用修复)。
场景: 用户提出函数需求 -> Coder 编写 -> Reviewer 审查 -> (循环修改) -> UserProxy 执行测试 -> 完成。
角色定义:
user_proxy
(UserProxyAgent
): 发起任务,批准,执行测试代码 (沙箱)。coder
(AssistantAgent
): 编写/修改代码。reviewer
(AssistantAgent
): 审查代码。
协作流程设计 (简化版):
- UserProxy -> Coder: “编写函数 X”
- Coder -> Reviewer: "
python ...
" - Reviewer -> Coder: “修改意见…” 或 “Approved”
- (循环 2-3)
- Coder -> UserProxy: “
python ...
(Approved)” - UserProxy: (提取代码块) -> 执行测试 -> GroupChat: “测试结果…”
代码实现 (关键部分):
import os
import autogen
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager
from dotenv import load_dotenv
load_dotenv()
# 配置 LLM (同 9.3 示例)
config_list = autogen.config_list_from_json("OAI_CONFIG_LIST", filter_dict={"model": ["gpt-4o"]})
llm_config = {"config_list": config_list, "cache_seed": 42}
# --- Agent 定义 ---
user_proxy = UserProxyAgent(
name="UserProxy",
system_message="Represents the user. Initiate the task. Execute Python code blocks for testing when received after approval. Report test results.",
human_input_mode="TERMINATE", # Allow human intervention on termination/error
max_consecutive_auto_reply=8, # Limit consecutive replies for safety
# Use 'ALWAYS' to require human approval before code execution for extra safety
# human_input_mode="ALWAYS",
code_execution_config={
"work_dir": "code_review_project",
"use_docker": True, # CRITICAL: Enforce Docker sandbox
},
# Automatically execute code blocks received
# This relies on the default behavior. More robust: check sender/context.
)
coder = AssistantAgent(
name="Coder",
system_message='''Python programmer. Write Python code based on requests.
Receive feedback from CodeReviewer and revise.
Enclose final code in ```python ... ```.
Reply TERMINATE only when code is approved and you are done.''',
llm_config=llm_config,
)
reviewer = AssistantAgent(
name="CodeReviewer",
system_message='''Code reviewer. Review Python code from Coder for correctness, clarity, bugs.
Provide specific feedback or reply "Approved" if it looks good.
Do not write code yourself.''',
llm_config=llm_config,
)
# --- GroupChat 设置 ---
agents = [user_proxy, coder, reviewer]
groupchat = GroupChat(
agents=agents,
messages=[],
max_round=12 # Limit conversation length
)
# GroupChatManager (使用 LLM 自动选择)
manager_llm_config = {"config_list": config_list, "cache_seed": 43}
manager = GroupChatManager(
groupchat=groupchat,
llm_config=manager_llm_config,
# Guiding the manager
system_message="Coordinate coding and reviewing. Ensure feedback is addressed. The UserProxy executes approved code for testing."
)
# --- 发起聊天 ---
initial_task = """
1. Coder: Write a Python function `calculate_rectangle_area(length: float, width: float) -> float` that calculates the area of a rectangle. Include a docstring.
2. Reviewer: Review the code.
3. UserProxy: After approval, test the function with length=5, width=10 and report the execution result.
"""
print("\n--- Starting Code Review Group Chat ---")
try:
user_proxy.initiate_chat(
manager,
message=initial_task,
)
except Exception as e:
print(f"An error occurred during the chat: {e}")
print("\n--- Code Review Group Chat Finished ---")
逐步代码讲解与分析:
- 定义了具有明确角色的三个 Agent。
- 为
UserProxyAgent
配置了 Docker 沙箱进行代码执行。 - 设置了
GroupChat
和GroupChatManager
。 UserProxy
发起了包含编码和测试指令的任务。- 观察对话流: Agent 间通过消息传递代码和评审意见。
- 代码执行触发 (说明):
- 当对话流进行到
UserProxy
接收到经Reviewer
“Approved” 的代码块时,由于code_execution_config
已启用,UserProxyAgent
会自动查找并提取最近消息中的 ` ```python … ````代码块。 - 它会将代码写入沙箱(Docker 容器内)的文件中。
- 然后,它在沙箱内执行该文件。
- 捕获执行的标准输出/错误。
- 将执行结果(成功输出或错误信息)作为新消息发送回群聊。
- (注意:更复杂的测试场景可能需要更精细的逻辑来生成和执行测试用例。)
- 当对话流进行到
9.5 AutoGen 落地应用与挑战
典型应用场景: 自动化编码、数据分析、研究助理、多智能体仿真、创意写作等。
面临的挑战:
- 对话可控性差: 可能陷入无效循环、偏离目标。
- 结果稳定性: 多次运行结果可能不同。
- 成本控制: 多轮对话可能导致大量 LLM 调用。
- 复杂状态管理: 跟踪共享状态困难。
- 调试难度: 追踪多 Agent 对话流复杂。
9.6 章节总结与框架对比
本章我们深入了 AutoGen 框架,掌握了其多 Agent 系统的核心方法。
-
核心优势总结: AutoGen 的核心竞争力在于其灵活的、基于对话的协作机制,以及内置的、强调安全的代码执行能力。
-
关键技术回顾: 学习了核心 Agent 类型、GroupChat/Manager 机制、安全代码执行配置与实践。
-
对比:
- 与 LangGraph (Ch8) 对比:
- LangGraph: 强于单 Agent 复杂流程的精确控制、显式状态管理。
- AutoGen: 强于多 Agent 间基于对话的灵活协作和代码执行。
- 与 CrewAI (Ch10 预告) 对比:
- AutoGen: 对话更自由、动态。
- CrewAI: 提供更结构化、面向流程的协作框架(角色、任务、流程)。
- 与 LangGraph (Ch8) 对比:
-
引出思考: AutoGen 的自由对话带来了灵活性,但也牺牲了部分可控性。对于需要明确角色分工和结构化流程的多 Agent 任务,下一章的 CrewAI 提供了另一种解决方案。
内容同步在我的公众号:智语Bot