├── .gitignore ├── LICENSE ├── README.md ├── chapter1 ├── README.md ├── demo-chapter1.ipynb ├── demo-prompt-engineering.ipynb ├── main_simple.py └── main_stream.py ├── chapter2 ├── README.md ├── custom-loader │ ├── aliyun_doc_loader.py │ ├── diff_docs.py │ └── main.py ├── data │ └── doc_urls.xlsx ├── demo-chapter2.ipynb ├── load-from-excel │ ├── diff_docs.py │ └── main.py ├── long-docs │ ├── aliyun_doc_loader.py │ ├── diff_docs.py │ ├── doc_spliter.py │ └── main.py ├── mvp │ └── main.py ├── requirements.txt └── utils │ └── ds_util.py ├── chapter3 ├── Agent结构.png ├── README.md ├── demo-chapter3.ipynb ├── requirements.txt └── search-with-tool.py ├── chapter4 ├── README.md ├── demo-chapter4.ipynb ├── more_tools │ ├── aliyun_resources.py │ └── search-with-tools.py ├── refactor_with_langchain │ ├── answer-with-tools.py │ └── tools │ │ ├── __init__.py │ │ ├── aliyun_resource_tool.py │ │ ├── auto_coding_tool.py │ │ └── service_search_tool.py └── requirements.txt ├── cloudapi ├── README.md ├── aliyun_resource_agent.py └── demo-cloudapi.ipynb └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | chapter4/more_tools/__pycache__/aliyun_resources.cpython-39.pyc 3 | chapter2/utils/__pycache__/ds_util.cpython-39.pyc 4 | chapter4/refactor_with_langchain/tools/__pycache__/service_search_tool.cpython-39.pyc 5 | chapter4/refactor_with_langchain/tools/__pycache__/auto_coding_tool.cpython-39.pyc 6 | chapter4/refactor_with_langchain/tools/__pycache__/aliyun_resource_tool.cpython-39.pyc 7 | chapter4/refactor_with_langchain/tools/__pycache__/__init__.cpython-39.pyc 8 | chapter2/load-from-excel/__pycache__/diff_docs.cpython-39.pyc 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 通义千问API入门教程 2 | 本教程将带你从零开始,快速了解如何通过 API 使用大模型,并尝试使用大模型 API 开发一些简单的应用应用到工作中,提升效率。 3 | 4 | ## 学习之前 5 | - 本教程假设你已经初步了解并使用过 python、git,因此不会涉及如何安装 [python](https://www.python.org/downloads/)、pip、[git](https://git-scm.com/) 等基础工具。 6 | - 本教程侧重于如何将大模型 API 应用到工作中,因此并不不会详细介绍大模型以及机器学习的基础概念。 7 | 8 | ## 教程目录 9 | | 章节 | Demos | 目标 | 10 | |:-----------------------------------|:------------------------|:---------------------------------------------------------| 11 | | [第 1 章 用4行代码与大模型对话](chapter1/README.md) |[Demo 1](chapter1/demo-chapter1.ipynb) | 了解如何通过 API 使用通义千问 | 12 | | [第 2 章 找出两篇文章的不同](chapter2/README.md) |[Demo 2](chapter2/demo-chapter2.ipynb) | 以对比阿里云中国站和国际站文档这个场景为例,让大模型对文档做对比和总结 | 13 | | [第 3 章 让大模型使用各种工具](chapter3/README.md) |[Demo 3](chapter3/demo-chapter3.ipynb) | 了解如何让大模型使用外部工具,以及背后的工作原理。 | 14 | | [第 4 章 让大模型写代码和跑代码](chapter4/README.md) |[Demo 4](chapter4/demo-chapter4.ipynb) | 了解如何添加更多工具,以及让大模型自己写代码来执行工作。 | 15 | 16 | ## 代码安装 17 | 18 | ```bash 19 | # 安装依赖 20 | git clone https://github.com/AlibabaCloudDocs/llm_learning.git 21 | cd llm_learning 22 | pip install -r requirements.txt 23 | ``` 24 | 25 | ## License 26 | 本项目使用 [Apache License (Version 2.0)](https://github.com/AlibabaCloudDocs/llm_learning/blob/master/LICENSE). -------------------------------------------------------------------------------- /chapter1/README.md: -------------------------------------------------------------------------------- 1 | # 第 1 章 用4行代码与大模型对话 2 | 以前经常使用搜索引擎来解决问题的你,现在大概率已经在工作中频繁使用大模型了。 但是只在网页聊天框中和大模型对话,终究有一些局限性:比如你在本地有一个超大的用户反馈表格、或者是有一些仅内部可访问的网页,想要借助大模型做一系列处理,就不太好做到了。 幸运的是,现在很多大模型服务提供商,都提供了 API 接口,可以让你方便地实现各种原本在网页聊天框中不方便、或无法实现的功能。 3 | 4 | 本章将通过一个简单的例子,让你快速进入到大模型应用开发的世界。 5 | 6 | ## 1. 准备工作 7 | 8 | ### 1.1. 安装 9 | 10 | 下载文档代码及安装依赖项 11 | ```bash 12 | git clone https://github.com/AlibabaCloudDocs/llm_learning.git 13 | cd llm_learning 14 | pip install -r requirements.txt 15 | ``` 16 | 17 | ### 1.2. 账号准备 18 | 19 | 首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey) 20 | 21 | #### MacOS or Linux 22 | 您可以使用以下命令行导入环境变量 23 | ```bash 24 | export DASHSCOPE_API_KEY="sk-****" 25 | ``` 26 | 27 | #### Windows 28 | 可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量 29 | ```bat 30 | set DASHSCOPE_API_KEY=sk-**** 31 | ``` 32 | 或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 33 | ```powershell 34 | $Env:DASHSCOPE_API_KEY = "sk-****" 35 | ``` 36 | 37 | #### Jupyter Notebook 38 | 您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。 39 | 40 | 41 | ## 2. 文本生成实验 42 | 43 | 通过 langchain_community 库中封装的 Tongyi 工具,我们可以通过[灵积API](https://dashscope.console.aliyun.com/apiKey)访问通义千问,让通义千问回答问题。 44 | 45 | ### 2.1. 简单文本生成 46 | 47 | 首先,Tongyi 工具的接口默认是一次性输出全部生成的答案,用户需要等待数秒才能看到完整答案。下面,我们先来看看一次性生成全部答案的代码和执行效果。代码只有三行,如下: 48 | 49 | ```python 50 | from langchain_community.llms import Tongyi 51 | 52 | llm = Tongyi() 53 | print(llm.invoke('阿里云成立于什么时间')) 54 | ``` 55 | 输出内容 56 | ```bash 57 | 阿里云成立于2009年9月10日,是阿里巴巴集团旗下的云计算服务品牌,提供包括计算、存储、网络、安全、数据库、大数据、人工智能等全面的云计算服务。 58 | ``` 59 | ### 2.2. 流式文本生成 60 | 61 | 采用LangChain封装的接口来实现流式文本生成,我们需要在创建Tongyi对象时声明采用流式输出(streaming=True),然后用一个循环来接收生成的文本,直到生成的文本为空。 62 | >(注意:如果你遇到了 TypeError: Additional kwargs key output_tokens already exists in left dict and value has unsupported type 这个错误,请参考[github](https://github.com/langchain-ai/langchain/pull/16580)中的说明) 63 | ```python 64 | from langchain_community.llms import Tongyi 65 | llm = Tongyi(streaming=True) 66 | for chunk in llm.stream('为什么要使用阿里云'): 67 | print(chunk, end="", flush=True) 68 | ``` 69 | 输出内容 70 | ```bash 71 | 阿里云作为全球领先的云计算及人工智能科技公司,提供了丰富且强大的云计算、大数据、人工智能等服务,以下是一些选择阿里云的主要原因: 72 | 73 | 1. 技术实力:阿里云拥有强大的技术研发能力,不断推出创新产品和服务,满足不同行业的数字化转型需求。在数据库、安全、物联网、人工智能等领域都有深厚的技术积累。 74 | 75 | 2. 稳定性:阿里云以其高可用性和稳定性著称,通过多地域、多可用区的架构设计,能够提供99.99%的服务可用性,保障业务连续性。 76 | 77 | 3. 安全性:阿里云提供全面的安全防护措施,包括防火墙、DDoS防护、数据加密、身份认证等,保护用户的数据和应用安全。 78 | 79 | 4. 丰富的服务:从基础设施即服务(IaaS)到平台即服务(PaaS),再到软件即服务(SaaS),阿里云提供一整套完整的云解决方案,覆盖了企业从开发、测试、部署到运营的全过程。 80 | 81 | 5. 全球化布局:阿里云在全球多个地区设有数据中心,可以为跨国企业提供本地化的云计算服务,帮助企业快速拓展海外市场。 82 | 83 | 6. 行业解决方案:针对电商、金融、教育、医疗等多个行业,阿里云提供了针对性的解决方案,帮助企业在特定领域实现高效运营。 84 | 85 | 7. 优质支持:阿里云提供24小时的技术支持和服务,有专业的团队解答用户问题,确保用户在使用过程中得到及时的帮助。 86 | 87 | 综上所述,无论你是初创公司还是大型企业,无论是国内业务还是全球化布局,阿里云都能提供合适的产品和服务,助力你的业务发展。 88 | ``` 89 | 90 | ## 3. 封装成文件来访问通义千问 91 | 92 | 我们将上述两端代码功能封装进文件,读者可以直接调用文件来实现与大模型的对话。 93 | 94 | ### 3.1. 批量输出 95 | ```bash 96 | python main_simple.py "阿里云是什么时候成立的" 97 | ``` 98 | 输出内容 99 | ``` 100 | 阿里云成立于2009年9月10日,是阿里巴巴集团旗下的云计算服务品牌,提供包括计算、存储、网络、安全、数据库、大数据、人工智能等全面的云计算服务。阿里云致力于为企业数字化转型提供技术支撑,帮助全球企业实现更高效、更绿色的运营。 101 | ``` 102 | 103 | ### 3.2. 流式输出 104 | ```bash 105 | python main_stream.py "为什么要使用阿里云" 106 | ``` 107 | 输出内容 108 | ```text 109 | 使用阿里云有以下几个主要原因: 110 | 111 | 1. 技术实力:阿里云是全球领先的云计算服务提供商,拥有强大的技术研发能力和丰富的实践经验。它提供了包括计算、存储、数据库、网络、安全、大数据、人工智能等全方位的云产品和服务。 112 | 113 | 2. 稳定性与安全性:阿里云在全球范围内拥有多个数据中心和边缘节点,通过负载均衡和容灾备份技术,保证了服务的高可用性和数据的安全性。 114 | 115 | 3. 弹性扩展:根据业务需求,用户可以轻松地在云端进行资源的按需分配和弹性伸缩,无需预先投入大量硬件成本,有效降低了运营成本。 116 | 117 | 4. 一站式解决方案:阿里云提供了一站式的企业级服务,包括云计算、域名注册、企业邮箱、网站托管、物联网、区块链等,满足不同企业的多元化需求。 118 | 119 | 5. 全球化服务:阿里云在全球多地设有数据中心,能够为跨国企业提供便捷的数据合规和本地化服务。 120 | 121 | 6. 售后支持:阿里云有专业的技术支持团队,提供7x24小时的技术咨询和问题解决服务,确保用户在遇到问题时能得到及时的帮助。 122 | 123 | 总的来说,选择阿里云可以帮助企业和个人开发者更高效、稳定、经济地运行其应用程序和业务,同时享受到前沿的云计算技术和优质的服务。 124 | ``` 125 | 126 | ## 4. 总结 127 | 128 | 通过本章的学习,你已经获得了两个与大模型对话聊天的命令行工具!你不仅已经了解了如何使用通义千问的 API,如何流式输出大模型返回的结果,并在本地运行了示例代码。 在开始下一章的学习之前,你也可以尝试调整 prompt 语句,让通义千问回答不同的问题。或者将这段代码集成到其他更复杂的场景中,来帮助你完成任务,比如从一个本地 excel 文件中逐行读取问题,并对其做出回答。 129 | 130 | ## 5. 参考资料 131 | - [DashScope](https://dashscope.aliyun.com/) 132 | - [LangChain](https://python.langchain.com/docs) 133 | 134 | 135 | ***** 136 | ## 本章代码 137 | - 请点击[本章实验代码](demo-chapter1.ipynb)查阅相关内容。 138 | 139 | ## 继续学习 140 | - [下一篇:第 2 章 找出网络上两篇文档之间的差异](../chapter2/README.md) 141 | - [【通义千问API入门教程】章节目录](../README.md) -------------------------------------------------------------------------------- /chapter1/demo-chapter1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 第 1 章 用4行代码与大模型对话\n", 8 | "*****" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "以前经常使用搜索引擎来解决问题的你,现在大概率已经在工作中频繁使用大模型了。 但是只在网页聊天框中和大模型对话,终究有一些局限性:比如你在本地有一个超大的用户反馈表格、或者是有一些仅内部可访问的网页,想要借助大模型做一系列处理,就不太好做到了。 幸运的是,现在很多大模型服务提供商,都提供了 API 接口,可以让你方便地实现各种原本在网页聊天框中不方便、或无法实现的功能。\n", 16 | "\n", 17 | "本章将通过一个简单的例子,让你快速进入到大模型应用开发的世界。\n", 18 | "\n" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## 1. 准备工作\n", 26 | "\n", 27 | "### 1.1. 安装\n", 28 | "\n", 29 | "下载文档代码及安装依赖项\n", 30 | "```bash\n", 31 | "git clone https://github.com/AlibabaCloudDocs/llm_learning.git\n", 32 | "cd llm_learning\n", 33 | "pip install -r requirements.txt\n", 34 | "```\n", 35 | "\n", 36 | "### 1.2. 账号准备\n", 37 | "\n", 38 | "首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey),\n", 39 | "\n", 40 | "#### MacOS or Linux\n", 41 | "您可以使用以下命令行导入环境变量\n", 42 | "```bash\n", 43 | "export DASHSCOPE_API_KEY=\"sk-****\"\n", 44 | "```\n", 45 | "\n", 46 | "#### Windows\n", 47 | "可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量\n", 48 | "```bat\n", 49 | "set DASHSCOPE_API_KEY=sk-****\n", 50 | "```\n", 51 | "或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 \n", 52 | "```powershell\n", 53 | "$Env:DASHSCOPE_API_KEY = \"sk-****\"\n", 54 | "```\n", 55 | "\n", 56 | "#### Jupyter Notebook\n", 57 | "您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。\n", 58 | "\n", 59 | "### 1.3. docenv模式\n", 60 | "\n", 61 | "将环境变量写入文件\"~/.zshrc\"中:\n", 62 | "\n", 63 | "```bash\n", 64 | "export OPENAI_API_KEY=\"sk-...\"\n", 65 | "```\n", 66 | "就可以执行如下命令 ```source ~/.zshrc``` 将这个环境变量绑定到全局shell中。\n", 67 | "\n", 68 | "接下来我们将加载这个环境变量到notebook中。执行如下命令\n", 69 | "\n", 70 | "**使用Windows,已经通过Windows PowerShell来注册环境变量的同事可以考虑跳过这里**" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 5, 76 | "metadata": {}, 77 | "outputs": [ 78 | { 79 | "name": "stderr", 80 | "output_type": "stream", 81 | "text": [ 82 | "Python-dotenv could not parse statement starting at line 7\n", 83 | "Python-dotenv could not parse statement starting at line 8\n", 84 | "Python-dotenv could not parse statement starting at line 10\n", 85 | "Python-dotenv could not parse statement starting at line 11\n", 86 | "Python-dotenv could not parse statement starting at line 16\n" 87 | ] 88 | }, 89 | { 90 | "data": { 91 | "text/plain": [ 92 | "True" 93 | ] 94 | }, 95 | "execution_count": 5, 96 | "metadata": {}, 97 | "output_type": "execute_result" 98 | } 99 | ], 100 | "source": [ 101 | "import os\n", 102 | "from dotenv import load_dotenv\n", 103 | "\n", 104 | "## for MacOS users\n", 105 | "filePath = os.path.abspath(os.path.expanduser(os.path.expandvars(\"~/.zshrc\")))\n", 106 | "load_dotenv(filePath)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 8, 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "# os.environ['DASHSCOPE_API_KEY']" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "## 2. 文本生成实验\n", 123 | "\n", 124 | "通过 langchain_community 库中封装的 Tongyi 工具,我们可以通过[灵积API](https://dashscope.console.aliyun.com/apiKey)访问通义千问,让通义千问回答问题。\n", 125 | "\n", 126 | "### 2.1. 简单文本生成\n", 127 | "\n", 128 | "首先,Tongyi 工具的接口默认是一次性输出全部生成的答案,用户需要等待数秒才能看到完整答案。下面,我们先来看看一次性生成全部答案的代码和执行效果。代码只有三行,如下:" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 7, 134 | "metadata": {}, 135 | "outputs": [ 136 | { 137 | "name": "stdout", 138 | "output_type": "stream", 139 | "text": [ 140 | "阿里云成立于2009年9月10日,是阿里巴巴集团旗下的云计算服务品牌,提供包括计算、存储、网络、安全、数据库、大数据、人工智能等全面的云计算服务。\n" 141 | ] 142 | } 143 | ], 144 | "source": [ 145 | "from langchain_community.llms import Tongyi\n", 146 | "llm = Tongyi()\n", 147 | "print(llm.invoke('阿里云成立于什么时间'))" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "#### 状态码 400 异常问题解决方法\n", 155 | "如果遇到程序抛出异常,请查看[**状态码**](https://help.aliyun.com/document_detail/2712216.html)\n", 156 | "\n", 157 | "例如遇到如下错误信息,请检查你的账户是不是**欠费**了,或者你的API KEY是不是配置错了。\n", 158 | "```sh\n", 159 | "ValueError: status_code: 400 \n", 160 | " code: Arrearage \n", 161 | " message: Access denied, please make sure your account is in good standing.\n", 162 | "```\n" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "### 2.2. 流式文本生成\n", 170 | "\n", 171 | "采用LangChain封装的接口来实现流式文本生成,我们需要在创建Tongyi对象时声明采用流式输出(streaming=True),然后用一个循环来接收生成的文本,直到生成的文本为空。\n", 172 | ">(注意:如果你遇到了 TypeError: Additional kwargs key output_tokens already exists in left dict and value has unsupported type 这个错误,请参考[github](https://github.com/langchain-ai/langchain/pull/16580)中的说明)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": 10, 178 | "metadata": {}, 179 | "outputs": [ 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "阿里云作为全球领先的云计算及人工智能科技公司,提供了丰富的云计算产品和服务,有以下几个主要的原因让你可能选择使用阿里云:\n", 185 | "\n", 186 | "1. 稳定性:阿里云拥有强大的基础设施和优秀的技术团队,提供高可用、低延迟的云服务,确保业务的稳定运行。\n", 187 | "\n", 188 | "2. 安全性:阿里云注重数据安全和隐私保护,提供多种安全防护措施,如DDoS防护、防火墙等,保障用户的数据安全。\n", 189 | "\n", 190 | "3. 丰富的产品线:从计算、存储、数据库到大数据、人工智能等,阿里云提供全方位的云服务,满足不同规模企业的需求。\n", 191 | "\n", 192 | "4. 弹性扩展:阿里云支持弹性伸缩,可以根据业务需求快速调整资源,避免资源浪费,节省成本。\n", 193 | "\n", 194 | "5. 技术支持:阿里云有专业的技术团队,提供24小时的技术支持和服务,帮助企业解决技术问题。\n", 195 | "\n", 196 | "6. 全球化布局:阿里云在全球多个地区设有数据中心,可以提供本地化的服务,帮助企业快速拓展全球业务。\n", 197 | "\n", 198 | "7. 成功案例:众多企业,包括互联网、金融、制造、零售等行业的头部企业,都在使用阿里云,并取得了显著的效果。\n", 199 | "\n", 200 | "8. 开发者友好:阿里云提供了丰富的开发工具和API,方便开发者进行快速开发和集成。\n", 201 | "\n", 202 | "综上,阿里云凭借其技术实力、服务质量、全球化视野等多方面优势,成为许多企业和开发者的选择。" 203 | ] 204 | } 205 | ], 206 | "source": [ 207 | "from langchain_community.llms import Tongyi\n", 208 | "llm = Tongyi(streaming=True)\n", 209 | "for chunk in llm.stream('为什么要使用阿里云'):\n", 210 | " print(chunk, end=\"\", flush=True)\n" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "## 3. 封装成文件来访问通义千问\n", 218 | "\n", 219 | "我们将上述两端代码功能封装进文件,读者可以直接调用文件来实现与大模型的对话。\n", 220 | "\n", 221 | "### 3.1. 批量输出" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": 11, 227 | "metadata": {}, 228 | "outputs": [ 229 | { 230 | "name": "stdout", 231 | "output_type": "stream", 232 | "text": [ 233 | "阿里云成立于2009年9月10日,是阿里巴巴集团旗下的云计算服务品牌,提供包括计算、存储、网络、安全、数据库、大数据、人工智能等全面的云计算服务。阿里云致力于为企业提供一站式的云计算解决方案,帮助企业实现数字化转型和业务增长。经过十多年的快速发展,阿里云已成为全球领先的云计算及人工智能科技公司之一,服务客户遍布全球200多个国家和地区。\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "! python main_simple.py \"阿里云是什么时候成立的\"" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "### 3.2. 流式输出\n" 246 | ] 247 | }, 248 | { 249 | "cell_type": "code", 250 | "execution_count": 12, 251 | "metadata": {}, 252 | "outputs": [ 253 | { 254 | "name": "stdout", 255 | "output_type": "stream", 256 | "text": [ 257 | "阿里云作为全球领先的云计算及人工智能科技公司,提供了丰富的云计算、大数据、人工智能等产品和服务,以下是一些使用阿里云的主要原因:\n", 258 | "\n", 259 | "1. 技术领先:阿里云拥有强大的技术研发实力,其云计算、大数据、人工智能等技术在全球范围内具有领先地位,能够为企业提供最先进的技术支持。\n", 260 | "\n", 261 | "2. 稳定可靠:阿里云的基础设施遍布全球,数据中心分布广泛,提供高可用性和灾备能力,确保业务的稳定运行。其“飞天”大规模操作系统能够处理海量数据和流量,保证服务的稳定性。\n", 262 | "\n", 263 | "3. 安全保障:阿里云在数据安全和隐私保护方面有着严格的标准和措施,包括防火墙、DDoS防护、加密技术等,保障用户的数据安全。\n", 264 | "\n", 265 | "4. 丰富的产品线:阿里云提供包括云服务器、数据库、存储、CDN、物联网、人工智能等在内的全方位云服务,满足不同行业、不同规模企业的需求。\n", 266 | "\n", 267 | "5. 弹性伸缩:基于云计算的弹性伸缩能力,企业可以根据业务需求快速调整资源,避免了传统IT设施的高昂投入和运维成本。\n", 268 | "\n", 269 | "6. 成本优化:使用阿里云可以按需付费,大大降低了企业的初始投资和运营成本,尤其对于初创企业和中小型企业来说,更加经济实惠。\n", 270 | "\n", 271 | "7. 专业服务:阿里云提供24小时的技术支持和咨询服务,有专业的团队帮助企业解决技术问题,提升效率。\n", 272 | "\n", 273 | "8. 生态系统:阿里云构建了一个庞大的开发者和合作伙伴生态系统,可以为企业提供一站式的解决方案,加速业务创新和发展。\n", 274 | "\n", 275 | "综上所述,无论从技术实力、服务稳定性、安全性、产品丰富度、成本效益还是生态建设等方面,阿里云都是企业进行数字化转型和业务发展的重要选择。" 276 | ] 277 | } 278 | ], 279 | "source": [ 280 | "! python main_stream.py \"为什么要使用阿里云\"" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "## 4. 总结\n", 288 | "\n", 289 | "通过本章的学习,你已经获得了两个与大模型对话聊天的命令行工具!你不仅已经了解了如何使用通义千问的 API,如何流式输出大模型返回的结果,并在本地运行了示例代码。 在开始下一章的学习之前,你也可以尝试调整 prompt 语句,让通义千问回答不同的问题。或者将这段代码集成到其他更复杂的场景中,来帮助你完成任务,比如从一个本地 excel 文件中逐行读取问题,并对其做出回答。\n", 290 | "\n" 291 | ] 292 | }, 293 | { 294 | "cell_type": "markdown", 295 | "metadata": {}, 296 | "source": [ 297 | "## 5. 参考资料\n", 298 | "- [DashScope](https://dashscope.aliyun.com/)\n", 299 | "- [LangChain](https://python.langchain.com/docs)" 300 | ] 301 | } 302 | ], 303 | "metadata": { 304 | "kernelspec": { 305 | "display_name": "aiworld", 306 | "language": "python", 307 | "name": "python3" 308 | }, 309 | "language_info": { 310 | "codemirror_mode": { 311 | "name": "ipython", 312 | "version": 3 313 | }, 314 | "file_extension": ".py", 315 | "mimetype": "text/x-python", 316 | "name": "python", 317 | "nbconvert_exporter": "python", 318 | "pygments_lexer": "ipython3", 319 | "version": "3.9.12" 320 | } 321 | }, 322 | "nbformat": 4, 323 | "nbformat_minor": 2 324 | } 325 | -------------------------------------------------------------------------------- /chapter1/main_simple.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from langchain_community.llms import Tongyi 4 | 5 | 6 | if __name__ == '__main__': 7 | question = sys.argv[1] 8 | llm = Tongyi() 9 | print(llm.invoke(question)) 10 | -------------------------------------------------------------------------------- /chapter1/main_stream.py: -------------------------------------------------------------------------------- 1 | # For prerequisites running the following sample, visit https://help.aliyun.com/document_detail/611472.html 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from langchain_community.llms import Tongyi 5 | 6 | if __name__ == '__main__': 7 | question = sys.argv[1] 8 | 9 | llm = Tongyi(streaming=True) 10 | for chunk in llm.stream(question): 11 | print(chunk, end="", flush=True) 12 | -------------------------------------------------------------------------------- /chapter2/README.md: -------------------------------------------------------------------------------- 1 | # 第 2 章 找出两篇文章的不同 2 | 开发一个大模型工具,实现文档内容比对。阿里云的文档工程师们遇到过这样的问题:同一篇文档在中国站和国际站有一些不同,能快速的指出一批文档的这种差异吗?收到这个问题的你起初感到有些犯难,但想到或许可以借助大模型来快速完成这个任务,你又觉得似乎可以很快做到。 3 | 4 | 本章我们将开发一个程序,该程序可以完成: 5 | - 可以指定文档 URL,程序根据 URL 自动爬取中国站、国际站的文档内容作比对 6 | - 因为要比对的文档很多,你希望是可以从一个 excel 文件中读取 URL 列表,并对其进行比对 7 | 8 | 9 | ## 1. 准备工作 10 | 11 | ### 1.1. 安装 12 | 13 | 下载文档代码及安装依赖项 14 | ```bash 15 | git clone https://github.com/AlibabaCloudDocs/llm_learning.git 16 | cd llm_learning 17 | pip install -r requirements.txt 18 | ``` 19 | 20 | ### 1.2. 账号准备 21 | 22 | 首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey) 23 | 24 | #### MacOS or Linux 25 | 您可以使用以下命令行导入环境变量 26 | ```bash 27 | export DASHSCOPE_API_KEY="sk-****" 28 | ``` 29 | 30 | #### Windows 31 | 可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量 32 | ```bat 33 | set DASHSCOPE_API_KEY=sk-**** 34 | ``` 35 | 或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 36 | ```powershell 37 | $Env:DASHSCOPE_API_KEY = "sk-****" 38 | ``` 39 | 40 | #### Jupyter Notebook 41 | 您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。 42 | 43 | 44 | ## 2. MVP 开发 45 | 在正式开发一个完整的程序之前,我们可以先开发一个最小可运行的程序,来验证一下效果。 46 | 47 | ### 2.1 根据 URL 加载文档 48 | 为了实现根据 URL 读取文档内容,你参考了 LangChain 的[这篇 WebBaseLoader 文档](https://python.langchain.com/docs/integrations/document_loaders/web_base),并尝试在代码中使用 WebBaseLoader 来加载一篇文档: 49 | ```python 50 | from langchain_community.document_loaders import WebBaseLoader 51 | 52 | loader = WebBaseLoader("https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab") 53 | 54 | doc = loader.load() 55 | print(doc[0].page_content) 56 | ``` 57 | ### 2.2 比较文档内容 58 | 接下来你可以再加载一下国际站的文档,并将其交给通义千问帮你比对差别了: 59 | ```python 60 | from langchain_community.document_loaders import WebBaseLoader 61 | from langchain_community.llms import Tongyi 62 | from utils.ds_util import sample_call_streaming 63 | 64 | llm = Tongyi() 65 | llm.model_name = 'qwen-max' 66 | 67 | 68 | def load_doc_content(url): 69 | loader = WebBaseLoader(url) 70 | doc = loader.load() 71 | return doc[0].page_content 72 | 73 | 74 | doc_content_cn = load_doc_content("https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab") 75 | doc_content_intl = load_doc_content("https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab") 76 | 77 | prompt = f"""你现在的任务是找出下面两篇文档的不同之处,请不要放过任何细节。 78 | -------- 79 | 【中国站的文档】: 80 | {doc_content_cn} 81 | -------- 82 | 【国际站的文档】: 83 | {doc_content_intl} 84 | -------- 85 | 请给出结论,并用列表形式指出不同之处的具体位置: 86 | """ 87 | 88 | sample_call_streaming(prompt) 89 | ``` 90 | 输出如下: 91 | ```text 92 | 以下是两篇文档的不同之处列表: 93 | 94 | 1. 文档标题: 95 | - 中国站文档:快速购买ECS实例_云服务器 ECS(ECS)-阿里云帮助中心 96 | - 国际站文档:快速购买ECS实例 - 云服务器 ECS - 阿里云 97 | 98 | 2. 页面布局与导航链接: 99 | - 中国站文档:文档结尾包含了联系信息、备案控制台登录/注册、产品文档、输入文档关键字查找等;还有“首页”、“云服务器 ECS”的子菜单,以及“操作指南”下的多个小节链接。 100 | - 国际站文档:文档结构更为简洁,只有“文档中心”、“全部产品”两个大类导航,且没有提及具体的子菜单或操作指南链接。 101 | 102 | 3. 内容差异: 103 | - 可用地区域范围:中国站文档提到“华东1(杭州)”,而国际站文档只提到了“华北2(北京)”作为实例创建时可以选择的地域,且未明确提到“华东1(杭州)”。 104 | - 实例规格:中国站文档列出“ecs.s6-c1m1.small”实例规格,而国际站文档只提及“突发性能实例t5”,并且只支持CentOS、Windows Server、Ubuntu三种操作系统,与中国站文档的四种操作系统有所不同。 105 | - 网络类型选项:中国站文档提及了“专有网络”和“公网IP”选项以及两种计费模式(固定带宽和按使用流量),而国际站文档只提到了“专有网络”和“公网带宽”选项,且只提供了固定带宽计费模式。 106 | - 自动续费选项:中国站文档明确提到了“自动续费”且允许用户选择是否开启,而国际站文档虽然提供了选择购买时长的选项,但没有直接提及自动续费功能。 107 | 108 | 4. 文档更新时间和语言版本: 109 | - 中国站文档提到“更新时间:一键部署”,没有具体日期;国际站文档的更新时间为“Nov 30, 2023”。 110 | - 中国站文档在某些语句后面注明了简体中文,如“操作步骤”、“重置实例登录密码”等;国际站文档则未明确标注语言,但整体内容倾向于英文描述。 111 | ``` 112 | 以上就是一个 MVP 版本的程序了。它可以根据 URL 加载文档并进行比较,一定程度上,它已经能帮你减轻很多工作量了。 113 | 114 | ## 3. 完善程序 115 | 接下来我们可以完善一下程序,以达成我们最初设想的实现目标。 116 | 117 | ### 3.1 遍历 Excel 中的文档 URL 118 | 因为要比对的文档比较多,而且也经常会变,所以在代码中写死这些链接并不是一个好的做法。 我们可以在 excel 文件中管理 url,然后在程序中读取出来。 119 | 120 | 参考代码如下: 121 | ```python 122 | import os 123 | from openpyxl import load_workbook 124 | 125 | file_path = 'data/doc_urls.xlsx' 126 | 127 | wb = load_workbook(file_path) 128 | sheet = wb.active 129 | for row in sheet.iter_rows(values_only=True, min_row=2): 130 | url_cn = row[0] 131 | url_intl = row[0].replace('help.aliyun.com', 'www.alibabacloud.com/help') 132 | print(url_cn) 133 | print(url_intl) 134 | ``` 135 | 输出如下: 136 | ```text 137 | https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 138 | https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 139 | https://help.aliyun.com/zh/ecs/user-guide/overview-52 140 | https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52 141 | ``` 142 | 你已经可以从 excel 中读取链接,并自动替换获取国际站的文档链接了。 我们可以对 MVP 程序进行一点封装,以便在遍历 excel 里的 url 时调用通义千问进行文档对比。 详细的代码可以查看: 143 | 144 | 详细的代码可以查看: 145 | - [chapter2/load-from-excel/diff_docs.py](load-from-excel/diff_docs.py) 146 | - [chapter2/load-from-excel/main.py](load-from-excel/main.py) 147 | 148 | 现在,你就可以运行程序,来实现批量自动文档对比了: 149 | ```bash 150 | python load-from-excel/main.py 151 | ``` 152 | 输出如下: 153 | ```text 154 | ------ 155 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 156 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 157 | 结论:两篇文档分别是中国站和国际站关于快速购买阿里云ECS实例的指南,它们在内容上大部分一致,但在细节、语言表述以及部分功能选项上存在差异。以下是具体的不同之处列表: 158 | 159 | 1. 文档标题与更新时间: 160 | - 中国站文档:快速购买包年包月实例_更新时间未明确 161 | - 国际站文档:一键购买包年包月实例 - 更新时间:Nov 30, 2023 162 | 163 | 2. 注册与实名认证指引: 164 | - 中国站文档:提及“已注册账号并完成实名认证”,并提供“阿里云账号注册流程”链接。 165 | - 国际站文档:提及“已注册账号并完成实名认证”,并指向“注册阿里云账号”。 166 | 167 | 3. 实例规格选择: 168 | - 中国站文档:默认配置中产品规格可选ecs.s6-c1m1.small,并提示如需更多实例规格可前往自定义购买。 169 | - 国际站文档:一键购买仅支持突发性能实例t5,如果需要更多实例规格,也建议前往自定义购买。 170 | 171 | 4. 操作系统选择: 172 | - 中国站文档:支持Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS四种操作系统。 173 | - 国际站文档:一键购买支持CentOS、Windows Server、Ubuntu三种操作系统。 174 | 175 | 5. 确认订单页面的服务条款说明: 176 | - 中国站文档:阅读《云服务器 ECS 服务条款》|《云服务器ECS退订说明》,选中《云服务器ECS服务条款》后点击确认订单。 177 | - 国际站文档:阅读《云服务器 ECS 服务条款》|《通用服务条款》,选中《云服务器ECS服务条款》后点击确认下单。 178 | 179 | 除此之外,文档中的一些语言表述、按钮名称(如“快速购买”与中国站对应的是“一键购买”)以及部分内容布局有所调整,但不影响整体操作流程的理解和执行。 180 | ------ 181 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52 182 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52 183 | 结论:这两篇文档内容实质上是相同的,主要描述了阿里云ECS实例的概述、基础配置、镜像、存储、购买方式和使用指导等信息。但是,两篇文档在排版布局、更新时间和联系方式等方面存在细微差异。 184 | 185 | 不同之处具体位置: 186 | 1. 文档标题及更新时间: 187 | - 中国站文档:【实例概述_云服务器 ECS】,更新时间:一键部署产品详情 188 | - 国际站文档:【实例概述 - 云服务器 ECS】,更新时间:Jul 18, 2023 189 | 190 | 2. 导航栏与底部联系信息: 191 | - 中国站文档提供了详细的导航栏,包括产品解决方案、社区权益中心、定价、云市场、合作伙伴支持与服务等,并且列出了售前咨询、售后咨询以及多种联系方式。 192 | - 国际站文档的导航栏和底部联系信息相对简洁,未列出详细联系方式,只在文档末尾有简单的反馈功能。 193 | 194 | 3. 排版细节: 195 | - 中国站文档包含较多的中文引导链接和分区标签,如“操作指南”、“实践教程”等; 196 | - 国际站文档整体排版更符合英文用户的阅读习惯,没有明显的分区标签和过多的内部链接。 197 | ``` 198 | 199 | ### 3.2 排除网页中的页头页尾等非关键内容 200 | 细心的你可能会发现运行前面的代码时,通义千问给出的文档差异,可能还会包含一些页面尾部的差异,比如: 201 | ```text 202 | ... 203 | 2. 联系方式和链接格式: 204 | - 中国站文档中提供了详细的联系方式,如“95187-1 在线服务”、“4008013260 在线服务”,并且附带了“我要建议”、“我要投诉”、“体验反馈”等功能入口,同时链接地址采用了中文描述且未直接展示超链接形式。 205 | - 国际站文档没有在该处明确提供电话联系方式,而是集中在文档底部显示版权信息时提及,并且所有链接均采用英文描述及超链接形式展现。 206 | 207 | 3. 页面底部信息: 208 | - 中国站文档在底部包含了更多的附加信息,如“阿里云首页”、“相关技术圈”、“为什么选择阿里云”等,并列出了各类服务热线、法律声明、隐私权政策、廉正举报等链接。 209 | - 国际站文档在底部只包含了版权信息、许可证编号以及指向其他阿里云产品的链接,联系方式较为简洁。 210 | ... 211 | ``` 212 | 出现这个情况的原因是,我们给到大模型的文档内容,其实是整个页面的内容。为了解决这一问题,我们需要想办法提取到文档正文本身,排除页头页尾等非文档正文内容。 213 | 214 | 这里我们可以选择针对阿里云文档页面的结构,编写一个自定义的 loader: 215 | ```python 216 | from typing import List 217 | from bs4 import BeautifulSoup 218 | from langchain_community.document_loaders.base import BaseLoader 219 | from langchain_community.document_transformers import Html2TextTransformer 220 | from langchain_core.documents import Document 221 | import requests 222 | 223 | 224 | class AliyunDocLoader(BaseLoader): 225 | """ 226 | 阿里云文档加载器 227 | """ 228 | 229 | def __init__(self, urls: List[str]): 230 | self.urls = urls 231 | 232 | def load(self) -> List[Document]: 233 | results = [] 234 | for url in self.urls: 235 | content, metadata = self._get_aliyun_help_doc(url) 236 | doc = Document(page_content=content, metadata=metadata) 237 | results.append(doc) 238 | html2text = Html2TextTransformer() 239 | return html2text.transform_documents(results) 240 | 241 | @staticmethod 242 | def _get_aliyun_help_doc(url: str): 243 | """ 244 | @param url: 文档 URL 245 | @return: content, metadata. content 为 html,metadata 里包含 TDK 246 | """ 247 | html = requests.get(url).text 248 | soup = BeautifulSoup(html, features='html.parser') 249 | markdown_body = soup.find('article', class_='markdown-body') or soup.find('div', class_='markdown-body') 250 | contents = markdown_body.contents 251 | content_html = ''.join([str(item) for item in contents]) 252 | content_html = f'

{soup.find("h1").text}

' + content_html 253 | 254 | metadata = { 255 | "title": soup.find('title').text, 256 | "keywords": soup.find('meta', attrs={'name': 'keywords'}).attrs['content'], 257 | "description": soup.find('meta', attrs={'name': 'description'}).attrs['content'] 258 | } 259 | return content_html, metadata 260 | ``` 261 | 完整的代码在 [`chapter2/custom-loader/`](custom-loader/) 中。 262 | 现在你可以尝试运行一下使用自定义阿里云文档 loader 的对比程序了: 263 | ```bash 264 | python custom-loader/main.py 265 | ``` 266 | 输出如下: 267 | ```text 268 | ------ 269 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 270 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 271 | 结论:两篇文档的主要内容和结构相似,但存在一些细节上的差异。以下是不同之处的具体位置列表: 272 | 273 | 1. 文档标题: 274 | - 中国站文档标题:快速购买包年包月实例 275 | - 国际站文档标题:云服务器 ECS:一键购买包年包月实例 276 | 277 | 2. 快速购买/一键购买页签的表述: 278 | - 中国站文档中使用“快速购买”页签 279 | - 国际站文档中使用“**** **** **一键购买** ”页签 280 | 281 | 3. 实例规格配置部分: 282 | - 中国站文档提供的是ecs.s6-c1m1.small产品规格示例 283 | - 国际站文档提供的是一键购买仅支持突发性能实例t5(2 vCPU 4 GiB) 284 | 285 | 4. 操作系统镜像选择: 286 | - 中国站文档提到支持Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS四种操作系统,并强调操作系统和预装应用仅支持选择其中一个。 287 | - 国际站文档提到一键购买支持CentOS、Windows Server、Ubuntu三种操作系统,没有提及预装应用选项。 288 | 289 | 5. 公网带宽计费模式下的配置项展示: 290 | - 中国站文档将“带宽计费模式”与“带宽值”分开描述。 291 | - 国际站文档在公网带宽部分直接列出“分配公网IPv4地址”、“按固定带宽”及“带宽值”。 292 | 293 | 6. 确认订单页面的描述: 294 | - 中国站文档提及阅读《云服务器 ECS 服务条款》|《云服务器ECS退订说明》并单击“确认订单”。 295 | - 国际站文档提及阅读《云服务器 ECS 服务条款》|《通用服务条款》并单击“确认下单”。 296 | ------ 297 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52 298 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52 299 | 经过比对,两篇文档内容完全一致,没有发现任何不同之处。 300 | ``` 301 | ### 3.3 应对超大文档 302 | 以上的程序似乎已经比较完善了。但很快你就会发现,有一些文档非常大,超出通义千问 API 的最大 token 数限制了。 303 | 304 | ### 3.3.1 更换模型 305 | 经过查阅阿里云的 [DashScope - 通义千问模型](https://help.aliyun.com/zh/dashscope/developer-reference/api-details)说明文档,你会发现正在使用的`qwen-max`最大 token 数是 6k。 306 | 另外,你还发现`qwen-plus`和`qwen-max-longcontext`支持的 token 数分别是 30k 和 28k。 307 | 所以最简单的办法就是修改现有的代码,使用`qwen-plus`和`qwen-max-longcontext`即可。 308 | ```python 309 | llm = Tongyi() 310 | llm.model_name = 'qwen-max-longcontext' 311 | ``` 312 | 尽管上面的代码改动已经解决了你遇到的问题,但严谨的你一定会想到,如果遇到了更大的文档,还是超过了 token 数量限制该怎么办。 313 | 314 | ### 3.3.2 分而治之 315 | 我们可以借鉴 MapReduce 的思想,将文档分成多个小文档分片,然后对每个分片做对比,最后汇总结果。 316 | 317 | 具体实现思路是: 318 | 1. 我们可以先找出两篇文档中相同的二级标题; 319 | 2. 然后以这些相同的二级标题对文档进行切片; 320 | 3. 对每个切片进行对比,并将结果汇总,得到最终结果。 321 | 322 | 我们在[`chapter2/long-docs/doc_spliter.py`](long-docs/doc_spliter.py)中提供了两个方法,用于找到相同的二级标题,和对文档进行切片。 323 | 另外在[`chapter2/long-docs/diff_docs.py`](long-docs/diff_docs.py)中调整了对比程序的实现,程序会先对第一个文档切片进行对比,然后带着对比结论,对剩余文档切片进行对比,最终遍历完所有的切片时,就得到了最终答案。 324 | 325 | 这是切片提问模式下的两段提示词: 326 | ```python 327 | def get_question_prompt(doc_content_cn, doc_content_intl): 328 | return f"""你现在的任务是找出下面两段文档的不同之处,请不要放过任何细节。 329 | -------- 330 | 【中国站的文档】: 331 | {doc_content_cn} 332 | -------- 333 | 【国际站的文档】: 334 | {doc_content_intl} 335 | -------- 336 | 请给出结论,并用列表形式指出不同之处的具体位置: 337 | """ 338 | 339 | 340 | def get_refine_prompt(doc_content_cn, doc_content_intl, existing_answer): 341 | return f"""这是你在前面的文档片段中找出的不同: 342 | {existing_answer} 343 | -------- 344 | 请找出下面两个文档片段不同之处的具体位置,并补充到之前的结果中: 345 | -------- 346 | 【中国站的文档】: 347 | {doc_content_cn} 348 | -------- 349 | 【国际站的文档】: 350 | {doc_content_intl} 351 | """ 352 | ``` 353 | 354 | 你可以运行一下试试看: 355 | ```bash 356 | python long-docs/main.py 357 | ``` 358 | 输出如下: 359 | ```text 360 | ------ 361 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 362 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab 363 | 正在对比第1个片段... 364 | 正在对比第2个片段... 365 | 正在对比第3个片段... 366 | 正在对比第4个片段... 367 | 在对比两个文档片段后,可以发现以下不同之处: 368 | 369 | 1. **页签名称**: 370 | - 中国站的文档中,用户需要单击左上角的“**快速购买**”页签。 371 | - 国际站的文档中,用户需要单击左上角的“**** **** **一键购买**”页签。 372 | 373 | 2. **产品规格说明和示例**: 374 | - 中国站的文档提供了实例规格ecs.s6-c1m1.small的示例,并指出可选规格与地域相关,用户可以在“使用说明”列查看适用场景。 375 | - 国际站的文档中,仅支持突发性能实例t5规格,示例为“2 vCPU 4 GiB(突发性能实例t5)”。 376 | 377 | 3. **操作系统及预装应用**: 378 | - 中国站的文档详细列举了四种操作系统供选择(Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS),并特别说明操作系统和预装应用只能选择其中一个。 379 | - 国际站的文档只提到一键购买支持CentOS、Windows Server、Ubuntu三种操作系统,未提及预装应用的选择。 380 | 381 | 4. **公网带宽配置**: 382 | - 中国站的文档将公网IP和带宽计费模式分为两个独立参数,其中带宽计费模式下有“按固定带宽”和“按使用流量”的选项,并提供了带宽值的例子。 383 | - 国际站的文档中公网带宽部分整合了公网IP分配和带宽计费模式的选择,在一个表格内展示了“分配公网IPv4地址”、“按固定带宽”以及“带宽值”的例子。 384 | 385 | 5. **购买时长和自动续费**: 386 | - 中国站的文档中,购买时长和自动续费是分开描述的参数。 387 | - 国际站的文档中,购买时长和是否启用自动续费是在同一个描述里,用户同时选择购买时长(例如:1个月)和是否启用自动续费。 388 | 389 | 6. **确认订单按钮后的操作**: 390 | - 中国站的文档在右侧“确认订单”面板阅读《云服务器 ECS 服务条款》和《云服务器ECS退订说明》,然后选中《云服务器ECS服务条款》后点击“确认订单”。 391 | - 国际站的文档在“确认订单”页面阅读《云服务器 ECS 服务条款》和《通用服务条款》,选中《云服务器ECS服务条款》后点击的是“确认下单”。 392 | ------ 393 | 中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52 394 | 国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52 395 | 正在对比第1个片段... 396 | 正在对比第2个片段... 397 | 正在对比第3个片段... 398 | 正在对比第4个片段... 399 | 正在对比第5个片段... 400 | 正在对比第6个片段... 401 | 正在对比第7个片段... 402 | 经过对比分析,两个文档片段的内容完全相同,没有找到任何具体的不同之处。 403 | ``` 404 | ## 4. 总结 405 | 通过本章的学习,你已经能借助通义千问的 API 来帮助自己完成一些原来要人工完成的工作了,也学会了通过分而治之的思路来解决提示词过长的问题。分而治之的思想在很多场景下都有应用,除了用于做两篇长文档的对比,也可以用于超长文档的总结。 406 | 407 | 需要注意的时,这里出于学习的目的,文档的切片方法并不是最优的: 408 | 409 | 提示词长度没有超限时,是可以不用切片的; 410 | 一些特殊的文档也可能存在一个二级标题下内容也超过 token 限制,实际用于生产时,需要结合具体业务来定制切片方法。 411 | 另外,在实际使用中,你也可以使用 LangChain 提供的 refine 类型的 load_summarize_chain 来完成这个切片后调用大模型的任务。详情可以参考:[LangChain Use cases:Summarization - Refine](https://python.langchain.com/docs/use_cases/summarization#option-3.-refine)。 412 | 413 | 414 | ## 5. 参考资料 415 | - [DashScope](https://dashscope.aliyun.com/) 416 | - [通义千问 - 模型概览](https://help.aliyun.com/zh/dashscope/developer-reference/api-details) 417 | - [LangChain](https://python.langchain.com/docs) 418 | - [LangChain Use cases:Summarization - Refine](https://python.langchain.com/docs/use_cases/summarization#option-3.-refine) 419 | 420 | 421 | ***** 422 | ## 本章代码 423 | - 请点击[本章实验代码](demo-chapter2.ipynb)查阅相关内容。 424 | 425 | ## 继续学习 426 | - [上一篇:第 1 章 用4行代码实现流式输出](../chapter1/README.md) 427 | - [下一篇:第 3 章 使用工具扩展问答能力的基本原理](../chapter3/README.md) 428 | - [【通义千问API入门教程】章节目录](../README.md) -------------------------------------------------------------------------------- /chapter2/custom-loader/aliyun_doc_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import List 3 | from bs4 import BeautifulSoup 4 | from langchain_community.document_loaders.base import BaseLoader 5 | from langchain_community.document_transformers import Html2TextTransformer 6 | from langchain_core.documents import Document 7 | import requests 8 | 9 | 10 | class AliyunDocLoader(BaseLoader): 11 | """ 12 | 阿里云文档加载器 13 | """ 14 | 15 | def __init__(self, urls: List[str]): 16 | self.urls = urls 17 | 18 | def load(self) -> List[Document]: 19 | results = [] 20 | for url in self.urls: 21 | content, metadata = self._get_aliyun_help_doc(url) 22 | doc = Document(page_content=content, metadata=metadata) 23 | results.append(doc) 24 | html2text = Html2TextTransformer() 25 | return html2text.transform_documents(results) 26 | 27 | @staticmethod 28 | def _get_aliyun_help_doc(url: str): 29 | """ 30 | @param url: 文档 URL 31 | @return: content, metadata. content 为 html,metadata 里包含 TDK 32 | """ 33 | html = requests.get(url).text 34 | soup = BeautifulSoup(html, features='html.parser') 35 | markdown_body = soup.find('article', class_='markdown-body') or soup.find('div', class_='markdown-body') 36 | contents = markdown_body.contents 37 | content_html = ''.join([str(item) for item in contents]) 38 | content_html = f'

{soup.find("h1").text}

' + content_html 39 | 40 | metadata = { 41 | "title": soup.find('title').text, 42 | "keywords": soup.find('meta', attrs={'name': 'keywords'}).attrs['content'], 43 | "description": soup.find('meta', attrs={'name': 'description'}).attrs['content'] 44 | } 45 | return content_html, metadata 46 | -------------------------------------------------------------------------------- /chapter2/custom-loader/diff_docs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from aliyun_doc_loader import AliyunDocLoader 3 | from langchain_community.llms import Tongyi 4 | 5 | llm = Tongyi() 6 | llm.model_name = 'qwen-max' 7 | 8 | 9 | def _load_doc_content(url): 10 | loader = AliyunDocLoader([url]) 11 | docs = loader.load() 12 | return docs[0].page_content 13 | 14 | 15 | def diff(url_cn, url_intl): 16 | doc_content_cn = _load_doc_content(url_cn) 17 | doc_content_intl = _load_doc_content(url_intl) 18 | 19 | prompt = f"""你现在的任务是找出下面两篇文档的不同之处,请不要放过任何细节。 20 | -------- 21 | 【中国站的文档】: 22 | {doc_content_cn} 23 | -------- 24 | 【国际站的文档】: 25 | {doc_content_intl} 26 | -------- 27 | 请给出结论,并用列表形式指出不同之处的具体位置: 28 | """ 29 | 30 | diff_result = llm.invoke(prompt) 31 | return diff_result 32 | -------------------------------------------------------------------------------- /chapter2/custom-loader/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from openpyxl import load_workbook 4 | from diff_docs import diff 5 | 6 | file_path = os.path.join(os.path.dirname(__file__), '../data/doc_urls.xlsx') 7 | 8 | wb = load_workbook(file_path) 9 | sheet = wb.active 10 | for row in sheet.iter_rows(values_only=True, min_row=2): 11 | url_cn = row[0] 12 | url_intl = row[0].replace('help.aliyun.com', 'www.alibabacloud.com/help') 13 | 14 | print('------') 15 | print(f'中国站文档:{url_cn}') 16 | print(f'国际站文档:{url_intl}') 17 | result = diff(url_cn, url_intl) 18 | print(result) 19 | 20 | -------------------------------------------------------------------------------- /chapter2/data/doc_urls.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlibabaCloudDocs/llm_learning/c27ee6deefb0f293b35786e7eed0097d4f655ca1/chapter2/data/doc_urls.xlsx -------------------------------------------------------------------------------- /chapter2/demo-chapter2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 第 2 章 找出两篇文章的不同\n", 8 | "*****" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "开发一个大模型工具,实现文档内容比对。阿里云的文档工程师们遇到过这样的问题:同一篇文档在中国站和国际站有一些不同,能快速的指出一批文档的这种差异吗?收到这个问题的你起初感到有些犯难,但想到或许可以借助大模型来快速完成这个任务,你又觉得似乎可以很快做到。\n", 16 | "\n", 17 | "本章我们将开发一个程序,该程序可以完成:\n", 18 | "- 可以指定文档 URL,程序根据 URL 自动爬取中国站、国际站的文档内容作比对\n", 19 | "- 因为要比对的文档很多,你希望是可以从一个 excel 文件中读取 URL 列表,并对其进行比对\n" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## 1. 准备工作\n", 27 | "\n", 28 | "### 1.1. 安装\n", 29 | "\n", 30 | "下载文档代码及安装依赖项\n", 31 | "```bash\n", 32 | "git clone https://github.com/AlibabaCloudDocs/llm_learning.git\n", 33 | "cd llm_learning\n", 34 | "pip install -r requirements.txt\n", 35 | "```\n", 36 | "\n", 37 | "### 1.2. 账号准备\n", 38 | "\n", 39 | "首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey)\n", 40 | "\n", 41 | "#### MacOS or Linux\n", 42 | "您可以使用以下命令行导入环境变量\n", 43 | "```bash\n", 44 | "export DASHSCOPE_API_KEY=\"sk-****\"\n", 45 | "```\n", 46 | "\n", 47 | "#### Windows\n", 48 | "可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量\n", 49 | "```bat\n", 50 | "set DASHSCOPE_API_KEY=sk-****\n", 51 | "```\n", 52 | "或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 \n", 53 | "```powershell\n", 54 | "$Env:DASHSCOPE_API_KEY = \"sk-****\"\n", 55 | "```\n", 56 | "\n", 57 | "#### Jupyter Notebook\n", 58 | "您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。\n", 59 | "\n", 60 | "### 1.3. docenv模式\n", 61 | "\n", 62 | "将环境变量写入文件\"~/.zshrc\"中:\n", 63 | "\n", 64 | "```bash\n", 65 | "export OPENAI_API_KEY=\"sk-...\"\n", 66 | "```\n", 67 | "就可以执行如下命令 ```source ~/.zshrc``` 将这个环境变量绑定到全局shell中。\n", 68 | "\n", 69 | "接下来我们将加载这个环境变量到notebook中。执行如下命令\n", 70 | "\n", 71 | "**使用Windows,已经通过Windows PowerShell来注册环境变量的同事可以考虑跳过这里**" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 1, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stderr", 81 | "output_type": "stream", 82 | "text": [ 83 | "Python-dotenv could not parse statement starting at line 7\n", 84 | "Python-dotenv could not parse statement starting at line 8\n", 85 | "Python-dotenv could not parse statement starting at line 10\n", 86 | "Python-dotenv could not parse statement starting at line 11\n", 87 | "Python-dotenv could not parse statement starting at line 16\n" 88 | ] 89 | }, 90 | { 91 | "data": { 92 | "text/plain": [ 93 | "True" 94 | ] 95 | }, 96 | "execution_count": 1, 97 | "metadata": {}, 98 | "output_type": "execute_result" 99 | } 100 | ], 101 | "source": [ 102 | "import os\n", 103 | "from dotenv import load_dotenv\n", 104 | "## for MacOS users\n", 105 | "filePath = os.path.abspath(os.path.expanduser(os.path.expandvars(\"~/.zshrc\")))\n", 106 | "load_dotenv(filePath)" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "## 2. MVP 开发\n", 114 | "在正式开发一个完整的程序之前,我们可以先开发一个最小可运行的程序,来验证一下效果。\n", 115 | "\n", 116 | "### 2.1 根据 URL 加载文档\n", 117 | "为了实现根据 URL 读取文档内容,你参考了 LangChain 的[这篇 WebBaseLoader 文档](https://python.langchain.com/docs/integrations/document_loaders/web_base),并尝试在代码中使用 WebBaseLoader 来加载一篇文档:" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 2, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "from langchain_community.document_loaders import WebBaseLoader\n", 127 | "\n", 128 | "loader = WebBaseLoader(\"https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\")\n", 129 | "\n", 130 | "doc = loader.load()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "markdown", 135 | "metadata": {}, 136 | "source": [ 137 | "### 2.2 比较文档内容\n", 138 | "接下来你可以再加载一下国际站的文档,并将其交给通义千问帮你比对差别了:" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 4, 144 | "metadata": {}, 145 | "outputs": [ 146 | { 147 | "name": "stdout", 148 | "output_type": "stream", 149 | "text": [ 150 | "以下是两篇文档的不同之处列表:\n", 151 | "\n", 152 | "1. 文档标题:\n", 153 | " - 中国站文档:快速购买ECS实例_云服务器 ECS(ECS)-阿里云帮助中心\n", 154 | " - 国际站文档:快速购买ECS实例 - 云服务器 ECS - 阿里云\n", 155 | "\n", 156 | "2. 文档内容差异:\n", 157 | " - **前提条件**:\n", 158 | " - 中国站文档提到“已注册账号并完成实名认证”,而国际站文档提到了“注册阿里云账号”。\n", 159 | "\n", 160 | " - **默认配置**:\n", 161 | " - 中国站文档列出的默认配置包括操作系统选项(Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS),而国际站文档只提到CentOS。\n", 162 | " - 可用的实例规格:\n", 163 | " - 中国站文档提到了“实例规格族”和“ecs.s6-c1m1.small”,而国际站文档仅提到“突发性能实例t5”。\n", 164 | " \n", 165 | " - **网络类型**:\n", 166 | " - 中国站文档提及“专有网络”,而国际站文档明确说明是“专有网络”。\n", 167 | " - 公网IP的分配方式:\n", 168 | " - 中国站文档提供两种选择(按固定带宽和按使用流量),国际站文档只有“按固定带宽”这一选项。\n", 169 | "\n", 170 | " - **购买数量**:\n", 171 | " - 中国站文档未提及购买数量的限制,而国际站文档明确提到可以购买的数量。\n", 172 | "\n", 173 | " - **购买时长和自动续费**:\n", 174 | " - 中国站文档让用户选择购买时长和是否自动续费,国际站文档让用户选择购买时长但未提及自动续费。\n", 175 | "\n", 176 | " - **后续步骤**:\n", 177 | " - 中国站文档提到了“反馈”链接,而国际站文档则提到“确认下单”过程,并且没有提及“反馈”。\n", 178 | "\n", 179 | " - **文档末尾**:\n", 180 | " - 中国站文档包含较多的联系方式、服务条款链接、政策声明等,国际站文档则相对简洁,只提到了服务条款链接。\n", 181 | "\n", 182 | "请注意,这些差异可能随时间和版本更新有所变化,具体信息请以最新文档为准。" 183 | ] 184 | } 185 | ], 186 | "source": [ 187 | "from langchain_community.document_loaders import WebBaseLoader\n", 188 | "from langchain_community.llms import Tongyi\n", 189 | "from utils.ds_util import sample_call_streaming\n", 190 | "\n", 191 | "llm = Tongyi()\n", 192 | "llm.model_name = 'qwen-max'\n", 193 | "\n", 194 | "\n", 195 | "def load_doc_content(url):\n", 196 | " loader = WebBaseLoader(url)\n", 197 | " doc = loader.load()\n", 198 | " return doc[0].page_content\n", 199 | "\n", 200 | "\n", 201 | "doc_content_cn = load_doc_content(\"https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\")\n", 202 | "doc_content_intl = load_doc_content(\"https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\")\n", 203 | "\n", 204 | "prompt = f\"\"\"你现在的任务是找出下面两篇文档的不同之处,请不要放过任何细节。\n", 205 | "--------\n", 206 | "【中国站的文档】:\n", 207 | "{doc_content_cn}\n", 208 | "--------\n", 209 | "【国际站的文档】:\n", 210 | "{doc_content_intl}\n", 211 | "--------\n", 212 | "请给出结论,并用列表形式指出不同之处的具体位置:\n", 213 | "\"\"\"\n", 214 | "\n", 215 | "sample_call_streaming(prompt)\n" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "以上就是一个 MVP 版本的程序了。它可以根据 URL 加载文档并进行比较,一定程度上,它已经能帮你减轻很多工作量了。\n", 223 | "\n", 224 | "## 3. 完善程序\n", 225 | "接下来我们可以完善一下程序,以达成我们最初设想的实现目标。\n", 226 | "\n", 227 | "### 3.1 遍历 Excel 中的文档 URL\n", 228 | "因为要比对的文档比较多,而且也经常会变,所以在代码中写死这些链接并不是一个好的做法。 我们可以在 excel 文件中管理 url,然后在程序中读取出来。\n", 229 | "\n", 230 | "参考代码如下:" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 37, 236 | "metadata": {}, 237 | "outputs": [ 238 | { 239 | "name": "stdout", 240 | "output_type": "stream", 241 | "text": [ 242 | "https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 243 | "https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 244 | "https://help.aliyun.com/zh/ecs/user-guide/overview-52\n", 245 | "https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52\n" 246 | ] 247 | } 248 | ], 249 | "source": [ 250 | "import os\n", 251 | "from openpyxl import load_workbook\n", 252 | "\n", 253 | "file_path = 'data/doc_urls.xlsx'\n", 254 | "\n", 255 | "wb = load_workbook(file_path)\n", 256 | "sheet = wb.active\n", 257 | "for row in sheet.iter_rows(values_only=True, min_row=2):\n", 258 | " url_cn = row[0]\n", 259 | " url_intl = row[0].replace('help.aliyun.com', 'www.alibabacloud.com/help')\n", 260 | " print(url_cn)\n", 261 | " print(url_intl)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "你已经可以从 excel 中读取链接,并自动替换获取国际站的文档链接了。 我们可以对 MVP 程序进行一点封装,以便在遍历 excel 里的 url 时调用通义千问进行文档对比。 详细的代码可以查看:\n", 269 | "\n", 270 | "详细的代码可以查看:\n", 271 | "- [chapter2/load-from-excel/diff_docs.py](load-from-excel/diff_docs.py)\n", 272 | "- [chapter2/load-from-excel/main.py](load-from-excel/main.py)\n", 273 | "\n", 274 | "现在,你就可以运行程序,来实现批量自动文档对比了:\n" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": 6, 280 | "metadata": {}, 281 | "outputs": [ 282 | { 283 | "name": "stdout", 284 | "output_type": "stream", 285 | "text": [ 286 | "------\n", 287 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 288 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 289 | "结论:两篇文档描述的是同一主题——快速购买阿里云ECS实例的流程,但存在一些细节差异,主要体现在产品细节、更新时间、操作步骤的细微差别、以及文档结构和布局的不同。\n", 290 | "\n", 291 | "不同之处具体位置列表:\n", 292 | "\n", 293 | "1. **文档标题和布局**\n", 294 | " - 中国站文档标题较长,包含“快速购买ECS实例_云服务器 ECS(ECS)-阿里云帮助中心”,并且文档开头有较多的导航链接和广告内容。\n", 295 | " - 国际站文档标题简洁为“快速购买ECS实例 - 云服务器 ECS - 阿里云”,文档开始直接进入主题,布局较为紧凑,导航元素相对较少。\n", 296 | "\n", 297 | "2. **更新时间**\n", 298 | " - 中国站文档未明确给出更新时间。\n", 299 | " - 国际站文档明确更新时间为“Nov 30, 2023”。\n", 300 | "\n", 301 | "3. **实例规格选项**\n", 302 | " - 中国站文档在“产品规格”部分提及“ecs.s6-c1m1.small”作为示例规格,并指出规格选择与地域等因素有关,同时提到了四种操作系统支持(Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS)。\n", 303 | " - 国际站文档中一键购买仅支持“突发性能实例t5”,规格示例为“2 vCPU 4 GiB(突发性能实例t5)”,并提到三种操作系统支持(CentOS、Windows Server、Ubuntu),未提及Alibaba Cloud Linux。\n", 304 | "\n", 305 | "4. **网络带宽配置说明**\n", 306 | " - 中国站文档在网络带宽配置时,直接列出“按固定带宽”和“按使用流量”的选择说明,而国际站文档在此处详细描述了选择公网带宽的两种模式后,直接给出默认选项“按固定带宽1 Mbps”。\n", 307 | "\n", 308 | "5. **服务条款和确认订单过程**\n", 309 | " - 中国站文档在确认订单时,提及阅读《云服务器 ECS 服务条款》和《云服务器ECS退订说明》。\n", 310 | " - 国际站文档则提及阅读《云服务器 ECS 服务条款》和《通用服务条款》。\n", 311 | "\n", 312 | "6. **文档结尾和反馈机制**\n", 313 | " - 中国站文档末尾包含大量的公司信息、服务链接、法律声明等附加内容。\n", 314 | " - 国际站文档以简短的“谢谢!我们已经收到了您的反馈。”结束,整体更加简洁。\n", 315 | "\n", 316 | "这些差异反映了针对不同地区用户习惯、产品策略和服务条款的调整。\n", 317 | "------\n", 318 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52\n", 319 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52\n", 320 | "经过仔细对比,两篇文档的内容实质上是相同的,都详细介绍了阿里云ECS实例的概述、基础配置、实例规格、镜像、存储、购买方式、使用指导及安全建议等内容。但是,在文档的布局、格式、以及一些辅助性信息上存在差异。以下是具体的不同之处列表:\n", 321 | "\n", 322 | "1. **文档标题和布局**:\n", 323 | " - 中国站文档的标题结构为“实例概述_云服务器 ECS(ECS)-阿里云帮助中心”,并且文档开头有一系列导航链接,如“产品解决方案文档与社区权益中心定价云市场合作伙伴支持与服务了解阿里云备案控制台”等。\n", 324 | " - 国际站文档的标题更为简洁,为“实例概述 - 云服务器 ECS - 阿里云”,文档开头则是一个搜索框和“全部产品”的链接。\n", 325 | "\n", 326 | "2. **更新时间格式**:\n", 327 | " - 中国站文档的更新时间显示为“实例概述更新时间:一键部署产品详情相关技术圈我的收藏”,并未直接给出明确日期。\n", 328 | " - 国际站文档直接给出了具体的更新日期:“更新时间:Jul 18, 2023”。\n", 329 | "\n", 330 | "3. **导航和搜索功能**:\n", 331 | " - 中国站文档中有“文档产品文档输入文档关键字查找”以及一系列快速导航链接到不同产品和功能说明。\n", 332 | " - 国际站文档则在顶部提供了一个统一的搜索框,用于搜索产品文档,并有“搜索本产品”和“全部产品”两个链接。\n", 333 | "\n", 334 | "4. **页面底部信息**:\n", 335 | " - 中国站文档底部包含了丰富的公司信息、服务条款链接、备案信息、许可证明、联系方式等,并且展示了一系列阿里巴巴集团旗下的其他服务链接。\n", 336 | " - 国际站文档底部信息相对精简,主要包含版权信息、更新日期和一个简单的反馈表单提示。\n", 337 | "\n", 338 | "综上所述,两份文档的主要区别在于界面布局、导航结构、更新时间的表达方式以及底部附加信息的详略程度,而关于ECS实例的核心内容描述基本一致。\n" 339 | ] 340 | } 341 | ], 342 | "source": [ 343 | "! python load-from-excel/main.py" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "### 3.2 排除网页中的页头页尾等非关键内容\n", 351 | "细心的你可能会发现运行前面的代码时,通义千问给出的文档差异,可能还会包含一些页面尾部的差异,比如:\n", 352 | "```\n", 353 | "...\n", 354 | "2. 联系方式和链接格式:\n", 355 | " - 中国站文档中提供了详细的联系方式,如“95187-1 在线服务”、“4008013260 在线服务”,并且附带了“我要建议”、“我要投诉”、“体验反馈”等功能入口,同时链接地址采用了中文描述且未直接展示超链接形式。\n", 356 | " - 国际站文档没有在该处明确提供电话联系方式,而是集中在文档底部显示版权信息时提及,并且所有链接均采用英文描述及超链接形式展现。\n", 357 | "\n", 358 | "3. 页面底部信息:\n", 359 | " - 中国站文档在底部包含了更多的附加信息,如“阿里云首页”、“相关技术圈”、“为什么选择阿里云”等,并列出了各类服务热线、法律声明、隐私权政策、廉正举报等链接。\n", 360 | " - 国际站文档在底部只包含了版权信息、许可证编号以及指向其他阿里云产品的链接,联系方式较为简洁。\n", 361 | "...\n", 362 | "```\n", 363 | "\n", 364 | "出现这个情况的原因是,我们给到大模型的文档内容,其实是整个页面的内容。为了解决这一问题,我们需要想办法提取到文档正文本身,排除页头页尾等非文档正文内容。\n", 365 | "\n", 366 | "这里我们可以选择针对阿里云文档页面的结构,编写一个自定义的 loader:" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 39, 372 | "metadata": {}, 373 | "outputs": [], 374 | "source": [ 375 | "from typing import List\n", 376 | "from bs4 import BeautifulSoup\n", 377 | "from langchain_community.document_loaders.base import BaseLoader\n", 378 | "from langchain_community.document_transformers import Html2TextTransformer\n", 379 | "from langchain_core.documents import Document\n", 380 | "import requests\n", 381 | "\n", 382 | "\n", 383 | "class AliyunDocLoader(BaseLoader):\n", 384 | " \"\"\"\n", 385 | " 阿里云文档加载器\n", 386 | " \"\"\"\n", 387 | "\n", 388 | " def __init__(self, urls: List[str]):\n", 389 | " self.urls = urls\n", 390 | "\n", 391 | " def load(self) -> List[Document]:\n", 392 | " results = []\n", 393 | " for url in self.urls:\n", 394 | " content, metadata = self._get_aliyun_help_doc(url)\n", 395 | " doc = Document(page_content=content, metadata=metadata)\n", 396 | " results.append(doc)\n", 397 | " html2text = Html2TextTransformer()\n", 398 | " return html2text.transform_documents(results)\n", 399 | "\n", 400 | " @staticmethod\n", 401 | " def _get_aliyun_help_doc(url: str):\n", 402 | " \"\"\"\n", 403 | " @param url: 文档 URL\n", 404 | " @return: content, metadata. content 为 html,metadata 里包含 TDK\n", 405 | " \"\"\"\n", 406 | " html = requests.get(url).text\n", 407 | " soup = BeautifulSoup(html, features='html.parser')\n", 408 | " markdown_body = soup.find('article', class_='markdown-body') or soup.find('div', class_='markdown-body')\n", 409 | " contents = markdown_body.contents\n", 410 | " content_html = ''.join([str(item) for item in contents])\n", 411 | " content_html = f'

{soup.find(\"h1\").text}

' + content_html\n", 412 | "\n", 413 | " metadata = {\n", 414 | " \"title\": soup.find('title').text,\n", 415 | " \"keywords\": soup.find('meta', attrs={'name': 'keywords'}).attrs['content'],\n", 416 | " \"description\": soup.find('meta', attrs={'name': 'description'}).attrs['content']\n", 417 | " }\n", 418 | " return content_html, metadata" 419 | ] 420 | }, 421 | { 422 | "cell_type": "markdown", 423 | "metadata": {}, 424 | "source": [ 425 | "完整的代码在 [`chapter2/custom-loader/`](custom-loader/) 中。\n", 426 | "现在你可以尝试运行一下使用自定义阿里云文档 loader 的对比程序了:" 427 | ] 428 | }, 429 | { 430 | "cell_type": "code", 431 | "execution_count": 40, 432 | "metadata": {}, 433 | "outputs": [ 434 | { 435 | "name": "stdout", 436 | "output_type": "stream", 437 | "text": [ 438 | "------\n", 439 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 440 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 441 | "根据对比分析,以下是两篇文档中的不同之处:\n", 442 | "\n", 443 | "1. 文档标题:\n", 444 | " - 中国站文档:快速购买包年包月实例\n", 445 | " - 国际站文档:云服务器 ECS:一键购买包年包月实例\n", 446 | "\n", 447 | "2. 操作步骤中关于产品规格的说明和示例:\n", 448 | " - 中国站文档:\n", 449 | " > 产品规格\n", 450 | " > 可选的实例规格和地域等因素有关,您可以在 **使用说明** 列,查看该实例规格适用的场景。如需更多实例规格,请前往自定义购买。\n", 451 | " > 示例:ecs.s6-c1m1.small\n", 452 | " - 国际站文档:\n", 453 | " > 实例规格\n", 454 | " > 一键购买仅支持突发性能实例t5,如需更多实例规格,请前往自定义购买。\n", 455 | " > 示例:2 vCPU 4 GiB(突发性能实例t5)\n", 456 | "\n", 457 | "3. 操作系统和预装应用的选择:\n", 458 | " - 中国站文档提供了操作系统和预装应用两种选择,并指出二者只能选其一。\n", 459 | " - 国际站文档未提及预装应用选项,只提供镜像选择,且列举了三种操作系统:CentOS、Windows Server、Ubuntu。\n", 460 | "\n", 461 | "4. 公网带宽计费模式及配置的展示方式:\n", 462 | " - 中国站文档将公网IP和带宽计费模式分开描述,并列举了两种带宽计费模式的具体内容。\n", 463 | " - 国际站文档在“公网带宽”部分同时包含了公网IP分配和带宽计费模式的选择,且整合在一个列表项中展示。\n", 464 | "\n", 465 | "5. 购买时长和自动续费的展示方式:\n", 466 | " - 中国站文档将购买时长和自动续费作为两个独立参数分别列出。\n", 467 | " - 国际站文档将购买时长和是否启用自动续费合并在一起作为一个整体进行展示。\n", 468 | "\n", 469 | "6. 确认订单环节中的服务条款:\n", 470 | " - 中国站文档提到的服务条款为《云服务器 ECS 服务条款》和《云服务器ECS退订说明》。\n", 471 | " - 国际站文档提到的服务条款为《云服务器 ECS 服务条款》和《通用服务条款》。\n", 472 | "\n", 473 | "7. 确认订单按钮的文字表述:\n", 474 | " - 中国站文档使用的是“确认订单”按钮。\n", 475 | " - 国际站文档使用的是“确认下单”按钮。\n", 476 | "------\n", 477 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52\n", 478 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52\n", 479 | "经过比对,两篇文档内容完全一致,没有任何不同之处。\n" 480 | ] 481 | } 482 | ], 483 | "source": [ 484 | "! python custom-loader/main.py" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "### 3.3 应对超大文档\n", 492 | "以上的程序似乎已经比较完善了。但很快你就会发现,有一些文档非常大,超出通义千问 API 的最大 token 数限制了。\n", 493 | "\n", 494 | "### 3.3.1 更换模型\n", 495 | "经过查阅阿里云的 [DashScope - 通义千问模型](https://help.aliyun.com/zh/dashscope/developer-reference/api-details)说明文档,你会发现正在使用的`qwen-max`最大 token 数是 6k。\n", 496 | "另外,你还发现`qwen-plus`和`qwen-max-longcontext`支持的 token 数分别是 30k 和 28k。\n", 497 | "所以最简单的办法就是修改现有的代码,使用`qwen-plus`和`qwen-max-longcontext`即可。" 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "execution_count": 41, 503 | "metadata": {}, 504 | "outputs": [], 505 | "source": [ 506 | "llm = Tongyi()\n", 507 | "llm.model_name = 'qwen-max-longcontext'" 508 | ] 509 | }, 510 | { 511 | "cell_type": "markdown", 512 | "metadata": {}, 513 | "source": [ 514 | "尽管上面的代码改动已经解决了你遇到的问题,但严谨的你一定会想到,如果遇到了更大的文档,还是超过了 token 数量限制该怎么办。\n", 515 | "\n", 516 | "### 3.3.2 分而治之\n", 517 | "我们可以借鉴 MapReduce 的思想,将文档分成多个小文档分片,然后对每个分片做对比,最后汇总结果。\n", 518 | "\n", 519 | "具体实现思路是:\n", 520 | "1. 我们可以先找出两篇文档中相同的二级标题;\n", 521 | "2. 然后以这些相同的二级标题对文档进行切片;\n", 522 | "3. 对每个切片进行对比,并将结果汇总,得到最终结果。\n", 523 | "\n", 524 | "我们在[`chapter2/long-docs/doc_spliter.py`](long-docs/doc_spliter.py)中提供了两个方法,用于找到相同的二级标题,和对文档进行切片。\n", 525 | "另外在[`chapter2/long-docs/diff_docs.py`](long-docs/diff_docs.py)中调整了对比程序的实现,程序会先对第一个文档切片进行对比,然后带着对比结论,对剩余文档切片进行对比,最终遍历完所有的切片时,就得到了最终答案。\n", 526 | "\n", 527 | "这是切片提问模式下的两段提示词:\n", 528 | "```python\n", 529 | "def get_question_prompt(doc_content_cn, doc_content_intl):\n", 530 | " return f\"\"\"你现在的任务是找出下面两段文档的不同之处,请不要放过任何细节。\n", 531 | "--------\n", 532 | "【中国站的文档】:\n", 533 | "{doc_content_cn}\n", 534 | "--------\n", 535 | "【国际站的文档】:\n", 536 | "{doc_content_intl}\n", 537 | "--------\n", 538 | "请给出结论,并用列表形式指出不同之处的具体位置:\n", 539 | "\"\"\"\n", 540 | "\n", 541 | "\n", 542 | "def get_refine_prompt(doc_content_cn, doc_content_intl, existing_answer):\n", 543 | " return f\"\"\"这是你在前面的文档片段中找出的不同:\n", 544 | "{existing_answer}\n", 545 | "--------\n", 546 | "请找出下面两个文档片段不同之处的具体位置,并补充到之前的结果中:\n", 547 | "--------\n", 548 | "【中国站的文档】:\n", 549 | "{doc_content_cn}\n", 550 | "--------\n", 551 | "【国际站的文档】:\n", 552 | "{doc_content_intl}\n", 553 | "\"\"\"\n", 554 | "```\n", 555 | "\n", 556 | "你可以运行一下试试看:" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 42, 562 | "metadata": {}, 563 | "outputs": [ 564 | { 565 | "name": "stdout", 566 | "output_type": "stream", 567 | "text": [ 568 | "------\n", 569 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 570 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab\n", 571 | "正在对比第1个片段...\n", 572 | "正在对比第2个片段...\n", 573 | "正在对比第3个片段...\n", 574 | "正在对比第4个片段...\n", 575 | "经过比对,两个文档片段在内容上存在以下不同之处:\n", 576 | "\n", 577 | "1. 创建ECS实例时选择产品规格的描述:\n", 578 | " - 中国站文档中:可选产品规格ecs.s6-c1m1.small,并提供了查看使用说明的提示。\n", 579 | " - 国际站文档中:一键购买仅支持突发性能实例t5。\n", 580 | "\n", 581 | "2. 操作系统和预装应用部分:\n", 582 | " - 中国站文档中:详细列出了操作系统(包括Alibaba Cloud Linux、Windows Server、Ubuntu、CentOS)和预装应用,并特别说明二者只能选择其中一个。\n", 583 | " - 国际站文档中:只提及一键购买支持的操作系统镜像类型(CentOS、Windows Server、Ubuntu),未提及预装应用的选择。\n", 584 | "\n", 585 | "3. 带宽计费模式与带宽值的呈现方式:\n", 586 | " - 中国站文档中:将带宽计费模式(按固定带宽或按使用流量)和带宽值分开表述。\n", 587 | " - 国际站文档中:公网带宽部分整合了公网IP分配、带宽计费模式选择(按固定带宽)以及具体的带宽值(1 Mbps)。\n", 588 | "\n", 589 | "4. 购买时长和自动续费选项的顺序和表述:\n", 590 | " - 中国站文档中:先列出购买时长,再列出自动续费选项。\n", 591 | " - 国际站文档中:购买数量后面直接是购买时长及其与自动续费的一体化选择。\n", 592 | "\n", 593 | "5. 确认订单步骤中的页面名称和协议内容:\n", 594 | " - 中国站文档中:在确认订单面板阅读《云服务器 ECS 服务条款》|《云服务器ECS退订说明》,然后确认订单。\n", 595 | " - 国际站文档中:在 **确认订单** 页面阅读《云服务器 ECS 服务条款》|《通用服务条款》,然后确认下单。\n", 596 | "------\n", 597 | "中国站文档:https://help.aliyun.com/zh/ecs/user-guide/overview-52\n", 598 | "国际站文档:https://www.alibabacloud.com/help/zh/ecs/user-guide/overview-52\n", 599 | "正在对比第1个片段...\n", 600 | "正在对比第2个片段...\n", 601 | "正在对比第3个片段...\n", 602 | "正在对比第4个片段...\n", 603 | "正在对比第5个片段...\n", 604 | "正在对比第6个片段...\n", 605 | "正在对比第7个片段...\n", 606 | "经过仔细对比,发现两个文档片段内容完全一致,无任何不同之处。\n" 607 | ] 608 | } 609 | ], 610 | "source": [ 611 | "! python long-docs/main.py" 612 | ] 613 | }, 614 | { 615 | "cell_type": "markdown", 616 | "metadata": {}, 617 | "source": [ 618 | "## 4. 总结\n", 619 | "通过本章的学习,你已经能借助通义千问的 API 来帮助自己完成一些原来要人工完成的工作了,也学会了通过分而治之的思路来解决提示词过长的问题。分而治之的思想在很多场景下都有应用,除了用于做两篇长文档的对比,也可以用于超长文档的总结。\n", 620 | "\n", 621 | "需要注意的时,这里出于学习的目的,文档的切片方法并不是最优的:\n", 622 | "- 提示词长度没有超限时,是可以不用切片的;\n", 623 | "- 一些特殊的文档也可能存在一个二级标题下内容也超过 token 限制,实际用于生产时,需要结合具体业务来定制切片方法。\n", 624 | "\n", 625 | "另外,在实际使用中,你也可以使用 LangChain 提供的 refine 类型的 load_summarize_chain 来完成这个切片后调用大模型的任务。详情可以参考:[LangChain Use cases:Summarization - Refine](https://python.langchain.com/docs/use_cases/summarization#option-3.-refine)。\n", 626 | "\n", 627 | "\n", 628 | "## 5. 参考资料\n", 629 | "- [DashScope](https://dashscope.aliyun.com/)\n", 630 | "- [通义千问 - 模型概览](https://help.aliyun.com/zh/dashscope/developer-reference/api-details)\n", 631 | "- [LangChain](https://python.langchain.com/docs)\n", 632 | "- [LangChain Use cases:Summarization - Refine](https://python.langchain.com/docs/use_cases/summarization#option-3.-refine)" 633 | ] 634 | } 635 | ], 636 | "metadata": { 637 | "kernelspec": { 638 | "display_name": "chatllm", 639 | "language": "python", 640 | "name": "python3" 641 | }, 642 | "language_info": { 643 | "codemirror_mode": { 644 | "name": "ipython", 645 | "version": 3 646 | }, 647 | "file_extension": ".py", 648 | "mimetype": "text/x-python", 649 | "name": "python", 650 | "nbconvert_exporter": "python", 651 | "pygments_lexer": "ipython3", 652 | "version": "3.9.12" 653 | } 654 | }, 655 | "nbformat": 4, 656 | "nbformat_minor": 2 657 | } 658 | -------------------------------------------------------------------------------- /chapter2/load-from-excel/diff_docs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from langchain_community.document_loaders import WebBaseLoader 3 | from langchain_community.llms import Tongyi 4 | 5 | llm = Tongyi() 6 | llm.model_name = 'qwen-max' 7 | 8 | 9 | def _load_doc_content(url): 10 | loader = WebBaseLoader([url]) 11 | docs = loader.load() 12 | return docs[0].page_content 13 | 14 | 15 | def diff(url_cn, url_intl): 16 | doc_content_cn = _load_doc_content(url_cn) 17 | doc_content_intl = _load_doc_content(url_intl) 18 | 19 | prompt = f"""你现在的任务是找出下面两篇文档的不同之处,请不要放过任何细节。 20 | -------- 21 | 【中国站的文档】: 22 | {doc_content_cn} 23 | -------- 24 | 【国际站的文档】: 25 | {doc_content_intl} 26 | -------- 27 | 请给出结论,并用列表形式指出不同之处的具体位置: 28 | """ 29 | 30 | diff_result = llm.invoke(prompt) 31 | return diff_result 32 | -------------------------------------------------------------------------------- /chapter2/load-from-excel/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from openpyxl import load_workbook 4 | from diff_docs import diff 5 | 6 | file_path = os.path.join(os.path.dirname(__file__), '../data/doc_urls.xlsx') 7 | 8 | wb = load_workbook(file_path) 9 | sheet = wb.active 10 | for row in sheet.iter_rows(values_only=True, min_row=2): 11 | url_cn = row[0] 12 | url_intl = row[0].replace('help.aliyun.com', 'www.alibabacloud.com/help') 13 | 14 | print('------') 15 | print(f'中国站文档:{url_cn}') 16 | print(f'国际站文档:{url_intl}') 17 | result = diff(url_cn, url_intl) 18 | print(result) 19 | 20 | -------------------------------------------------------------------------------- /chapter2/long-docs/aliyun_doc_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from typing import List 3 | from bs4 import BeautifulSoup 4 | from langchain_community.document_loaders.base import BaseLoader 5 | from langchain_community.document_transformers import Html2TextTransformer 6 | from langchain_core.documents import Document 7 | import requests 8 | 9 | 10 | class AliyunDocLoader(BaseLoader): 11 | """ 12 | 阿里云文档加载器 13 | """ 14 | 15 | def __init__(self, urls: List[str]): 16 | self.urls = urls 17 | 18 | def load(self) -> List[Document]: 19 | results = [] 20 | for url in self.urls: 21 | content, metadata = self._get_aliyun_help_doc(url) 22 | doc = Document(page_content=content, metadata=metadata) 23 | results.append(doc) 24 | html2text = Html2TextTransformer() 25 | return html2text.transform_documents(results) 26 | 27 | @staticmethod 28 | def _get_aliyun_help_doc(url: str): 29 | """ 30 | @param url: 文档 URL 31 | @return: content, metadata. content 为 html,metadata 里包含 TDK 32 | """ 33 | html = requests.get(url).text 34 | soup = BeautifulSoup(html, features='html.parser') 35 | markdown_body = soup.find('article', class_='markdown-body') or soup.find('div', class_='markdown-body') 36 | contents = markdown_body.contents 37 | content_html = ''.join([str(item) for item in contents]) 38 | content_html = f'

{soup.find("h1").text}

' + content_html 39 | 40 | metadata = { 41 | "title": soup.find('title').text, 42 | "keywords": soup.find('meta', attrs={'name': 'keywords'}).attrs['content'], 43 | "description": soup.find('meta', attrs={'name': 'description'}).attrs['content'] 44 | } 45 | return content_html, metadata 46 | -------------------------------------------------------------------------------- /chapter2/long-docs/diff_docs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from aliyun_doc_loader import AliyunDocLoader 3 | from langchain_community.llms import Tongyi 4 | from doc_spliter import find_same_headers, split_docs 5 | 6 | llm = Tongyi() 7 | llm.model_name = 'qwen-max' 8 | 9 | 10 | def _load_doc_content(url): 11 | loader = AliyunDocLoader([url]) 12 | docs = loader.load() 13 | return docs[0].page_content 14 | 15 | 16 | def get_question_prompt(doc_content_cn, doc_content_intl): 17 | return f"""你现在的任务是找出下面两段文档的不同之处,请不要放过任何细节。 18 | -------- 19 | 【中国站的文档】: 20 | {doc_content_cn} 21 | -------- 22 | 【国际站的文档】: 23 | {doc_content_intl} 24 | -------- 25 | 请给出结论,并用列表形式指出不同之处的具体位置: 26 | """ 27 | 28 | 29 | def get_refine_prompt(doc_content_cn, doc_content_intl, existing_answer): 30 | return f"""这是你在前面的文档片段中找出的不同: 31 | {existing_answer} 32 | -------- 33 | 请找出下面两个文档片段不同之处的具体位置,并补充到之前的结果中: 34 | -------- 35 | 【中国站的文档】: 36 | {doc_content_cn} 37 | -------- 38 | 【国际站的文档】: 39 | {doc_content_intl} 40 | """ 41 | 42 | 43 | def diff(url_cn, url_intl): 44 | doc_content_cn = _load_doc_content(url_cn) 45 | doc_content_intl = _load_doc_content(url_intl) 46 | 47 | same_headers = find_same_headers(doc_content_cn, doc_content_intl) 48 | chunks_cn = split_docs(doc_content_cn, same_headers) 49 | chunks_intl = split_docs(doc_content_intl, same_headers) 50 | 51 | answer = '' 52 | 53 | for index, chunk in enumerate(same_headers): 54 | print(f'正在对比第{index + 1}个片段...') 55 | if index == 0: 56 | prompt = get_question_prompt(chunks_cn[index], chunks_intl[index]) 57 | answer = llm.invoke(prompt) 58 | else: 59 | refine_prompt = get_refine_prompt(chunks_cn[index], chunks_intl[index], answer) 60 | answer = llm.invoke(refine_prompt) 61 | 62 | return answer 63 | -------------------------------------------------------------------------------- /chapter2/long-docs/doc_spliter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import re 3 | 4 | pattern = r"(##\s*.*?)\n" 5 | 6 | 7 | def find_same_headers(content1, content2): 8 | """ 9 | 找出两个文档中相同的标题 10 | @param content1: 文档 1 11 | @param content2: 文档 2 12 | @return: 相同的标题数组 13 | """ 14 | headers1 = re.findall(pattern, content1) 15 | headers2 = re.findall(pattern, content2) 16 | return _find_common_elements(headers1, headers2) 17 | 18 | 19 | def split_docs(content, same_headers): 20 | """ 21 | 根据相同的标题,将文档切片成更小的文档单元 22 | @param content: 文档内容 23 | @param same_headers: 相同的标题 24 | @return: chunks, 文档分片 25 | """ 26 | chunks = [] 27 | start_index = 0 28 | for same_header in same_headers: 29 | index = content.find(same_header) 30 | chunks.append(content[start_index:index]) 31 | start_index = index 32 | chunks.append(content[start_index:]) 33 | return chunks 34 | 35 | 36 | def _find_common_elements(array1, array2): 37 | """ 38 | 按顺序找出两个数组中的相同元素 39 | @param array1: 数组 1 40 | @param array2: 数组 2 41 | @return: 相同的元素数组 42 | """ 43 | common_elements = [] 44 | for elem1 in array1: 45 | if elem1 in array2 and elem1 not in common_elements: 46 | common_elements.append(elem1) 47 | return common_elements 48 | 49 | 50 | if __name__ == '__main__': 51 | content_1 = '''# 快速购买包年包月实例 52 | ... 53 | ## **前提条件** 54 | ... 55 | ## **默认配置** 56 | ... 57 | ## 操作步骤 58 | ... 59 | 60 | ''' 61 | 62 | content_2 = '''# 快速购买包年包月实例 63 | ... 64 | ## **必要条件** 65 | ... 66 | ## **默认配置** 67 | ... 68 | ## 操作步骤 69 | ... 70 | ... 71 | 72 | ''' 73 | common_headers = find_same_headers(content_1, content_2) 74 | print(common_headers) 75 | print(split_docs(content_1, common_headers)) 76 | print(split_docs(content_2, common_headers)) 77 | -------------------------------------------------------------------------------- /chapter2/long-docs/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from openpyxl import load_workbook 4 | from diff_docs import diff 5 | 6 | file_path = os.path.join(os.path.dirname(__file__), '../data/doc_urls.xlsx') 7 | 8 | wb = load_workbook(file_path) 9 | sheet = wb.active 10 | for row in sheet.iter_rows(values_only=True, min_row=2): 11 | url_cn = row[0] 12 | url_intl = row[0].replace('help.aliyun.com', 'www.alibabacloud.com/help') 13 | 14 | print('------') 15 | print(f'中国站文档:{url_cn}') 16 | print(f'国际站文档:{url_intl}') 17 | result = diff(url_cn, url_intl) 18 | print(result) 19 | 20 | -------------------------------------------------------------------------------- /chapter2/mvp/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from langchain_community.document_loaders import WebBaseLoader 3 | from langchain_community.llms import Tongyi 4 | from chapter2.utils.ds_util import sample_call_streaming 5 | 6 | llm = Tongyi() 7 | llm.model_name = 'qwen-max' 8 | 9 | 10 | def load_doc_content(url): 11 | loader = WebBaseLoader(url) 12 | doc = loader.load() 13 | return doc[0].page_content 14 | 15 | 16 | doc_content_cn = load_doc_content("https://help.aliyun.com/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab") 17 | doc_content_intl = load_doc_content("https://www.alibabacloud.com/help/zh/ecs/user-guide/create-a-subscription-instance-on-the-quick-launch-tab") 18 | 19 | prompt = f"""你现在的任务是找出下面两篇文档的不同之处,请不要放过任何细节。 20 | -------- 21 | 【中国站的文档】: 22 | {doc_content_cn} 23 | -------- 24 | 【国际站的文档】: 25 | {doc_content_intl} 26 | -------- 27 | 请给出结论,并用列表形式指出不同之处的具体位置: 28 | """ 29 | 30 | sample_call_streaming(prompt) 31 | 32 | -------------------------------------------------------------------------------- /chapter2/requirements.txt: -------------------------------------------------------------------------------- 1 | dashscope==1.13.6 2 | langchain==0.1.0 3 | langchainhub==0.1.14 4 | beautifulsoup4==4.12.2 5 | openpyxl==3.1.2 6 | html2text==2020.1.16 -------------------------------------------------------------------------------- /chapter2/utils/ds_util.py: -------------------------------------------------------------------------------- 1 | import dashscope 2 | def sample_call_streaming(prompt_text): 3 | response_generator = dashscope.Generation.call( 4 | model='qwen-turbo', 5 | prompt=prompt_text, 6 | stream=True, 7 | top_p=0.8) 8 | # When stream=True, the return is Generator, 9 | # need to get results through iteration 10 | head_idx = 0 11 | for response in response_generator: 12 | paragraph = response.output['text'] 13 | print("\r%s" % paragraph[head_idx:len(paragraph)], end='') 14 | if(paragraph.rfind('\n') != -1): 15 | head_idx = paragraph.rfind('\n') + 1 16 | -------------------------------------------------------------------------------- /chapter3/Agent结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlibabaCloudDocs/llm_learning/c27ee6deefb0f293b35786e7eed0097d4f655ca1/chapter3/Agent结构.png -------------------------------------------------------------------------------- /chapter3/README.md: -------------------------------------------------------------------------------- 1 | # 第 3 章 使用工具扩展问答能力的基本原理 2 | 3 | 经常使用大模型的你,一定会发现大模型并不能回答所有的问题。比如大模型大概率无法回答:你的同事小明是谁,如果小明不是特别出名的话。 4 | 5 | 本章将通过一个简单的例子,让你快速了解如何让大模型利用外部工具,来回答一些原本无法回答的问题。 6 | 7 | 8 | ## 1. 准备工作 9 | 10 | ### 1.1. 安装 11 | 12 | 下载文档代码及安装依赖项 13 | ```bash 14 | git clone https://github.com/AlibabaCloudDocs/llm_learning.git 15 | cd llm_learning 16 | pip install -r requirements.txt 17 | ``` 18 | 19 | ### 1.2. 账号准备 20 | 21 | 首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey) 22 | 23 | #### MacOS or Linux 24 | 您可以使用以下命令行导入环境变量 25 | ```bash 26 | export DASHSCOPE_API_KEY="sk-****" 27 | ``` 28 | 29 | #### Windows 30 | 可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量 31 | ```bat 32 | set DASHSCOPE_API_KEY=sk-**** 33 | ``` 34 | 或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 35 | ```powershell 36 | $Env:DASHSCOPE_API_KEY = "sk-****" 37 | ``` 38 | 39 | #### Jupyter Notebook 40 | 您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。 41 | 42 | ## 2. 快速开始 43 | ### 2.1. 与Qwen-MAX直接对话 44 | 首先我们尝试直接询问大模型关于“阿里云灵积服务”是什么的问题,方式如下: 45 | ```python 46 | from langchain_community.llms import Tongyi 47 | 48 | llm = Tongyi() 49 | # 使用更强大的通义千问 max 50 | llm.model_name = 'qwen-max' 51 | 52 | print(llm.invoke('灵积是什么服务')) 53 | ``` 54 | 输出如下: 55 | ```text 56 | 灵积(或“灵聚”)是一家提供人工智能技术服务的公司,主要服务包括智能语音交互技术、自然语言处理技术及AI解决方案等。其核心技术应用于智能家居、智能车载、机器人等领域,为各类智能终端产品赋予能听、会说、懂思考、有情感的智慧能力。但请注意,由于信息可能具有时效性,建议直接查询最新的官方资料以获取准确信息。 57 | ``` 58 | 我们可以看到由于大模型不了解这个服务,所以在创造一个它认为合理的答案。 59 | 60 | 下面我们来为大模型添加一些知识,让他能正确回答这个问题。 61 | 62 | ### 2.2.基于Langchain调用自定义工具 63 | 64 | 我们首先通过调用langchain来实现自定义工具的调用,然后我们将手动实现langchain的能力,从而让大家很好的理解其中的奥妙。 65 | 66 | 这里我们将使用langchain中的AgentExecutor来实现这个能力: 67 | 68 | (注意:如果你遇到了 TypeError: Additional kwargs key output_tokens already exists in left dict and value has unsupported type 这个错误,请参考[github](https://github.com/langchain-ai/langchain/pull/16580)中的说明) 69 | 70 | ```python 71 | from langchain import hub 72 | from langchain.agents import AgentExecutor 73 | from langchain.agents import create_react_agent 74 | from langchain_community.llms import Tongyi 75 | from langchain_core.tools import BaseTool 76 | 77 | model = Tongyi() 78 | model.model_name = 'qwen-max' 79 | 80 | 81 | class SearchTool(BaseTool): 82 | """服务查询工具""" 83 | 84 | name: str = "服务查询工具" 85 | description: str = ( 86 | "当你不确定一个服务是什么,才使用此工具。" 87 | ) 88 | 89 | def _run(self, name: str) -> str: 90 | if name == '灵积': 91 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 92 | return name + '抱歉,没有查到相关信息。' 93 | 94 | 95 | tools = [SearchTool()] 96 | 97 | prompt = hub.pull("hwchase17/react") 98 | 99 | agent = create_react_agent(model, tools, prompt) 100 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 101 | 102 | result = agent_executor.invoke({'input': '灵积是什么服务'}) 103 | print(result['output']) 104 | ``` 105 | 输出如下: 106 | ```text 107 | > Entering new AgentExecutor chain... 108 | 需要查询服务查询工具来了解“灵积”是什么服务。 109 | Action: 服务查询工具 110 | Action Input: 灵积DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。 111 | DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅! 根据查询结果,“灵积”是阿里云提供的一种模型服务,它基于“模型即服务”(MaaS)的理念,通过标准化API为用户提供多种AI模型服务,包括模型推理、模型微调训练等。 112 | Final Answer: “灵积”是阿里云推出的一款围绕AI各领域模型的模型服务产品,基于“模型即服务”理念,通过标准化API接口为用户提供模型推理、模型微调训练等多种功能。 113 | 114 | > Finished chain. 115 | “灵积”是阿里云推出的一款围绕AI各领域模型的模型服务产品,基于“模型即服务”理念,通过标准化API接口为用户提供模型推理、模型微调训练等多种功能。 116 | ``` 117 | ## 3. 原理说明 118 | 第一次执行时,我们直接向大模型提问:灵积是什么服务。 这时大模型并没有在预训练中学习过关于“灵积”服务的知识,因此无法回答。 119 | 120 | 为了解决这个问题,我们可以借助外部工具,比如搜索引擎工具如Elastic Search等,先查询一下灵积是什么服务,然后再向大模型提问。有了准确的信息补充,大模型就能回答的更准确了。 121 | 122 | 上述代码采用了"hwchase17/react"风格,这种prompt风格定义为: 123 | ```bash 124 | input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'] 125 | template='Answer the following questions as best you can. You have access to the following tools: 126 | 127 | {tools} 128 | 129 | Use the following format: 130 | 131 | Question: the input question you must answer 132 | Thought: you should always think about what to do 133 | Action: the action to take, should be one of [{tool_names}] 134 | Action Input: the input to the action 135 | Observation: the result of the action 136 | ... (this Thought/Action/Action Input/Observation can repeat N times) 137 | Thought: I now know the final answer 138 | Final Answer: the final answer to the original input question 139 | 140 | Begin! 141 | 142 | Question: {input} 143 | Thought:{agent_scratchpad}' 144 | ``` 145 | 在上述prompt中,大模型根据输入的问题{question},来构造了一个思维链。在这个思维链中:问题、思考、行为、行动的输入、行动的结果,这五点是Agent工作的核心。在思考中,我们定义了Agent的scratchpad,这个scratchpad可以用于记录Agent的思考过程。在行动中,我们定义了Agent的工具{tools},这个工具可以用于帮助Agent解决问题。这个思考过程可以进行N次直到Agent认为自己已经解决了问题。这时通过Thought: I now know the final answer来结束这个思考过程,然后通过Final Answer来输出最终的答案。 146 | 147 | 理解了上述过程,我们就可以考虑如何实现Agent的工具。 148 | 149 | ## 4. 自定义大模型XML交互工具 150 | ### 4.1. 定义XML交互格式 151 | 基于上述Langchain的思路我们来研究如何自定义一个工具。为了便于和大模型交互,我们定义一个XML交互工具。当用户向大模型提问时,大模型必须返回XML格式的回复,便于我们后续解析和调用工具。 152 | ```python 153 | from langchain_community.llms import Tongyi 154 | 155 | llm = Tongyi() 156 | # 使用更强大的通义千问 max 157 | llm.model_name = 'qwen-max' 158 | 159 | prompt = '''你是一个可以回答任何问题的助手。 160 | 你可以使用下列工具: 161 | 162 | find_service: 当你不确定答案时,你可以使用此工具。 163 | 164 | 为了使用这个工具,你必须用标签。 165 | 例如,如果您有一个名为“find_service”的工具,可以查询企业内部服务信息,为了搜索阿里云是什么服务,你会返回: 166 | find_service阿里云 167 | 168 | 使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为 169 | find_service阿里云阿里云服务简介... 170 | 171 | 你需要判断标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案: 172 | 阿里云服务简介... 173 | 174 | 如果你不需要使用工具也能回答问题,你也必须要用标签来包裹答案。比如 175 | 阿里云服务详细介绍... 176 | 177 | 开始任务: 178 | 179 | 问题: 180 | ''' 181 | 182 | print(llm.invoke(prompt + '灵积是什么服务')) 183 | ``` 184 | 输出内容 185 | ```bash 186 | find_service灵积 187 | ``` 188 | 189 | ### 4.2. 定义XML解析函数和搜索工具 190 | ```python 191 | from langchain.output_parsers import XMLOutputParser 192 | 193 | # 从xml中获取tool和tool_input 194 | def get_tool_and_input(response): 195 | xml_response = f"{response}" 196 | parsed_xml_response = XMLOutputParser().invoke(xml_response) 197 | response_dict = {} 198 | for item in parsed_xml_response['xml']: 199 | for key in item: 200 | response_dict[key] = item[key] 201 | return response_dict 202 | 203 | # 运行tool 204 | def run_tool(response_dict): 205 | search_input ='' 206 | search_result = '' 207 | if response_dict.get('tool') is None: 208 | search_result = response 209 | elif response_dict['tool'] == 'find_service': 210 | search_input = response_dict['tool_input'] 211 | search_result = SearchTool().run(search_input) 212 | return search_input, search_result 213 | 214 | response="""find_service灵积""" 215 | response_dict = get_tool_and_input(response) 216 | search_input, search_result = run_tool(response_dict) 217 | search_result 218 | ``` 219 | 输出如下: 220 | ```text 221 | 'DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!' 222 | ``` 223 | 224 | ### 4.3.一个完整的工具定义 225 | 226 | 为了更好帮助读者理解大模型Agent调用的原理,下面的代码是我们自己定义了一个简单的工具类 SearchTool 类,而不是继承自langchain的 BaseTool。并且我们实现了一个简单的 DIYAgent 类,这个类参考了langchain的接口,实现基本的调用工具的能力。这样读者可以看到大模型是如何理解用户问题并返回调用工具的指令,而Agent又是如何理解这个指令,并启动对应工具的。 227 | 228 | ```python 229 | from langchain_community.llms import Tongyi 230 | import json 231 | 232 | # 定义模型 233 | llm = Tongyi() 234 | llm.model_name = 'qwen-max' 235 | 236 | # 定义搜索工具 237 | class SearchTool(): 238 | """服务查询工具""" 239 | 240 | name: str = "服务查询工具" 241 | description: str = ( 242 | "当你不确定一个服务是什么,才使用此工具。" 243 | ) 244 | 245 | def run(self, name: str) -> str: 246 | if name == '灵积': 247 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 248 | return name + '抱歉,没有查到相关信息。' 249 | 250 | 251 | # 定义查询模版 252 | prompt_template = '''你是一个可以回答任何问题的助手。 253 | 你可以使用下列工具: 254 | 255 | {tools}: 当你不确定答案时,你可以使用此工具。 256 | 257 | 为了使用这个工具,你必须用标签。 258 | 例如,如果您有一个名为“{tool1}”的工具,可以查询企业内部服务信息,为了搜索阿里云是什么服务,你会返回: 259 | {tool1}阿里云 260 | 261 | 使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为 262 | {tool1}阿里云阿里云服务简介XXX 263 | 264 | 你需要判断标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案: 265 | 阿里云服务简介XXX 266 | 267 | 如果你不需要使用工具也能回答问题,你也必须要用标签来包裹答案。比如 268 | 阿里云服务详细介绍XXX 269 | 270 | 开始任务: 271 | 272 | 问题: 273 | ''' 274 | 275 | 276 | # 定义一个简单的工具调用Agent 277 | class DIYAgent(): 278 | def __init__(self,tool,model=None,prompt=None): 279 | self.tool = tool 280 | self.model = model 281 | self.verbose = False 282 | self.tool_name = self.get_class_name(self.tool) 283 | self.prompt = prompt.format(tools=self.tool_name,tool1 = self.tool_name) 284 | 285 | def get_class_name(self,tool): 286 | return tool.__class__.__name__ 287 | 288 | # 从xml中获取tool和tool_input 289 | def get_tool_and_input(self, response): 290 | xml_response = f"{response}" 291 | parsed_xml_response = XMLOutputParser().invoke(xml_response) 292 | response_dict = {} 293 | for item in parsed_xml_response['xml']: 294 | for key in item: 295 | response_dict[key] = item[key] 296 | return response_dict 297 | 298 | # 运行tool 299 | def run_tool(self, response_dict): 300 | search_input ='' 301 | search_result = '' 302 | 303 | if response_dict.get('tool') is None: 304 | if response_dict.get('final_answer') is not None: 305 | return search_input, response_dict['final_answer'] 306 | else: 307 | return search_input, "Error in tool"+json.dumps(response_dict) 308 | 309 | if response_dict['tool'] == self.tool_name: 310 | search_input = response_dict['tool_input'] 311 | search_result = self.tool.run(search_input) 312 | return search_input, search_result 313 | 314 | # run agent 315 | def run(self, request): 316 | raw_response = self.model.invoke(self.prompt + request) 317 | response_dict = self.get_tool_and_input(raw_response) 318 | print("使用工具:", response_dict['tool']) 319 | print("查询内容:", response_dict['tool_input']) 320 | search_input, search_result = self.run_tool(response_dict) 321 | print("搜索结果:", search_result) 322 | 323 | new_prompt = self.prompt + f"{self.tool_name}{search_input}{search_result}" 324 | final_response = self.model.invoke(new_prompt) 325 | final_response_dict = self.get_tool_and_input(final_response) 326 | search_input, final_answer = self.run_tool(final_response_dict) 327 | return final_answer 328 | 329 | 330 | 331 | # MIAN Function 332 | tool = SearchTool() 333 | 334 | diyAgent = DIYAgent(tool ,llm, prompt_template) 335 | 336 | result = diyAgent.run("灵积是什么服务") 337 | print("最终输出:",result) 338 | ``` 339 | 输出如下: 340 | ```text 341 | 使用工具: SearchTool 342 | 查询内容: 灵积 343 | 搜索结果: DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅! 344 | 最终输出: DashScope灵积模型服务是一个基于“模型即服务”(Model-as-a-Service,MaaS)理念的平台,它提供了AI各领域模型的标准化API服务,包括模型推理、模型微调训练等。该服务依托于业界各领域的优质模型资源,并借助阿里云强大的基础设施构建而成。其目的是为了方便AI应用开发者能够便捷地使用和探索各类模型。 345 | ``` 346 | 347 | ### 4.4. 添加更多的搜索词条 348 | 扩展搜索范围时,我们只需要扩展SearchTool函数的能力即可,比如从搜索引擎上下载信息。为了进行对比,我们直接填入一些已知信息。 349 | ```python 350 | # 扩展支持的服务查询 351 | class SearchTool2(): 352 | """服务查询工具""" 353 | 354 | name: str = "服务查询工具" 355 | description: str = ( 356 | "当你不确定一个服务是什么,才使用此工具。" 357 | ) 358 | 359 | def run(self, name: str) -> str: 360 | if name == '灵积': 361 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 362 | elif name=="百炼": 363 | return "百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。" 364 | elif (name=="PAI") or (name=="阿里云 PAI"): 365 | return "人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。" 366 | elif name=="OSS": 367 | return "阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。" 368 | return name + '抱歉,没有查到相关信息。' 369 | 370 | 371 | # MIAN Function 372 | tool = SearchTool2() 373 | 374 | diyAgent = DIYAgent(tool ,llm, prompt_template) 375 | 376 | result = diyAgent.run("百炼是什么服务") 377 | print("最终输出:",result) 378 | ``` 379 | 输出如下: 380 | ```text 381 | 使用工具: SearchTool2 382 | 查询内容: 百炼 383 | 搜索结果: 百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。 384 | 最终输出: 百炼是阿里云推出的大模型服务平台,旨在为企业客户及合作伙伴提供一站式大模型商业化解决方案。该平台基于通义大模型、行业大模型以及三方大模型,并结合企业专属数据,提供了全链路的大模型开发工具。通过百炼,用户可以进行模型训练、微调、评估等操作,并且拥有预置的丰富应用插件和便捷的集成方式,从而高效快速地构建大模型应用。 385 | ``` 386 | 387 | ### 4.5.使用封装好的代码来实现服务查询 388 | 389 | 我们将上述代码重构后封装在文件[`search-with-tool.py`](search-with-tool.py)中。为了便于后续章节扩展,我们实现了列表式工具加载,与langchain的接口保持一致。 390 | 391 | 列表式工具加载:```tool = [SearchTool()]``` 392 | 393 | 你可以尝试用自己的方式来修改代码。这段代码可以用以下方式来运行: 394 | 395 | ```python3 search-with-tool.py -question="the question"``` 396 | 397 | 尝试执行代码 398 | ```bash 399 | python search-with-tool.py --question="百炼是什么服务" 400 | ``` 401 | 输出内容 402 | ```text 403 | 正在分析内容... 404 | 使用工具:SearchTool... 405 | 查询内容:百炼... 406 | 查询到参考信息:百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义... 407 | 正在分析内容... 408 | 最终答案:百炼是阿里云推出的大模型服务平台,旨在为客户提供一站式大模型商业化解决方案。该平台基于通义大模型、行业大模型及第三方大模型,并结合企业专属数据,提供全链路大模型开发工具,包括模型训练、微调和评估等功能。此外,百炼还预置了丰富的应用插件,方便用户快速集成构建大模型应用,以实现更高效便捷的应用落地。 409 | ``` 410 | 尝试执行代码 411 | ```bash 412 | python search-with-tool.py --question="PAI是什么服务" 413 | ``` 414 | 输出内容 415 | ```text 416 | 正在分析内容... 417 | 使用工具:SearchTool... 418 | 查询内容:PAI 阿里云服务... 419 | 查询到参考信息:人工智能平台 PAI(Platform of Artific... 420 | 正在分析内容... 421 | 最终答案:阿里云PAI服务是面向企业客户及开发者的人工智能平台,提供轻量化、高性价比的云原生人工智能解决方案,覆盖从DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。 422 | ``` 423 | 424 | ## 5. 总结 425 | 本章我们首先用LangChain的的接口定义了一个Agent应用,为了理解大模型使用Agent的内核,我们用自己的代码实现了一遍。这段代码包括工具定义类prompt定义模版,工具调用,工具查询等功能。从这里我们看到,LangChain方法主要思路是通过详细定义的Prompt让大模型反复思考“大模型目前获得的信息是否可以回答用户的问题”,如果不能解答用户的问题,大模型应该使用何种工具;如果可以解答用户的问题,那么就输出结果。 426 | 基于以上分析,为了实现更强大的大模型助理的能力,我们还可能需要为大模型加载更多功能。比如加入一些记忆能力让大模型可以把查询结果、历史对话、历史运行结果进行缓存,这样大模型有可能知道他的目标是什么,现在是什么进度,接下来可以怎么做。这样我们还需要为大模型加入规划的能力,大模型就可以在某一个阶段里,更好的规划下一步行动的计划。我们还可以为大模型加入更多外界反馈,甚至加入机械手和视觉系统,这样大模型就可能做到指导一台机器人去为人类打一杯咖啡。但是,接下来我们想先走一小步,让大模型写代码。 427 | 428 | ## 6. 参考资料 429 | - [DashScope](https://dashscope.aliyun.com/) 430 | - [LangChain](https://python.langchain.com/docs) 431 | 432 | ***** 433 | ## 本章代码 434 | - 请点击[本章实验代码](demo-chapter3.ipynb)查阅相关内容。 435 | 436 | ## 继续学习 437 | - [上一篇:第 2 章 找出网络上两篇文档之间的差异](../chapter2/README.md) 438 | - [下一篇:第 4 章 让通义千问编写并执行代码](../chapter4/README.md) 439 | - [【通义千问API入门教程】章节目录](../README.md) 440 | 441 | 442 | 443 | -------------------------------------------------------------------------------- /chapter3/demo-chapter3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 第 3 章 让大模型使用各种工具\n", 8 | "经常使用大模型的你,一定会发现大模型并不能回答所有的问题。比如大模型大概率无法回答:你的同事小明是谁,如果小明不是特别出名的话。\n", 9 | "\n", 10 | "本章将通过一个简单的例子,让你快速了解如何让大模型利用外部工具,来回答一些原本无法回答的问题。\n" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "## 1. 准备工作\n", 18 | "\n", 19 | "### 1.1. 安装\n", 20 | "\n", 21 | "下载文档代码及安装依赖项\n", 22 | "```bash\n", 23 | "git clone https://github.com/AlibabaCloudDocs/llm_learning.git\n", 24 | "cd llm_learning\n", 25 | "pip install -r requirements.txt\n", 26 | "```\n", 27 | "\n", 28 | "### 1.2. 账号准备\n", 29 | "\n", 30 | "首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey)\n", 31 | "\n", 32 | "#### MacOS or Linux\n", 33 | "您可以使用以下命令行导入环境变量\n", 34 | "```bash\n", 35 | "export DASHSCOPE_API_KEY=\"sk-****\"\n", 36 | "```\n", 37 | "\n", 38 | "#### Windows\n", 39 | "可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量\n", 40 | "```bat\n", 41 | "set DASHSCOPE_API_KEY=sk-****\n", 42 | "```\n", 43 | "或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 \n", 44 | "```powershell\n", 45 | "$Env:DASHSCOPE_API_KEY = \"sk-****\"\n", 46 | "```\n", 47 | "\n", 48 | "#### Jupyter Notebook\n", 49 | "您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。\n", 50 | "\n", 51 | "### 1.3. docenv模式\n", 52 | "\n", 53 | "将环境变量写入文件\"~/.zshrc\"中:\n", 54 | "\n", 55 | "```bash\n", 56 | "export OPENAI_API_KEY=\"sk-...\"\n", 57 | "```\n", 58 | "就可以执行如下命令 ```source ~/.zshrc``` 将这个环境变量绑定到全局shell中。\n", 59 | "\n", 60 | "接下来我们将加载这个环境变量到notebook中。执行如下命令\n", 61 | "\n", 62 | "**使用Windows,已经通过Windows PowerShell来注册环境变量的同事可以考虑跳过这里**" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": 1, 68 | "metadata": {}, 69 | "outputs": [ 70 | { 71 | "name": "stderr", 72 | "output_type": "stream", 73 | "text": [ 74 | "Python-dotenv could not parse statement starting at line 7\n", 75 | "Python-dotenv could not parse statement starting at line 8\n", 76 | "Python-dotenv could not parse statement starting at line 10\n", 77 | "Python-dotenv could not parse statement starting at line 11\n", 78 | "Python-dotenv could not parse statement starting at line 16\n" 79 | ] 80 | }, 81 | { 82 | "data": { 83 | "text/plain": [ 84 | "True" 85 | ] 86 | }, 87 | "execution_count": 1, 88 | "metadata": {}, 89 | "output_type": "execute_result" 90 | } 91 | ], 92 | "source": [ 93 | "import os\n", 94 | "from dotenv import load_dotenv\n", 95 | "## for MacOS users\n", 96 | "filePath = os.path.abspath(os.path.expanduser(os.path.expandvars(\"~/.zshrc\")))\n", 97 | "load_dotenv(filePath)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "## 2.快速开始\n", 105 | "\n", 106 | "### 2.1.与Qwen-MAX直接对话\n" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "首先我们尝试直接询问大模型关于“阿里云灵积服务”是什么的问题,方式如下:" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 2, 119 | "metadata": {}, 120 | "outputs": [ 121 | { 122 | "name": "stdout", 123 | "output_type": "stream", 124 | "text": [ 125 | "对不起,您的问题中提到的“灵积”服务,目前在我的知识库中并未找到相关确切的信息。由于各类服务层出不穷,且可能具有特定行业背景或地域特色,或者可能是新兴、小众的服务品牌或产品名称,因此我无法直接为您提供准确的解释。\n", 126 | "\n", 127 | "为了更好地帮助您,能否请您提供更多关于“灵积”的详细信息?比如它所属的行业领域(如科技、金融、教育等)、主要功能、使用场景,或者任何其他有助于描述这项服务的关键信息。如果有官方网站、相关新闻报道、应用下载链接等,也请一并提供,这样我就能更有效地为您查询和解析。\n", 128 | "\n", 129 | "如果您是指某个特定的公司、产品或平台,请核实其准确名称,以便我进行更精确的搜索和解答。如果“灵积”是您记忆中的名称但细节不太确定,也可以尝试描述您记得的其主要特点或服务内容,我将尽力根据这些线索为您提供相关信息或相似服务的解读。\n", 130 | "\n", 131 | "期待您的进一步反馈,我会竭力帮您了解这个服务的相关情况。\n" 132 | ] 133 | } 134 | ], 135 | "source": [ 136 | "from langchain_community.llms import Tongyi\n", 137 | "\n", 138 | "llm = Tongyi()\n", 139 | "# 使用更强大的通义千问 max\n", 140 | "llm.model_name = 'qwen-max'\n", 141 | "\n", 142 | "print(llm.invoke('灵积是什么服务'))" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "我们可以看到由于大模型不了解这个服务,所以在创造一个它认为合理的答案。\n", 150 | "\n", 151 | "接下来我们来为大模型添加一些知识,让他能正确回答这个问题。\n", 152 | "\n", 153 | "\n", 154 | "### 2.2.基于Langchain调用自定义工具\n", 155 | "\n", 156 | "我们首先通过调用langchain来实现自定义工具的调用,然后我们将手动实现langchain的能力,从而让大家很好的理解其中的奥妙。\n", 157 | "\n", 158 | "这里我们将使用langchain中的AgentExecutor来实现这个能力:\n", 159 | "\n", 160 | "(注意:如果你遇到了 TypeError: Additional kwargs key output_tokens already exists in left dict and value has unsupported type 这个错误,请参考[github](https://github.com/langchain-ai/langchain/pull/16580)中的说明)" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": 3, 166 | "metadata": {}, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "\n", 173 | "\n", 174 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 175 | "\u001b[32;1m\u001b[1;3m 需要查询服务查询工具以了解“灵积”是什么服务\n", 176 | "Action: 服务查询工具\n", 177 | "Action Input: 灵积\u001b[0m\u001b[36;1m\u001b[1;3mDashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\n", 178 | "DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!\u001b[0m\u001b[32;1m\u001b[1;3m 我现在知道“灵积”是阿里云提供的一个基于“模型即服务”理念的模型服务平台,它提供了模型推理、模型微调训练等服务,并整合了业界各领域的优质模型资源,旨在为AI应用开发者提供便捷、高效的模型服务支持。\n", 179 | "Final Answer: “灵积”是阿里云推出的一项“模型即服务”(MaaS)产品,该服务围绕人工智能(AI)各领域的模型构建,通过标准应用程序接口(API)提供模型推理、模型微调训练等多种功能。它整合并利用了行业内的高质量模型资源,借助阿里云的强大基础设施运行。其主要目标是为AI应用开发者打造一条轻松访问与利用各类模型的通道,助力他们在开发过程中快速实现模型应用与创新。\u001b[0m\n", 180 | "\n", 181 | "\u001b[1m> Finished chain.\u001b[0m\n", 182 | "“灵积”是阿里云推出的一项“模型即服务”(MaaS)产品,该服务围绕人工智能(AI)各领域的模型构建,通过标准应用程序接口(API)提供模型推理、模型微调训练等多种功能。它整合并利用了行业内的高质量模型资源,借助阿里云的强大基础设施运行。其主要目标是为AI应用开发者打造一条轻松访问与利用各类模型的通道,助力他们在开发过程中快速实现模型应用与创新。\n" 183 | ] 184 | } 185 | ], 186 | "source": [ 187 | "from langchain import hub\n", 188 | "from langchain.agents import AgentExecutor\n", 189 | "from langchain.agents import create_react_agent\n", 190 | "from langchain_community.llms import Tongyi\n", 191 | "from langchain_core.tools import BaseTool\n", 192 | "\n", 193 | "model = Tongyi()\n", 194 | "model.model_name = 'qwen-max'\n", 195 | "\n", 196 | "\n", 197 | "class SearchTool(BaseTool):\n", 198 | " \"\"\"服务查询工具\"\"\"\n", 199 | "\n", 200 | " name: str = \"服务查询工具\"\n", 201 | " description: str = (\n", 202 | " \"当你不确定一个服务是什么,才使用此工具。\"\n", 203 | " )\n", 204 | "\n", 205 | " def _run(self, name: str) -> str:\n", 206 | " if name == '灵积':\n", 207 | " return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\n", 208 | "DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''\n", 209 | " return name + '抱歉,没有查到相关信息。'\n", 210 | "\n", 211 | "\n", 212 | "tools = [SearchTool()]\n", 213 | "\n", 214 | "prompt = hub.pull(\"hwchase17/react\")\n", 215 | "\n", 216 | "agent = create_react_agent(model, tools, prompt)\n", 217 | "agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n", 218 | "\n", 219 | "result = agent_executor.invoke({'input': '灵积是什么服务'})\n", 220 | "print(result['output'])\n" 221 | ] 222 | }, 223 | { 224 | "cell_type": "markdown", 225 | "metadata": {}, 226 | "source": [ 227 | "## 3.原理说明\n", 228 | "\n", 229 | "第一次执行时,我们直接向大模型提问:灵积是什么服务。 这时大模型并没有在预训练中学习过关于“灵积”服务的知识,因此无法回答。\n", 230 | "\n", 231 | "为了解决这个问题,我们可以借助外部工具,比如搜索引擎工具如Elastic Search等,先查询一下灵积是什么服务,然后再向大模型提问。有了准确的信息补充,大模型就能回答的更准确了。\n", 232 | "\n", 233 | "上述代码采用了\"hwchase17/react\"风格,这种prompt风格定义为:\n", 234 | "```\n", 235 | "input_variables=['agent_scratchpad', 'input', 'tool_names', 'tools'] \n", 236 | "template='Answer the following questions as best you can. You have access to the following tools:\n", 237 | "\n", 238 | "{tools}\n", 239 | "\n", 240 | "Use the following format:\n", 241 | "\n", 242 | "Question: the input question you must answer\n", 243 | "Thought: you should always think about what to do\n", 244 | "Action: the action to take, should be one of [{tool_names}]\n", 245 | "Action Input: the input to the action\n", 246 | "Observation: the result of the action\n", 247 | "... (this Thought/Action/Action Input/Observation can repeat N times)\n", 248 | "Thought: I now know the final answer\n", 249 | "Final Answer: the final answer to the original input question\n", 250 | "\n", 251 | "Begin!\n", 252 | "\n", 253 | "Question: {input}\n", 254 | "Thought:{agent_scratchpad}'\n", 255 | "```\n", 256 | "\n", 257 | "在上述prompt中,大模型根据输入的问题{question},来构造了一个思维链。在这个思维链中:问题、思考、行为、行动的输入、行动的结果,这五点是Agent工作的核心。在思考中,我们定义了Agent的scratchpad,这个scratchpad可以用于记录Agent的思考过程。在行动中,我们定义了Agent的工具{tools},这个工具可以用于帮助Agent解决问题。这个思考过程可以进行N次直到Agent认为自己已经解决了问题。这时通过Thought: I now know the final answer来结束这个思考过程,然后通过Final Answer来输出最终的答案。\n", 258 | "\n", 259 | "理解了上述过程,我们就可以考虑如何实现Agent的工具。\n", 260 | "\n", 261 | "![image.png](Agent结构.png)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "markdown", 266 | "metadata": {}, 267 | "source": [ 268 | "## 4.自定义大模型XML交互工具\n", 269 | "\n", 270 | "### 4.1.定义XML交互格式\n", 271 | "\n", 272 | "基于上述Langchain的思路我们来研究如何自定义一个工具。为了便于和大模型交互,我们定义一个XML交互工具。当用户向大模型提问时,大模型必须返回XML格式的回复,便于我们后续解析和调用工具。\n" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": 6, 278 | "metadata": {}, 279 | "outputs": [ 280 | { 281 | "name": "stdout", 282 | "output_type": "stream", 283 | "text": [ 284 | "很抱歉,作为当前知识库的一部分,我无法找到关于“灵积”这项具体服务的相关信息。它可能是一个较为冷门、新兴的服务,或者是我所访问的信息源中尚未涵盖的内容。如果您能提供更详细的背景信息或上下文,我或许能够更有效地帮助您。另外,如果“灵积”是您所在企业或特定行业内部使用的专属服务,请您直接咨询相关团队或查阅内部文档以获取准确信息。如果您希望我继续尝试使用“find_service”工具进行查询,请告知,但请注意,由于该工具主要适用于查询企业内部服务信息,对于未知的外部服务或公众不普遍知晓的服务,其查询结果可能仍然有限。\n" 285 | ] 286 | } 287 | ], 288 | "source": [ 289 | "from langchain_community.llms import Tongyi\n", 290 | "\n", 291 | "llm = Tongyi()\n", 292 | "# 使用更强大的通义千问 max\n", 293 | "llm.model_name = 'qwen-max'\n", 294 | "\n", 295 | "prompt = '''你是一个可以回答任何问题的助手。\n", 296 | "你可以使用下列工具: \n", 297 | "\n", 298 | "find_service: 当你不确定答案时,你可以使用此工具。\n", 299 | "\n", 300 | "为了使用这个工具,你必须用标签。\n", 301 | "-----\n", 302 | "例如,如果您有一个名为“find_service”的工具,可以查询企业内部服务信息,\n", 303 | "question: 阿里云是什么服务\n", 304 | "completion: find_service阿里云\n", 305 | "\n", 306 | "使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为\n", 307 | "find_service阿里云阿里云服务简介...\n", 308 | "\n", 309 | "你需要判断标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案:\n", 310 | "阿里云服务简介...\n", 311 | "\n", 312 | "如果你不需要使用工具也能回答问题,你也必须要用标签来包裹答案。比如\n", 313 | "阿里云服务详细介绍...\n", 314 | "\n", 315 | "\n", 316 | "开始任务:\n", 317 | "\n", 318 | "问题:\n", 319 | "'''\n", 320 | "\n", 321 | "print(llm.invoke(prompt + '灵积是什么服务'))" 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "### 4.2.定义XML解析函数和搜索工具" 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 7, 334 | "metadata": {}, 335 | "outputs": [ 336 | { 337 | "data": { 338 | "text/plain": [ 339 | "'DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。\\nDashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'" 340 | ] 341 | }, 342 | "execution_count": 7, 343 | "metadata": {}, 344 | "output_type": "execute_result" 345 | } 346 | ], 347 | "source": [ 348 | "from langchain.output_parsers import XMLOutputParser\n", 349 | "\n", 350 | "# 从xml中获取tool和tool_input\n", 351 | "def get_tool_and_input(response):\n", 352 | " xml_response = f\"{response}\"\n", 353 | " parsed_xml_response = XMLOutputParser().invoke(xml_response)\n", 354 | " response_dict = {}\n", 355 | " for item in parsed_xml_response['xml']:\n", 356 | " for key in item:\n", 357 | " response_dict[key] = item[key]\n", 358 | " return response_dict\n", 359 | "\n", 360 | "# 运行tool\n", 361 | "def run_tool(response_dict):\n", 362 | " search_input =''\n", 363 | " search_result = ''\n", 364 | " if response_dict.get('tool') is None:\n", 365 | " search_result = response\n", 366 | " elif response_dict['tool'] == 'find_service':\n", 367 | " search_input = response_dict['tool_input']\n", 368 | " search_result = SearchTool().run(search_input)\n", 369 | " return search_input, search_result\n", 370 | "\n", 371 | "response=\"\"\"find_service灵积\"\"\"\n", 372 | "response_dict = get_tool_and_input(response)\n", 373 | "search_input, search_result = run_tool(response_dict)\n", 374 | "search_result\n" 375 | ] 376 | }, 377 | { 378 | "cell_type": "markdown", 379 | "metadata": {}, 380 | "source": [ 381 | "### 4.3.一个完整的工具定义\n", 382 | "\n", 383 | "为了更好帮助读者理解大模型Agent调用的原理,下面的代码是我们自己定义了一个简单的工具类 SearchTool 类,而不是继承自langchain的 BaseTool。并且我们实现了一个简单的 DIYAgent 类,这个类参考了langchain的接口,实现基本的调用工具的能力。这样读者可以看到大模型是如何理解用户问题并返回调用工具的指令,而Agent又是如何理解这个指令,并启动对应工具的。" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": 8, 389 | "metadata": {}, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "原始响应: SearchTool灵积\n", 396 | "使用工具: SearchTool\n", 397 | "查询内容: 灵积\n", 398 | "搜索结果: DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!\n", 399 | "最终输出: DashScope灵积模型服务是一种基于“模型即服务”(Model-as-a-Service,MaaS)理念构建的服务平台,专注于提供人工智能各领域的模型服务。该服务以标准化API接口的形式,支持模型推理与模型微调训练等多样化功能。其核心优势在于整合并利用了行业内的优质模型资源,且依托于阿里巴巴集团强大的云计算基础设施。对于AI应用开发者而言,DashScope灵积模型服务旨在成为他们便捷获取、使用及定制模型的理想之选,助力他们在AI开发过程中快速推进模型探索与应用实践。\n" 400 | ] 401 | } 402 | ], 403 | "source": [ 404 | "from langchain_community.llms import Tongyi\n", 405 | "import json\n", 406 | "\n", 407 | "# 定义模型\n", 408 | "llm = Tongyi()\n", 409 | "llm.model_name = 'qwen-max'\n", 410 | "\n", 411 | "# 定义搜索工具\n", 412 | "class SearchTool():\n", 413 | " \"\"\"服务查询工具\"\"\"\n", 414 | "\n", 415 | " name: str = \"服务查询工具\"\n", 416 | " description: str = (\n", 417 | " \"当你不确定一个服务是什么,才使用此工具。\"\n", 418 | " )\n", 419 | "\n", 420 | " def run(self, name: str) -> str:\n", 421 | " if name == '灵积':\n", 422 | " return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''\n", 423 | " return name + '抱歉,没有查到相关信息。'\n", 424 | " \n", 425 | "\n", 426 | "# 定义查询模版\n", 427 | "prompt_template = '''你是一个可以回答任何问题的助手。\n", 428 | "你可以使用下列工具: \n", 429 | "\n", 430 | "{tools}: 当你不确定答案时,你可以使用此工具。\n", 431 | "\n", 432 | "为了使用这个工具,你必须用标签。\n", 433 | "例如,如果您有一个名为“{tool1}”的工具,可以查询企业内部服务信息,为了搜索(阿里云)是什么服务,你会返回:\n", 434 | "{tool1}阿里云\n", 435 | "\n", 436 | "使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为\n", 437 | "{tool1}阿里云阿里云服务简介XXX\n", 438 | "\n", 439 | "你需要判断标签中的内容是不是你需要的答案。如果是最终答案final_answer,你需要返回答案:\n", 440 | "阿里云服务简介XXX\n", 441 | "\n", 442 | "如果你不需要使用工具也能回答问题,你也必须要用标签来包裹答案。比如\n", 443 | "阿里云服务详细介绍XXX\n", 444 | "\n", 445 | "开始任务:\n", 446 | "\n", 447 | "问题:\n", 448 | "'''\n", 449 | "\n", 450 | "\n", 451 | "# 定义一个简单的工具调用Agent\n", 452 | "class DIYAgent():\n", 453 | " def __init__(self,tool,model=None,prompt=None):\n", 454 | " self.tool = tool\n", 455 | " self.model = model\n", 456 | " self.verbose = False\n", 457 | " self.tool_name = self.get_class_name(self.tool) \n", 458 | " self.prompt = prompt.format(tools=self.tool_name,tool1 = self.tool_name)\n", 459 | "\n", 460 | " def get_class_name(self,tool):\n", 461 | " return tool.__class__.__name__\n", 462 | " \n", 463 | " # 从xml中获取tool和tool_input\n", 464 | " def get_tool_and_input(self, response):\n", 465 | " xml_response = f\"{response}\"\n", 466 | " parsed_xml_response = XMLOutputParser().invoke(xml_response)\n", 467 | " response_dict = {}\n", 468 | " for item in parsed_xml_response['xml']:\n", 469 | " for key in item:\n", 470 | " response_dict[key] = item[key]\n", 471 | " return response_dict\n", 472 | "\n", 473 | " # 执行工具\n", 474 | " def run_tool(self, response_dict):\n", 475 | " search_input =''\n", 476 | " search_result = ''\n", 477 | "\n", 478 | " if response_dict.get('tool') is None:\n", 479 | " if response_dict.get('final_answer') is not None:\n", 480 | " return search_input, response_dict['final_answer']\n", 481 | " else:\n", 482 | " return search_input, \"Error in tool\"+json.dumps(response_dict)\n", 483 | "\n", 484 | " if response_dict['tool'] == self.tool_name:\n", 485 | " search_input = response_dict['tool_input']\n", 486 | " search_result = self.tool.run(search_input)\n", 487 | " return search_input, search_result\n", 488 | "\n", 489 | " # run agent\n", 490 | " def run(self, request): \n", 491 | " raw_response = self.model.invoke(self.prompt + request)\n", 492 | " print(\"原始响应:\", raw_response)\n", 493 | " response_dict = self.get_tool_and_input(raw_response)\n", 494 | " print(\"使用工具:\", response_dict['tool'])\n", 495 | " print(\"查询内容:\", response_dict['tool_input'])\n", 496 | " search_input, search_result = self.run_tool(response_dict)\n", 497 | " print(\"搜索结果:\", search_result)\n", 498 | "\n", 499 | " new_prompt = self.prompt + f\"{self.tool_name}{search_input}{search_result}\"\n", 500 | " final_response = self.model.invoke(new_prompt)\n", 501 | " final_response_dict = self.get_tool_and_input(final_response)\n", 502 | " search_input, final_answer = self.run_tool(final_response_dict)\n", 503 | " return final_answer\n", 504 | "\n", 505 | "\n", 506 | "# MIAN Function\n", 507 | "tool = SearchTool()\n", 508 | "\n", 509 | "diyAgent = DIYAgent(tool ,llm, prompt_template)\n", 510 | "\n", 511 | "result = diyAgent.run(\"灵积是什么服务\")\n", 512 | "print(\"最终输出:\",result)\n" 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": {}, 518 | "source": [ 519 | "### 4.4.添加更多的搜索词条\n", 520 | "\n", 521 | "扩展搜索范围时,我们只需要扩展SearchTool函数的能力即可,比如从搜索引擎上下载信息。为了进行对比,我们直接填入一些已知信息。\n" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": 16, 527 | "metadata": {}, 528 | "outputs": [ 529 | { 530 | "name": "stdout", 531 | "output_type": "stream", 532 | "text": [ 533 | "原始响应: SearchTool2百炼\n", 534 | "使用工具: SearchTool2\n", 535 | "查询内容: 百炼\n", 536 | "搜索结果: 百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。\n", 537 | "最终输出: 百炼是面向企业客户及合作伙伴的大模型服务平台,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,提供全链路大模型开发工具,包括模型训练、微调、评估等产品工具。该平台预置丰富应用插件,支持便捷的集成方式,旨在帮助用户快速高效地构建大模型应用。\n" 538 | ] 539 | } 540 | ], 541 | "source": [ 542 | "# 扩展支持的服务查询\n", 543 | "class SearchTool2():\n", 544 | " \"\"\"服务查询工具\"\"\"\n", 545 | "\n", 546 | " name: str = \"服务查询工具\"\n", 547 | " description: str = (\n", 548 | " \"当你不确定一个服务是什么,才使用此工具。\"\n", 549 | " )\n", 550 | "\n", 551 | " def run(self, name: str) -> str:\n", 552 | " if name == '灵积':\n", 553 | " return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!'''\n", 554 | " elif name==\"百炼\":\n", 555 | " return \"百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。\"\n", 556 | " elif (name==\"PAI\") or (name==\"阿里云 PAI\"):\n", 557 | " return \"人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。\"\n", 558 | " elif name==\"OSS\":\n", 559 | " return \"阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。\"\n", 560 | " return name + '抱歉,没有查到相关信息。'\n", 561 | " \n", 562 | "\n", 563 | "# MIAN Function\n", 564 | "tool = SearchTool2()\n", 565 | "\n", 566 | "diyAgent = DIYAgent(tool ,llm, prompt_template)\n", 567 | "\n", 568 | "result = diyAgent.run(\"百炼是什么服务\")\n", 569 | "print(\"最终输出:\",result)\n" 570 | ] 571 | }, 572 | { 573 | "cell_type": "markdown", 574 | "metadata": {}, 575 | "source": [ 576 | "### 4.5.使用封装好的代码来实现服务查询\n", 577 | "\n", 578 | "我们将上述代码重构后封装在文件[`search-with-tool.py`](search-with-tool.py)中。为了便于后续章节扩展,我们实现了列表式工具加载,与langchain的接口保持一致。\n", 579 | " \n", 580 | "列表式工具加载:```tool = [SearchTool()]```\n", 581 | "\n", 582 | "你可以尝试用自己的方式来修改代码。这段代码可以用以下方式来运行:\n", 583 | "\n", 584 | "```bash\n", 585 | "python3 search-with-tool.py -question=\"the question\"\n", 586 | "```" 587 | ] 588 | }, 589 | { 590 | "cell_type": "code", 591 | "execution_count": 17, 592 | "metadata": {}, 593 | "outputs": [ 594 | { 595 | "name": "stdout", 596 | "output_type": "stream", 597 | "text": [ 598 | "\u001b[32m正在分析内容...\u001b[0m\n", 599 | "\u001b[32m使用工具:SearchTool...\u001b[0m\n", 600 | "\u001b[32m查询内容:百炼...\u001b[0m\n", 601 | "\u001b[32m查询到参考信息:百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义...\u001b[0m\n", 602 | "\u001b[32m正在分析内容...\u001b[0m\n", 603 | "\u001b[32m最终答案:百炼是面向企业客户及合作伙伴的大模型服务平台,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,具备全链路大模型开发工具,支持一站式大模型商业化应用。平台提供模型训练、微调、评估等全套产品工具,内置丰富应用插件,并以便捷的集成方式助力用户快速高效构建大模型应用。\u001b[0m\n" 604 | ] 605 | } 606 | ], 607 | "source": [ 608 | "! python search-with-tool.py --question=\"百炼是什么服务\"" 609 | ] 610 | }, 611 | { 612 | "cell_type": "code", 613 | "execution_count": 18, 614 | "metadata": {}, 615 | "outputs": [ 616 | { 617 | "name": "stdout", 618 | "output_type": "stream", 619 | "text": [ 620 | "\u001b[32m正在分析内容...\u001b[0m\n", 621 | "\u001b[32m使用工具:SearchTool...\u001b[0m\n", 622 | "\u001b[32m查询内容:PAI...\u001b[0m\n", 623 | "\u001b[32m查询到参考信息:人工智能平台 PAI(Platform of Artific...\u001b[0m\n", 624 | "\u001b[32m正在分析内容...\u001b[0m\n", 625 | "\u001b[32m最终答案:PAI(Platform of Artificial Intelligence)是面向企业客户及开发者的人工智能平台,它提供轻量化、高性价比的云原生人工智能服务。PAI覆盖了从DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。\u001b[0m\n" 626 | ] 627 | } 628 | ], 629 | "source": [ 630 | "! python search-with-tool.py --question=\"PAI是什么服务\"" 631 | ] 632 | }, 633 | { 634 | "cell_type": "markdown", 635 | "metadata": {}, 636 | "source": [ 637 | "## 5. 总结\n", 638 | "本章我们首先用LangChain的的接口定义了一个Agent应用,为了理解大模型使用Agent的内核,我们用自己的代码实现了一遍。这段代码包括工具定义类prompt定义模版,工具调用,工具查询等功能。从这里我们看到,LangChain方法主要思路是通过详细定义的Prompt让大模型反复思考“大模型目前获得的信息是否可以回答用户的问题”,如果不能解答用户的问题,大模型应该使用何种工具;如果可以解答用户的问题,那么就输出结果。\n", 639 | "基于以上分析,为了实现更强大的大模型助理的能力,我们还可能需要为大模型加载更多功能。比如加入一些记忆能力让大模型可以把查询结果、历史对话、历史运行结果进行缓存,这样大模型有可能知道他的目标是什么,现在是什么进度,接下来可以怎么做。这样我们还需要为大模型加入规划的能力,大模型就可以在某一个阶段里,更好的规划下一步行动的计划。我们还可以为大模型加入更多外界反馈,甚至加入机械手和视觉系统,这样大模型就可能做到指导一台机器人去为人类打一杯咖啡。但是,接下来我们想先走一小步,让大模型写代码。\n", 640 | "\n", 641 | "## 6. 参考资料\n", 642 | "- [DashScope](https://dashscope.aliyun.com/)\n", 643 | "- [LangChain](https://python.langchain.com/docs)" 644 | ] 645 | }, 646 | { 647 | "cell_type": "markdown", 648 | "metadata": {}, 649 | "source": [] 650 | } 651 | ], 652 | "metadata": { 653 | "kernelspec": { 654 | "display_name": "chatllm", 655 | "language": "python", 656 | "name": "python3" 657 | }, 658 | "language_info": { 659 | "codemirror_mode": { 660 | "name": "ipython", 661 | "version": 3 662 | }, 663 | "file_extension": ".py", 664 | "mimetype": "text/x-python", 665 | "name": "python", 666 | "nbconvert_exporter": "python", 667 | "pygments_lexer": "ipython3", 668 | "version": "3.9.12" 669 | } 670 | }, 671 | "nbformat": 4, 672 | "nbformat_minor": 2 673 | } 674 | -------------------------------------------------------------------------------- /chapter3/requirements.txt: -------------------------------------------------------------------------------- 1 | dashscope==1.13.6 2 | langchain==0.1.0 3 | langchainhub==0.1.14 -------------------------------------------------------------------------------- /chapter3/search-with-tool.py: -------------------------------------------------------------------------------- 1 | from langchain_community.llms import Tongyi 2 | from langchain.output_parsers import XMLOutputParser 3 | import argparse 4 | 5 | # 定义模型 6 | llm = Tongyi() 7 | llm.model_name = 'qwen-max' 8 | 9 | # 定义搜索工具 10 | class SearchTool(): 11 | """服务查询工具""" 12 | 13 | name: str = "阿里云服务查询工具" 14 | description: str = ( 15 | "当你不确定一个阿里云服务是什么,才使用此工具。" 16 | ) 17 | 18 | def run(self, name: str) -> str: 19 | if(name=="灵积") or (name=="灵积 阿里云服务"): 20 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 21 | elif name=="百炼": 22 | return "百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。" 23 | elif (name=="PAI") or (name=="阿里云 PAI")or (name=="PAI 阿里云服务"): 24 | return "人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。" 25 | elif name=="OSS": 26 | return "阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。" 27 | return name + '抱歉,没有查到相关信息。' 28 | 29 | 30 | # 定义查询模版 31 | prompt_template = '''你是一个可以回答任何问题的助手。 32 | 你可以使用下列工具: 33 | 34 | {tools} 35 | 36 | 为了使用这个工具,你必须用标签。 37 | 例如,如果您有一个名为“{tool1}”的工具,可以查询企业内部服务信息,为了搜索(阿里云)是什么服务,你会返回: 38 | {tool1}阿里云 39 | 40 | 使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为 41 | {tool1}阿里云阿里云服务简介等等 42 | 43 | 你需要判断标签中的内容是不是你需要的答案。如果是final_answer,你需要返回答案: 44 | 阿里云服务简介等等 45 | 46 | 不管你是否选用工具你都必须要用标签来包裹最终返回答案。比如 47 | 阿里云服务详细介绍等等 48 | 49 | 如果是final_answer,请检查返回答案的结尾,该结尾必须有标签结束 50 | 51 | 开始任务: 52 | 53 | 问题: 54 | ''' 55 | 56 | 57 | # 定义一个简单的工具调用Agent 58 | class DIYAgent(): 59 | def __init__(self,tools,model=None,prompt=None): 60 | self.tools = tools 61 | self.model = model 62 | self.verbose = False 63 | self.tool_name = [self.get_class_name(t) for t in self.tools] 64 | self.tool_prompt_desc = [self.get_class_name(t)+": "+t.name for t in self.tools] 65 | self.prompt = prompt.format(tools=self.tool_prompt_desc, tool1 = self.tool_name[0]) 66 | 67 | def get_class_name(self,tool): 68 | return tool.__class__.__name__ 69 | 70 | # 从xml中获取tool和tool_input 71 | def _parse_tool_args(self, response): 72 | xml_response = f"{response}" 73 | parsed_xml_response = XMLOutputParser().invoke(xml_response) 74 | response_dict = {} 75 | for item in parsed_xml_response['xml']: 76 | for key in item: 77 | response_dict[key] = item[key] 78 | return response_dict 79 | 80 | # 由于大模型可能不能正确返回,所以需要增加大量异常处理和判断逻辑 81 | def parsed_xml_response(self, response): 82 | response_dict={} 83 | try: 84 | response_dict = self._parse_tool_args(response) 85 | except Exception as e: 86 | if (response.startswith('')): 87 | response += '' 88 | try: 89 | response_dict = self._parse_tool_args(response) 90 | except Exception as e: 91 | return False, response 92 | else: 93 | return False, response 94 | # 正常返回结束 95 | if response_dict.get('final_answer') is not None: 96 | return False, response_dict['final_answer'] 97 | elif response_dict.get('tool') is None: 98 | return False, response 99 | # 正常返回工具信息 100 | return True, response_dict 101 | 102 | # 找到合适的工具并执行 103 | def run_tool(self, response_dict): 104 | search_input ='' 105 | search_result = '' 106 | for t in self.tools: 107 | tool_name = self.get_class_name(t) 108 | if response_dict['tool'] == tool_name: 109 | search_input = response_dict['tool_input'] 110 | search_result = t.run(search_input) 111 | return tool_name, search_input, search_result 112 | 113 | # run agent 114 | def run(self, request): 115 | prompt= self.prompt + request 116 | while True: 117 | print(f"\033[32m正在分析内容...\033[0m") 118 | raw_response = self.model.invoke(prompt) 119 | is_dict, response_dict=self.parsed_xml_response(raw_response) 120 | if(is_dict==False): 121 | return response_dict 122 | 123 | print(f"\033[32m使用工具:{response_dict['tool']}...\033[0m") 124 | print(f"\033[32m查询内容:{response_dict['tool_input']}...\033[0m") 125 | 126 | tool_name, search_input, search_result = self.run_tool(response_dict) 127 | 128 | prompt = self.prompt + f"{tool_name}{search_input}{search_result}" 129 | print(f"\033[32m查询到参考信息:{search_result[0:30]}...\033[0m") 130 | 131 | 132 | if __name__ == '__main__': 133 | # define arguments 134 | parser = argparse.ArgumentParser() 135 | parser.add_argument('--question', type=str, default='灵积是什么服务', help='question to be answered') 136 | 137 | # extract arguments 138 | args = parser.parse_args() 139 | 140 | # define tools 141 | tool = [SearchTool()] 142 | 143 | # run agent 144 | diyAgent = DIYAgent(tool ,llm, prompt_template) 145 | result = diyAgent.run(args.question) 146 | print(f"\033[32m最终答案:{result}\033[0m") 147 | -------------------------------------------------------------------------------- /chapter4/README.md: -------------------------------------------------------------------------------- 1 | # 第 4 章 让大模型写代码和跑代码 2 | 基于前面三章的铺垫,本章我们将展示大模型Agent的强大能力。我们不仅要实现让大模型同时使用多种查询工具,还要实现让大模型能查询天气情况,最后让大模型自己写代码来查询天气情况。 3 | 4 | 在前面的学习中,你已经尝试让大模型学会了使用一个工具,来回答原本不能回答的问题。但是你可能会发现,实际问题中你需要更多的工具: 5 | - Q:能不能问天气情况? 6 | - Q:能不能写代码? 7 | - Q:能不能代码开发再精简一点,好维护一点? 8 | 9 | 可以,本章不仅一股脑全部教给你,还要让大模型自己写代码自己查天气! 10 | 11 | 12 | ## 1. 准备工作 13 | 14 | ### 1.1. 安装 15 | 16 | 下载文档代码及安装依赖项 17 | ```bash 18 | git clone https://github.com/AlibabaCloudDocs/llm_learning.git 19 | cd llm_learning 20 | pip install -r requirements.txt 21 | ``` 22 | 23 | ### 1.2. 账号准备 24 | 25 | 首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey),请于[RAM访问控制](https://ram.console.aliyun.com/manage/ak)创建和查询您的阿里云AK/SK。 26 | 27 | #### MacOS or Linux 28 | 您可以使用以下命令行导入环境变量 29 | ```bash 30 | export DASHSCOPE_API_KEY="sk-****" 31 | export ALIBABA_CLOUD_ACCESS_KEY_ID="" 32 | export ALIBABA_CLOUD_ACCESS_KEY_SECRET="" 33 | ``` 34 | 35 | #### Windows 36 | 可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量 37 | ```bat 38 | set DASHSCOPE_API_KEY=sk-**** 39 | set ALIBABA_CLOUD_ACCESS_KEY_ID=your access key id 40 | set ALIBABA_CLOUD_ACCESS_KEY_SECRET=your access key secret 41 | ``` 42 | 或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 43 | ```powershell 44 | $Env:DASHSCOPE_API_KEY = "sk-****" 45 | $Env:ALIBABA_CLOUD_ACCESS_KEY_ID = "" 46 | $Env:ALIBABA_CLOUD_ACCESS_KEY_SECRET = "" 47 | ``` 48 | 49 | #### Jupyter Notebook 50 | 您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。 51 | 52 | 53 | 54 | 55 | ## 2. 搭建能同时使用更多工具的Agent 56 | 第二章的例子,通义千问能够回答"灵积服务是什么"这个原本它无法回答的问题了。如果我们想让通义千问回答更多问题,比如:杭州的天气怎么样?我的ECS实例什么时候到期?很明显,目前通义千问是无法回答这些问题的。但是你现在已经知道,给它加两个工具就行了。 57 | 58 | 首先,我们可以尝试自定义Agent的方法。我们用第三章自定义的类`DIYAgent`同时加载了三个Agent查询工具,代码在文件[`search-with-tools.py`](more_tools/search-with-tools.py)中,接下来我们看一下大模型能否自动使用不同工具完成任务。 59 | 60 | ### 2.1. 搭建天气查询工具 61 | 天气查询工具的代码如下 62 | ```python 63 | # 定义天气查询工具 64 | class WeatherSearch(BaseTool): 65 | """天气查询工具""" 66 | 67 | name: str = "天气查询工具" 68 | description: str = ( 69 | "用于查询近期的天气,使用此工具。" 70 | ) 71 | 72 | def _run(self, city: str) -> str: 73 | result = requests.get('http://wttr.in/' + city) 74 | return result.text 75 | ``` 76 | 你可以尝试运行一下这段代码,它会回答"杭州的天气怎么样"这个问题: 77 | ```bash 78 | python more_tools/search-with-tools.py --question="杭州的天气怎么样" 79 | ``` 80 | 输出内容 81 | ```text 82 | 正在分析内容... 83 | 使用工具:WeatherSearch... 84 | 查询内容:杭州... 85 | 查询到参考信息:Weather report: 杭州 86 | 87 | 正在分析内容... 88 | 最终答案:杭州的当前天气情况如下: 89 | 90 | - 实时天气:晴朗 91 | - 温度:23°C(体感温度25°C) 92 | - 风速:22 km/h,方向为下降风 93 | - 能见度:8公里 94 | - 湿度:0.0 mm 95 | 96 | 未来几天预报: 97 | - 2月19日(周一):早晨部分多云,午后部分多云,傍晚有小雨,夜间有零星小雨。气温在18°C至21°C之间变化,风速从2-3 km/h到8-9 km/h不等。 98 | - 2月20日(周二):全天均有零星小雨,气温维持在+10°C左右,风向偏南风,风速约在11-16 km/h范围内。 99 | - 2月21日(周三):早晨有小雨,午后短暂阵雨,傍晚局部地区有小雨,夜间中雨。气温逐渐降低,从+9°C降至+4°C,风速在9-18 km/h到11-15 km/h之间。 100 | 101 | 地点:杭州市, 江干区 (Jianggan), 杭州市 Hangzhou, 浙江省, 中国 [30.2489468,120.2052547] 102 | ``` 103 | 104 | ### 2.2. 自定义查询工具 105 | 我们仍然可以为大模型加载在第三章定义过的自定义查询工具, 106 | 107 | 自定义查询工具的代码如下 108 | ```python 109 | # 定义搜索工具 110 | class SearchTool(BaseTool): 111 | """服务查询工具""" 112 | 113 | name: str = "阿里云服务查询工具" 114 | description: str = ( 115 | "当你不确定一个阿里云服务是什么,才使用此工具。" 116 | ) 117 | 118 | def _run(self, name: str) -> str: 119 | if(name=="灵积") or (name=="灵积 阿里云服务"): 120 | return '''DashScope灵积模型服...(此处省略)''' 121 | elif name=="百炼": 122 | return "百炼,即大模型服务平台...(此处省略)" 123 | elif (name=="PAI") or (name=="阿里云 PAI"): 124 | return "人工智能平台 PAI...(此处省略)" 125 | elif name=="OSS": 126 | return "阿里云对象存储OSS(...(此处省略)" 127 | return name + '抱歉,没有查到相关信息。' 128 | ``` 129 | 130 | 我们看一下其输出: 131 | 132 | ```bash 133 | python more_tools/search-with-tools.py --question="灵积是什么服务" 134 | ``` 135 | 输出内容 136 | ``` 137 | 正在分析内容... 138 | 使用工具:SearchTool... 139 | 查询内容:灵积... 140 | 查询到参考信息:DashScope灵积模型服务建立在“模型即服务”(Mode... 141 | 正在分析内容... 142 | 最终答案:DashScope灵积模型服务是一个基于“模型即服务”(MaaS)理念,提供模型推理、模型微调训练等多元服务的平台。该服务整合了业界各领域的高质量模型,并依托阿里云强大的基础设施搭建而成,旨在为AI应用开发者提供便捷高效的模型探索和使用环境。 143 | ``` 144 | 145 | ### 2.3. 阿里云资源查询 146 | 你也可以运行另一段代码,它会告诉你你在杭州地域的ECS实例什么时候到期: 147 | 148 | >ALIBABA_CLOUD_ACCESS_KEY_ID 与 ALIBABA_CLOUD_ACCESS_KEY_SECRET 的导入方法请参考本章前文 [1.2.账号准备] 149 | 150 | 参考阿里云API,我们实现了阿里云资源查询类[`aliyun_resources.py`](more_tools/aliyun_resources.py),并用下面定义的`ECSSearch`工具来调用这个类。 151 | 152 | 出于学习的目的,我们也用langchain接口实现了一个阿里云资源查询工具[`aliyun_resource_tool.py`](refactor_with_langchain/tools/aliyun_resource_tool.py),并应用于3.1节的场景中,您可以对比这两种实现的异同。 153 | 154 | ```python 155 | # 定义ECS查询工具 156 | class ECSSearch(BaseTool): 157 | """ECS查询工具""" 158 | 159 | name: str = "ECS查询工具" 160 | description: str = ( 161 | "用于查询ECS资源使用情况。返回json格式ECS列表。" 162 | ) 163 | 164 | def _run(self, city: str) -> str: 165 | return '这是 json 格式的 ECS 列表信息:' + str(AliyunResources.get_resource_details()) 166 | ``` 167 | 168 | 这里我们需要额外定义一个类AliyunResources,封装查询阿里云资源的具体方法(暂时不讨论 AliyunResources 类,本段主要目的是展示工具接口定义的基本思路和接口一致性) 169 | 170 | 下面我们让大模型使用这个工具: 171 | 172 | ```bash 173 | python more_tools/search-with-tools.py --question="我的ECS实例什么时候到期" 174 | ``` 175 | 176 | 输出内容 177 | ``` 178 | 正在分析内容... 179 | 使用工具:ECSSearch... 180 | 查询内容:查询ECS实例到期时间... 181 | 查询到参考信息:这是 json 格式的 ECS 列表信息:{'maxResu... 182 | 正在分析内容... 183 | 最终答案:根据查询结果,当前没有ECS实例信息或者没有实例即将到期。观测到的JSON数据显示'totalCount': 0,意味着没有找到任何ECS实例。 184 | ``` 185 | ### 2.4. 更多阿里云资源查询方法 186 | 187 | 使用阿里云的[云控制 API](https://help.aliyun.com/zh/cloud-control-api/),它可以让你使用风格一致的 API 管理绝大多数阿里云资源。 188 | 189 | 你可以尝试运行以下查询: 190 | 191 | ```bash 192 | python more_tools/search-with-tools.py --question="我在上海有没有VPC" 193 | ``` 194 | 输出内容 195 | ``` 196 | 正在分析内容... 197 | 最终答案:您在上海拥有名为“vpc-123456”的VPC资源。详细信息包括:[此处列出VPC详细信息]。 198 | ``` 199 | 你可以尝试查询ECS安全组: 200 | ```bash 201 | python more_tools/search-with-tools.py --question="列出我在杭州的ECS安全组" 202 | ``` 203 | 输出内容 204 | ``` 205 | 正在分析内容... 206 | 使用工具:ECSSearch... 207 | 查询内容:杭州 ECS 安全组 列出... 208 | 查询到参考信息:这是 json 格式的 ECS 列表信息:{'maxResu... 209 | 正在分析内容... 210 | 最终答案:当前在杭州地域没有查到任何ECS安全组。列表信息如下:{'maxResults': 2, 'requestId': '0F3B5EAD-C386-5116-9748-10336CD31D93', 'resources': [], 'totalCount': 0} 211 | ``` 212 | 由于本章使用的测试账号没有开通ECS服务,因此反馈没有查询到ECS实例。 213 | 214 | >您可以[查看云服务产品信息](https://www.aliyun.com/product/ecs?source=5176.11533457#/)通过阿里云权益中心购买的 99 元/年的经济型 2核2G ECS 实例,新老客户都可以购买,并且后续可以 99 元续费。您可以参考[阿里云便宜服务器优惠合集](https://developer.aliyun.com/article/1445729)参与优惠活动 215 | 216 | 217 | ### 2.5. 让代码更好维护 218 | 219 | 以上我们仿照langchain的代码风格构造了Agent工具调用方法,读者可以根据自己的喜好来定义类似的Agent类。但是工具越多,代码管理起来越难。特别是不同工具导致大模型输出性能没有对齐的情况下,工具之间也有可能出现性能差异,代码维护难度较大。因此,我们可以看一下langchain的实现方法是什么样。 220 | 221 | 有人说 「LangChain 是大模型时代的 Spring」。LangChain 确实就像 Spring 一样,是一个框架,合理的使用它,可以使得代码更清晰,更易维护。 222 | 223 | 我们直接来看一下,通过 LangChain 重构后的代码吧: 224 | 225 | ```python 226 | # -*- coding: utf-8 -*- 227 | import sys 228 | from langchain import hub 229 | from langchain.agents import AgentExecutor 230 | from langchain.agents import create_react_agent 231 | from langchain_community.llms import Tongyi 232 | from tools.aliyun_resource_tool import AliyunResourceTool 233 | from tools.auto_coding_tool import AutoCodingTool 234 | from tools.service_search_tool import SearchTool 235 | 236 | model = Tongyi() 237 | model.model_name = 'qwen-max' 238 | model.model_kwargs = {'temperature': 0.5} 239 | 240 | tools = [ 241 | AliyunResourceTool(), 242 | AutoCodingTool(), 243 | SearchTool() 244 | ] 245 | 246 | prompt = hub.pull("hwchase17/react") 247 | 248 | agent = create_react_agent(model, tools, prompt) 249 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 250 | 251 | if len(sys.argv) < 2: 252 | print('请输入问题') 253 | sys.exit(1) 254 | question = sys.argv[1] 255 | 256 | print('问题:' + question) 257 | result = agent_executor.invoke({'input': question}) 258 | print(result['output']) 259 | 260 | ``` 261 | 262 | 263 | ## 3. 让通义千问自己写代码解决问题 264 | 265 | 如果我们想让通义千问可以帮我们查询天气、根据 URL 读取文档里的一些信息,我们就要不断的添加工具,我并不擅长写代码,该怎么办? 266 | 267 | 针对这个问题,有好几个解决办法: 268 | 269 | 耐着性子学习一下写代码。遇到问题时你可以使用通义千问等大模型来教你如何写代码。 270 | 在你的 IDE 上安装通义灵码,它会根据你的注释自动编写代码,让写代码变得更加简单。 271 | 看到这里,机智的你可能会想到,既然通义千问都能自己根据需求写代码了,那是不是可以自动生成并运行代码来解决问题? 272 | 273 | 这里我们基于 langchain_experimental.tools.PythonREPLTool 封装一个自动代码生成工具,生成一个react_agent。 274 | 275 | 对象初始化部分如: 276 | ```python 277 | tools = [PythonREPLTool()] 278 | 279 | model = Tongyi() 280 | model.model_name = 'qwen-max' 281 | ... 282 | ... 283 | agent = create_react_agent(model, tools, prompt) 284 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 285 | 286 | ``` 287 | 288 | 自动代码生成工具的类定义如下 289 | 290 | ```python 291 | class AutoCodingTool(BaseTool): 292 | """自动编写代码并执行的工具""" 293 | 294 | name: str = "自动编写并运行代码的工具" 295 | description: str = ( 296 | "此工具适用于天气查询、加载页面、数学运算,需要将结果通过 print 函数打印出来:print(result)。" 297 | "查询天气时,可以请求 http://wttr.in 获取对应城市天气。" 298 | "根据URL链接加载页面内容任务时,可以使用 requests 库。" 299 | ) 300 | 301 | def _run(self, task: str) -> str: 302 | result = agent_executor.invoke({'input': '请编写并运行 python 代码来解决这个问题:' + task + '。记得使用 print() 函数将结果打印出来'}) 303 | return result['output'] 304 | ``` 305 | 306 | 下面我们来看看大模型遇到不懂的问题就写代码来查询答案的整体效果。 307 | 308 | 309 | ### 3.1. 让通义千问自己写代码来实现功能 310 | 我们基于langchain的接口重新定义了一个可以自动写代码完成任务的Agent,代码在文件[`refactor_with_langchain/answer-with-tools.py`](refactor_with_langchain/answer-with-tools.py)中。 311 | ```bash 312 | ! python refactor_with_langchain/answer-with-tools.py '加载并列出这个页面的二级标题https://help.aliyun.com/zh/oss/product-overview/what-is-oss' 313 | ``` 314 | 输出内容 315 | ``` 316 | 问题:加载并列出这个页面的二级标题https://help.aliyun.com/zh/oss/product-overview/what-is-oss 317 | 318 | 319 | > Entering new AgentExecutor chain... 320 | 我需要使用自动编写并运行代码的工具来加载页面内容,并从中提取二级标题。 321 | Action: 自动编写并运行代码的工具 322 | Action Input: 323 | ··· 324 | import requests 325 | from bs4 import BeautifulSoup 326 | 327 | # 加载页面内容 328 | response = requests.get('https://help.aliyun.com/zh/oss/product-overview/what-is-oss') 329 | soup = BeautifulSoup(response.text, 'html.parser') 330 | 331 | # 提取二级标题(h2标签) 332 | second_level_headers = soup.find_all('h2') 333 | 334 | # 打印二级标题 335 | for header in second_level_headers: 336 | print(header.text) 337 | ··· 338 | 339 | ··· (代码省略,如果有兴趣可以查看demo并执行) 340 | 341 | ··· 342 | 343 | > Finished chain. 344 | 页面https://help.aliyun.com/zh/oss/product-overview/what-is-oss的二级标题包括: 345 | 346 | 1. 快速了解OSS 347 | 2. OSS工作原理 348 | 3. OSS重要特性 349 | 4. OSS使用方式 350 | 5. OSS计费 351 | 6. 其他相关服务 352 | 7. 其他阿里云存储服务 353 | ``` 354 | 355 | ### 3.2. 让通义千问自己实现天气查询 356 | 我们在注释中告诉了大模型查询天气的地址,现在我们来看看他如何自己写代码来查询天气 357 | 358 | > "查询天气时,可以请求 http://wttr.in 获取对应城市天气。" 359 | 360 | 执行如下代码 361 | ```bash 362 | ! python refactor_with_langchain/answer-with-tools.py '杭州气温如何' 363 | ``` 364 | 输出内容 365 | ``` 366 | 问题:杭州气温如何 367 | 368 | 369 | > Entering new AgentExecutor chain... 370 | 我需要通过自动编写并运行代码的工具来查询杭州的天气 371 | Action: 自动编写并运行代码的工具 372 | Action Input: 373 | ··· 374 | import requests 375 | response = requests.get('http://wttr.in/杭州') 376 | weather_info = response.text 377 | print(weather_info) 378 | ··· 379 | 380 | ··· (代码省略,如果有兴趣可以查看demo并执行) 381 | 382 | ··· 383 | > Finished chain. 384 | 杭州目前天气晴朗,气温为23°C(体感温度25°C),风速22公里/小时,能见度8公里,当前无降水。未来几天预报显示有局部多云到小雨的天气变化,气温和风速也有所波动。 385 | ``` 386 | 387 | 388 | ## 4. 总结 389 | 通过本章的学习,你学会了如何通过 LangChain 来大幅简化你的代码。 390 | 391 | 接下来你可能已经迫不及待地想要添加更多工具了。但是在一个 agent 里不断叠加 tool 会让提示词越来越长,最后可能会超过 token 限制。 392 | 为了解决这一问题,你还可以配置一个[基于语义相似度的路由](https://python.langchain.com/docs/expression_language/cookbook/embedding_router),让最前端的 agent 判断该让背后哪个领域的 agent 回答问题,背后的 agent 再去选取该领域合适的 tool 解答问题,最终由最前端的 agent 总结答案给用户。 393 | 394 | 更令人兴奋的是,你的程序可以自己根据需求编写并运行代码来解决问题了。 395 | 396 | 目前大模型生成的代码可能会曲解你的需求,导致答案不那么符合预期,你可以进一步完善提示词,甚至是使用搜索工具,来给通义千问一些代码参考,来让它更准确。与此同时,随着通义千问等大模型的不断演进,未来终有一天,它会在绝大多数场景下都能高效且正确的写出代码,解答你的问题。 397 | 398 | ## 5. 参考资料 399 | - [DashScope](https://dashscope.aliyun.com/) 400 | - [通义灵码](https://help.aliyun.com/document_detail/2590613.html) 401 | - [LangChain- 基于语义相似度的路由](https://python.langchain.com/docs/expression_language/cookbook/embedding_router) 402 | 403 | ***** 404 | ## 本章代码 405 | - 请点击[本章实验代码](demo-chapter4.ipynb)查阅相关内容。 406 | 407 | ## 继续学习 408 | - [上一篇:第 3 章 使用工具扩展问答能力的基本原理](../chapter3/README.md) 409 | - [【通义千问API入门教程】章节目录](../README.md) -------------------------------------------------------------------------------- /chapter4/more_tools/aliyun_resources.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | from alibabacloud_cloudcontrol20220830.client import Client as cloudcontrol20220830Client 4 | from alibabacloud_tea_openapi import models as open_api_models 5 | from alibabacloud_cloudcontrol20220830 import models as cloudcontrol_20220830_models 6 | from alibabacloud_tea_util import models as util_models 7 | 8 | 9 | class AliyunResources: 10 | def __init__(self): 11 | pass 12 | 13 | @staticmethod 14 | def create_client( 15 | access_key_id: str, 16 | access_key_secret: str, 17 | ) -> cloudcontrol20220830Client: 18 | """ 19 | 使用AK&SK初始化账号Client 20 | @param access_key_id: 21 | @param access_key_secret: 22 | @return: Client 23 | @throws Exception 24 | """ 25 | config = open_api_models.Config( 26 | # 必填,您的 AccessKey ID, 27 | access_key_id=access_key_id, 28 | # 必填,您的 AccessKey Secret, 29 | access_key_secret=access_key_secret 30 | ) 31 | # Endpoint 请参考 https://api.aliyun.com/product/cloudcontrol 32 | config.endpoint = f'cloudcontrol.aliyuncs.com' 33 | return cloudcontrol20220830Client(config) 34 | 35 | @staticmethod 36 | def get_resource_details() -> None: 37 | # 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。 38 | # 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378659.html 39 | client = AliyunResources.create_client(os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']) 40 | request_path = '/api/v1/providers/Aliyun/products/ECS/resources/Instance' 41 | get_resources_request = cloudcontrol_20220830_models.GetResourcesRequest( 42 | region_id='cn-hangzhou', 43 | max_results=2 44 | ) 45 | runtime = util_models.RuntimeOptions() 46 | headers = {} 47 | try: 48 | # 复制代码运行请自行打印 API 的返回值 49 | result = client.get_resources_with_options(request_path, get_resources_request, headers, runtime) 50 | # print(result.body) 51 | return result.body 52 | except Exception as error: 53 | # 错误 message 54 | print(error) 55 | # 诊断地址 56 | print(error.data.get("Recommend")) 57 | 58 | 59 | if __name__ == '__main__': 60 | AliyunResources.get_resource_details() 61 | -------------------------------------------------------------------------------- /chapter4/more_tools/search-with-tools.py: -------------------------------------------------------------------------------- 1 | from langchain_core.tools import BaseTool 2 | from langchain_community.llms import Tongyi 3 | from langchain.output_parsers import XMLOutputParser 4 | from aliyun_resources import AliyunResources 5 | import argparse 6 | import requests 7 | 8 | # 定义模型 9 | llm = Tongyi() 10 | llm.model_name = 'qwen-max' 11 | 12 | # 定义搜索工具 13 | class SearchTool(BaseTool): 14 | """服务查询工具""" 15 | 16 | name: str = "阿里云服务查询工具" 17 | description: str = ( 18 | "当你不确定一个阿里云服务是什么,才使用此工具。" 19 | ) 20 | 21 | def _run(self, name: str) -> str: 22 | if(name=="灵积") or (name=="灵积 阿里云服务"): 23 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 24 | elif name=="百炼": 25 | return "百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。" 26 | elif (name=="PAI") or (name=="阿里云 PAI"): 27 | return "人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。" 28 | elif name=="OSS": 29 | return "阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。" 30 | return name + '抱歉,没有查到相关信息。' 31 | 32 | 33 | # 定义天气查询工具 34 | class WeatherSearch(BaseTool): 35 | """天气查询工具""" 36 | 37 | name: str = "天气查询工具" 38 | description: str = ( 39 | "用于查询近期的天气,使用此工具。" 40 | ) 41 | 42 | def _run(self, city: str) -> str: 43 | result = requests.get('http://wttr.in/' + city) 44 | return result.text 45 | 46 | 47 | # 定义ECS查询工具 48 | class ECSSearch(BaseTool): 49 | """ECS查询工具""" 50 | 51 | name: str = "ECS查询工具" 52 | description: str = ( 53 | "用于查询ECS资源使用情况。返回json格式ECS列表。" 54 | ) 55 | 56 | def _run(self, city: str) -> str: 57 | return '这是 json 格式的 ECS 列表信息:' + str(AliyunResources.get_resource_details()) 58 | 59 | 60 | 61 | # 定义查询模版 62 | prompt_template = '''你是一个可以回答任何问题的助手。 63 | 你可以使用下列工具: 64 | 65 | {tools} 66 | 67 | 为了使用这个工具,你必须用标签。 68 | 例如,如果您有一个名为“{tool1}”的工具,可以查询企业内部服务信息,为了搜索(阿里云)是什么服务,你会返回: 69 | {tool1}阿里云 70 | 71 | 使用工具后你会得到一个形式为的响应,因此在第二轮你会得到输入为 72 | {tool1}阿里云阿里云服务简介等等 73 | 74 | 你需要判断标签中的内容是不是你需要的答案。如果是final_answer,你需要返回答案: 75 | 阿里云服务简介等等 76 | 77 | 不管你是否选用工具你都必须要用标签来包裹最终返回答案。比如 78 | 阿里云服务详细介绍等等 79 | 80 | 如果是final_answer,请检查返回答案的结尾,该结尾必须有标签结束 81 | 82 | 开始任务: 83 | 84 | 问题: 85 | ''' 86 | 87 | 88 | # 定义一个简单的工具调用Agent 89 | class DIYAgent(): 90 | def __init__(self,tools,model=None,prompt=None): 91 | self.tools = tools 92 | self.model = model 93 | self.verbose = False 94 | self.tool_name = [self.get_class_name(t) for t in self.tools] 95 | self.tool_prompt_desc = [self.get_class_name(t)+": "+t.name for t in self.tools] 96 | self.prompt = prompt.format(tools=self.tool_prompt_desc, tool1 = self.tool_name[0]) 97 | 98 | def get_class_name(self,tool): 99 | return tool.__class__.__name__ 100 | 101 | # 从xml中获取tool和tool_input 102 | def _parse_tool_args(self, response): 103 | xml_response = f"{response}" 104 | parsed_xml_response = XMLOutputParser().invoke(xml_response) 105 | response_dict = {} 106 | for item in parsed_xml_response['xml']: 107 | for key in item: 108 | response_dict[key] = item[key] 109 | return response_dict 110 | 111 | # 由于大模型可能不能正确返回,所以需要增加大量异常处理和判断逻辑 112 | def parsed_xml_response(self, response): 113 | response_dict={} 114 | try: 115 | response_dict = self._parse_tool_args(response) 116 | except Exception as e: 117 | if (response.startswith('')): 118 | response += '' 119 | try: 120 | response_dict = self._parse_tool_args(response) 121 | except Exception as e: 122 | return False, response 123 | else: 124 | return False, response 125 | # 正常返回结束 126 | if response_dict.get('final_answer') is not None: 127 | return False, response_dict['final_answer'] 128 | elif response_dict.get('tool') is None: 129 | return False, response 130 | # 正常返回工具信息 131 | return True, response_dict 132 | 133 | # 找到合适的工具并执行 134 | def run_tool(self, response_dict): 135 | search_input ='' 136 | search_result = '' 137 | for t in self.tools: 138 | tool_name = self.get_class_name(t) 139 | if response_dict['tool'] == tool_name: 140 | search_input = response_dict['tool_input'] 141 | search_result = t.run(search_input) 142 | return tool_name, search_input, search_result 143 | 144 | # run agent 145 | def run(self, request): 146 | prompt= self.prompt + request 147 | while True: 148 | print(f"\033[32m正在分析内容...\033[0m") 149 | raw_response = self.model.invoke(prompt) 150 | is_dict, response_dict=self.parsed_xml_response(raw_response) 151 | if(is_dict==False): 152 | return response_dict 153 | 154 | print(f"\033[32m使用工具:{response_dict['tool']}...\033[0m") 155 | print(f"\033[32m查询内容:{response_dict['tool_input']}...\033[0m") 156 | 157 | tool_name, search_input, search_result = self.run_tool(response_dict) 158 | 159 | prompt = self.prompt + f"{tool_name}{search_input}{search_result}" 160 | print(f"\033[32m查询到参考信息:{search_result[0:30]}...\033[0m") 161 | 162 | 163 | if __name__ == '__main__': 164 | # define arguments 165 | parser = argparse.ArgumentParser() 166 | parser.add_argument('--question', type=str, default='灵积是什么服务', help='question to be answered') 167 | 168 | # extract arguments 169 | args = parser.parse_args() 170 | 171 | # define tools 172 | tool = [SearchTool() 173 | ,WeatherSearch() 174 | ,ECSSearch()] 175 | 176 | # run agent 177 | diyAgent = DIYAgent(tool ,llm, prompt_template) 178 | result = diyAgent.run(args.question) 179 | print(f"\033[32m最终答案:{result}\033[0m") 180 | -------------------------------------------------------------------------------- /chapter4/refactor_with_langchain/answer-with-tools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from langchain import hub 4 | from langchain.agents import AgentExecutor 5 | from langchain.agents import create_react_agent 6 | from langchain_community.llms import Tongyi 7 | from tools.aliyun_resource_tool import AliyunResourceTool 8 | from tools.auto_coding_tool import AutoCodingTool 9 | from tools.service_search_tool import SearchTool 10 | 11 | model = Tongyi() 12 | model.model_name = 'qwen-max' 13 | model.model_kwargs = {'temperature': 0.5} 14 | 15 | tools = [ 16 | AliyunResourceTool(), 17 | AutoCodingTool(), 18 | SearchTool() 19 | ] 20 | 21 | prompt = hub.pull("hwchase17/react") 22 | 23 | agent = create_react_agent(model, tools, prompt) 24 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 25 | 26 | if len(sys.argv) < 2: 27 | print('请输入问题') 28 | sys.exit(1) 29 | question = sys.argv[1] 30 | 31 | print('问题:' + question) 32 | result = agent_executor.invoke({'input': question}) 33 | print(result['output']) 34 | -------------------------------------------------------------------------------- /chapter4/refactor_with_langchain/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlibabaCloudDocs/llm_learning/c27ee6deefb0f293b35786e7eed0097d4f655ca1/chapter4/refactor_with_langchain/tools/__init__.py -------------------------------------------------------------------------------- /chapter4/refactor_with_langchain/tools/aliyun_resource_tool.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | from typing import Any 5 | from langchain_core.tools import BaseTool 6 | from alibabacloud_cloudcontrol20220830.client import Client as cloudcontrol20220830Client 7 | from alibabacloud_tea_openapi import models as open_api_models 8 | from alibabacloud_cloudcontrol20220830 import models as cloudcontrol_20220830_models 9 | from alibabacloud_tea_util import models as util_models 10 | 11 | 12 | class AliyunResourceTool(BaseTool): 13 | """查询阿里云资源的工具""" 14 | 15 | name: str = "阿里云的云资源查询工具" 16 | description: str = ( 17 | "用于查询账号下的阿里云的源信息,需要指通过 json 指定产品名 product、资源类型 resource、地域 region。" 18 | "ECS 的默认资源为 Instance,VPC 的默认资源为 VPC。" 19 | "region 只能是某一个指定的地域,参考格式:cn-hangzhou、cn-beijing、us-east-1" 20 | ) 21 | 22 | def _run(self, params) -> Any: 23 | params_dict = json.loads(params) 24 | request_path = f'/api/v1/providers/Aliyun/products/{params_dict["product"]}/resources/{params_dict["resource"]}' 25 | get_resources_request = cloudcontrol_20220830_models.GetResourcesRequest( 26 | region_id=params_dict.get("region") or 'cn-hangzhou', 27 | max_results=2 28 | ) 29 | runtime = util_models.RuntimeOptions() 30 | headers = {} 31 | try: 32 | config = open_api_models.Config( 33 | access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], 34 | access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'] 35 | ) 36 | # Endpoint 请参考 https://api.aliyun.com/product/cloudcontrol 37 | config.endpoint = f'cloudcontrol.aliyuncs.com' 38 | client = cloudcontrol20220830Client(config) 39 | # 复制代码运行请自行打印 API 的返回值 40 | result = client.get_resources_with_options(request_path, get_resources_request, headers, runtime) 41 | return result.body 42 | except Exception as error: 43 | # 错误 message 44 | print(error) 45 | return '查询失败' 46 | -------------------------------------------------------------------------------- /chapter4/refactor_with_langchain/tools/auto_coding_tool.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from langchain_core.tools import BaseTool 3 | from langchain import hub 4 | from langchain.agents import AgentExecutor 5 | from langchain_experimental.tools import PythonREPLTool 6 | from langchain.agents import create_react_agent 7 | from langchain_community.llms import Tongyi 8 | 9 | tools = [PythonREPLTool()] 10 | model = Tongyi() 11 | model.model_name = 'qwen-max' 12 | model.model_kwargs = {'temperature': 0} 13 | 14 | 15 | instructions = """You are an agent designed to write and execute python code to answer questions. 16 | You have access to a python REPL, which you can use to execute python code. 17 | If you get an error, debug your code and try again. 18 | Only use the output of your code to answer the question. 19 | You might know the answer without running any code, but you should still run the code to get the answer. 20 | If it does not seem like you can write code to answer the question, just return "I don't know" as the answer. 21 | """ 22 | base_prompt = hub.pull("langchain-ai/react-agent-template") 23 | prompt = base_prompt.partial(instructions=instructions) 24 | agent = create_react_agent(model, tools, prompt) 25 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 26 | 27 | 28 | class AutoCodingTool(BaseTool): 29 | """自动编写代码并执行的工具""" 30 | 31 | name: str = "自动编写并运行代码的工具" 32 | description: str = ( 33 | "此工具适用于天气查询、加载页面、数学运算,需要将结果通过 print 函数打印出来:print(result)。" 34 | "查询天气时,可以请求 http://wttr.in 获取对应城市天气。" 35 | "根据URL链接加载页面内容任务时,可以使用 requests 库。" 36 | ) 37 | 38 | def _run(self, task: str) -> str: 39 | result = agent_executor.invoke({'input': '请编写并运行 python 代码来解决这个问题:' + task + '。记得使用 print() 函数将结果打印出来'}) 40 | return result['output'] 41 | -------------------------------------------------------------------------------- /chapter4/refactor_with_langchain/tools/service_search_tool.py: -------------------------------------------------------------------------------- 1 | from langchain_core.tools import BaseTool 2 | # 定义搜索工具 3 | class SearchTool(BaseTool): 4 | """服务查询工具""" 5 | 6 | name: str = "阿里云服务查询工具" 7 | description: str = ( 8 | "当你不确定一个阿里云服务是什么,才使用此工具。" 9 | ) 10 | 11 | def _run(self, name: str) -> str: 12 | if(name=="灵积") or (name=="灵积 阿里云服务"): 13 | return '''DashScope灵积模型服务建立在“模型即服务”(Model-as-a-Service,MaaS)的理念基础之上,围绕AI各领域模型,通过标准化的API提供包括模型推理、模型微调训练在内的多种模型服务。DashScope灵积模型服务依托于业界各领域的优质模型,基于阿里云强大的基础设施搭建。欢迎AI应用开发者由此开启模型探索之旅!''' 14 | elif name=="百炼": 15 | return "百炼,即大模型服务平台,是面向企业客户及合作伙伴的,基于通义大模型、行业大模型以及三方大模型,结合企业专属数据,包含全链路大模型开发工具的一站式大模型商业化平台。提供完整的模型训练、微调、评估等产品工具,预置丰富的应用插件,提供便捷的集成方式,更快更高效地完成大模型应用的构建。" 16 | elif (name=="PAI") or (name=="阿里云 PAI"): 17 | return "人工智能平台 PAI(Platform of Artificial Intelligence)面向企业客户及开发者,提供轻量化、高性价比的云原生人工智能,涵盖DSW交互式建模、Designer拖拽式可视化建模、DLC分布式训练到EAS模型在线部署的全流程。" 18 | elif name=="OSS": 19 | return "阿里云对象存储OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,可提供99.9999999999%(12个9)的数据持久性,99.995%的数据可用性。多种存储类型供选择,全面优化存储成本。" 20 | return name + '抱歉,没有查到相关信息。' -------------------------------------------------------------------------------- /chapter4/requirements.txt: -------------------------------------------------------------------------------- 1 | dashscope==1.13.6 2 | langchain==0.1.0 3 | langchainhub==0.1.14 4 | langchain-experimental==0.0.49 5 | beautifulsoup4==4.12.2 6 | html2text==2020.1.16 7 | alibabacloud_cloudcontrol20220830==1.1.0 -------------------------------------------------------------------------------- /cloudapi/README.md: -------------------------------------------------------------------------------- 1 | # 如何利用通义千问查询阿里云资源 2 | 3 | 作为阿里云开发者,我们常常需要查询阿里云上的资源信息,比如ECS、RDS等资源详情或产品说明。以往我们只能在官网上按步骤一页一页找到控制中心,再查到资源页面,节奏略显拖沓。如今,我们已经进入大模型时代,有什么办法可以更简便快捷的获取这些信息呢?能不能让大模型来做这件事?于是,基于LangChain的Agent架构,我们尝试制作了一个实现上述想法的大模型Agent,让大模型可以协助您查询您在阿里云上开通了哪些资源。您可以进入Jupyter Notebook环境来运行本章的代码,查询您在阿里云的资源使用情况,也可以将代码加入到您的工程项目中,利用[阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis)实现更多功能,我们欢迎您将实验结果分享出来。 4 | 5 | 本我呢我们将使用灵积API调用通义千问大模型,并利用[阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis)来查询您的阿里云资源。 6 | 7 | ## 学习之前 8 | - 本文假设你已经初步了解并使用过 python、git,因此不会涉及如何安装 [python](https://www.python.org/downloads/)、pip、[git](https://git-scm.com/) 等基础工具。 9 | - 本文侧重于如何将大模型 API 应用到工作中,因此并不不会详细介绍大模型以及机器学习的基础概念。 10 | 11 | ## 1. 准备工作 12 | ### 1.1. 环境配置 13 | 在开始本文的学习之前,你可以先安装一下必要的依赖,以便运行相关代码: 14 | ```bash 15 | pip install dashscope==1.13.6 16 | pip install langchain==0.1.0 17 | pip install langchainhub==0.1.14 18 | pip install langchain-experimental==0.0.49 19 | pip install beautifulsoup4==4.12.2 20 | pip install html2text==2020.1.16 21 | pip install alibabacloud_cloudcontrol20220830==1.1.0 22 | ``` 23 | 24 | ### 1.2. 账号准备 25 | 26 | 首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey),您可以使用以下命令行导入系统,请于[RAM访问控制](https://ram.console.aliyun.com/manage/ak)查询和创建您的阿里云AK/SK 27 | 28 | #### MacOS or Linux 29 | 您可以使用以下命令行导入环境变量 30 | ```bash 31 | export DASHSCOPE_API_KEY="sk-****" 32 | export ALIBABA_CLOUD_ACCESS_KEY_ID="" 33 | export ALIBABA_CLOUD_ACCESS_KEY_SECRET="" 34 | ``` 35 | 36 | #### Windows 37 | 可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量 38 | ```bat 39 | set DASHSCOPE_API_KEY=sk-**** 40 | set ALIBABA_CLOUD_ACCESS_KEY_ID=your access key id 41 | set ALIBABA_CLOUD_ACCESS_KEY_SECRET=your access key secret 42 | ``` 43 | 或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 44 | ```powershell 45 | $Env:DASHSCOPE_API_KEY = "sk-****" 46 | $Env:ALIBABA_CLOUD_ACCESS_KEY_ID = "" 47 | $Env:ALIBABA_CLOUD_ACCESS_KEY_SECRET = "" 48 | ``` 49 | 50 | #### Jupyter Notebook 51 | 您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。 52 | 53 | 54 | ## 2. 搭建云资源查询AGENT 55 | ### 2.1. 创建阿里云资源查询API工具 56 | ```python 57 | # 阿里云资源查询工具 58 | 59 | # -*- coding: utf-8 -*- 60 | import json 61 | import os 62 | from typing import Any 63 | from langchain_core.tools import BaseTool 64 | from alibabacloud_cloudcontrol20220830.client import Client as cloudcontrol20220830Client 65 | from alibabacloud_tea_openapi import models as open_api_models 66 | from alibabacloud_cloudcontrol20220830 import models as cloudcontrol_20220830_models 67 | from alibabacloud_tea_util import models as util_models 68 | 69 | 70 | class AliyunResourceTool(BaseTool): 71 | """查询阿里云资源的工具""" 72 | 73 | name: str = "阿里云的云资源查询工具" 74 | description: str = ( 75 | "用于查询账号下的阿里云的源信息,需要指通过 json 指定产品名 product、资源类型 resource、地域 region。" 76 | "ECS 的默认资源为 Instance,VPC 的默认资源为 VPC。" 77 | "region 只能是某一个指定的地域,参考格式:cn-hangzhou、cn-beijing、us-east-1" 78 | ) 79 | 80 | def _run(self, params) -> Any: 81 | params_dict = json.loads(params) 82 | request_path = f'/api/v1/providers/Aliyun/products/{params_dict["product"]}/resources/{params_dict["resource"]}' 83 | get_resources_request = cloudcontrol_20220830_models.GetResourcesRequest( 84 | region_id=params_dict.get("region") or 'cn-hangzhou', 85 | max_results=2 86 | ) 87 | runtime = util_models.RuntimeOptions() 88 | headers = {} 89 | try: 90 | config = open_api_models.Config( 91 | access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], 92 | access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'] 93 | ) 94 | # Endpoint 请参考 https://api.aliyun.com/product/cloudcontrol 95 | config.endpoint = f'cloudcontrol.aliyuncs.com' 96 | client = cloudcontrol20220830Client(config) 97 | # 复制代码运行请自行打印 API 的返回值 98 | result = client.get_resources_with_options(request_path, get_resources_request, headers, runtime) 99 | return result.body 100 | except Exception as error: 101 | # 错误 message 102 | print(error) 103 | return '查询失败' 104 | ``` 105 | 106 | ### 2.2. 创建通义千问AGENT 107 | ```python 108 | # 定义通义千问大模型AGENT 109 | 110 | # -*- coding: utf-8 -*- 111 | import sys 112 | from langchain import hub 113 | from langchain.agents import AgentExecutor 114 | from langchain.agents import create_react_agent 115 | from langchain_community.llms import Tongyi 116 | 117 | class MyAgent(): 118 | def __init__(self): 119 | self.agent_executor=self.assemble_agent_executor() 120 | 121 | def assemble_agent_executor(self): 122 | model = Tongyi() 123 | model.model_name = 'qwen-max' 124 | model.model_kwargs = {'temperature': 0.5} 125 | 126 | tools = [ 127 | AliyunResourceTool(), 128 | ] 129 | 130 | prompt = hub.pull("hwchase17/react") 131 | 132 | agent = create_react_agent(model, tools, prompt) 133 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 134 | return agent_executor 135 | 136 | def ask(self, question): 137 | result = self.agent_executor.invoke({'input': question}) 138 | return result['output'] 139 | 140 | llm = MyAgent() 141 | ``` 142 | 143 | ### 2.3. 阿里云资源查询 144 | 145 | 下面我们尝试利用上述代码工具来查询阿里云资源,API应用[详情点击](https://api.aliyun.com/product/cloudcontrol?tab=apis) 146 | 147 | #### 2.3.1. 使用工具查询ECS资源 148 | 首先测试Agent初始化是否成功,是否能查询“杭州的ECS实例信息”。本账号并没有关联ECS实例,所以查询结果为空。 149 | ```python 150 | llm.ask("杭州的ECS实例什么时候到期") 151 | ``` 152 | 输出内容 153 | ```text 154 | > Entering new AgentExecutor chain... 155 | 需要使用阿里云的云资源查询工具来获取ECS实例的信息,包括到期时间 156 | Action: 阿里云的云资源查询工具 157 | Action Input: {"product": "ECS", "resource": "Instance", "region": "cn-hangzhou"} 158 | {'maxResults': 2, 'requestId': '3A02BE18-DA09-576D-BD98-68FDAD75DDDF', 'resources': [], 'totalCount': 0} 根据API返回结果,当前在杭州地域下没有ECS实例。 159 | Final Answer: 目前在杭州地域下没有到期的ECS实例。如果有实例并需要查询其到期时间,请确保输入正确的region信息,并且该账号下在对应地域存在ECS实例。 160 | 161 | > Finished chain. 162 | '目前在杭州地域下没有到期的ECS实例。如果有实例并需要查询其到期时间,请确保输入正确的region信息,并且该账号下在对应地域存在ECS实例。' 163 | ``` 164 | 165 | 由于本章使用的测试账号没有开通ECS服务,因此反馈没有查询到ECS实例。 166 | 167 | >您可以[查看云服务产品信息](https://www.aliyun.com/product/ecs?source=5176.11533457#/)通过阿里云权益中心购买的 99 元/年的经济型 2核2G ECS 实例,新老客户都可以购买,并且后续可以 99 元续费。您可以参考[阿里云便宜服务器优惠合集](https://developer.aliyun.com/article/1445729)参与优惠活动 168 | 169 | #### 2.3.2. 查询更多阿里云资源 170 | 171 | 接下来,我们在不做任何代码改动的情况下查询VPC信息。理论上我们可以查询[API应用范围](https://api.aliyun.com/product/cloudcontrol?tab=apis)的所有资源。 172 | ```python 173 | llm.ask("我在上海有没有VPC") 174 | ``` 175 | 176 | 输出内容 177 | ```text 178 | > Entering new AgentExecutor chain... 179 | 需要查询我在上海地区的VPC资源 180 | Action: 阿里云的云资源查询工具 181 | Action Input: {"product": "VPC", "resource": "VPC", "region": "cn-shanghai"} 182 | {'maxResults': 2, 'requestId': 'BC5C3BBE-762F-5451-830C-40FB15E806EC', 'resources': [], 'totalCount': 0} 查询结果显示在上海地区没有VPC资源 183 | Final Answer: 您在上海地区目前没有VPC资源。 184 | 185 | > Finished chain. 186 | '您在上海地区目前没有VPC资源。' 187 | ``` 188 | #### 2.3.3. 通义千问无需工具就能回答的问题 189 | 190 | 最后,我们看看大模型是如何回答不在API范围内的问题的,这里我们将询问ECS的定价策略,通义千问能直接给出合理的答案。 191 | ```python 192 | ans= llm.ask("ECS服务如何定价") 193 | ``` 194 | 输出内容 195 | ``` 196 | > Entering new AgentExecutor chain... 197 | 提供的工具对回答该问题帮助较小,我将不使用工具直接作答。 198 | Final Answer: 阿里云函数计算服务(Function Compute)的定价模式基于以下几个方面: 199 | 200 | 1. **计费单位**:函数计算以 GB-s(GB 内存每秒)为计费单位。即您的函数运行时消耗的内存大小乘以运行时间(以秒计)。 201 | 202 | 2. **资源使用**: 203 | - **内存**:您可以根据实际需求配置函数运行所需的最小和最大内存,价格会随着内存规格不同而变化。 204 | - **执行时间**:函数每次执行的实际耗时,精确到毫秒级计费,但不足 1ms 的部分不收费。 205 | - **请求次数**:除了按量计费的资源使用外,函数计算还有免费额度的调用次数。 206 | 207 | 3. **冷启动**:首次创建或长时间未被调用后重新调用时可能会产生额外的冷启动费用,具体取决于函数的运行环境和镜像大小。 208 | 209 | 4. **免费额度**:阿里云为每个账号提供了一定额度的免费资源包,包括一定的函数调用次数、GB-s 计算资源以及一定数量的免费日志存储空间等。 210 | 211 | 5. **长期优惠**:对于持续运行的函数实例,可以采用预留实例(RI)的方式获得更优惠的价格。 212 | 213 | 为了获取最新的、详细的定价信息,请直接访问阿里云函数计算官网的定价页面,那里会有详尽且实时更新的价格列表和计费示例说明。 214 | 215 | > Finished chain. 216 | ``` 217 | 218 | #### 2.3.4. 封装成文件来执行 219 | 220 | 上述代码我们封装在文件 aliyun_resource_agent.py 中,你可以使用如下的指令来执行 221 | 222 | ```bash 223 | python aliyun_resource_agent.py "我在上海有没有VPC" 224 | ``` 225 | 输出内容 226 | ``` 227 | 问题:我在上海有没有VPC 228 | 229 | > Entering new AgentExecutor chain... 230 | 需要查询用户在上海的VPC资源 231 | Action: 阿里云的云资源查询工具 232 | Action Input: {"product": "VPC", "resource": "VPC", "region": "cn-shanghai"} 233 | {'maxResults': 2, 'requestId': '99B869A2-8F8C-53A8-AC0C-F53DD4571BC9', 'resources': [], 'totalCount': 0} 根据查询结果,用户在上海没有VPC资源 234 | Final Answer: 您在上海没有VPC资源。 235 | 236 | > Finished chain. 237 | 您在上海没有VPC资源。 238 | ``` 239 | 240 | 241 | ## 3. 参考资料 242 | - [阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis) 243 | - [DashScope](https://dashscope.aliyun.com/) 244 | - [通义灵码](https://help.aliyun.com/document_detail/2590613.html) 245 | - [LangChain- 基于语义相似度的路由](https://python.langchain.com/docs/expression_language/cookbook/embedding_router) 246 | 247 | 248 | ***** 249 | ## 本文代码 250 | - 请点击[实验代码](demo-cloudapi.ipynb)查阅相关内容。 251 | 252 | ## 继续学习 253 | - [【通义千问API入门教程】章节目录](../README.md) -------------------------------------------------------------------------------- /cloudapi/aliyun_resource_agent.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import os 4 | import sys 5 | from typing import Any 6 | from langchain_core.tools import BaseTool 7 | from alibabacloud_cloudcontrol20220830.client import Client as cloudcontrol20220830Client 8 | from alibabacloud_tea_openapi import models as open_api_models 9 | from alibabacloud_cloudcontrol20220830 import models as cloudcontrol_20220830_models 10 | from alibabacloud_tea_util import models as util_models 11 | from langchain import hub 12 | from langchain.agents import AgentExecutor 13 | from langchain.agents import create_react_agent 14 | from langchain_community.llms import Tongyi 15 | 16 | # 阿里云资源查询工具 17 | class AliyunResourceTool(BaseTool): 18 | """查询阿里云资源的工具""" 19 | 20 | name: str = "阿里云的云资源查询工具" 21 | description: str = ( 22 | "用于查询账号下的阿里云的源信息,需要指通过 json 指定产品名 product、资源类型 resource、地域 region。" 23 | "ECS 的默认资源为 Instance,VPC 的默认资源为 VPC。" 24 | "region 只能是某一个指定的地域,参考格式:cn-hangzhou、cn-beijing、us-east-1" 25 | ) 26 | 27 | def _run(self, params) -> Any: 28 | params_dict = json.loads(params) 29 | request_path = f'/api/v1/providers/Aliyun/products/{params_dict["product"]}/resources/{params_dict["resource"]}' 30 | get_resources_request = cloudcontrol_20220830_models.GetResourcesRequest( 31 | region_id=params_dict.get("region") or 'cn-hangzhou', 32 | max_results=2 33 | ) 34 | runtime = util_models.RuntimeOptions() 35 | headers = {} 36 | try: 37 | config = open_api_models.Config( 38 | access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'], 39 | access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET'] 40 | ) 41 | # Endpoint 请参考 https://api.aliyun.com/product/cloudcontrol 42 | config.endpoint = f'cloudcontrol.aliyuncs.com' 43 | client = cloudcontrol20220830Client(config) 44 | # 复制代码运行请自行打印 API 的返回值 45 | result = client.get_resources_with_options(request_path, get_resources_request, headers, runtime) 46 | return result.body 47 | except Exception as error: 48 | # 错误 message 49 | print(error) 50 | return '查询失败' 51 | 52 | 53 | # 定义通义千问大模型AGENT 54 | 55 | class MyAgent(): 56 | def __init__(self): 57 | self.agent_executor=self.assemble_agent_executor() 58 | 59 | def assemble_agent_executor(self): 60 | model = Tongyi() 61 | model.model_name = 'qwen-max' 62 | model.model_kwargs = {'temperature': 0.5} 63 | 64 | tools = [ 65 | AliyunResourceTool(), 66 | ] 67 | 68 | prompt = hub.pull("hwchase17/react") 69 | 70 | agent = create_react_agent(model, tools, prompt) 71 | agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True) 72 | return agent_executor 73 | 74 | def ask(self, question): 75 | result = self.agent_executor.invoke({'input': question}) 76 | return result['output'] 77 | 78 | 79 | 80 | 81 | if __name__ == '__main__': 82 | if len(sys.argv) < 2: 83 | print('请输入问题') 84 | sys.exit(1) 85 | question = sys.argv[1] 86 | 87 | print('问题:' + question) 88 | llm = MyAgent() 89 | result = llm.ask(question) 90 | print(result) -------------------------------------------------------------------------------- /cloudapi/demo-cloudapi.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 如何利用通义千问查询阿里云资源\n", 8 | "\n", 9 | "作为阿里云开发者,我们常常需要查询阿里云上的资源信息,比如ECS、RDS等资源详情或产品说明。以往我们只能在官网上按步骤一页一页找到控制中心,再查到资源页面,节奏略显拖沓。如今,我们已经进入大模型时代,有什么办法可以更简便快捷的获取这些信息呢?能不能让大模型来做这件事?于是,基于LangChain的Agent架构,我们尝试制作了一个实现上述想法的大模型Agent,让大模型可以协助您查询您在阿里云上开通了哪些资源。您可以进入Jupyter Notebook环境来运行本章的代码,查询您在阿里云的资源使用情况,也可以将代码加入到您的工程项目中,利用[阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis)实现更多功能,我们欢迎您将实验结果分享出来。\n", 10 | "\n", 11 | "本文我们将使用灵积API调用通义千问大模型,并利用[阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis)来查询您的阿里云资源。\n", 12 | "\n", 13 | "## 学习之前\n", 14 | "- 本文假设你已经初步了解并使用过 python、git,因此不会涉及如何安装 [python](https://www.python.org/downloads/)、pip、[git](https://git-scm.com/) 等基础工具。\n", 15 | "- 本文侧重于如何将大模型 API 应用到工作中,因此并不不会详细介绍大模型以及机器学习的基础概念。\n", 16 | "\n", 17 | "\n", 18 | "## 1. 准备工作\n", 19 | "### 1.1. 环境配置\n", 20 | "在开始本文的学习之前,你可以先安装一下必要的依赖,以便运行相关代码:\n", 21 | "```bash\n", 22 | "pip install dashscope==1.13.6\n", 23 | "pip install langchain==0.1.0\n", 24 | "pip install langchainhub==0.1.14\n", 25 | "pip install langchain-experimental==0.0.49\n", 26 | "pip install beautifulsoup4==4.12.2\n", 27 | "pip install html2text==2020.1.16\n", 28 | "pip install alibabacloud_cloudcontrol20220830==1.1.0\n", 29 | "```\n", 30 | "\n", 31 | "\n", 32 | "### 1.2. 账号准备\n", 33 | "\n", 34 | "首先,您需要前往 [官网创建 API Key](https://help.aliyun.com/zh/dashscope/developer-reference/activate-dashscope-and-create-an-api-key)。接下来,请获取你的 [DASHSCOPE_API_KEY](https://dashscope.console.aliyun.com/apiKey),您可以使用以下命令行导入系统,请于[RAM访问控制](https://ram.console.aliyun.com/manage/ak)查询和创建您的阿里云AK/SK\n", 35 | "\n", 36 | "#### MacOS or Linux\n", 37 | "您可以使用以下命令行导入环境变量\n", 38 | "```bash\n", 39 | "export DASHSCOPE_API_KEY=\"sk-****\"\n", 40 | "export ALIBABA_CLOUD_ACCESS_KEY_ID=\"\"\n", 41 | "export ALIBABA_CLOUD_ACCESS_KEY_SECRET=\"\"\n", 42 | "```\n", 43 | "\n", 44 | "#### Windows\n", 45 | "可以在终端使用[`SET`](https://learn.microsoft.com/zh-cn/windows-server/administration/windows-commands/set_1)命令设置环境变量\n", 46 | "```bat\n", 47 | "set DASHSCOPE_API_KEY=sk-****\n", 48 | "set ALIBABA_CLOUD_ACCESS_KEY_ID=your access key id\n", 49 | "set ALIBABA_CLOUD_ACCESS_KEY_SECRET=your access key secret\n", 50 | "```\n", 51 | "或者在[`PowerShell`](https://learn.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_environment_variables?view=powershell-7.4)中使用以下命令行配置环境变量 \n", 52 | "```powershell\n", 53 | "$Env:DASHSCOPE_API_KEY = \"sk-****\"\n", 54 | "$Env:ALIBABA_CLOUD_ACCESS_KEY_ID = \"\"\n", 55 | "$Env:ALIBABA_CLOUD_ACCESS_KEY_SECRET = \"\"\n", 56 | "```\n", 57 | "\n", 58 | "#### Jupyter Notebook\n", 59 | "您可以使用[`os.environ`](https://docs.python.org/3/library/os.html)方法,在代码开头设置临时环境变量。\n", 60 | "\n", 61 | "### 1.3. docenv模式\n", 62 | "\n", 63 | "将环境变量写入文件\"~/.zshrc\"中:\n", 64 | "\n", 65 | "```bash\n", 66 | "export OPENAI_API_KEY=\"sk-...\"\n", 67 | "```\n", 68 | "就可以执行如下命令 ```source ~/.zshrc``` 将这个环境变量绑定到全局shell中。\n", 69 | "\n", 70 | "接下来我们将加载这个环境变量到notebook中。执行如下命令\n", 71 | "\n", 72 | "**使用Windows,已经通过Windows PowerShell来注册环境变量的同事可以考虑跳过这里**" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 1, 78 | "metadata": {}, 79 | "outputs": [ 80 | { 81 | "name": "stderr", 82 | "output_type": "stream", 83 | "text": [ 84 | "Python-dotenv could not parse statement starting at line 5\n", 85 | "Python-dotenv could not parse statement starting at line 6\n", 86 | "Python-dotenv could not parse statement starting at line 8\n", 87 | "Python-dotenv could not parse statement starting at line 9\n", 88 | "Python-dotenv could not parse statement starting at line 14\n" 89 | ] 90 | }, 91 | { 92 | "data": { 93 | "text/plain": [ 94 | "True" 95 | ] 96 | }, 97 | "execution_count": 1, 98 | "metadata": {}, 99 | "output_type": "execute_result" 100 | } 101 | ], 102 | "source": [ 103 | "import os\n", 104 | "from dotenv import load_dotenv\n", 105 | "## for MacOS users\n", 106 | "filePath = os.path.abspath(os.path.expanduser(os.path.expandvars(\"~/.zshrc\")))\n", 107 | "load_dotenv(filePath)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "## 2. 搭建云资源查询AGENT\n", 115 | "\n", 116 | "### 2.1. 创建阿里云资源查询API工具" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 2, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "# 阿里云资源查询工具\n", 126 | "\n", 127 | "# -*- coding: utf-8 -*-\n", 128 | "import json\n", 129 | "import os\n", 130 | "from typing import Any\n", 131 | "from langchain_core.tools import BaseTool\n", 132 | "from alibabacloud_cloudcontrol20220830.client import Client as cloudcontrol20220830Client\n", 133 | "from alibabacloud_tea_openapi import models as open_api_models\n", 134 | "from alibabacloud_cloudcontrol20220830 import models as cloudcontrol_20220830_models\n", 135 | "from alibabacloud_tea_util import models as util_models\n", 136 | "\n", 137 | "\n", 138 | "class AliyunResourceTool(BaseTool):\n", 139 | " \"\"\"查询阿里云资源的工具\"\"\"\n", 140 | "\n", 141 | " name: str = \"阿里云的云资源查询工具\"\n", 142 | " description: str = (\n", 143 | " \"用于查询账号下的阿里云的源信息,需要指通过 json 指定产品名 product、资源类型 resource、地域 region。\"\n", 144 | " \"ECS 的默认资源为 Instance,VPC 的默认资源为 VPC。\"\n", 145 | " \"region 只能是某一个指定的地域,参考格式:cn-hangzhou、cn-beijing、us-east-1\"\n", 146 | " )\n", 147 | "\n", 148 | " def _run(self, params) -> Any:\n", 149 | " params_dict = json.loads(params)\n", 150 | " request_path = f'/api/v1/providers/Aliyun/products/{params_dict[\"product\"]}/resources/{params_dict[\"resource\"]}'\n", 151 | " get_resources_request = cloudcontrol_20220830_models.GetResourcesRequest(\n", 152 | " region_id=params_dict.get(\"region\") or 'cn-hangzhou',\n", 153 | " max_results=2\n", 154 | " )\n", 155 | " runtime = util_models.RuntimeOptions()\n", 156 | " headers = {}\n", 157 | " try:\n", 158 | " config = open_api_models.Config(\n", 159 | " access_key_id=os.environ['ALIBABA_CLOUD_ACCESS_KEY_ID'],\n", 160 | " access_key_secret=os.environ['ALIBABA_CLOUD_ACCESS_KEY_SECRET']\n", 161 | " )\n", 162 | " # Endpoint 请参考 https://api.aliyun.com/product/cloudcontrol\n", 163 | " config.endpoint = f'cloudcontrol.aliyuncs.com'\n", 164 | " client = cloudcontrol20220830Client(config)\n", 165 | " # 复制代码运行请自行打印 API 的返回值\n", 166 | " result = client.get_resources_with_options(request_path, get_resources_request, headers, runtime)\n", 167 | " return result.body\n", 168 | " except Exception as error:\n", 169 | " # 错误 message\n", 170 | " print(error)\n", 171 | " return '查询失败'\n" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": {}, 177 | "source": [ 178 | "### 2.2. 创建通义千问AGENT" 179 | ] 180 | }, 181 | { 182 | "cell_type": "code", 183 | "execution_count": 3, 184 | "metadata": {}, 185 | "outputs": [], 186 | "source": [ 187 | "# 定义通义千问大模型AGENT\n", 188 | "\n", 189 | "# -*- coding: utf-8 -*-\n", 190 | "from langchain import hub\n", 191 | "from langchain.agents import AgentExecutor\n", 192 | "from langchain.agents import create_react_agent\n", 193 | "from langchain_community.llms import Tongyi\n", 194 | "\n", 195 | "class MyAgent():\n", 196 | " def __init__(self):\n", 197 | " self.agent_executor=self.assemble_agent_executor()\n", 198 | "\n", 199 | " def assemble_agent_executor(self):\n", 200 | " model = Tongyi()\n", 201 | " model.model_name = 'qwen-max'\n", 202 | " model.model_kwargs = {'temperature': 0.5}\n", 203 | "\n", 204 | " tools = [\n", 205 | " AliyunResourceTool(),\n", 206 | " ]\n", 207 | "\n", 208 | " prompt = hub.pull(\"hwchase17/react\")\n", 209 | "\n", 210 | " agent = create_react_agent(model, tools, prompt)\n", 211 | " agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)\n", 212 | " return agent_executor\n", 213 | "\n", 214 | " def ask(self, question):\n", 215 | " result = self.agent_executor.invoke({'input': question})\n", 216 | " return result['output']" 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "### 2.3. 阿里云资源查询\n", 224 | "\n", 225 | "下面我们尝试利用上述代码工具来查询阿里云资源,API应用[详情点击](https://api.aliyun.com/product/cloudcontrol?tab=apis)\n", 226 | "\n", 227 | "#### 2.3.1. 使用工具查询ECS资源\n", 228 | "首先测试Agent初始化是否成功,是否能查询“杭州的ECS实例信息”。本账号并没有关联ECS实例,所以查询结果为空。" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": 4, 234 | "metadata": {}, 235 | "outputs": [ 236 | { 237 | "name": "stdout", 238 | "output_type": "stream", 239 | "text": [ 240 | "\n", 241 | "\n", 242 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 243 | "\u001b[32;1m\u001b[1;3m 需要使用阿里云的云资源查询工具来查询杭州地区(cn-hangzhou)的ECS实例到期时间。\n", 244 | "Action: 阿里云的云资源查询工具\n", 245 | "Action Input: {\n", 246 | " \"product\": \"ECS\",\n", 247 | " \"resource\": \"Instance\",\n", 248 | " \"region\": \"cn-hangzhou\"\n", 249 | "}\u001b[0m'ALIBABA_CLOUD_ACCESS_KEY_ID'\n", 250 | "\u001b[36;1m\u001b[1;3m查询失败\u001b[0m" 251 | ] 252 | }, 253 | { 254 | "ename": "ValueError", 255 | "evalue": "An output parsing error occurred. In order to pass this error back to the agent and have it try again, pass `handle_parsing_errors=True` to the AgentExecutor. This is the error: Could not parse LLM output: ` 由于查询失败,无法直接获取到杭州地区ECS实例的到期时间。通常情况下,查询失败可能由以下原因导致:\n\n1. **权限问题**:当前使用的账号可能没有足够的权限访问或查询该区域的ECS实例信息。\n2. **参数错误**:提供的产品名、资源类型或地域信息可能有误,需要核实输入数据的准确性。\n3. **网络问题**:与阿里云服务器之间的网络连接可能出现故障,导致查询请求无法送达或响应未返回。\n4. **服务异常**:阿里云的云资源查询工具本身可能存在临时性故障或维护状态。\n\n由于无法确定具体原因,我无法立即给出杭州地区ECS实例的到期时间。建议进行以下操作以解决问题:\n\n1. **检查账号权限**:确保当前使用的账号具有查询ECS实例信息的权限,如有必要,可联系账号管理员调整权限设置。\n2. **核实输入数据**:再次确认输入的JSON参数中产品名、资源类型和地域是否正确无误,特别是地域名称“cn-hangzhou”。\n3. **测试网络连接**:尝试访问其他阿里云服务或重新执行查询操作,以判断是否存在网络问题。\n4. **联系阿里云支持**:如果以上步骤均未能解决问题,建议联系阿里云官方技术支持,报告查询失败的情况,寻求专业帮助。\n\n在问题解决后,才能成功查询到杭州地区ECS实例的到期时间。因此,目前无法提供最终答案。`", 256 | "output_type": "error", 257 | "traceback": [ 258 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 259 | "\u001b[0;31mOutputParserException\u001b[0m Traceback (most recent call last)", 260 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:1083\u001b[0m, in \u001b[0;36mAgentExecutor._iter_next_step\u001b[0;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)\u001b[0m\n\u001b[1;32m 1082\u001b[0m \u001b[38;5;66;03m# Call the LLM to see what to do.\u001b[39;00m\n\u001b[0;32m-> 1083\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mplan\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1084\u001b[0m \u001b[43m \u001b[49m\u001b[43mintermediate_steps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1085\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[1;32m 1086\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1087\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1088\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m OutputParserException \u001b[38;5;28;01mas\u001b[39;00m e:\n", 261 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:386\u001b[0m, in \u001b[0;36mRunnableAgent.plan\u001b[0;34m(self, intermediate_steps, callbacks, **kwargs)\u001b[0m\n\u001b[1;32m 385\u001b[0m inputs \u001b[38;5;241m=\u001b[39m {\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mintermediate_steps\u001b[39m\u001b[38;5;124m\"\u001b[39m: intermediate_steps}}\n\u001b[0;32m--> 386\u001b[0m output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrunnable\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mcallbacks\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 387\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m output\n", 262 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/runnables/base.py:1774\u001b[0m, in \u001b[0;36mRunnableSequence.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 1773\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m i, step \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39msteps):\n\u001b[0;32m-> 1774\u001b[0m \u001b[38;5;28minput\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[43mstep\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1775\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1776\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# mark each step as a child run\u001b[39;49;00m\n\u001b[1;32m 1777\u001b[0m \u001b[43m \u001b[49m\u001b[43mpatch_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1778\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mget_child\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43mf\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mseq:step:\u001b[39;49m\u001b[38;5;132;43;01m{\u001b[39;49;00m\u001b[43mi\u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[38;5;132;43;01m}\u001b[39;49;00m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1779\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1780\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1781\u001b[0m \u001b[38;5;66;03m# finish the root run\u001b[39;00m\n", 263 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/output_parsers/base.py:176\u001b[0m, in \u001b[0;36mBaseOutputParser.invoke\u001b[0;34m(self, input, config)\u001b[0m\n\u001b[1;32m 175\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m--> 176\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call_with_config\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 177\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43minner_input\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 178\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 179\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 180\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_type\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mparser\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m 181\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", 264 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/runnables/base.py:975\u001b[0m, in \u001b[0;36mRunnable._call_with_config\u001b[0;34m(self, func, input, config, run_type, **kwargs)\u001b[0m\n\u001b[1;32m 972\u001b[0m context\u001b[38;5;241m.\u001b[39mrun(var_child_runnable_config\u001b[38;5;241m.\u001b[39mset, child_config)\n\u001b[1;32m 973\u001b[0m output \u001b[38;5;241m=\u001b[39m cast(\n\u001b[1;32m 974\u001b[0m Output,\n\u001b[0;32m--> 975\u001b[0m \u001b[43mcontext\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 976\u001b[0m \u001b[43m \u001b[49m\u001b[43mcall_func_with_variable_args\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 977\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 978\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;66;43;03m# type: ignore[arg-type]\u001b[39;49;00m\n\u001b[1;32m 979\u001b[0m \u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 980\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 981\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 982\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 983\u001b[0m )\n\u001b[1;32m 984\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n", 265 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/runnables/config.py:323\u001b[0m, in \u001b[0;36mcall_func_with_variable_args\u001b[0;34m(func, input, config, run_manager, **kwargs)\u001b[0m\n\u001b[1;32m 322\u001b[0m kwargs[\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mrun_manager\u001b[39m\u001b[38;5;124m\"\u001b[39m] \u001b[38;5;241m=\u001b[39m run_manager\n\u001b[0;32m--> 323\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", 266 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/output_parsers/base.py:177\u001b[0m, in \u001b[0;36mBaseOutputParser.invoke..\u001b[0;34m(inner_input)\u001b[0m\n\u001b[1;32m 175\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 176\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call_with_config(\n\u001b[0;32m--> 177\u001b[0m \u001b[38;5;28;01mlambda\u001b[39;00m inner_input: \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse_result\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[43mGeneration\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43minner_input\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m,\n\u001b[1;32m 178\u001b[0m \u001b[38;5;28minput\u001b[39m,\n\u001b[1;32m 179\u001b[0m config,\n\u001b[1;32m 180\u001b[0m run_type\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mparser\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 181\u001b[0m )\n", 267 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain_core/output_parsers/base.py:219\u001b[0m, in \u001b[0;36mBaseOutputParser.parse_result\u001b[0;34m(self, result, partial)\u001b[0m\n\u001b[1;32m 207\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Parse a list of candidate model Generations into a specific format.\u001b[39;00m\n\u001b[1;32m 208\u001b[0m \n\u001b[1;32m 209\u001b[0m \u001b[38;5;124;03mThe return value is parsed from only the first Generation in the result, which\u001b[39;00m\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 217\u001b[0m \u001b[38;5;124;03m Structured output.\u001b[39;00m\n\u001b[1;32m 218\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m--> 219\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mparse\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresult\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;241;43m0\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mtext\u001b[49m\u001b[43m)\u001b[49m\n", 268 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/output_parsers/react_single_input.py:75\u001b[0m, in \u001b[0;36mReActSingleInputOutputParser.parse\u001b[0;34m(self, text)\u001b[0m\n\u001b[1;32m 74\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m re\u001b[38;5;241m.\u001b[39msearch(\u001b[38;5;124mr\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAction\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124md*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*:[\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms]*(.*?)\u001b[39m\u001b[38;5;124m\"\u001b[39m, text, re\u001b[38;5;241m.\u001b[39mDOTALL):\n\u001b[0;32m---> 75\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m OutputParserException(\n\u001b[1;32m 76\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mCould not parse LLM output: `\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mtext\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m`\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 77\u001b[0m observation\u001b[38;5;241m=\u001b[39mMISSING_ACTION_AFTER_THOUGHT_ERROR_MESSAGE,\n\u001b[1;32m 78\u001b[0m llm_output\u001b[38;5;241m=\u001b[39mtext,\n\u001b[1;32m 79\u001b[0m send_to_llm\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[1;32m 80\u001b[0m )\n\u001b[1;32m 81\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m re\u001b[38;5;241m.\u001b[39msearch(\n\u001b[1;32m 82\u001b[0m \u001b[38;5;124mr\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m[\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms]*Action\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124md*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*Input\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124md*\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms*:[\u001b[39m\u001b[38;5;124m\\\u001b[39m\u001b[38;5;124ms]*(.*)\u001b[39m\u001b[38;5;124m\"\u001b[39m, text, re\u001b[38;5;241m.\u001b[39mDOTALL\n\u001b[1;32m 83\u001b[0m ):\n", 269 | "\u001b[0;31mOutputParserException\u001b[0m: Could not parse LLM output: ` 由于查询失败,无法直接获取到杭州地区ECS实例的到期时间。通常情况下,查询失败可能由以下原因导致:\n\n1. **权限问题**:当前使用的账号可能没有足够的权限访问或查询该区域的ECS实例信息。\n2. **参数错误**:提供的产品名、资源类型或地域信息可能有误,需要核实输入数据的准确性。\n3. **网络问题**:与阿里云服务器之间的网络连接可能出现故障,导致查询请求无法送达或响应未返回。\n4. **服务异常**:阿里云的云资源查询工具本身可能存在临时性故障或维护状态。\n\n由于无法确定具体原因,我无法立即给出杭州地区ECS实例的到期时间。建议进行以下操作以解决问题:\n\n1. **检查账号权限**:确保当前使用的账号具有查询ECS实例信息的权限,如有必要,可联系账号管理员调整权限设置。\n2. **核实输入数据**:再次确认输入的JSON参数中产品名、资源类型和地域是否正确无误,特别是地域名称“cn-hangzhou”。\n3. **测试网络连接**:尝试访问其他阿里云服务或重新执行查询操作,以判断是否存在网络问题。\n4. **联系阿里云支持**:如果以上步骤均未能解决问题,建议联系阿里云官方技术支持,报告查询失败的情况,寻求专业帮助。\n\n在问题解决后,才能成功查询到杭州地区ECS实例的到期时间。因此,目前无法提供最终答案。`", 270 | "\nDuring handling of the above exception, another exception occurred:\n", 271 | "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", 272 | "Cell \u001b[0;32mIn[4], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m llm \u001b[38;5;241m=\u001b[39m MyAgent()\n\u001b[0;32m----> 3\u001b[0m \u001b[43mllm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mask\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m杭州的ECS实例什么时候到期\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", 273 | "Cell \u001b[0;32mIn[3], line 29\u001b[0m, in \u001b[0;36mMyAgent.ask\u001b[0;34m(self, question)\u001b[0m\n\u001b[1;32m 28\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mask\u001b[39m(\u001b[38;5;28mself\u001b[39m, question):\n\u001b[0;32m---> 29\u001b[0m result \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43magent_executor\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43minvoke\u001b[49m\u001b[43m(\u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43minput\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mquestion\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 30\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m result[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124moutput\u001b[39m\u001b[38;5;124m'\u001b[39m]\n", 274 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/chains/base.py:162\u001b[0m, in \u001b[0;36mChain.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 160\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 161\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n\u001b[0;32m--> 162\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m e\n\u001b[1;32m 163\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_end(outputs)\n\u001b[1;32m 164\u001b[0m final_outputs: Dict[\u001b[38;5;28mstr\u001b[39m, Any] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprep_outputs(\n\u001b[1;32m 165\u001b[0m inputs, outputs, return_only_outputs\n\u001b[1;32m 166\u001b[0m )\n", 275 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/chains/base.py:156\u001b[0m, in \u001b[0;36mChain.invoke\u001b[0;34m(self, input, config, **kwargs)\u001b[0m\n\u001b[1;32m 149\u001b[0m run_manager \u001b[38;5;241m=\u001b[39m callback_manager\u001b[38;5;241m.\u001b[39mon_chain_start(\n\u001b[1;32m 150\u001b[0m dumpd(\u001b[38;5;28mself\u001b[39m),\n\u001b[1;32m 151\u001b[0m inputs,\n\u001b[1;32m 152\u001b[0m name\u001b[38;5;241m=\u001b[39mrun_name,\n\u001b[1;32m 153\u001b[0m )\n\u001b[1;32m 154\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m 155\u001b[0m outputs \u001b[38;5;241m=\u001b[39m (\n\u001b[0;32m--> 156\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_call\u001b[49m\u001b[43m(\u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 157\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m new_arg_supported\n\u001b[1;32m 158\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_call(inputs)\n\u001b[1;32m 159\u001b[0m )\n\u001b[1;32m 160\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mBaseException\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[1;32m 161\u001b[0m run_manager\u001b[38;5;241m.\u001b[39mon_chain_error(e)\n", 276 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:1329\u001b[0m, in \u001b[0;36mAgentExecutor._call\u001b[0;34m(self, inputs, run_manager)\u001b[0m\n\u001b[1;32m 1327\u001b[0m \u001b[38;5;66;03m# We now enter the agent loop (until it returns something).\u001b[39;00m\n\u001b[1;32m 1328\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_should_continue(iterations, time_elapsed):\n\u001b[0;32m-> 1329\u001b[0m next_step_output \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_take_next_step\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 1330\u001b[0m \u001b[43m \u001b[49m\u001b[43mname_to_tool_map\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1331\u001b[0m \u001b[43m \u001b[49m\u001b[43mcolor_mapping\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1332\u001b[0m \u001b[43m \u001b[49m\u001b[43minputs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1333\u001b[0m \u001b[43m \u001b[49m\u001b[43mintermediate_steps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1334\u001b[0m \u001b[43m \u001b[49m\u001b[43mrun_manager\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mrun_manager\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 1335\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 1336\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(next_step_output, AgentFinish):\n\u001b[1;32m 1337\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_return(\n\u001b[1;32m 1338\u001b[0m next_step_output, intermediate_steps, run_manager\u001b[38;5;241m=\u001b[39mrun_manager\n\u001b[1;32m 1339\u001b[0m )\n", 277 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:1055\u001b[0m, in \u001b[0;36mAgentExecutor._take_next_step\u001b[0;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)\u001b[0m\n\u001b[1;32m 1046\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_take_next_step\u001b[39m(\n\u001b[1;32m 1047\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 1048\u001b[0m name_to_tool_map: Dict[\u001b[38;5;28mstr\u001b[39m, BaseTool],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1052\u001b[0m run_manager: Optional[CallbackManagerForChainRun] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 1053\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[AgentFinish, List[Tuple[AgentAction, \u001b[38;5;28mstr\u001b[39m]]]:\n\u001b[1;32m 1054\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_consume_next_step(\n\u001b[0;32m-> 1055\u001b[0m [\n\u001b[1;32m 1056\u001b[0m a\n\u001b[1;32m 1057\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_next_step(\n\u001b[1;32m 1058\u001b[0m name_to_tool_map,\n\u001b[1;32m 1059\u001b[0m color_mapping,\n\u001b[1;32m 1060\u001b[0m inputs,\n\u001b[1;32m 1061\u001b[0m intermediate_steps,\n\u001b[1;32m 1062\u001b[0m run_manager,\n\u001b[1;32m 1063\u001b[0m )\n\u001b[1;32m 1064\u001b[0m ]\n\u001b[1;32m 1065\u001b[0m )\n", 278 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:1055\u001b[0m, in \u001b[0;36m\u001b[0;34m(.0)\u001b[0m\n\u001b[1;32m 1046\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21m_take_next_step\u001b[39m(\n\u001b[1;32m 1047\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m 1048\u001b[0m name_to_tool_map: Dict[\u001b[38;5;28mstr\u001b[39m, BaseTool],\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 1052\u001b[0m run_manager: Optional[CallbackManagerForChainRun] \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m 1053\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m Union[AgentFinish, List[Tuple[AgentAction, \u001b[38;5;28mstr\u001b[39m]]]:\n\u001b[1;32m 1054\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_consume_next_step(\n\u001b[0;32m-> 1055\u001b[0m [\n\u001b[1;32m 1056\u001b[0m a\n\u001b[1;32m 1057\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_iter_next_step(\n\u001b[1;32m 1058\u001b[0m name_to_tool_map,\n\u001b[1;32m 1059\u001b[0m color_mapping,\n\u001b[1;32m 1060\u001b[0m inputs,\n\u001b[1;32m 1061\u001b[0m intermediate_steps,\n\u001b[1;32m 1062\u001b[0m run_manager,\n\u001b[1;32m 1063\u001b[0m )\n\u001b[1;32m 1064\u001b[0m ]\n\u001b[1;32m 1065\u001b[0m )\n", 279 | "File \u001b[0;32m~/miniconda3/envs/aiworld/lib/python3.9/site-packages/langchain/agents/agent.py:1094\u001b[0m, in \u001b[0;36mAgentExecutor._iter_next_step\u001b[0;34m(self, name_to_tool_map, color_mapping, inputs, intermediate_steps, run_manager)\u001b[0m\n\u001b[1;32m 1092\u001b[0m raise_error \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[1;32m 1093\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m raise_error:\n\u001b[0;32m-> 1094\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\n\u001b[1;32m 1095\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mAn output parsing error occurred. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1096\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mIn order to pass this error back to the agent and have it try \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1097\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124magain, pass `handle_parsing_errors=True` to the AgentExecutor. \u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1098\u001b[0m \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThis is the error: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mstr\u001b[39m(e)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 1099\u001b[0m )\n\u001b[1;32m 1100\u001b[0m text \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mstr\u001b[39m(e)\n\u001b[1;32m 1101\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mhandle_parsing_errors, \u001b[38;5;28mbool\u001b[39m):\n", 280 | "\u001b[0;31mValueError\u001b[0m: An output parsing error occurred. In order to pass this error back to the agent and have it try again, pass `handle_parsing_errors=True` to the AgentExecutor. This is the error: Could not parse LLM output: ` 由于查询失败,无法直接获取到杭州地区ECS实例的到期时间。通常情况下,查询失败可能由以下原因导致:\n\n1. **权限问题**:当前使用的账号可能没有足够的权限访问或查询该区域的ECS实例信息。\n2. **参数错误**:提供的产品名、资源类型或地域信息可能有误,需要核实输入数据的准确性。\n3. **网络问题**:与阿里云服务器之间的网络连接可能出现故障,导致查询请求无法送达或响应未返回。\n4. **服务异常**:阿里云的云资源查询工具本身可能存在临时性故障或维护状态。\n\n由于无法确定具体原因,我无法立即给出杭州地区ECS实例的到期时间。建议进行以下操作以解决问题:\n\n1. **检查账号权限**:确保当前使用的账号具有查询ECS实例信息的权限,如有必要,可联系账号管理员调整权限设置。\n2. **核实输入数据**:再次确认输入的JSON参数中产品名、资源类型和地域是否正确无误,特别是地域名称“cn-hangzhou”。\n3. **测试网络连接**:尝试访问其他阿里云服务或重新执行查询操作,以判断是否存在网络问题。\n4. **联系阿里云支持**:如果以上步骤均未能解决问题,建议联系阿里云官方技术支持,报告查询失败的情况,寻求专业帮助。\n\n在问题解决后,才能成功查询到杭州地区ECS实例的到期时间。因此,目前无法提供最终答案。`" 281 | ] 282 | } 283 | ], 284 | "source": [ 285 | "llm = MyAgent()\n", 286 | "\n", 287 | "llm.ask(\"杭州的ECS实例什么时候到期\")\n" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "由于本章使用的测试账号没有开通ECS服务,因此反馈没有查询到ECS实例。\n", 295 | "\n", 296 | ">您可以[查看云服务产品信息](https://www.aliyun.com/product/ecs?source=5176.11533457#/)通过阿里云权益中心购买的 99 元/年的经济型 2核2G ECS 实例,新老客户都可以购买,并且后续可以 99 元续费。您可以参考[阿里云便宜服务器优惠合集](https://developer.aliyun.com/article/1445729)参与优惠活动\n", 297 | "\n", 298 | "#### 2.3.2. 查询更多阿里云资源\n", 299 | "\n", 300 | "接下来,我们在不做任何代码改动的情况下查询VPC信息。理论上我们可以查询[API应用范围](https://api.aliyun.com/product/cloudcontrol?tab=apis)的所有资源。" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 19, 306 | "metadata": {}, 307 | "outputs": [ 308 | { 309 | "name": "stdout", 310 | "output_type": "stream", 311 | "text": [ 312 | "\n", 313 | "\n", 314 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 315 | "\u001b[32;1m\u001b[1;3m 需要查询我在上海地区的VPC资源\n", 316 | "Action: 阿里云的云资源查询工具\n", 317 | "Action Input: {\"product\": \"VPC\", \"resource\": \"VPC\", \"region\": \"cn-shanghai\"}\n", 318 | "\u001b[0m\u001b[36;1m\u001b[1;3m{'maxResults': 2, 'requestId': '448C226E-CD30-5F33-BCA6-DCEE8794F8BD', 'resources': [], 'totalCount': 0}\u001b[0m\u001b[32;1m\u001b[1;3m 我现在知道最终答案\n", 319 | "Final Answer: 您在上海地区没有VPC资源。\u001b[0m\n", 320 | "\n", 321 | "\u001b[1m> Finished chain.\u001b[0m\n" 322 | ] 323 | }, 324 | { 325 | "data": { 326 | "text/plain": [ 327 | "'您在上海地区没有VPC资源。'" 328 | ] 329 | }, 330 | "execution_count": 19, 331 | "metadata": {}, 332 | "output_type": "execute_result" 333 | } 334 | ], 335 | "source": [ 336 | "llm.ask(\"我在上海有没有VPC\")" 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "#### 2.3.3. 通义千问无需工具就能回答的问题\n", 344 | "\n", 345 | "最后,我们看看大模型是如何回答不在API范围内的问题的,这里我们将询问ECS的定价策略,通义千问能直接给出合理的答案。" 346 | ] 347 | }, 348 | { 349 | "cell_type": "code", 350 | "execution_count": 20, 351 | "metadata": {}, 352 | "outputs": [ 353 | { 354 | "name": "stdout", 355 | "output_type": "stream", 356 | "text": [ 357 | "\n", 358 | "\n", 359 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 360 | "\u001b[32;1m\u001b[1;3m 我会回答。\n", 361 | "Final Answer: 阿里云ECS服务的定价主要基于以下几个因素:\n", 362 | "\n", 363 | "1. 实例类型:包括实例规格(如计算型c6、内存型r5等)、CPU核数、内存大小等。不同的实例类型性能配置不同,价格也会有所差异。\n", 364 | "\n", 365 | "2. 地域:同一实例在不同地域的价格可能会有所不同。\n", 366 | "\n", 367 | "3. 系统镜像:选择的操作系统类型和镜像版本也会影响定价,例如Windows镜像会比Linux镜像收费高一些。\n", 368 | "\n", 369 | "4. 存储类型与容量:ECS实例使用的数据盘类型(如高效云盘、SSD云盘、ESSD云盘等)及容量大小会影响价格。\n", 370 | "\n", 371 | "5. 计费方式:阿里云提供了按量付费(即按小时计费)和包年包月两种计费方式。按量付费模式下,您只需为实际使用的计算资源付费;包年包月则需要预先支付一定费用,一般情况下此种计费方式相比按量付费更为经济。\n", 372 | "\n", 373 | "6. 其他增值服务:如安全组、负载均衡、快照、备份等额外服务会产生额外费用。\n", 374 | "\n", 375 | "要获取准确的ECS实例定价信息,请访问阿里云官方产品页面或者使用阿里云官方提供的定价计算器进行详细计算:https://www.aliyun.com/price/product#/ecs 。\u001b[0m\n", 376 | "\n", 377 | "\u001b[1m> Finished chain.\u001b[0m\n" 378 | ] 379 | } 380 | ], 381 | "source": [ 382 | "ans = llm.ask(\"ECS服务如何定价\")" 383 | ] 384 | }, 385 | { 386 | "cell_type": "markdown", 387 | "metadata": {}, 388 | "source": [ 389 | "#### 2.3.4. 封装成文件来执行\n", 390 | "\n", 391 | "上述代码我们封装在文件 aliyun_resource_agent.py 中,你可以使用如下的指令来执行" 392 | ] 393 | }, 394 | { 395 | "cell_type": "code", 396 | "execution_count": 4, 397 | "metadata": {}, 398 | "outputs": [ 399 | { 400 | "name": "stdout", 401 | "output_type": "stream", 402 | "text": [ 403 | "问题:我在上海有没有VPC\n", 404 | "\n", 405 | "\n", 406 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", 407 | "\u001b[32;1m\u001b[1;3m 需要查询用户在上海的VPC资源\n", 408 | "Action: 阿里云的云资源查询工具\n", 409 | "Action Input: {\"product\": \"VPC\", \"resource\": \"VPC\", \"region\": \"cn-shanghai\"}\n", 410 | "\u001b[0m\u001b[36;1m\u001b[1;3m{'maxResults': 2, 'requestId': '99B869A2-8F8C-53A8-AC0C-F53DD4571BC9', 'resources': [], 'totalCount': 0}\u001b[0m\u001b[32;1m\u001b[1;3m 根据查询结果,用户在上海没有VPC资源\n", 411 | "Final Answer: 您在上海没有VPC资源。\u001b[0m\n", 412 | "\n", 413 | "\u001b[1m> Finished chain.\u001b[0m\n", 414 | "您在上海没有VPC资源。\n" 415 | ] 416 | } 417 | ], 418 | "source": [ 419 | "!python aliyun_resource_agent.py \"我在上海有没有VPC\"" 420 | ] 421 | }, 422 | { 423 | "cell_type": "markdown", 424 | "metadata": {}, 425 | "source": [ 426 | "## 3. 参考资料\n", 427 | "- [阿里云控制API](https://api.aliyun.com/product/cloudcontrol?tab=apis)\n", 428 | "- [DashScope](https://dashscope.aliyun.com/)\n", 429 | "- [通义灵码](https://help.aliyun.com/document_detail/2590613.html)\n", 430 | "- [LangChain- 基于语义相似度的路由](https://python.langchain.com/docs/expression_language/cookbook/embedding_router)\n", 431 | "\n" 432 | ] 433 | } 434 | ], 435 | "metadata": { 436 | "kernelspec": { 437 | "display_name": "chatllm", 438 | "language": "python", 439 | "name": "python3" 440 | }, 441 | "language_info": { 442 | "codemirror_mode": { 443 | "name": "ipython", 444 | "version": 3 445 | }, 446 | "file_extension": ".py", 447 | "mimetype": "text/x-python", 448 | "name": "python", 449 | "nbconvert_exporter": "python", 450 | "pygments_lexer": "ipython3", 451 | "version": "3.9.12" 452 | } 453 | }, 454 | "nbformat": 4, 455 | "nbformat_minor": 2 456 | } 457 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | dashscope==1.13.6 2 | langchain==0.1.0 3 | langchain_core==0.1.8 4 | langchainhub==0.1.14 5 | langchain-experimental==0.0.49 6 | beautifulsoup4==4.12.2 7 | html2text==2020.1.16 8 | alibabacloud_cloudcontrol20220830==1.1.0 9 | openpyxl==3.1.2 10 | python-dotenv 11 | ipykernel --------------------------------------------------------------------------------