Add files via upload

main
Jianxin Ma 1 year ago committed by GitHub
parent 19ae06b8b5
commit 3157ed4867
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -9,7 +9,7 @@
"source": [ "source": [
"# 如何让 Qwen-7b 使用 Langchain 中的 工具\n", "# 如何让 Qwen-7b 使用 Langchain 中的 工具\n",
"\n", "\n",
"本文档主要介绍如何让千问调用 [LangChain](https://python.langchain.com/docs/get_started/introduction.html) 框架中实现好的谷歌搜索、WolframAlpha 等工具。将主要基于 [ReAct Prompting](https://github.com/QwenLM/Qwen-7B/blob/main/examples/react_prompt.md) 技术一种特殊的链式思考Chain-of-Thought简称 CoT提示技巧来实现这一目的。" "本文档主要介绍如何让千问调用 [LangChain](https://python.langchain.com/docs/get_started/introduction.html) 框架中实现好的谷歌搜索、 WolframAlpha 等工具。将主要基于 [ReAct Prompting](https://github.com/QwenLM/Qwen-7B/blob/main/examples/react_prompt.md) 技术一种特殊的链式思考Chain-of-Thought简称 CoT提示技巧来实现这一目的。"
] ]
}, },
{ {
@ -56,6 +56,7 @@
" - [谷歌搜索API](https://serper.dev/?gclid=EAIaIQobChMIj9eqof7OgAMV44VbCh1F3QZoEAAYASABEgIh3fD_BwE#google-search-api)\n", " - [谷歌搜索API](https://serper.dev/?gclid=EAIaIQobChMIj9eqof7OgAMV44VbCh1F3QZoEAAYASABEgIh3fD_BwE#google-search-api)\n",
" - [WolframAlpha](https://products.wolframalpha.com/api/)\n", " - [WolframAlpha](https://products.wolframalpha.com/api/)\n",
" - arxiv论文搜索\n", " - arxiv论文搜索\n",
" - python shell (需升级python至3.9以上使用)\n",
"\n", "\n",
"注1此处推荐模仿此案例细致地构造给千问看的工具描述。\n", "注1此处推荐模仿此案例细致地构造给千问看的工具描述。\n",
"\n", "\n",
@ -64,7 +65,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 10,
"id": "07e49b98-9d6c-41f2-9b18-f043f2d13e1a", "id": "07e49b98-9d6c-41f2-9b18-f043f2d13e1a",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
@ -72,6 +73,7 @@
"from langchain import SerpAPIWrapper\n", "from langchain import SerpAPIWrapper\n",
"from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper\n", "from langchain.utilities.wolfram_alpha import WolframAlphaAPIWrapper\n",
"from langchain.utilities import ArxivAPIWrapper\n", "from langchain.utilities import ArxivAPIWrapper\n",
"from langchain.tools.python.tool import PythonAstREPLTool\n",
"\n", "\n",
"from typing import Dict, Tuple\n", "from typing import Dict, Tuple\n",
"import os\n", "import os\n",
@ -87,6 +89,7 @@
"search = SerpAPIWrapper()\n", "search = SerpAPIWrapper()\n",
"WolframAlpha = WolframAlphaAPIWrapper()\n", "WolframAlpha = WolframAlphaAPIWrapper()\n",
"arxiv = ArxivAPIWrapper()\n", "arxiv = ArxivAPIWrapper()\n",
"python=PythonAstREPLTool()\n",
"\n", "\n",
"def tool_wrapper_for_qwen(tool):\n", "def tool_wrapper_for_qwen(tool):\n",
" def tool_(query):\n", " def tool_(query):\n",
@ -140,7 +143,24 @@
" 'required': True\n", " 'required': True\n",
" }], \n", " }], \n",
" 'tool_api': tool_wrapper_for_qwen(arxiv)\n", " 'tool_api': tool_wrapper_for_qwen(arxiv)\n",
" } \n", " },\n",
" {\n",
" 'name_for_human':\n",
" 'python',\n",
" 'name_for_model':\n",
" 'python',\n",
" 'description_for_model':\n",
" \"A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. \"\n",
" \"Don't add comments to your python code.\",\n",
" 'parameters': [{\n",
" \"name\": \"query\",\n",
" \"type\": \"string\",\n",
" \"description\": \"a valid python command.\",\n",
" 'required': True\n",
" }],\n",
" 'tool_api': tool_wrapper_for_qwen(python)\n",
" }\n",
"\n",
"]\n" "]\n"
] ]
}, },
@ -162,7 +182,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 4, "execution_count": 3,
"id": "4a8feb0e-22f7-4184-9ea0-b864812c9b09", "id": "4a8feb0e-22f7-4184-9ea0-b864812c9b09",
"metadata": { "metadata": {
"scrolled": true "scrolled": true
@ -249,10 +269,50 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": 4,
"id": "f71b2577-118c-4ce2-a0ed-a45ec59ea35b", "id": "f71b2577-118c-4ce2-a0ed-a45ec59ea35b",
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to \"AutoModelForCausalLM.from_pretrained\".\n",
"Try importing flash-attention for faster inference...\n",
"Warning: import flash_attn rotary fail, please install FlashAttention rotary to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/rotary\n",
"Warning: import flash_attn rms_norm fail, please install FlashAttention layer_norm to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm\n",
"Warning: import flash_attn fail, please install FlashAttention to get higher efficiency https://github.com/Dao-AILab/flash-attention\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "ed76951f2662402c9932f0b27548a47b",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Loading checkpoint shards: 0%| | 0/8 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d448bf9d60dd42648e60918276c0729e",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Downloading (…)neration_config.json: 0%| | 0.00/194 [00:00<?, ?B/s]"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [ "source": [
"# 国内连 hugginface 网络不好,这段代码可能需要多重试\n", "# 国内连 hugginface 网络不好,这段代码可能需要多重试\n",
"checkpoint = \"Qwen/Qwen-7B-Chat\"\n", "checkpoint = \"Qwen/Qwen-7B-Chat\"\n",
@ -264,7 +324,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 5,
"id": "dc0dbd6c-5a0f-44c9-a019-0ec0283ca92d", "id": "dc0dbd6c-5a0f-44c9-a019-0ec0283ca92d",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@ -296,7 +356,7 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 6,
"id": "1a431670-a1f6-4afd-972f-1cfd6d06e8c9", "id": "1a431670-a1f6-4afd-972f-1cfd6d06e8c9",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@ -349,12 +409,12 @@
}, },
"source": [ "source": [
"## 第三步:让千问根据工具返回结果继续作答\n", "## 第三步:让千问根据工具返回结果继续作答\n",
"拼接上述返回答案形成新的prompt,并获得生成最终结果" "拼接上述返回答案形成新的prompt并获得生成最终结果"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 9, "execution_count": 7,
"id": "a9d4d42d", "id": "a9d4d42d",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
@ -405,9 +465,40 @@
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 10, "execution_count": 8,
"id": "1e51a8ea", "id": "1e51a8ea",
"metadata": {}, "metadata": {},
"outputs": [],
"source": [
"def main(query, choose_tools):\n",
" prompt = build_planning_prompt(choose_tools, query) # 组织prompt\n",
" print(prompt)\n",
" stop = [\"Observation:\", \"Observation:\\n\"]\n",
" react_stop_words_tokens = [TOKENIZER.encode(stop_) for stop_ in stop]\n",
" response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens)\n",
"\n",
" while \"Final Answer:\" not in response: # 出现final Answer时结束\n",
" api_output = use_api(choose_tools, response) # 抽取入参并执行api\n",
" api_output = str(api_output) # 部分api工具返回结果非字符串格式需进行转化后输出\n",
" if \"no tool founds\" == api_output:\n",
" break\n",
" print(\"\\033[32m\" + response + \"\\033[0m\" + \"\\033[34m\" + ' ' + api_output + \"\\033[0m\")\n",
" prompt = prompt + response + ' ' + api_output # 合并api输出\n",
" response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens) # 继续生成\n",
"\n",
" print(\"\\033[32m\" + response + \"\\033[0m\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "6dc38a34",
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
}
},
"outputs": [ "outputs": [
{ {
"name": "stdout", "name": "stdout",
@ -422,11 +513,13 @@
"\n", "\n",
"Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n", "Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n", "\n",
"python: Call this tool to interact with the python API. What is the python API useful for? A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. Don't add comments to your python code. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"a valid python command.\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Use the following format:\n", "Use the following format:\n",
"\n", "\n",
"Question: the input question you must answer\n", "Question: the input question you must answer\n",
"Thought: you should always think about what to do\n", "Thought: you should always think about what to do\n",
"Action: the action to take, should be one of [Search,Math,Arxiv]\n", "Action: the action to take, should be one of [Search,Math,Arxiv,python]\n",
"Action Input: the input to the action\n", "Action Input: the input to the action\n",
"Observation: the result of the action\n", "Observation: the result of the action\n",
"... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n", "... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n",
@ -451,11 +544,13 @@
"\n", "\n",
"Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n", "Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n", "\n",
"python: Call this tool to interact with the python API. What is the python API useful for? A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. Don't add comments to your python code. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"a valid python command.\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Use the following format:\n", "Use the following format:\n",
"\n", "\n",
"Question: the input question you must answer\n", "Question: the input question you must answer\n",
"Thought: you should always think about what to do\n", "Thought: you should always think about what to do\n",
"Action: the action to take, should be one of [Search,Math,Arxiv]\n", "Action: the action to take, should be one of [Search,Math,Arxiv,python]\n",
"Action Input: the input to the action\n", "Action Input: the input to the action\n",
"Observation: the result of the action\n", "Observation: the result of the action\n",
"... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n", "... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n",
@ -465,13 +560,20 @@
"Begin!\n", "Begin!\n",
"\n", "\n",
"Question: 求解方程 2x+5 = -3x + 7\n", "Question: 求解方程 2x+5 = -3x + 7\n",
"\u001b[32mThought: 我应该使用数学工具帮助我完成任务。Wolfram Alpha API能完成求解方程的任务。\n", "\u001b[32mThought: 提供的工具对回答该问题帮助较小,我将不使用工具直接作答。\n",
"Action: Math\n", "Final Answer: 为了解这个方程我们需要对方程进行变换以便找到x的值。首先我们可以将方程中的2x移到右边得到\n",
"Action Input: {\"query\": \"2x+5 = -3x + 7\"}\n", "\n",
"Observation:\u001b[0m\u001b[34m Assumption: 2 x + 5 = -3 x + 7 \n", "2x - 3x = 7 - 5\n",
"Answer: x = 2/5\u001b[0m\n", "\n",
"\u001b[32mThought: I now know the final answer.\n", "然后,我们可以简化这个方程,得到:\n",
"Final Answer: x = 2/5\u001b[0m\n", "\n",
"-1x = 2\n",
"\n",
"接下来,我们将方程两边除以-1以求得x的值\n",
"\n",
"x = -2\n",
"\n",
"所以方程2x + 5 = -3x + 7的解为x = -2。\u001b[0m\n",
"==========\n", "==========\n",
"Answer the following questions as best you can. You have access to the following tools:\n", "Answer the following questions as best you can. You have access to the following tools:\n",
"\n", "\n",
@ -481,11 +583,13 @@
"\n", "\n",
"Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n", "Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n", "\n",
"python: Call this tool to interact with the python API. What is the python API useful for? A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. Don't add comments to your python code. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"a valid python command.\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Use the following format:\n", "Use the following format:\n",
"\n", "\n",
"Question: the input question you must answer\n", "Question: the input question you must answer\n",
"Thought: you should always think about what to do\n", "Thought: you should always think about what to do\n",
"Action: the action to take, should be one of [Search,Math,Arxiv]\n", "Action: the action to take, should be one of [Search,Math,Arxiv,python]\n",
"Action Input: the input to the action\n", "Action Input: the input to the action\n",
"Observation: the result of the action\n", "Observation: the result of the action\n",
"... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n", "... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n",
@ -495,7 +599,7 @@
"Begin!\n", "Begin!\n",
"\n", "\n",
"Question: 编号是1605.08386的论文讲了些什么?\n", "Question: 编号是1605.08386的论文讲了些什么?\n",
"\u001b[32mThought: 我需要调用Arxiv API来获取论文信息。\n", "\u001b[32mThought: 我需要使用Arxiv API来搜索论文\n",
"Action: Arxiv\n", "Action: Arxiv\n",
"Action Input: {\"query\": \"1605.08386\"}\n", "Action Input: {\"query\": \"1605.08386\"}\n",
"Observation:\u001b[0m\u001b[34m Published: 2016-05-26\n", "Observation:\u001b[0m\u001b[34m Published: 2016-05-26\n",
@ -509,29 +613,41 @@
"walk, a generalization of the Glauber dynamics, is an expander in fixed\n", "walk, a generalization of the Glauber dynamics, is an expander in fixed\n",
"dimension.\u001b[0m\n", "dimension.\u001b[0m\n",
"\u001b[32mThought: I now know the final answer.\n", "\u001b[32mThought: I now know the final answer.\n",
"Final Answer: 论文编号为1605.08386的论文主要研究了热浴随机行走的性质。论文中,作者通过考虑有限个允许的任意长度的移动,研究了在有限矩阵的纤维上的图的直径的上界。此外,作者还研究了热浴随机行走的混合行为,并给出了在固定维度下,热浴随机行走是一种扩张的条件。\u001b[0m\n" "Final Answer: 论文编号为1605.08386的论文主要研究了热浴随机行走的性质。论文中,作者通过考虑有限个允许的任意长度的移动,研究了在有限矩阵的纤维上的图的直径的上界。此外,作者还研究了热浴随机行走的混合行为,并给出了在固定维度下,热浴随机行走是一种扩张的条件。\u001b[0m\n",
"Answer the following questions as best you can. You have access to the following tools:\n",
"\n",
"Search: Call this tool to interact with the google search API. What is the google search API useful for? useful for when you need to answer questions about current events. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"search query of google\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Math: Call this tool to interact with the Wolfram Alpha API. What is the Wolfram Alpha API useful for? Useful for when you need to answer questions about Math, Science, Technology, Culture, Society and Everyday Life. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"search query of Wolfram Alpha\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Arxiv: Call this tool to interact with the arxiv API. What is the arxiv API useful for? A wrapper around Arxiv.org Useful for when you need to answer questions about Physics, Mathematics, Computer Science, Quantitative Biology, Quantitative Finance, Statistics, Electrical Engineering, and Economics from scientific articles on arxiv.org. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"the document id of arxiv to search\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"python: Call this tool to interact with the python API. What is the python API useful for? A Python shell. Use this to execute python commands. When using this tool, sometimes output is abbreviated - Make sure it does not look abbreviated before using it in your answer. Don't add comments to your python code. Parameters: [{\"name\": \"query\", \"type\": \"string\", \"description\": \"a valid python command.\", \"required\": true}] Format the arguments as a JSON object.\n",
"\n",
"Use the following format:\n",
"\n",
"Question: the input question you must answer\n",
"Thought: you should always think about what to do\n",
"Action: the action to take, should be one of [Search,Math,Arxiv,python]\n",
"Action Input: the input to the action\n",
"Observation: the result of the action\n",
"... (this Thought/Action/Action Input/Observation can be repeated zero or more times)\n",
"Thought: I now know the final answer\n",
"Final Answer: the final answer to the original input question\n",
"\n",
"Begin!\n",
"\n",
"Question: 使用python对下面的列表进行排序 [2, 4135, 523, 2, 3]\n",
"\u001b[32mThought: 我应该使用python API来执行python命令。\n",
"Action: python\n",
"Action Input: {\"query\": \"sorted([2, 4135, 523, 2, 3])\"}\n",
"Observation:\u001b[0m\u001b[34m [2, 2, 3, 523, 4135]\u001b[0m\n",
"\u001b[32mThought: I now know the final answer.\n",
"Final Answer: 使用python对给定的列表进行排序结果为 [2, 2, 3, 523, 4135]。\u001b[0m\n"
] ]
} }
], ],
"source": [ "source": [
"def main(query, choose_tools):\n",
" prompt = build_planning_prompt(choose_tools, query) # 组织prompt\n",
" print(prompt)\n",
" stop = [\"Observation:\", \"Observation:\\n\"]\n",
" react_stop_words_tokens = [TOKENIZER.encode(stop_) for stop_ in stop]\n",
" response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens)\n",
"\n",
" while \"Final Answer:\" not in response: # 出现final Answer时结束\n",
" api_output = use_api(choose_tools, response) # 抽取入参并执行api\n",
" if \"no tool founds\" == api_output:\n",
" break\n",
" print(\"\\033[32m\" + response + \"\\033[0m\" + \"\\033[34m\" + ' ' + api_output + \"\\033[0m\")\n",
" prompt = prompt + response + ' ' + api_output # 合并api输出\n",
" response, _ = MODEL.chat(TOKENIZER, prompt, history=None, stop_words_ids=react_stop_words_tokens) # 继续生成\n",
"\n",
" print(\"\\033[32m\" + response + \"\\033[0m\")\n",
"\n",
" \n",
"query = \"加拿大2022年的人口数量有多少\" # 所提问题\n", "query = \"加拿大2022年的人口数量有多少\" # 所提问题\n",
"choose_tools = TOOLS # 选择备选工具\n", "choose_tools = TOOLS # 选择备选工具\n",
"print(\"=\" * 10)\n", "print(\"=\" * 10)\n",
@ -545,6 +661,10 @@
"query = \"编号是1605.08386的论文讲了些什么?\" # 所提问题\n", "query = \"编号是1605.08386的论文讲了些什么?\" # 所提问题\n",
"choose_tools = TOOLS # 选择备选工具\n", "choose_tools = TOOLS # 选择备选工具\n",
"print(\"=\" * 10)\n", "print(\"=\" * 10)\n",
"main(query, choose_tools)\n",
"\n",
"query =\"使用python对下面的列表进行排序 [2, 4135, 523, 2, 3]\"\n",
"choose_tools = TOOLS # 选择备选工具\n",
"main(query, choose_tools)" "main(query, choose_tools)"
] ]
} }

Loading…
Cancel
Save