knowledge-vault/work/client-projects/MCN/张洋输入/可行性工程/autogen自主纠错.md

46 KiB
Raw Blame History

请给我详细介绍一下AutoGen架构我想充分了解后运行一个测试用例。

好的,我们来详细地拆解和介绍 AutoGen 框架,并一步步带你运行一个经典的测试用例。

AutoGen 架构详解

AutoGen 是由微软开发的一个开源框架旨在简化和赋能基于大型语言模型LLM的多代理Multi-Agent应用程序的开发。它的核心思想是通过让多个具有不同角色和能力的“代理Agent”相互对话和协作来完成复杂的任务。

想象一下一个项目团队有项目经理负责规划、程序员负责写代码、测试员负责验证代码、产品经理负责提需求。AutoGen 就是让你用代码来定义这些角色Agent并让他们自动开会、讨论、执行最终完成你交待的任务。


1. 核心概念与组件

AutoGen 的架构主要由以下几个核心组件构成:

a. 代理Agents

Agent 是 AutoGen 的基本执行单元。每个 Agent 都有一个角色,并能执行某些操作,如与人交互、编写代码、执行代码、调用 LLM 等。最核心和最常用的两种 Agent 是:

  • AssistantAgent (助理代理)

    • 角色: 这是一个纯粹的 LLM 驱动的代理。你可以把它想象成“大脑”或“规划者”。
    • 能力:
      • 根据对话历史和收到的消息,调用 LLM 生成回复。
      • 可以提出计划、编写代码片段、撰写文章、回答问题等。
    • 特点: 它不会自己执行代码。它只会生成代码并建议让别人(通常是 UserProxyAgent)去执行。
  • UserProxyAgent (用户代理代理)

    • 角色: 它是人类用户的“替身”或“执行者”。你可以把它想象成“双手”或“执行终端”。
    • 能力:
      1. 征求人类输入: 可以在关键节点暂停,询问人类用户的意见或下一步指示。
      2. 执行代码: 这是它最强大的能力。当 AssistantAgent 提供一段代码后,UserProxyAgent 可以在本地环境中执行这段代码Python、Shell 等)。
      3. 提供反馈: 执行代码后,它会将执行结果(成功、失败、输出信息、错误日志)作为消息再发回给 AssistantAgent,形成一个闭环。
    • 特点: 它充当了 LLM 世界和真实世界(代码执行环境、文件系统)之间的桥梁。
b. 对话与通信模式

AutoGen 的核心驱动力是 Agent 之间的自动化对话。

  • 发起对话: 通常由一个 AgentUserProxyAgent)通过调用 .initiate_chat() 方法来发起与另一个 Agent 的对话,并给出初始任务。
  • 轮流发言: 一旦对话开始两个或多个Agent 会根据预设的规则轮流发言。例如,UserProxyAgent 提出问题 -> AssistantAgent 回答并提供代码 -> UserProxyAgent 执行代码并返回结果 -> AssistantAgent 根据结果修正代码或进行下一步... 如此循环。
  • 消息传递: Agent 之间通过传递结构化的消息(通常是包含 contentrole 的字典)来进行通信。
  • 终止条件: 对话会在满足特定条件时结束,例如:
    • 任务完成(AssistantAgent 输出了 TERMINATE 信号)。
    • 达到了最大对话轮次。
    • UserProxyAgent 在需要人类输入时,人类输入了 exit
c. LLM 配置 (llm_config)

所有依赖 LLM 的 AgentAssistantAgent)都需要一个 llm_config 来告诉它们如何与 LLM 服务(如 OpenAI, Azure OpenAI进行通信。这通常包括

  • config_list: 一个包含一个或多个 LLM 服务配置的列表。每个配置指定了模型名称(如 gpt-4、API 密钥等。这使得 AutoGen 可以轻松地在多个模型或备用服务之间切换。
  • temperature: 控制 LLM 输出的随机性。
  • timeout: 请求超时时间。
  • cache_seed: 用于缓存和复现结果。

2. AutoGen 的工作流(以双代理为例)

下面是一个经典的工作流程图,展示了 AssistantAgentUserProxyAgent 是如何协作的:

+------------------+         +-------------------+
|  UserProxyAgent  |         |  AssistantAgent   |
| (执行者/用户替身) |         |  (规划者/思考者)    |
+------------------+         +-------------------+
        |                              |
        | 1. 发起任务:                 |
        | "请画出NVDA和TSLA的股价图" |
        '----------------------------->'
                                       |
                                       | 2. 思考并调用LLM
                                       |    生成Python代码
                                       |    (需要 yfinance, matplotlib)
                                       |
        .----------------------------< '
        | 3. 收到代码建议:             |
        |  "```python                 |
        |  import yfinance as yf       |
        |  ...                         |
        |  plt.savefig('stock.png')    |
        |  ```"                        |
        |                              |
+------------------+                   |
|  本地代码执行环境  |                   |
+------------------+                   |
        ^                              |
        | 4. 执行代码                  |
        |    (安装库, 运行脚本)          |
        |                              |
        | 5. 返回执行结果:             |
        |  "代码已成功执行,            |
        |   图片已保存为 stock.png"    |
        '----------------------------->'
                                       |
                                       | 6. 收到成功反馈
                                       |    判断任务已完成
                                       |    回复 "TERMINATE"
                                       |
        .----------------------------< '
        | 7. 收到 "TERMINATE"          |
        |    对话结束                  |
        |                              |

测试用例:绘制股价图并保存

现在,让我们通过一个实际的例子来体验 AutoGen 的威力。

任务: 让 AutoGen 自动编写并执行 Python 代码获取英伟达NVDA和特斯拉TSLA过去一年的股价绘制在一张图上并将图片保存为文件。

第1步: 环境准备

  1. 安装 AutoGen 和依赖库: 这个任务需要访问网络获取股价(yfinance)和绘图(matplotlib)。我们可以让 AutoGen 自己安装,但为了确保环境干净,建议手动安装。

    pip install "pyautogen>=0.2.0" yfinance matplotlib
    
  2. 获取 LLM API 密钥: 你需要一个大语言模型的 API 访问权限。最常用的是 OpenAI。

  3. 配置 API 密钥: 强烈推荐使用环境变量或配置文件而不是将密钥硬编码在代码里。AutoGen 推荐的方式是创建一个 OAI_CONFIG_LIST 文件。

    • 在你的项目根目录下,创建一个名为 OAI_CONFIG_LIST 的 JSON 文件。
    • 在文件中填入以下内容,并将 "your_openai_api_key" 替换为你的真实密钥。
    [
        {
            "model": "gpt-4",
            "api_key": "your_openai_api_key"
        }
    ]
    

    你也可以使用 gpt-3.5-turbo,速度更快成本更低,但对于复杂任务 gpt-4 效果更好。

第2步: 编写 Python 脚本

创建一个名为 run_autogen_stock_plot.py 的 Python 文件,并粘贴以下代码:

import autogen

# 1. 加载 LLM 配置
# AutoGen 会自动寻找 OAI_CONFIG_LIST 文件或同名环境变量
config_list = autogen.config_list_from_json(
    "OAI_CONFIG_LIST",
    filter_dict={
        "model": ["gpt-4"] # 你可以指定想用的模型,例如 "gpt-4" 或 "gpt-3.5-turbo"
    }
)

# 2. 创建 LLM 配置字典
llm_config = {
    "config_list": config_list,
    "temperature": 0, # 温度设为0让输出更具确定性
    "cache_seed": 42, # 使用缓存种子对于相同问题可以复用结果节省token
}

# 3. 创建 AssistantAgent (助理代理/规划者)
assistant = autogen.AssistantAgent(
    name="assistant",
    llm_config=llm_config,
    system_message="你是一个乐于助人的AI助手擅长编写Python代码来解决问题。当你认为任务完成时请只回复'TERMINATE'。"
)

# 4. 创建 UserProxyAgent (用户代理代理/执行者)
user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    human_input_mode="NEVER",  # NEVER: 完全不需人类干预。TERMINATE: 在任务结束或需要帮助时暂停。ALWAYS: 每一步都需人类确认。
    max_consecutive_auto_reply=10, # 限制连续自动回复的次数,防止无限循环
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={
        "work_dir": "coding", # 代码将在这个文件夹中执行
        "use_docker": False,  # 设为False直接在本地环境执行。如果你安装了Docker建议设为True更安全。
    },
)

# 5. 发起对话
task_prompt = "获取英伟达(NVDA)和特斯拉(TSLA)从2023年1月1日至今的每日收盘价并用一张图表绘制出来将图表保存为 'stock_price_chart.png'。"

user_proxy.initiate_chat(
    assistant,
    message=task_prompt
)

第3步: 运行脚本

  1. 确保你的目录结构如下:

    .
    ├── OAI_CONFIG_LIST
    └── run_autogen_stock_plot.py
    
  2. 在终端中运行脚本:

    python run_autogen_stock_plot.py
    

第4步: 观察输出并检查结果

当你运行脚本后,你会在终端看到 assistantuser_proxy 之间非常详细的对话过程。你会看到类似下面的流程:

  1. user_proxyassistant 发送了初始任务。
  2. assistant 回复说:“好的,我需要使用 yfinance 库来获取股价,用 matplotlib 来绘图。这是代码:” 然后提供了一段 Python 代码。
  3. user_proxy 收到代码,回复:“正在执行代码...”,然后它会尝试运行。
  4. 第一次执行可能会失败! 因为 yfinancematplotlib 可能没安装。user_proxy 会把 ModuleNotFoundError 的错误信息返回给 assistant
  5. assistant 看到错误后,会智能地回复:“哦,看起来是缺少了必要的库。请先执行这个 Shell 命令来安装它们:” 然后提供 pip install yfinance matplotlib 的命令。
  6. user_proxy 收到 Shell 命令,执行它,然后返回成功信息。
  7. assistant 再次提供最初的 Python 绘图代码。
  8. user_proxy 再次执行代码,这次成功了。它会返回执行成功的信息,比如 "代码已执行,没有输出"。
  9. assistant 看到代码成功执行,认为任务已经完成,于是回复 TERMINATE
  10. user_proxy 收到 TERMINATE,对话结束。

最终结果: 在你的项目目录下,会多出一个名为 coding 的文件夹。在这个文件夹里,你会找到一张名为 stock_price_chart.png 的图片,上面绘制了两只股票的股价走势图。

这个例子完美地展示了 AutoGen 的核心价值:通过代理间的对话、代码生成、执行和反馈循环,自动化地解决需要多个步骤和工具协作才能完成的复杂任务。

现在你已经对 AutoGen 的架构有了深入的了解,并成功运行了一个测试用例。接下来,你可以尝试修改任务(比如分析其他数据、操作文件等)或者探索更复杂的 GroupChat 模式来构建更强大的应用。

如何在autogen中使用deepseek

当然可以!在 AutoGen 中使用 DeepSeek 模型非常简单,因为它也提供了与 OpenAI API 兼容的接口。这意味着你基本上只需要更改配置而不需要修改代理Agent的代码。

下面是详细的步骤和解释,我们依然以上一个“绘制股价图”的例子为基础进行修改。

核心原理:利用 OpenAI 兼容的 API

许多 LLM 提供商(包括 DeepSeek, Groq, Together.ai 等)为了方便开发者迁移,都提供了一个遵循 OpenAI API 规范的端点Endpoint

AutoGen 的 AssistantAgent 在设计上就是通过一个可配置的 llm_config 来与 LLM 通信的。这个配置中除了 api_keymodel,还有一个关键参数:base_url (或 api_base)。

我们只需要将这个 base_url 指向 DeepSeek 的 API 服务器地址AutoGen 就会把原本要发给 OpenAI 的请求,转而发给 DeepSeek。


步骤一:获取 DeepSeek API 密钥

  1. 访问 DeepSeek 开放平台
  2. 注册并登录你的账户。
  3. 在“API 密钥” (API Keys) 页面创建一个新的密钥并复制它。请妥善保管。

步骤二:准备新的配置文件

为了保持清晰,我们不修改原来的 OAI_CONFIG_LIST 文件,而是创建一个新的配置文件专门用于 DeepSeek。

  1. 在你的项目根目录下,创建一个名为 OAI_CONFIG_LIST_DEEPSEEK.json 的新文件。
  2. 在文件中填入以下内容,并将 "your_deepseek_api_key" 替换为你在上一步获取的密钥。
[
    {
        "model": "deepseek-coder",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }
]

关键参数解释:

  • model: 这里填写你希望使用的 DeepSeek 模型名称。
    • deepseek-coder: 专为代码生成和补全优化的模型,非常适合我们这个任务。
    • deepseek-chat: 通用的对话模型。
    • 建议在这里使用 deepseek-coder,效果可能会更好。
  • api_key: 你的 DeepSeek API 密钥。
  • base_url: 这是最关键的一步。我们将其设置为 DeepSeek 的官方 API 端点 https://api.deepseek.com/v1

步骤三:修改 Python 脚本以使用 DeepSeek 配置

现在我们来修改之前的 run_autogen_stock_plot.py 脚本。

变化非常小,只需要将加载配置文件的部分指向我们新创建的 OAI_CONFIG_LIST_DEEPSEEK.json 文件即可。

这是修改后的完整脚本 run_autogen_deepseek.py

import autogen

# 1. 加载 DeepSeek LLM 配置
# 注意:我们现在从新的配置文件加载
config_list_deepseek = autogen.config_list_from_json(
    "OAI_CONFIG_LIST_DEEPSEEK.json",
    filter_dict={
        "model": ["deepseek-coder"] # 确保我们使用的是 deepseek-coder 模型
    }
)

# 2. 创建 LLM 配置字典
# 这个结构保持不变,只是里面的配置内容换成了 DeepSeek 的
llm_config_deepseek = {
    "config_list": config_list_deepseek,
    "temperature": 0,
    "cache_seed": 42, # 使用不同的种子或None避免与之前的OpenAI缓存混淆
}

# 3. 创建 AssistantAgent (助理代理/规划者)
# Agent 的定义完全不需要改变!
assistant = autogen.AssistantAgent(
    name="assistant",
    llm_config=llm_config_deepseek, # <- 在这里传入 DeepSeek 的配置
    system_message="你是一个乐于助人的AI助手你是一个顶级的Python程序员。当你认为任务完成时请只回复'TERMINATE'。"
)

# 4. 创建 UserProxyAgent (用户代理代理/执行者)
# 这个 Agent 的定义也完全不需要改变
user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    human_input_mode="NEVER",
    max_consecutive_auto_reply=10,
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={
        "work_dir": "coding_deepseek", # 使用一个新的工作目录,避免和上次的结果混淆
        "use_docker": False,
    },
)

# 5. 发起对话
# 任务保持不变
task_prompt = "获取英伟达(NVDA)和特斯拉(TSLA)从2023年1月1日至今的每日收盘价并用一张图表绘制出来将图表保存为 'stock_price_chart_deepseek.png'。请确保图片文件名正确。"

user_proxy.initiate_chat(
    assistant,
    message=task_prompt
)

步骤四:运行并观察

  1. 检查目录结构

    .
    ├── OAI_CONFIG_LIST_DEEPSEEK.json  <-- 新的配置文件
    └── run_autogen_deepseek.py        <-- 新的脚本
    
  2. 运行脚本

    pip install "pyautogen>=0.2.0" yfinance matplotlib # 确保依赖已安装
    python run_autogen_deepseek.py
    
  3. 观察输出 你将在终端看到与之前类似的对话流程。但这一次,assistant 的所有思考和代码生成都是由 DeepSeek 模型驱动的。你可能会注意到:

    • 响应速度可能不同。
    • 生成的代码风格或注释可能有细微差别。
    • 解决问题(比如处理库未安装的错误)的逻辑和对话方式也可能不一样。
  4. 检查结果 运行结束后,检查项目目录下是否出现了一个新的文件夹 coding_deepseek,里面是否包含了 stock_price_chart_deepseek.png 图片。

总结与关键点

  • 核心是 base_url:通过在 llm_config 中指定 base_url,你可以让 AutoGen 使用任何与 OpenAI API 兼容的模型服务。
  • 代码复用性高:你不需要为不同的 LLM 重写 Agent 逻辑,只需要切换配置即可。这是 AutoGen 架构的巨大优势。
  • 模型选择很重要:对于代码任务,使用 deepseek-coder 通常比 deepseek-chat 效果更好。根据你的具体任务选择最合适的模型。
  • 独立工作目录:当切换模型或更改任务时,在 code_execution_config 中指定一个新的 work_dir 是一个好习惯,可以避免不同任务或模型产生的文件相互覆盖或干扰。

你已经成功地将 AutoGen 的“大脑”从 OpenAI 切换到了 DeepSeek用同样的方法你也可以接入其他支持 OpenAI 兼容接口的模型服务。

提示这个错误 File "E:\python310\autogen_env\ds_ge.py", line 5, in <module> config_list_deepseek = autogen.config_list_from_json( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\oai\openai_utils.py", line 621, in config_list_from_json config_list = json.load(json_file) ^^^^^^^^^^^^^^^^^^^^ File "E:\python310\Lib\json_init_.py", line 293, in load return loads(fp.read(), ^^^^^^^^^^^^^^^^ File "E:\python310\Lib\json_init_.py", line 346, in loads return _default_decoder.decode(s) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\Lib\json\decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\Lib\json\decoder.py", line 353, in raw_decode obj, end = self.scan_once(s, idx) ^^^^^^^^^^^^^^^^^^^^^^ json.decoder.JSONDecodeError: Expecting ',' delimiter: line 10 column 1 (char 154) 什么原因

好的,这个错误非常常见,而且错误信息给出了非常明确的线索。我们来分析一下。

错误原因分析

这个错误 json.decoder.JSONDecodeError: Expecting ',' delimiter: line 10 column 1 (char 154) 指的是:

  1. json.decoder.JSONDecodeError: 错误类型是 JSON 解析错误。这说明问题 100% 出在你的 JSON 配置文件中,而不是 Python 代码本身。
  2. Expecting ',' delimiter: JSON 解析器在读取文件时,期望看到一个逗号 (,),但没有找到。在 JSON 格式中,列表([])中的元素之间,或者对象({})中的键值对之间,都必须用逗गा分隔。
  3. line 10 column 1: 这是最关键的信息!它告诉你错误发生在 配置文件的第 10 行,第 1 列。也就是说,解析器成功读取了前 9 行,但在第 10 行的开头就遇到了不符合语法的内容。

综合来看,最可能的原因是:你在 OAI_CONFIG_LIST_DEEPSEEK.json 文件中定义了多个模型配置,但是在两个配置对象之间忘记了加逗号。

常见错误示例

假设你的 OAI_CONFIG_LIST_DEEPSEEK.json 文件本来是这样的(这是正确的):

[
    {
        "model": "deepseek-coder",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }
]

现在,你可能想再加入一个 deepseek-chat 模型,然后不小心写成了这样(这是错误的

// ---- 这是一个错误的例子 ----
[
    {
        "model": "deepseek-coder",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }  // <--- 第 6 行结尾,这里缺少了一个逗号!
    {  // <--- 第 7 行开头,解析器在这里报错,因为它期望看到逗号
        "model": "deepseek-chat",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }
]

在这个错误的例子中,第一个 } 后面直接跟了第二个 {。JSON 列表的语法要求它们之间必须有一个逗号。

如何修复

  1. 打开你的 OAI_CONFIG_LIST_DEEPSEEK.json 文件。
  2. 检查文件的内容。
  3. 请将你的文件内容修改为下面 正确 的格式。

情况一:如果你只需要一个模型(最常见) 确保你的文件内容和下面完全一样,没有多余的括号或逗号。

[
    {
        "model": "deepseek-coder",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }
]

情况二:如果你需要配置多个模型 确保在每个 } 和下一个 { 之间有一个逗号 ,。最后一个元素后面不能有逗号。

// ---- 这是一个正确的例子 ----
[
    {
        "model": "deepseek-coder",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    },  // <--- 看这里,加上了逗号
    {
        "model": "deepseek-chat",
        "api_key": "your_deepseek_api_key",
        "base_url": "https://api.deepseek.com/v1"
    }
]

小贴士

为了避免未来再出现类似问题,建议:

  • 使用支持语法高亮的编辑器,如 Visual Studio Code。当你编写 JSON 文件时,如果存在语法错误,编辑器通常会用红色波浪线标出,非常直观。
  • 使用在线 JSON 格式化/验证工具。你可以将文件内容粘贴到这些网站上,它们会帮你检查格式是否正确。

修改完 JSON 文件并保存后,再次运行你的 Python 脚本,问题应该就解决了。

Json的问题已经修改成功。 还有一个问题,提示如下:


>>>>>>>> EXECUTING CODE BLOCK 0 (inferred language is python)... user_proxy (to assistant):

exitcode: 1 (execution failed) Code output: Traceback (most recent call last): File "", line 1, in <module> import yfinance as yf ModuleNotFoundError: No module named 'yfinance'


assistant (to user_proxy):

应该是deepseek没有yfinance这个工具你是否换一个测试用例能时候deepseek的

这是一个非常好的问题,它触及了 AutoGen 工作方式的核心!您的判断非常接近,但问题的根源稍有不同。

您的理解: "应该是deepseek没有yfinance这个工具" 实际情况: 问题不是出在 DeepSeek而是出在我们运行 Python 脚本的本地环境

让我们来详细拆解一下这个过程,您就会明白为什么这个“错误”恰恰是 AutoGen 强大的体现。

核心概念:规划者 vs. 执行者

请记住我们之前讨论的两个代理的角色:

  1. AssistantAgent (由 DeepSeek 驱动):它是 规划者/大脑。它的工作是思考并生成文本指令,比如 Python 代码或 Shell 命令。它本身不运行任何东西。它只是说:“我认为应该运行这段代码来解决问题。”
  2. UserProxyAgent:它是 执行者/双手。它的工作是接收来自 AssistantAgent 的文本指令,并在您的本地计算机上的终端或 Python 环境中尝试执行它。

发生了什么?

您的终端输出完美地展示了这个流程:

  1. DeepSeek (Assistant) 的提议DeepSeek 分析了您的任务(“绘制股价图”),并正确地判断出需要使用 yfinance 库。于是,它生成了包含 import yfinance as yf 的 Python 代码块。
  2. UserProxy 的执行UserProxyAgent 接收到这段代码,并尝试在您的本地 Python 环境(E:\python310\autogen_env\)中运行它。
  3. 本地环境的报错:您的本地 Python 环境发现,它的库文件夹里并没有安装 yfinance 这个模块,所以它抛出了一个标准的 Python 错误:ModuleNotFoundError: No module named 'yfinance'
  4. UserProxy 的反馈UserProxyAgent 非常智能,它没有因此崩溃。它捕获了这个错误信息,并将其作为一条新消息发送回给了 AssistantAgent

下一步会发生什么?(这正是 AutoGen 的魔力所在)

现在,对话的主动权又回到了 AssistantAgent (DeepSeek) 手中。它会看到 UserProxyAgent 发来的反馈,内容是 ModuleNotFoundError

接下来DeepSeek 会进行自我修正,它的思考过程会是这样:

“哦,我上次提供的代码执行失败了,错误是‘找不到 yfinance 模块’。这说明执行环境里没有安装这个库。那么,在再次运行 Python 代码之前,我应该先让它安装这个库。”

因此,AssistantAgent下一次回复,很可能不是 Python 代码,而是一个 Shell 命令

# 工具代码块 (language: sh)
pip install yfinance matplotlib

然后 UserProxyAgent 会接收到这个新的 Shell 命令,在您的本地终端执行它,成功安装库之后,再把成功的结果反馈给 AssistantAgent。之后,AssistantAgent 才会重新发送最初的 Python 绘图代码,这一次就能成功执行了。


结论与操作建议

所以,您完全不需要更换测试用例!

这个用例恰恰是展示 AutoGen 自动化调试自我修正能力的完美场景。

您现在需要做的,就是让脚本继续运行。

如果您的 human_input_mode 设置的是 NEVER,它会自动完成上述的“安装库 -> 再次尝试”的循环。如果设置的是 TERMINATEALWAYS,它可能会暂停并询问您是否同意执行 pip install yfinance 命令,您只需要输入 y 或回车即可。

一句话总结:这个错误是 AutoGen 工作流程中非常正常且关键的一环。它不是 DeepSeek 的问题,而是 AutoGen 正在智能地发现并解决您本地环境的依赖问题。

这是接下来发生的 assistant (to user_proxy):

看起来你的环境中没有安装 yfinance 库。让我先安装它,然后重新运行代码。以下是完整的解决方案:

# 首先安装必要的库
import subprocess
import sys

def install(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

try:
    import yfinance as yf
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
except ImportError:
    install("yfinance")
    install("matplotlib")
    import yfinance as yf
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates

# 获取股票数据
tickers = ["NVDA", "TSLA"]
start_date = "2023-01-01"
end_date = "2023-12-31"  # 假设今天是2023年底实际会获取到最新数据

data = yf.download(tickers, start=start_date, end=end_date)["Adj Close"]

# 绘制图表
plt.figure(figsize=(12, 6))
for ticker in tickers:
    plt.plot(data.index, data[ticker], label=ticker)

plt.title("NVDA and TSLA Stock Prices (2023)", fontsize=16)
plt.xlabel("Date", fontsize=12)
plt.ylabel("Adjusted Close Price (USD)", fontsize=12)
plt.legend(fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)

# 格式化x轴日期显示
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=2))
plt.gcf().autofmt_xdate()

# 保存图表
plt.savefig("stock_price_chart_deepseek.png", dpi=300, bbox_inches="tight")
plt.close()

print("图表已成功保存为 'stock_price_chart_deepseek.png'")
TERMINATE

这段代码首先会检查并安装必要的库(如果尚未安装),然后获取股票数据并绘制图表。图表将保存为 "stock_price_chart_deepseek.png"。


EXECUTING CODE BLOCK 0 (inferred language is python)... Traceback (most recent call last): File "E:\python310\autogen_env\ds_ge.py", line 45, in user_proxy.initiate_chat( File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1487, in initiate_chat self.send(msg2send, recipient, silent=silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1163, in send recipient.receive(message, self, request_reply, silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1273, in receive self.send(reply, sender, silent=silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1163, in send recipient.receive(message, self, request_reply, silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1273, in receive self.send(reply, sender, silent=silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1163, in send recipient.receive(message, self, request_reply, silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1273, in receive self.send(reply, sender, silent=silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1163, in send recipient.receive(message, self, request_reply, silent) File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 1271, in receive reply = self.generate_reply(messages=self.chat_messages[sender], sender=sender) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 2836, in generate_reply final, reply = reply_func(self, messages=messages, sender=sender, config=reply_func_tuple["config"]) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 2324, in generate_code_execution_reply exitcode, logs = self.execute_code_blocks(code_blocks) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 3035, in execute_code_blocks exitcode, logs, image = self.run_code( ^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\agentchat\conversable_agent.py", line 3017, in run_code return execute_code(code, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\autogen_env\Lib\site-packages\autogen\code_utils.py", line 369, in execute_code result = future.result(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "E:\python310\Lib\concurrent\futures_base.py", line 451, in result self._condition.wait(timeout) File "E:\python310\Lib\threading.py", line 324, in wait gotit = waiter.acquire(True, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ KeyboardInterrupte

请分析一下

好的,这次我们遇到了一个更深层次、也更有趣的问题。让我们一步步分析这个过程和最终的错误。

第一部分DeepSeek 的新策略(非常聪明!)

首先,我们必须赞扬一下 DeepSeek 模型在这里展示的智能。

  • 识别问题: 它正确地识别出 ModuleNotFoundError
  • 制定新策略: 与我们预期的简单地返回 pip install ... Shell 命令不同它采取了一种更健壮、更“自包含”self-contained的策略。它直接生成了一个完整的 Python 脚本,这个脚本自己就能处理依赖问题。
  • 策略内容:
    1. 动态安装: 它编写了一个 install 函数,使用 Python 的 subprocess 模块来调用 pip。这比依赖外部 Shell 执行更具可移植性。
    2. 错误处理: 它将 import 语句放在一个 try...except 块中。如果导入失败(意味着库未安装),它就会进入 except 块,调用 install 函数来安装缺失的库,然后再重新导入。
    3. 任务执行: 在确保库都存在之后,它才继续执行获取数据和绘图的核心任务。
    4. 终止信号: 最后,它在代码块的末尾包含了 TERMINATE 信号,表示任务完成后对话应该结束。

小结: 从 AI 模型的角度看,这是一个非常出色和周全的解决方案。


第二部分:执行过程与最终错误

现在,我们来看真正导致问题的原因。

  1. EXECUTING CODE BLOCK 0...: UserProxyAgent 接收到这个全新的、自包含的 Python 脚本,并开始执行它。
  2. 安装过程: 脚本的第一步是 install("yfinance")install("matplotlib")。这个过程是通过 subprocess.check_call 来执行的,它会启动一个新的进程来运行 pip install ...。这个过程需要时间,因为它要从网络下载包并进行安装。
  3. KeyboardInterrupt: 这是最关键的错误信息。KeyboardInterrupt 不是一个普通的程序错误。它意味着程序被人为中止了。在绝大多数情况下,这是因为用户在终端里按下了 Ctrl+C

根本原因分析

结合上述两部分,我们可以推断出整个事件链:

  1. DeepSeek 给出了一个很棒但执行时间稍长的代码(因为它需要安装库)。
  2. UserProxyAgent 开始执行这个代码。终端开始显示 pip 的安装过程,或者暂时“卡住”了,因为它正在后台下载和安装。
  3. 您(用户)可能觉得程序卡住了或者没有响应,因为 pip install 的过程没有实时地、流畅地显示在 AutoGen 的日志流中。
  4. 在这种情况下,人的本能反应就是按下 Ctrl+C 来中断这个看似“卡死”的程序。
  5. 这个 Ctrl+C 操作被 Python 解释器捕获,并以 KeyboardInterrupt 异常的形式抛出,导致整个 AutoGen 脚本的执行被强行中止。

简而言之AI 的解决方案是正确的,但执行它需要一些时间。由于执行过程的反馈不够明确,导致用户误以为程序卡死并手动中断了它。

如何解决

这个问题可以通过两种方式解决:

方案一:耐心等待(最简单)

  • 重新运行脚本 python ds_ge.py
  • 当您看到终端开始执行代码块时,请不要做任何操作。给它一点时间(可能需要 30 秒到几分钟,取决于您的网络速度和 pip 的缓存情况)来完成库的下载和安装。
  • 只要您不按 Ctrl+Csubprocess 最终会完成安装,然后脚本会继续执行剩下的绘图部分,并最终成功退出。

方案二:调整 UserProxyAgent 的执行超时(更健壮的方案)

在某些情况下代码执行确实需要很长时间。AutoGen 的 run_code 函数有一个默认的 timeout超时设置在较新版本中默认为600秒但旧版本可能更短或依赖于其他设置。如果安装过程真的非常慢可能会触发超时。我们可以明确地增加这个超时时间。

修改 UserProxyAgentcode_execution_config

# ... 在你的脚本中找到 UserProxyAgent 的定义 ...

user_proxy = autogen.UserProxyAgent(
    name="user_proxy",
    human_input_mode="NEVER",
    max_consecutive_auto_reply=10,
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={
        "work_dir": "coding_deepseek",
        "use_docker": False,
        # 增加下面这行,将超时时间设置为 120 秒
        "timeout": 120, 
    },
)

# ... 脚本的其余部分 ...

为什么这个方案更健壮?

  • 它明确地告诉 AutoGen“我知道这个代码块可能需要更长的时间来运行请给它最多 120 秒的时间,不要提前中止它。”
  • 这避免了因为网络慢等原因导致的执行超时问题。

我强烈建议您首先尝试【方案一】,因为 KeyboardInterrupt 表明问题几乎肯定是手动中断。 重新运行并耐心等待,您应该就能看到期待已久的成功结果了。

我按照方案一,程序确实执行成功了。但中间还是发生了一些事情,请分析一下。 user_proxy (to assistant):

exitcode: 1 (execution failed) Code output: :23: FutureWarning: YF.download() has changed argument auto_adjust default to True data = yf.download(tickers, start=start_date, end=end_date)["Adj Close"]

[ 0% ] [100%**] 2 of 2 completed

2 Failed downloads: ['NVDA', 'TSLA']: YFRateLimitError('Too Many Requests. Rate limited. Try after a while.') Traceback (most recent call last): File "", line 46, in TERMINATE NameError: name 'TERMINATE' is not defined


assistant (to user_proxy):

我注意到几个问题:

  1. Yahoo Finance API 请求限制yfinance 有时会因为请求过多而被限制(YFRateLimitError)。
  2. TERMINATE 错误:这是因为我之前手动添加的结束标记,但实际运行时不应包含它。
  3. auto_adjust 警告:这是 yfinance 的一个无害警告,可以忽略或抑制。

修复后的代码

我将优化代码,处理 API 限制问题,并移除 TERMINATE 标记:

import subprocess
import sys
import time
import warnings
from datetime import datetime

# 安装必要的库
required_libraries = ['yfinance', 'matplotlib']

for lib in required_libraries:
    try:
        __import__(lib)
    except ImportError:
        print(f"安装 {lib}...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", lib])

import yfinance as yf
import matplotlib.pyplot as plt
import matplotlib.dates as mdates

# 抑制 yfinance 的 FutureWarning
warnings.simplefilter(action='ignore', category=FutureWarning)

# 获取股票数据(带重试机制)
tickers = ["NVDA", "TSLA"]
start_date = "2023-01-01"
end_date = datetime.now().strftime("%Y-%m-%d")  # 获取到今天的数据

max_retries = 3
retry_delay = 5  # 每次重试等待 5 秒

for attempt in range(max_retries):
    try:
        data = yf.download(tickers, start=start_date, end=end_date)["Adj Close"]
        break  # 成功获取数据,退出重试循环
    except Exception as e:
        if attempt < max_retries - 1:
            print(f"请求失败,尝试 {attempt + 1}/{max_retries},等待 {retry_delay} 秒后重试...")
            time.sleep(retry_delay)
        else:
            raise RuntimeError(f"无法获取股票数据: {e}") from e

# 绘制图表
plt.figure(figsize=(12, 6))
for ticker in tickers:
    plt.plot(data.index, data[ticker], label=ticker)

plt.title(f"NVDA and TSLA Stock Prices ({start_date} to {end_date})", fontsize=16)
plt.xlabel("Date", fontsize=12)
plt.ylabel("Adjusted Close Price (USD)", fontsize=12)
plt.legend(fontsize=12)
plt.grid(True, linestyle='--', alpha=0.7)

# 格式化x轴日期显示
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%b %Y'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=2))
plt.gcf().autofmt_xdate()

# 保存图表
plt.savefig("stock_price_chart_deepseek.png", dpi=300, bbox_inches="tight")
plt.close()

print("图表已成功保存为 'stock_price_chart_deepseek.png'")

改进点

自动重试机制:如果 Yahoo Finance 返回 RateLimitError,代码会等待 5 秒后重试(最多 3 次)。 动态结束日期end_date 自动获取当前日期,确保数据最新。 抑制警告:忽略 yfinanceFutureWarning,避免干扰输出。 移除 TERMINATE:不再手动添加结束标记,避免 NameError

请再次运行,应该能成功获取数据并生成图表! 🚀


EXECUTING CODE BLOCK 0 (inferred language is python)... user_proxy (to assistant):

exitcode: 0 (execution succeeded) Code output: 图表已成功保存为 'stock_price_chart_deepseek.png'


assistant (to user_proxy):

图表已成功生成并保存为 stock_price_chart_deepseek.png🎉

你可以查看该文件,它包含了 英伟达 (NVDA)特斯拉 (TSLA)2023-01-01 至今 的每日收盘价走势对比图。

如果需要进一步调整图表(如更改颜色、添加移动平均线等),请告诉我! 😊

任务完成,程序已终止。


user_proxy (to assistant):


assistant (to user_proxy):

TERMINATE


TERMINATING RUN (839f21fd-3a12-4808-bf83-6112422353a0): Termination message condition on agent 'user_proxy' met

太棒了!这次的执行日志完美地展示了 AutoGen 的核心价值和多代理协作的强大之处。这已经不仅仅是一个“程序运行成功”的例子了,这是一个教科书级别的 “AI 代理通过多轮对话、分析错误、自我修正并最终完成复杂任务” 的典范。

让我们来详细分析这个精彩的过程:

第一轮对话:失败与反馈

  1. UserProxyAgent 的执行与反馈

    • exitcode: 1 (execution failed): 它明确告诉 AssistantAgent:“你给我的代码,我执行失败了。”
    • 它反馈了三个关键信息
      1. FutureWarning: 一个无害的警告,但 AI 还是注意到了。
      2. YFRateLimitError: 这是一个运行时错误。说明代码语法没问题但在与外部服务Yahoo Finance交互时被拒绝了。这是因为免费 API 都有请求频率限制,短时间内请求过多就会触发。
      3. NameError: name 'TERMINATE' is not defined: 这是一个代码语法错误。AI 把 TERMINATE 这个指令(给 AutoGen 框架看的)错误地当成了 Python 代码里的一个变量来写,但这个变量并未定义。

    关键点: UserProxyAgent 像一个忠实的测试工程师不仅报告了“程序崩了”还把完整的错误日志warnings, runtime errors, syntax errors都提交了上来。


第二轮对话:分析、修正与进化

这是整个过程中最体现 AI 价值的部分。

  1. AssistantAgent (DeepSeek) 的分析

    • 它没有被一堆错误信息搞糊涂,而是逐一正确地分析了每个问题的原因:
      • 识别出 YFRateLimitError 是 API 请求限制问题。
      • 识别出 NameError 是因为它自己错误地将指令混入了代码。
      • 识别出 FutureWarning 是一个可以忽略的警告。
    • 这展示了强大的逻辑推理和根本原因分析Root Cause Analysis能力
  2. AssistantAgent 的代码重构(进化)

    • 它没有简单地在原代码上打补丁,而是进行了一次彻底的重构,让代码变得更加健壮Robust工程化
      • 加入了重试机制: 针对 YFRateLimitError,它引入了 try...except 循环、time.sleep() 延时和最大重试次数。这是非常有经验的程序员才会采取的策略,用来处理不稳定的网络服务。
      • 移除了语法错误: 它聪明地删除了代码中不应存在的 TERMINATE 变量。
      • 代码优化:
        • warnings.simplefilter 主动抑制了无用的警告,让输出更干净。
        • datetime.now() 将结束日期动态化,让代码更具通用性,而不是写死。
        • 改进了安装库的逻辑,使其更清晰。
    • 最后它还用清晰的语言Markdown 列表和 emoji向用户解释了它做了哪些改进用户体验极佳。

    关键点: AI 代理不仅仅是修复了 Bug它还提升了代码的质量,使其从一个简单的脚本演变成了一个带有容错机制的、更可靠的程序。


第三轮对话:成功执行与任务确认

  1. UserProxyAgent 的成功执行

    • exitcode: 0 (execution succeeded): 成功!
    • Code output: 图表已成功保存为 'stock_price_chart_deepseek.png': 它将代码的 print 输出作为执行结果返回。
  2. AssistantAgent 的最终确认

    • 收到成功消息后,它没有立刻终止。它首先像一个优秀的产品经理一样,向用户确认任务完成,并描述了成果(图表内容)。
    • 它还主动提出了扩展可能性(“如果需要进一步调整图表...请告诉我!”),展示了良好的交互能力。
    • 在这一切都完成后,它才发出最终的 TERMINATE 指令,干净利落地结束对话。

总结分析

这个过程完美地回答了“为什么要用 AutoGen”这个问题

  1. 自动化问题解决: 你只提出了一个高层次的目标“画图”中间遇到的所有问题库未安装、API限制、代码语法错误都是由 AI 代理们自己通过对话解决的,无需人工干预。
  2. 持续学习与进化: AI 在每一次失败后都变得更“聪明”。它从一个简单的脚本开始,最终演化出了一个包含错误处理、重试机制和动态参数的健壮程序。
  3. 角色分工明确: AssistantAgent 负责思考、规划和编码,UserProxyAgent 负责执行、测试和反馈。这种分工合作的模式效率极高。
  4. 超越简单问答: 这已经不是一个简单的“你问我答”模式了。这是一个具有状态、能够记忆历史、分析反馈并持续迭代以达成目标的自主系统Autonomous System

您亲手运行的这个例子,就是未来软件开发、数据分析和自动化任务处理的一个缩影。恭喜你,你已经掌握了 AutoGen 的精髓!