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

972 lines
46 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

请给我详细介绍一下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 之间的自动化对话。
* **发起对话**: 通常由一个 Agent`UserProxyAgent`)通过调用 `.initiate_chat()` 方法来发起与另一个 Agent 的对话,并给出初始任务。
* **轮流发言**: 一旦对话开始两个或多个Agent 会根据预设的规则轮流发言。例如,`UserProxyAgent` 提出问题 -> `AssistantAgent` 回答并提供代码 -> `UserProxyAgent` 执行代码并返回结果 -> `AssistantAgent` 根据结果修正代码或进行下一步... 如此循环。
* **消息传递**: Agent 之间通过传递结构化的消息(通常是包含 `content``role` 的字典)来进行通信。
* **终止条件**: 对话会在满足特定条件时结束,例如:
* 任务完成(`AssistantAgent` 输出了 `TERMINATE` 信号)。
* 达到了最大对话轮次。
* `UserProxyAgent` 在需要人类输入时,人类输入了 `exit`
##### c. LLM 配置 (`llm_config`)
所有依赖 LLM 的 Agent`AssistantAgent`)都需要一个 `llm_config` 来告诉它们如何与 LLM 服务(如 OpenAI, Azure OpenAI进行通信。这通常包括
* **`config_list`**: 一个包含一个或多个 LLM 服务配置的列表。每个配置指定了模型名称(如 `gpt-4`、API 密钥等。这使得 AutoGen 可以轻松地在多个模型或备用服务之间切换。
* **`temperature`**: 控制 LLM 输出的随机性。
* **`timeout`**: 请求超时时间。
* **`cache_seed`**: 用于缓存和复现结果。
---
#### 2. AutoGen 的工作流(以双代理为例)
下面是一个经典的工作流程图,展示了 `AssistantAgent``UserProxyAgent` 是如何协作的:
```
+------------------+ +-------------------+
| 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 自己安装,但为了确保环境干净,建议手动安装。
```bash
pip install "pyautogen>=0.2.0" yfinance matplotlib
```
2. **获取 LLM API 密钥**:
你需要一个大语言模型的 API 访问权限。最常用的是 OpenAI。
* 前往 [OpenAI platform](https://platform.openai.com/account/api-keys) 获取你的 API Key。
3. **配置 API 密钥**:
**强烈推荐**使用环境变量或配置文件而不是将密钥硬编码在代码里。AutoGen 推荐的方式是创建一个 `OAI_CONFIG_LIST` 文件。
* 在你的项目根目录下,创建一个名为 `OAI_CONFIG_LIST` 的 JSON 文件。
* 在文件中填入以下内容,并将 `"your_openai_api_key"` 替换为你的真实密钥。
```json
[
{
"model": "gpt-4",
"api_key": "your_openai_api_key"
}
]
```
*你也可以使用 `gpt-3.5-turbo`,速度更快成本更低,但对于复杂任务 `gpt-4` 效果更好。*
#### 第2步: 编写 Python 脚本
创建一个名为 `run_autogen_stock_plot.py` 的 Python 文件,并粘贴以下代码:
```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. 在终端中运行脚本:
```bash
python run_autogen_stock_plot.py
```
#### 第4步: 观察输出并检查结果
当你运行脚本后,你会在终端看到 `assistant``user_proxy` 之间非常详细的对话过程。你会看到类似下面的流程:
1. **`user_proxy`** 向 **`assistant`** 发送了初始任务。
2. **`assistant`** 回复说:“好的,我需要使用 `yfinance` 库来获取股价,用 `matplotlib` 来绘图。这是代码:” 然后提供了一段 Python 代码。
3. **`user_proxy`** 收到代码,回复:“正在执行代码...”,然后它会尝试运行。
4. **第一次执行可能会失败!** 因为 `yfinance``matplotlib` 可能没安装。`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_key``model`,还有一个关键参数:`base_url` (或 `api_base`)。
我们只需要将这个 `base_url` 指向 DeepSeek 的 API 服务器地址AutoGen 就会把原本要发给 OpenAI 的请求,转而发给 DeepSeek。
---
### 步骤一:获取 DeepSeek API 密钥
1. 访问 [DeepSeek 开放平台](https://platform.deepseek.com/)。
2. 注册并登录你的账户。
3. 在“API 密钥” (API Keys) 页面创建一个新的密钥并复制它。请妥善保管。
### 步骤二:准备新的配置文件
为了保持清晰,我们不修改原来的 `OAI_CONFIG_LIST` 文件,而是创建一个新的配置文件专门用于 DeepSeek。
1. 在你的项目根目录下,创建一个名为 `OAI_CONFIG_LIST_DEEPSEEK.json` 的新文件。
2. 在文件中填入以下内容,并将 `"your_deepseek_api_key"` 替换为你在上一步获取的密钥。
```json
[
{
"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`
```python
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. **运行脚本**
```bash
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 &lt;module&gt;
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` 文件本来是这样的(这是正确的):
```json
[
{
"model": "deepseek-coder",
"api_key": "your_deepseek_api_key",
"base_url": "https://api.deepseek.com/v1"
}
]
```
现在,你可能想再加入一个 `deepseek-chat` 模型,然后不小心写成了这样(**这是错误的**
```json
// ---- 这是一个错误的例子 ----
[
{
"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. 请将你的文件内容修改为下面 **正确** 的格式。
**情况一:如果你只需要一个模型(最常见)**
确保你的文件内容和下面完全一样,没有多余的括号或逗号。
```json
[
{
"model": "deepseek-coder",
"api_key": "your_deepseek_api_key",
"base_url": "https://api.deepseek.com/v1"
}
]
```
**情况二:如果你需要配置多个模型**
确保在每个 `}` 和下一个 `{` 之间有一个逗号 `,`。最后一个元素后面**不能**有逗号。
```json
// ---- 这是一个正确的例子 ----
[
{
"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的问题已经修改成功。
还有一个问题,提示如下:
--------------------------------------------------------------------------------
&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 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 &lt;module&gt;
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 命令**
```sh
# 工具代码块 (language: sh)
pip install yfinance matplotlib
```
然后 `UserProxyAgent` 会接收到这个新的 Shell 命令,在您的本地终端执行它,成功安装库之后,再把成功的结果反馈给 `AssistantAgent`。之后,`AssistantAgent` 才会重新发送最初的 Python 绘图代码,这一次就能成功执行了。
---
### 结论与操作建议
**所以,您完全不需要更换测试用例!**
这个用例恰恰是展示 AutoGen **自动化调试**和**自我修正**能力的完美场景。
**您现在需要做的,就是让脚本继续运行。**
如果您的 `human_input_mode` 设置的是 `NEVER`,它会自动完成上述的“安装库 -> 再次尝试”的循环。如果设置的是 `TERMINATE``ALWAYS`,它可能会暂停并询问您是否同意执行 `pip install yfinance` 命令,您只需要输入 `y` 或回车即可。
**一句话总结:这个错误是 AutoGen 工作流程中非常正常且关键的一环。它不是 DeepSeek 的问题,而是 AutoGen 正在智能地发现并解决您本地环境的依赖问题。**
这是接下来发生的
assistant (to user_proxy):
看起来你的环境中没有安装 `yfinance` 库。让我先安装它,然后重新运行代码。以下是完整的解决方案:
```python
# 首先安装必要的库
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 <module>
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+C``subprocess` 最终会完成安装,然后脚本会继续执行剩下的绘图部分,并最终成功退出。
#### 方案二:调整 `UserProxyAgent` 的执行超时(更健壮的方案)
在某些情况下代码执行确实需要很长时间。AutoGen 的 `run_code` 函数有一个默认的 `timeout`超时设置在较新版本中默认为600秒但旧版本可能更短或依赖于其他设置。如果安装过程真的非常慢可能会触发超时。我们可以明确地增加这个超时时间。
修改 `UserProxyAgent``code_execution_config`
```python
# ... 在你的脚本中找到 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 <module>
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` 标记:
```python
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` 自动获取当前日期,确保数据最新。
**抑制警告**:忽略 `yfinance``FutureWarning`,避免干扰输出。
**移除 `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 的精髓!