请给我详细介绍一下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 <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` 文件本来是这样的(这是正确的): ```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的问题已经修改成功。 还有一个问题,提示如下: -------------------------------------------------------------------------------- >>>>>>>> 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 命令**: ```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 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 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 的精髓!