Skip to content

第九阶段:AutoGen 多 Agent 团队 ​

本阶段开始项目 3:AutoGen 多 Agent 数据分析。我们让 4 个 Agent 组成一个小团队,协作分析一份销售数据。

一、多 Agent 原理 ​

一个 Agent 既要想思路、又要写代码、还要自查,容易出错。多 Agent 的做法是分工 + 互相检查:

角色职责
analyst(分析师)拆解任务、提出分析步骤,看到结果后给业务结论
coder(程序员)按要求写 pandas 代码
executor(执行器)真的运行代码,把输出交回群聊
reviewer(审核员)检查结论是否有数字支撑,通过就输出 APPROVE

RoundRobinGroupChat 让它们轮流发言:analyst → coder → executor → reviewer → analyst …… 所有人共享同一份对话记录。

终止条件:TextMentionTermination("APPROVE") | MaxMessageTermination(20),意思是审核员说出 APPROVE 就结束,或者总消息数超过 20 条强制结束(| 表示“任一满足”),防止无限对话烧钱。

二、安装 ​

bash
pip install autogen-agentchat "autogen-ext[openai]" pandas

如果看到 googleapis-common-protos ... requires protobuf<8.0.0,>=6.33.5, but you have protobuf 5.29.6 这类依赖冲突警告,可以忽略:安装已经成功,我们的代码用不到那个包。

三、非 OpenAI 模型要写 model_info ​

AutoGen 认识 OpenAI 官方的模型名(如 gpt-4o),知道它们能不能调工具、能不能看图。deepseek-flash 它不认识,所以必须用 model_info 告诉它:不支持图像(vision False)、支持函数调用(function_calling True)、支持 JSON 输出等。不写会直接报错。

四、代码 ​

1. 生成示例数据 make_data.py ​

生成 6 个月 × 3 个产品共 18 行的销售汇总表,保存到 data/sales.csv。

python
import pandas as pd, numpy as np
np.random.seed(0)
df = pd.DataFrame({
    "月份": np.repeat([f"2026-{m:02d}" for m in range(1, 7)], 3),
    "产品": ["折叠键盘", "鼠标", "耳机"] * 6,
    "销量": np.random.randint(50, 300, 18),
    "单价": [199, 89, 299] * 6,
})
df["销售额"] = df["销量"] * df["单价"]
df.to_csv("data/sales.csv", index=False, encoding="utf-8-sig")
print(df)

2. 多 Agent 团队 09_autogen_team.py ​

python
import os, asyncio
from dotenv import load_dotenv
from autogen_agentchat.agents import AssistantAgent, CodeExecutorAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from autogen_ext.code_executors.local import LocalCommandLineCodeExecutor

load_dotenv()

# 模型客户端:非 OpenAI 官方的模型要写 model_info,告诉 AutoGen 这个模型具备哪些能力
model = OpenAIChatCompletionClient(
    model=os.getenv("LLM_MODEL"),
    base_url=os.getenv("LLM_BASE_URL"),
    api_key=os.getenv("LLM_API_KEY"),
    model_info={"vision": False, "function_calling": True, "json_output": True,
                "family": "unknown", "structured_output": False},
)

# 1. 分析师:拆解任务、提出分析思路
analyst = AssistantAgent("analyst", model_client=model, system_message=
    "你是数据分析师。根据任务提出清晰的分析步骤,交给 coder 写代码。看到执行结果后给出业务结论。")

# 2. 程序员:只负责写代码
coder = AssistantAgent("coder", model_client=model, system_message=
    "你是 Python 程序员。按分析师的要求写 pandas 代码,放在 ```python 代码块里。"
    "数据文件是 data/sales.csv(utf-8-sig 编码)。用 print 输出结果,不要画图。")

# 3. 执行器:真的去运行 coder 写的代码,结果交回群聊
executor = CodeExecutorAgent("executor",
    code_executor=LocalCommandLineCodeExecutor(work_dir="."))

# 4. 审核员:检查结论是否有数据支撑
reviewer = AssistantAgent("reviewer", model_client=model, system_message=
    "你是审核员。检查分析师的结论是否都有执行结果中的数字支撑。"
    "有问题就指出让大家修改;没问题就总结最终报告,并在最后单独写一行 APPROVE。")

# 终止条件:审核员说 APPROVE,或者总共超过 20 条消息(防止无限对话烧钱)
stop = TextMentionTermination("APPROVE") | MaxMessageTermination(20)

# 轮流发言:analyst → coder → executor → reviewer → analyst ...
team = RoundRobinGroupChat([analyst, coder, executor, reviewer], termination_condition=stop)

async def main():
    await Console(team.run_stream(task = """分析 data/sales.csv(utf-8-sig 编码)。
表结构:列为 月份(字符串, 如 2026-01)、产品、销量、单价、销售额;共 18 行,是“月份×产品”的汇总表,不是订单明细。
问题:1)哪个产品总销售额最高;2)各产品的月度销量趋势;3)给出一条经营建议。"""))

asyncio.run(main())

运行:

bash
python make_data.py
python 09_autogen_team.py

五、第一次运行:互相纠错 ​

第一次的 task 里没写表结构。分析师看不到数据,只能盲猜有个 date 列;程序员照着写代码,执行器一跑就报:

KeyError: 'date'

审核员对照输出里真实的列名 ['月份','产品','销量','单价','销售额'] 指出问题,判不通过。这正好演示了多 Agent 的价值:一个人犯错,另一个人拦住。

根本原因是分析师在盲猜表结构。两种修法:

  1. 把表结构写进 task(最简单,本节采用,见上面代码里的 task)。
  2. 先探查再分析:规定第一轮只看 df.head() 和列名,之后再分析。

六、第二次运行:APPROVE ​

写明表结构后,团队很快拿到正确数字,审核员输出 APPROVE。结论:

  • 耳机总销售额最高:338,767(占 55.69%);折叠键盘 191,637;鼠标 77,875。
  • 趋势:鼠标销量上升,耳机和折叠键盘首末对比下滑。

七、指标打架:审核员的局限 ​

耳机的首末变化率是 -35.33%,平均月环比却是 +27.22%,方向相反。原因是数据波动大时,个别月份的暴涨会把环比平均值拉高。

审核员却通过了。因为审核员只查提示词让它查的东西(“结论有没有数字支撑”),没要求它检查指标口径是否一致。真实业务里应该把“指标冲突时要说明使用哪个口径”写进审核员的提示词。

八、安全提醒:代码执行器 ​

LocalCommandLineCodeExecutor 会直接在你的电脑上执行 LLM 写的代码。学习时没问题,但生产环境必须换成 Docker 沙箱(如 DockerCommandLineCodeExecutor),否则模型写出删文件之类的代码就会真的执行。

下一阶段预告 ​

第十阶段:给团队加上画图 + 生成报告文件,让分析结果变成图表和可交付的报告。

👉 下一阶段:第十阶段:报告生成、SelectorGroupChat 与审核死循环