{"cells": [{"cell_type": "markdown", "id": "ba5f8741", "metadata": {}, "source": ["# 插拔式", "", "这个笔记本是基于[插件检索](./custom_agent_with_plugin_retrieval.html)的想法构建的,但是从`plugnplai`目录中提取所有工具 - 一组AI插件。"]}, {"cell_type": "markdown", "id": "fea4812c", "metadata": {}, "source": ["## 设置环境", "", "进行必要的导入等操作。"]}, {"cell_type": "markdown", "id": "aca08be8", "metadata": {}, "source": ["安装 plugnplai 库,从 https://plugplai.com 目录获取活动插件列表。"]}, {"cell_type": "code", "execution_count": 1, "id": "52e248c9", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.1.1\u001b[0m\n", "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n", "Note: you may need to restart the kernel to use updated packages.\n"]}], "source": ["# 安装 plugnplai 包", "!pip install plugnplai -q"]}, {"cell_type": "code", "execution_count": 2, "id": "9af9734e", "metadata": {}, "outputs": [], "source": ["import re # 导入正则表达式模块", "from typing import Union # 导入Union类型", "", "import plugnplai # 导入plugnplai模块", "from langchain.agents import ( # 导入langchain.agents模块中的以下类", " AgentExecutor, # AgentExecutor类", " AgentOutputParser, # AgentOutputParser类", " LLMSingleActionAgent, # LLMSingleActionAgent类", ")", "from langchain.chains import LLMChain # 导入LLMChain类", "from langchain.prompts import StringPromptTemplate # 导入StringPromptTemplate类", "from langchain_community.agent_toolkits import NLAToolkit # 导入NLAToolkit类", "from langchain_community.tools.plugin import AIPlugin # 导入AIPlugin类", "from langchain_core.agents import AgentAction, AgentFinish # 导入AgentAction和AgentFinish类", "from langchain_openai import OpenAI # 导入OpenAI类", "以上是给定的代码的翻译结果,已经在代码上方添加了注释。"]}, {"cell_type": "markdown", "id": "2f91d8b4", "metadata": {}, "source": ["## 设置LLM"]}, {"cell_type": "code", "execution_count": 4, "id": "a1a3b59c", "metadata": {}, "outputs": [], "source": ["# 导入OpenAI类", "from openai import OpenAI", "", "# 创建OpenAI对象并设置温度为0", "llm = OpenAI(temperature=0)"]}, {"cell_type": "markdown", "id": "6df0253f", "metadata": {}, "source": ["## 设置插件", "", "加载并索引插件"]}, {"cell_type": "code", "execution_count": 8, "id": "9e0f7882", "metadata": {}, "outputs": [], "source": ["# 从plugnplai.com获取所有插件", "urls = plugnplai.get_plugins()", "", "# 获取ChatGPT插件 - 仅获取ChatGPT验证的插件", "urls = plugnplai.get_plugins(filter=\"ChatGPT\")", "", "# 获取可用的插件 - 仅获取经过测试的插件(进行中)", "urls = plugnplai.get_plugins(filter=\"working\")", "", "# 从urls中的每个url获取AI插件的信息并创建AIPlugin对象", "AI_PLUGINS = [AIPlugin.from_url(url + \"/.well-known/ai-plugin.json\") for url in urls]"]}, {"cell_type": "markdown", "id": "17362717", "metadata": {}, "source": ["## 工具检索器", "", "我们将使用一个向量存储库为每个工具描述创建嵌入。然后,对于传入的查询,我们可以为该查询创建嵌入,并对相关工具进行相似性搜索。"]}, {"cell_type": "code", "execution_count": 4, "id": "77c4be4b", "metadata": {}, "outputs": [], "source": ["# 导入所需的模块", "from langchain_community.vectorstores import FAISS", "from langchain_core.documents import Document", "from langchain_openai import OpenAIEmbeddings"]}, {"cell_type": "code", "execution_count": 5, "id": "9092a158", "metadata": {}, "outputs": [{"name": "stderr", "output_type": "stream", "text": ["Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.2 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load an OpenAPI 3.0.1 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n", "Attempting to load a Swagger 2.0 spec. This may result in degraded performance. Convert your OpenAPI spec to 3.1.* spec for better support.\n"]}], "source": ["# 导入OpenAIEmbeddings模块", "embeddings = OpenAIEmbeddings()", "", "# 创建一个包含多个Document对象的列表", "docs = [", " Document(", " page_content=plugin.description_for_model,", " metadata={\"plugin_name\": plugin.name_for_model},", " )", " for plugin in AI_PLUGINS", "]", "", "# 使用docs和embeddings创建一个vector_store对象", "vector_store = FAISS.from_documents(docs, embeddings)", "", "# 创建一个包含多个NLAToolkit对象的字典", "toolkits_dict = {", " plugin.name_for_model: NLAToolkit.from_llm_and_ai_plugin(llm, plugin)", " for plugin in AI_PLUGINS", "}"]}, {"cell_type": "code", "execution_count": 6, "id": "735a7566", "metadata": {}, "outputs": [], "source": ["retriever = vector_store.as_retriever() # 将vector_store转换为检索器", "", "def get_tools(query):", " # 获取包含要使用的插件的文档", " docs = retriever.invoke(query)", " # 获取每个插件的工具包", " tool_kits = [toolkits_dict[d.metadata[\"plugin_name\"]] for d in docs]", " # 获取每个端点的单独NLAChain工具", " tools = []", " for tk in tool_kits:", " tools.extend(tk.nla_tools)", " return tools # 返回工具列表"]}, {"cell_type": "markdown", "id": "7699afd7", "metadata": {}, "source": ["我们现在可以测试这个检索器,看看它是否能正常工作。"]}, {"cell_type": "code", "execution_count": 7, "id": "425f2886", "metadata": {}, "outputs": [{"data": {"text/plain": ["['Milo.askMilo',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.search_all_actions',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.preview_a_zap',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_configuration_link',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.list_exposed_actions',\n", " 'SchoolDigger_API_V2.0.Autocomplete_GetSchools',\n", " 'SchoolDigger_API_V2.0.Districts_GetAllDistricts2',\n", " 'SchoolDigger_API_V2.0.Districts_GetDistrict2',\n", " 'SchoolDigger_API_V2.0.Rankings_GetSchoolRank2',\n", " 'SchoolDigger_API_V2.0.Rankings_GetRank_District',\n", " 'SchoolDigger_API_V2.0.Schools_GetAllSchools20',\n", " 'SchoolDigger_API_V2.0.Schools_GetSchool20',\n", " 'Speak.translate',\n", " 'Speak.explainPhrase',\n", " 'Speak.explainTask']"]}, "execution_count": 7, "metadata": {}, "output_type": "execute_result"}], "source": ["# 导入get_tools函数", "from some_module import get_tools", "", "# 调用get_tools函数,并传入\"What could I do today with my kiddo\"作为参数", "tools = get_tools(\"What could I do today with my kiddo\")", "", "# 使用列表推导式,获取tools列表中每个元素的name属性,并将结果存储在新的列表中", "[name for t in tools]"]}, {"cell_type": "code", "execution_count": 8, "id": "3aa88768", "metadata": {}, "outputs": [{"data": {"text/plain": ["['Open_AI_Klarna_product_Api.productsUsingGET',\n", " 'Milo.askMilo',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.search_all_actions',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.preview_a_zap',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.get_configuration_link',\n", " 'Zapier_Natural_Language_Actions_(NLA)_API_(Dynamic)_-_Beta.list_exposed_actions',\n", " 'SchoolDigger_API_V2.0.Autocomplete_GetSchools',\n", " 'SchoolDigger_API_V2.0.Districts_GetAllDistricts2',\n", " 'SchoolDigger_API_V2.0.Districts_GetDistrict2',\n", " 'SchoolDigger_API_V2.0.Rankings_GetSchoolRank2',\n", " 'SchoolDigger_API_V2.0.Rankings_GetRank_District',\n", " 'SchoolDigger_API_V2.0.Schools_GetAllSchools20',\n", " 'SchoolDigger_API_V2.0.Schools_GetSchool20']"]}, "execution_count": 8, "metadata": {}, "output_type": "execute_result"}], "source": ["# 获取工具函数,参数为用户输入的问题", "tools = get_tools(\"我可以买什么样的衬衫?\")", "# 打印工具列表中每个工具的名称", "[t.name for t in tools]"]}, {"cell_type": "markdown", "id": "2e7a075c", "metadata": {}, "source": ["## 提示模板", "", "提示模板相当标准,因为我们实际上并没有在实际提示模板中改变太多逻辑,而是改变了检索的方式。"]}, {"cell_type": "code", "execution_count": 9, "id": "339b1bb8", "metadata": {}, "outputs": [], "source": ["# 设置基本模板", "template = \"\"\"以海盗的口吻回答以下问题,尽力而为。你可以使用以下工具:", "", "{tools}", "", "使用以下格式:", "", "问题:你必须回答的输入问题", "思考:你应该始终考虑该做什么", "行动:要采取的行动,应该是[{tool_names}]中的一个", "行动输入:行动的"]}, {"cell_type": "markdown", "id": "1583acdc", "metadata": {}, "source": ["自定义提示模板现在具有一个名为tools_getter的概念,我们在输入上调用它来选择要使用的工具。"]}, {"cell_type": "code", "execution_count": 10, "id": "fd969d31", "metadata": {}, "outputs": [], "source": ["从typing模块导入Callable", "", "", "# 设置一个提示模板", "class CustomPromptTemplate(StringPromptTemplate):", " # 要使用的模板", " template: str", " ############## 新增 ######################", " # 可用工具的列表", " tools_getter: Callable", "", " def format(self, **kwargs) -> str:", " # 获取中间步骤(AgentAction,Observation元组)", " # 以特定方式格式化它们", " intermediate_steps = kwargs.pop(\"intermediate_steps\")", " thoughts = \"\"", " for action, observation in intermediate_steps:", " thoughts += action.log", " thoughts += f\"\\nObservation: {observation}\\nThought: \"", " # 将agent_scratchpad变量设置为该值", " kwargs[\"agent_scratchpad\"] = thoughts", " ############## 新增 ######################", " tools = self.tools_getter(kwargs[\"input\"])", " # 从提供的工具列表创建一个tools变量", " kwargs[\"tools\"] = \"\\n\".join(", " [f\"{tool.name}: {tool.description}\" for tool in tools]", " )", " # 为提供的工具创建一个工具名称列表", " kwargs[\"tool_names\"] = \", \".join([tool.name for tool in tools])", " return self.template.format(**kwargs)"]}, {"cell_type": "code", "execution_count": 11, "id": "798ef9fb", "metadata": {}, "outputs": [], "source": ["prompt = CustomPromptTemplate(", " template=template,", " tools_getter=get_tools,", " # 这里省略了`agent_scratchpad`、`tools`和`tool_names`变量,因为它们是动态生成的", " # 这里包括`intermediate_steps`变量,因为它是必需的", " input_variables=[\"input\", \"intermediate_steps\"],", ")"]}, {"cell_type": "markdown", "id": "ef3a1af3", "metadata": {}, "source": ["## 输出解析器", "", "输出解析器与之前的笔记本保持不变,因为我们不会改变输出格式的任何内容。"]}, {"cell_type": "code", "execution_count": 12, "id": "7c6fe0d3", "metadata": {}, "outputs": [], "source": ["class CustomOutputParser(AgentOutputParser):", " def parse(self, llm_output: str) -> Union[AgentAction, AgentFinish]:", " # 检查代理是否应该完成", " if \"Final Answer:\" in llm_output:", " return AgentFinish(", " # 返回值通常始终是一个带有单个 `output` 键的字典", " # 目前不建议尝试其他操作 :)", " return_values={\"output\": llm_output.split(\"Final Answer:\")[-1].strip()},", " log=llm_output,", " )", " # 解析动作和动作输入", " regex = r\"Action\\s*\\d*\\s*:(.*?)\\nAction\\s*\\d*\\s*Input\\s*\\d*\\s*:[\\s]*(.*)\"", " match = re.search(regex, llm_output, re.DOTALL)", " if not match:", " raise ValueError(f\"无法解析LLM输出:`{llm_output}`\")", " action = match.group(1).strip()", " action_input = match.group(2)", " # 返回动作和动作输入", " return AgentAction(", " tool=action, tool_input=action_input.strip(\" \").strip('\"'), log=llm_output", " )"]}, {"cell_type": "code", "execution_count": 13, "id": "d278706a", "metadata": {}, "outputs": [], "source": ["# 创建一个名为output_parser的对象,类型为CustomOutputParser()", "output_parser = CustomOutputParser()"]}, {"cell_type": "markdown", "id": "170587b1", "metadata": {}, "source": ["## 设置LLM、停止序列和代理", "", "与之前的笔记本相同。"]}, {"cell_type": "code", "execution_count": 14, "id": "f9d4c374", "metadata": {}, "outputs": [], "source": ["# 创建一个OpenAI对象,设置温度参数为0", "llm = OpenAI(temperature=0)"]}, {"cell_type": "code", "execution_count": 15, "id": "9b1cc2a2", "metadata": {}, "outputs": [], "source": ["# 创建一个LLM链,包括LLM模型和一个提示", "llm_chain = LLMChain(llm=llm, prompt=prompt)"]}, {"cell_type": "code", "execution_count": 16, "id": "e4f5092f", "metadata": {}, "outputs": [], "source": ["# 创建一个列表,包含了所有工具的名称", "tool_names = [tool.name for tool in tools]", "", "# 创建一个LLMSingleActionAgent对象", "# 参数:", "# llm_chain: LLM链", "# output_parser: 输出解析器", "# stop: 停止标志,当遇到\"\\nObservation:\"时停止", "# allowed_tools: 允许使用的工具名称列表", "agent = LLMSingleActionAgent(", " llm_chain=llm_chain,", " output_parser=output_parser,", " stop=[\"\\nObservation:\"],", " allowed_tools=tool_names,", ")"]}, {"cell_type": "markdown", "id": "aa8a5326", "metadata": {}, "source": ["## 使用代理", "", "现在我们可以使用它了!"]}, {"cell_type": "code", "execution_count": 17, "id": "490604e9", "metadata": {}, "outputs": [], "source": ["# 导入AgentExecutor类", "from rasa.core.agent import AgentExecutor", "", "# 创建AgentExecutor对象,并传入agent和tools参数,verbose设置为True", "agent_executor = AgentExecutor.from_agent_and_tools(", " agent=agent, tools=tools, verbose=True", ")"]}, {"cell_type": "code", "execution_count": 18, "id": "653b1617", "metadata": {}, "outputs": [{"name": "stdout", "output_type": "stream", "text": ["\n", "\n", "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n", "\u001b[32;1m\u001b[1;3mThought: I need to find a product API\n", "Action: Open_AI_Klarna_product_Api.productsUsingGET\n", "Action Input: shirts\u001b[0m\n", "\n", "Observation:\u001b[36;1m\u001b[1;3mI found 10 shirts from the API response. They range in price from $9.99 to $450.00 and come in a variety of materials, colors, and patterns.\u001b[0m\u001b[32;1m\u001b[1;3m I now know what shirts I can buy\n", "Final Answer: Arg, I found 10 shirts from the API response. They range in price from $9.99 to $450.00 and come in a variety of materials, colors, and patterns.\u001b[0m\n", "\n", "\u001b[1m> Finished chain.\u001b[0m\n"]}, {"data": {"text/plain": ["'Arg, I found 10 shirts from the API response. They range in price from $9.99 to $450.00 and come in a variety of materials, colors, and patterns.'"]}, "execution_count": 18, "metadata": {}, "output_type": "execute_result"}], "source": ["# 导入所需的库", "import agent_executor", "", "# 调用agent_executor的run函数,并传入参数\"what shirts can i buy?\"", "agent_executor.run(\"what shirts can i buy?\")"]}, {"cell_type": "code", "execution_count": null, "id": "2481ee76", "metadata": {}, "outputs": [], "source": []}], "metadata": {"kernelspec": {"display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.3"}, "vscode": {"interpreter": {"hash": "3ccef4e08d87aa1eeb90f63e0f071292ccb2e9c42e70f74ab2bf6f5493ca7bbc"}}}, "nbformat": 4, "nbformat_minor": 5}