From 6d335e68632b3a16a5ba46cd35e74106a34661fd Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:09:02 +0630 Subject: [PATCH 01/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch02.ipynb | 611 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 GPT4Dev_ch02.ipynb diff --git a/GPT4Dev_ch02.ipynb b/GPT4Dev_ch02.ipynb new file mode 100644 index 000000000..11dcd1ab7 --- /dev/null +++ b/GPT4Dev_ch02.ipynb @@ -0,0 +1,611 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true, + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 2 使用 Python 呼叫 API\n", + "\n", + "OpenAI 官方提供有 openai 套件, 可以簡化直接使用 requests 模組的複雜度。" + ], + "metadata": { + "id": "a42M4mg1qNQ5" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 2-1 使用官方 openai 套件" + ], + "metadata": { + "id": "IQrw-pWsth0b" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 安裝與使用 openai **套件**" + ], + "metadata": { + "id": "rwGTl75BLNwu" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V9x1F86C4T9u" + }, + "outputs": [], + "source": [ + "!pip install openai" + ] + }, + { + "cell_type": "code", + "source": [ + "import openai\n", + "client = openai.OpenAI(api_key='你的金鑰')" + ], + "metadata": { + "id": "YUrSE6EAtPat" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")" + ], + "metadata": { + "id": "OBkpRomPEXd1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "檢視傳回物件" + ], + "metadata": { + "id": "pyelatvvLiIn" + } + }, + { + "cell_type": "code", + "source": [ + "print(reply)" + ], + "metadata": { + "id": "aSjownHrGnde" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!pip install rich" + ], + "metadata": { + "id": "k1ZiBQSrtj_N" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from rich import print as pprint\n", + "pprint(reply)" + ], + "metadata": { + "id": "KtgXUDs9tn1N" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "oDHvn0VCGPzH" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 直接透過模組使用 API" + ], + "metadata": { + "id": "Ev4TvoQ3ps-z" + } + }, + { + "cell_type": "code", + "source": [ + "openai.api_key = '你的金鑰'\n", + "\n", + "reply = openai.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "gvomwkY3pwNx" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "pprint(reply)" + ], + "metadata": { + "id": "joNfGjzFJb5q" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "48eLL4VEQGza" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 傳遞多筆訊息" + ], + "metadata": { + "id": "BP5FkDxALp4e" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " messages = [\n", + " {\"role\":\"system\",\n", + " \"content\":\"你是條住在深海、只會台灣中文的魚\"},\n", + " {\"role\":\"user\", \"content\": \"你住的地方很亮嗎?\"}\n", + " ]\n", + ")" + ], + "metadata": { + "id": "j1x0glPsNJe2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "P_EyFHjnu3-7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 設定與隱藏金鑰的方法\n", + "\n", + "Colab 提供有 Secret, 可以依帳戶儲存機密資料, 並設定是否允許必記本存取。機密資料不會隨筆記本分享出去, 只有帳戶擁有者才能讀取。" + ], + "metadata": { + "id": "7KJQF5gbmnD0" + } + }, + { + "cell_type": "code", + "source": [ + "from google.colab import userdata\n", + "userdata.get('OPENAI_API_KEY')" + ], + "metadata": { + "id": "Bl8V96ifcvDn" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "程式中就可以不用出現金鑰了" + ], + "metadata": { + "id": "oNlCvuaWLBgt" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 2-2 認識 token" + ], + "metadata": { + "id": "65D0ie-qt8_d" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 使用 tiktoken 套件計算精確 token 數\n" + ], + "metadata": { + "id": "2Z-Ab0R2Kg4V" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install tiktoken\n", + "import tiktoken" + ], + "metadata": { + "id": "L98OeqzKjJVm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "encoder = tiktoken.encoding_for_model('gpt-3.5-turbo')\n", + "print(encoder.name)\n", + "encoder = tiktoken.encoding_for_model('gpt-4')\n", + "print(encoder.name)" + ], + "metadata": { + "id": "SZxDdv76jrvO" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "tokens = encoder.encode(\"你好\")\n", + "print(tokens)\n", + "print(encoder.decode(tokens))" + ], + "metadata": { + "id": "O8SFWn0HHtsp" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### ChatML 標記語言" + ], + "metadata": { + "id": "0Pqpu0XGMDcD" + } + }, + { + "cell_type": "code", + "source": [ + "print(encoder.encode(\"user\"))\n", + "print(encoder.encode(\"assistant\"))\n", + "print(encoder.encode(\"system\"))\n", + "print(encoder.encode(\"\\n\"))" + ], + "metadata": { + "id": "tWDiULI-My-x" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "計算 message 總 tokens 數" + ], + "metadata": { + "id": "Pp8Dlbd8MNPL" + } + }, + { + "cell_type": "code", + "source": [ + "def tokens_in_messages(messages, model=\"gpt-3.5-turbo\",\n", + " show=False):\n", + " totals = 0\n", + " for message in messages:\n", + " for k in message:\n", + " if k == \"content\":\n", + " totals += 4 # <|im_start|>user\\n{內容}<|im_end|>\n", + " totals += len(encoder.encode(message[k]))\n", + " totals += 3 # <|im_start|>assistant<|message|>\n", + " return totals" + ], + "metadata": { + "id": "UTMcDBnLuz4Z" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(tokens_in_messages([\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]))" + ], + "metadata": { + "id": "bHO6ozY5wT4J" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 2-3 使用 Python requests 模組呼叫 API" + ], + "metadata": { + "id": "IeuwNf0Yu1Ns" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 使用 Python requests 模組\n", + "\n", + "Python 提供有 requests 模組, 可以快速發送 HTTP 請求。
\n", + "利用 requests 模組叫用 API。" + ], + "metadata": { + "id": "mKw5tOFpKonw" + } + }, + { + "cell_type": "code", + "source": [ + "# 如果是要在本機上執行, 必須安裝 requests 套件\n", + "!pip install requests" + ], + "metadata": { + "id": "ss8DbRyEibHA" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "import os\n", + "\n", + "response = requests.post(\n", + " 'https://api.openai.com/v1/chat/completions',\n", + " headers = {\n", + " 'Content-Type': 'application/json',\n", + " 'Authorization': f'Bearer {userdata.get(\"OPENAI_API_KEY\")}'\n", + " },\n", + " json = {\n", + " 'model': 'gpt-3.5-turbo',\n", + " \"messages\": [{\"role\": \"user\", \"content\": \"你好\"}]}\n", + ")" + ], + "metadata": { + "id": "uAm9PP81kJ2Z" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = response.json()" + ], + "metadata": { + "id": "_LWTNsg3KYfC" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "pprint(reply)" + ], + "metadata": { + "id": "c_wdjKa2xdZI" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(reply[\"choices\"][0][\"message\"][\"content\"])" + ], + "metadata": { + "id": "em0QqDHkxDtO" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 利用 curl 工具快速測試 API\n" + ], + "metadata": { + "id": "K8zuEzkFKNjQ" + } + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "os.environ['OPENAI_API_KEY'] = userdata.get(\"OPENAI_API_KEY\")" + ], + "metadata": { + "id": "asI4Linsx6a3" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%bash\n", + "curl -s https://api.openai.com/v1/chat/completions \\\n", + "-H 'Content-Type: application/json' \\\n", + "-H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n", + "-d '{\n", + " \"model\": \"gpt-3.5-turbo\",\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"user\", \"content\": \"你好\"\n", + " }\n", + " ]\n", + " }'" + ], + "metadata": { + "id": "shYWnoMHXO9V" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 2-4 加入組織成員" + ], + "metadata": { + "id": "OrfD1QvfNb-E" + } + }, + { + "cell_type": "code", + "source": [ + "client = openai.OpenAI(\n", + " api_key=userdata.get('OPENAI_API_KEY'),\n", + " organization='你的組織識別碼'\n", + ")\n", + "\n", + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "hMnIMxKjNhq_" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "import os\n", + "\n", + "response = requests.post(\n", + " 'https://api.openai.com/v1/chat/completions',\n", + " headers = {\n", + " 'Content-Type': 'application/json',\n", + " 'Authorization': f'Bearer {userdata.get(\"OPENAI_API_KEY\")}',\n", + " 'OpenAI-Organization': '你的組織識別碼'\n", + " },\n", + " json = {\n", + " 'model': 'gpt-3.5-turbo',\n", + " \"messages\": [{\"role\": \"user\", \"content\": \"你好\"}]}\n", + ")\n", + "\n", + "print(response.json()['choices'][0]['message']['content'])" + ], + "metadata": { + "id": "rFTHNgTgTomy" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%bash\n", + "curl -s https://api.openai.com/v1/chat/completions \\\n", + "-H 'Content-Type: application/json' \\\n", + "-H \"Authorization: Bearer $OPENAI_API_KEY\" \\\n", + "-H \"OpenAI-Organization: 請換成你的組織識別碼\" \\\n", + "-d '{\n", + " \"model\": \"gpt-3.5-turbo\",\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"user\", \"content\": \"你好\"\n", + " }\n", + " ]\n", + " }'" + ], + "metadata": { + "id": "WoN8zKuEVdql" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 37a85642b1d0bb9a7b1c0be2c66b849b3cbfeb78 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:10:46 +0630 Subject: [PATCH 02/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch03.ipynb | 975 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 975 insertions(+) create mode 100644 GPT4Dev_ch03.ipynb diff --git a/GPT4Dev_ch03.ipynb b/GPT4Dev_ch03.ipynb new file mode 100644 index 000000000..14d9f2ef8 --- /dev/null +++ b/GPT4Dev_ch03.ipynb @@ -0,0 +1,975 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 3 API 參數解析與錯誤處理" + ], + "metadata": { + "id": "AG7gBKUV92Bu" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 3-1 事前準備\n", + "\n", + "安裝必要的套件與匯入相關模組後建立用戶端物件" + ], + "metadata": { + "id": "ccVUpuXM9dRz" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install openai\n", + "!pip install rich\n", + "import openai\n", + "from google.colab import userdata\n", + "from rich import print as pprint\n", + "client = openai.OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ], + "metadata": { + "id": "Dd2tbXUFAMgR" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 3-2 控制生成訊息與 token 數量" + ], + "metadata": { + "id": "v1-ga-Tyw5Ou" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 指定生成的訊息數量 - n" + ], + "metadata": { + "id": "ODW5sx_XMSYG" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " n=2\n", + ")\n", + "\n", + "pprint(reply)\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ], + "metadata": { + "id": "kRgJDzMUilrz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 設定詞彙黑名單 - stop" + ], + "metadata": { + "id": "RqbMN9POMUkr" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " stop=['好']\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)\n", + "print(reply.choices[0].finish_reason)" + ], + "metadata": { + "id": "yXJqNS3fjRZx" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 設定回覆語句的 tokens 數量上限 - max_tokens" + ], + "metadata": { + "id": "1KUb_1ELLvyc" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"您好!\"}\n", + " ],\n", + " max_tokens = 5\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)\n", + "print(reply.choices[0].finish_reason)\n", + "print(reply.usage.completion_tokens)" + ], + "metadata": { + "id": "EPrrVXy2Pf6M" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!pip install tiktoken\n", + "import tiktoken\n", + "encoder = tiktoken.encoding_for_model('gpt-3.5-turbo')" + ], + "metadata": { + "id": "ADzh725yf96r" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "encoder.encode(\"您好!有什\")" + ], + "metadata": { + "id": "Lww8VuHe5TgP" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "超過模型限制的 tokens 數" + ], + "metadata": { + "id": "TPQbUVh3L9tf" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ],\n", + " max_tokens = 16380\n", + ")" + ], + "metadata": { + "id": "7L-Bvt2NHDcB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 3-3 控制回覆內容的變化性" + ], + "metadata": { + "id": "Bp3B7u-pw_qL" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 讓回覆更具彈性 - temperature" + ], + "metadata": { + "id": "JQhZoMpINYAr" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " temperature=0,\n", + " n=2\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ], + "metadata": { + "id": "ECWZd8GUkIfj" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " temperature=2,\n", + " n=2\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ], + "metadata": { + "id": "xXAJhTiVkRYZ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 控制詞彙的豐富度 - top_p" + ], + "metadata": { + "id": "JM6fYpXYJ4P2" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " top_p=0,\n", + " n=2\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ], + "metadata": { + "id": "ZC5i5Lt864EW" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 控制詞彙的重複性 - presence_penalty 與 frequency_penalty" + ], + "metadata": { + "id": "0XwQ69fZNsDX" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " temperature=1,\n", + " presence_penalty=-2,\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "L-yFTbubsROQ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " temperature=1,\n", + " presence_penalty=2,\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "ilf99zqz4B0f" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"}],\n", + " temperature=1,\n", + " frequency_penalty=-2,\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "T_VmWcN66DDv" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"}],\n", + " temperature=1,\n", + " frequency_penalty=2,\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "nnE5E1lu4EeU" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 調整特定 token 的分數 - logi-bias\n" + ], + "metadata": { + "id": "lc2KqtIVKBeB" + } + }, + { + "cell_type": "code", + "source": [ + "encoder.encode('你好')" + ], + "metadata": { + "id": "Ecv9UTckKC0T" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " temperature=1,\n", + " logit_bias={\n", + " 53901: -100,\n", + " 57668: -100\n", + " },\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "wVKafC6IKGnR" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "encoder.encode('哈')" + ], + "metadata": { + "id": "wfphuAh7KHA8" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " temperature=1,\n", + " logit_bias={\n", + " 99771: 100,\n", + " },\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "KveBU5Qff6tE" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 固定可預測的輸出 - seed\n", + "\n", + "使用亂數種子固定輸出, 不保證, 自己要檢查回覆指紋。1106 的模型才提供。" + ], + "metadata": { + "id": "EgsbCjDGJWJ2" + } + }, + { + "cell_type": "code", + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-0613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"請用兩句話介紹台北市\"\n", + " }],\n", + " seed=1\n", + ")\n", + "\n", + "print(replies.system_fingerprint)\n", + "print(replies.choices[0].message.content)" + ], + "metadata": { + "id": "QPu-CP-5LYnY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"請用兩句話介紹台北市\"\n", + " }],\n", + " seed=2\n", + ")\n", + "\n", + "print(replies.system_fingerprint)\n", + "print(replies.choices[0].message.content)" + ], + "metadata": { + "id": "dRs8oH8qNjUB" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 3-4 串流輸出" + ], + "metadata": { + "id": "9oBB8v9idNQb" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 可循序傳回結果的迭代器 (iterator) - stream\n" + ], + "metadata": { + "id": "p-798ckrMcjT" + } + }, + { + "cell_type": "code", + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\"\n", + " }],\n", + " stream=True,\n", + ")\n", + "\n", + "for reply in replies:\n", + " pprint(reply)" + ], + "metadata": { + "id": "UKOBD3_WdMIf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " stream=True\n", + ")\n", + "\n", + "for reply in replies:\n", + " print(reply.choices[0].delta.content or '', end='')" + ], + "metadata": { + "id": "OSUKBv9hk9aX" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 串流多個語句" + ], + "metadata": { + "id": "HNMaghUIMXjV" + } + }, + { + "cell_type": "code", + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt-3.5-turbo\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\"\n", + " }],\n", + " stream=True,\n", + " n=2\n", + ")\n", + "\n", + "for reply in replies:\n", + " print(reply.choices[0].delta.content or '', end='')" + ], + "metadata": { + "id": "IpvOB9g9MkaY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "##3-5 進階控制" + ], + "metadata": { + "id": "GYrRV0k1ga0B" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 控制輸出格式 - response_format\n" + ], + "metadata": { + "id": "Lq6AgwlP9L-q" + } + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = [{\n", + " \"role\":\"user\",\n", + " \"content\": \"台灣最高的山高度是多少?\"\n", + " \"請分別告訴我山名和高度\"\n", + " }\n", + " ]\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "Vs7Sa7yG9ORm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = [\n", + " {\"role\":\"user\",\n", + " \"content\": \"台灣最高的山高度是多少?\"\n", + " \"請分別告訴我山名和高度\"},\n", + " {\"role\":\"system\", \"content\": \"請用 json 格式回覆\"}\n", + " ],\n", + " response_format={'type': 'json_object'} # or 'text'\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ], + "metadata": { + "id": "kL3ax4Uz9s2o" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 取得底層 HTTP 回應內容" + ], + "metadata": { + "id": "BblEBT0A5C-H" + } + }, + { + "cell_type": "code", + "source": [ + "# 取得原始 HTTP 回覆內容\n", + "reply = client.chat.completions.with_raw_response.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")" + ], + "metadata": { + "id": "RXHwp-NY1EFl" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "print(reply.status_code)\n", + "print('------')\n", + "print(reply.text) # JSON 格式文字\n", + "print('------')\n", + "reply_dic = json.loads(reply.text) # 轉成 Python 字典\n", + "pprint(reply_dic)" + ], + "metadata": { + "id": "3lxlCxYz1K1v" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")\n", + "\n", + "pprint(reply.model_dump())" + ], + "metadata": { + "id": "6Fnz9WeyKQLl" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 有眼睛的模型 - gpt-4-vision (GPT-4V)" + ], + "metadata": { + "id": "tinUei5sXLh8" + } + }, + { + "cell_type": "code", + "source": [ + "response = client.chat.completions.create(\n", + " model=\"gpt-4-vision-preview\",\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"圖片裡有什麼?\"},\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": \"https://flagtech.github.io/F3762/\"\n", + " \"images/cat1.jpg\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " ],\n", + " }\n", + " ],\n", + " max_tokens=1000,\n", + ")\n", + "\n", + "pprint(response.choices[0].message.content)" + ], + "metadata": { + "id": "_TB7TsFdXRnH" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "r = requests.get(\n", + " 'https://flagtech.github.io/F3762/images/cat2.jpg'\n", + ")\n", + "\n", + "with open('cat2.jpg', 'wb') as f:\n", + " f.write(r.content)" + ], + "metadata": { + "id": "1C-70pKZHpJQ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import base64\n", + "\n", + "def encode_image(image_path):\n", + " with open(image_path, \"rb\") as image_file:\n", + " return base64.b64encode(image_file.read()).decode('utf-8')" + ], + "metadata": { + "id": "OQXov1_OcDZ8" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "img = encode_image('cat2.jpg')\n", + "\n", + "response = client.chat.completions.create(\n", + " model=\"gpt-4-vision-preview\",\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"圖片裡有什麼?\"},\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": f\"data:image/jpeg;base64,{img}\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " ],\n", + " }\n", + " ],\n", + " max_tokens=300,\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ], + "metadata": { + "id": "pNs9jSuKbxPI" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "response = client.chat.completions.create(\n", + " model=\"gpt-4-vision-preview\",\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"這些圖片裡相同的是什麼?\"},\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": \"https://flagtech.github.io/F3762/\"\n", + " \"images/cat1.jpg\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": \"https://flagtech.github.io/F3762/\"\n", + " \"images/cat2.jpg\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " ],\n", + " }\n", + " ],\n", + " max_tokens=1000,\n", + ")\n", + "\n", + "pprint(response.choices[0].message.content)" + ], + "metadata": { + "id": "qv9i3ceLOuSf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 3-5 錯誤處理與使用限制" + ], + "metadata": { + "id": "MzbgDbQCMInK" + } + }, + { + "cell_type": "code", + "source": [ + "'''\n", + "Exception\n", + "+--OpenAIError\n", + " +--APIError ◆ message: str\n", + " | ◆ request: httpx.Request\n", + " +--APIResponseValidationError ◆ response: httpx.Response\n", + " | (回覆內容格式有誤) ◆ status_code: int\n", + " +--APIStatusError ◆ response: httpx.Response\n", + " | | ◆ status_code: int\n", + " | +--BadRequestError (請求參數或是格式錯誤)\n", + " | +--AuthenticationError (金鑰認證有問題)\n", + " | +--PermissionDeniedError\n", + " | +--NotFoundError\n", + " | +--ConflictError\n", + " | +--UnprocessableEntityError\n", + " | +--RateLimitError (超過次數限制)\n", + " | +--InternalServerError\n", + " +--APIConnectionError (無法連線)\n", + " +--APITimeoutError (連線逾時)\n", + "'''" + ], + "metadata": { + "id": "28kEhkf5RTJc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 使用例外機制處理錯誤" + ], + "metadata": { + "id": "1bV1BZvzMF06" + } + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ],\n", + " max_tokens = 20000\n", + " )\n", + " print(reply.choices[0].message.content)\n", + "\n", + "except openai.APIError as err:\n", + " print(err.message)" + ], + "metadata": { + "id": "fBmSX7n1KsDV" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "try:\n", + " reply = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ],\n", + " max_tokens = 20000\n", + " )\n", + " print(reply.choices[0].message.content)\n", + "\n", + "except openai.APIStatusError as err:\n", + " err_json = err.response.json()\n", + " print(f\"錯誤類型:{err_json['error']['type']}\")\n", + " print(f\"錯誤碼:{err_json['error']['code']}\")\n", + " print(f\"錯誤訊息:{err_json['error']['message']}\")" + ], + "metadata": { + "id": "l4YlKKlPszBd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "eOyPzdCRs66r" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From f4012ed7de809eb249fbd4f1694fde6f0cf1052b Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:12:06 +0630 Subject: [PATCH 03/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch04.ipynb | 388 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 GPT4Dev_ch04.ipynb diff --git a/GPT4Dev_ch04.ipynb b/GPT4Dev_ch04.ipynb new file mode 100644 index 000000000..babfad023 --- /dev/null +++ b/GPT4Dev_ch04.ipynb @@ -0,0 +1,388 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 4 打造自己的 ChatGPT\n", + "\n", + "學會使用 API 後, 下一步就是設計自己的 ChatGPT, 首先從簡單的文字模式開始。" + ], + "metadata": { + "id": "eNYngg3YPEp5" + } + }, + { + "cell_type": "markdown", + "source": [ + "**準備工作**" + ], + "metadata": { + "id": "ccVUpuXM9dRz" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install openai\n", + "!pip install rich\n", + "import openai\n", + "from google.colab import userdata\n", + "from rich import print as pprint\n", + "client = openai.OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ], + "metadata": { + "id": "R1CAsaxl9_v4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 4-1 文字模式簡易聊天程式" + ], + "metadata": { + "id": "kR8G5znxxuht" + } + }, + { + "cell_type": "markdown", + "source": [ + "設計簡易對談程式" + ], + "metadata": { + "id": "X021wDnuPYlx" + } + }, + { + "cell_type": "code", + "source": [ + "def get_reply(messages):\n", + " try:\n", + " response = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = messages\n", + " )\n", + " reply = response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.error.message}\"\n", + " return reply" + ], + "metadata": { + "id": "LRBgH2SzEjLr" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " messages = [{\"role\":\"user\", \"content\":msg}]\n", + " reply = get_reply(messages)\n", + " print(f\"ㄟ唉:{reply}\\n\")" + ], + "metadata": { + "id": "ptsDFS0mFZ_b" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 4-2 加入聊天記錄維持聊天脈絡" + ], + "metadata": { + "id": "VyO0gDj7yD5M" + } + }, + { + "cell_type": "markdown", + "source": [ + "把歷史紀錄加入 prompt" + ], + "metadata": { + "id": "iIsWBAoaPj1z" + } + }, + { + "cell_type": "code", + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat(sys_msg, user_msg):\n", + " global hist\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " reply = get_reply(hist\n", + " + [{\"role\":\"system\", \"content\":sys_msg}])\n", + " hist.append({\"role\":\"assistant\", \"content\":reply})\n", + " hist = hist[-2 * backtrace:] # 保留新的對話\n", + " return reply" + ], + "metadata": { + "id": "V--0U28tI17U" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " reply = chat(sys_msg, msg)\n", + " print(f\"{sys_msg}:{reply}\\n\")\n", + "hist = []" + ], + "metadata": { + "id": "uwhnfM-6JAvA" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 4-3 串流版本的聊天程式" + ], + "metadata": { + "id": "Qqa3sGEDyMp1" + } + }, + { + "cell_type": "markdown", + "source": [ + "串流版本的聊天程式" + ], + "metadata": { + "id": "G2Ge034UfYDd" + } + }, + { + "cell_type": "code", + "source": [ + "def get_reply_s(messages):\n", + " try:\n", + " response = client.chat.completions.create(\n", + " model = \"gpt-3.5-turbo-1106\",\n", + " messages = messages,\n", + " stream = True\n", + " )\n", + " for chunk in response:\n", + " yield chunk.choices[0].delta.content or ''\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.error.message}\"" + ], + "metadata": { + "id": "bpHqQNiMfemv" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "for reply in get_reply_s([{\n", + " \"role\":\"user\",\n", + " \"content\":\"請介紹台北市\"\n", + "}]):\n", + " print(reply, end='')\n", + "print('')" + ], + "metadata": { + "id": "KZciBFLufywW" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_s(sys_msg, user_msg):\n", + " global hist\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " reply_full = \"\"\n", + " for reply in get_reply_s( # 使用串流版的函式\n", + " hist + [{\"role\":\"system\", \"content\":sys_msg}]):\n", + " reply_full += reply # 記錄到目前為止收到的訊息\n", + " yield reply # 傳回本次收到的片段訊息\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新紀錄\n" + ], + "metadata": { + "id": "1UIcjOjqgffE" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_s(sys_msg, msg):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ], + "metadata": { + "id": "21fxgL_qg7r-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 4-4 儲存歷史紀錄下次繼續聊" + ], + "metadata": { + "id": "BBJ3bg-Yybrj" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 掛接 Google 雲端硬碟" + ], + "metadata": { + "id": "elmbokywh8nJ" + } + }, + { + "cell_type": "code", + "source": [ + "from google.colab import drive\n", + "drive.mount('/content/drive')" + ], + "metadata": { + "id": "l2TpFXgARStK" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 製作復原/儲存歷史紀錄的函式" + ], + "metadata": { + "id": "f0tvFK6lQ1xh" + } + }, + { + "cell_type": "code", + "source": [ + "import pickle" + ], + "metadata": { + "id": "-BmlbZgdiu4l" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "def save_hist(hist, sys_msg):\n", + " try:\n", + " with open('/content/drive/MyDrive/hist.dat', 'wb') as f:\n", + " db = {\n", + " 'hist': hist,\n", + " 'sys_msg': sys_msg\n", + " }\n", + " pickle.dump(db, f)\n", + " except:\n", + " # 歷史檔開啟錯誤\n", + " print('無法寫入歷史檔')" + ], + "metadata": { + "id": "WuC11lpIjwD6" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "def load_hist():\n", + " try:\n", + " with open('/content/drive/MyDrive/hist.dat', 'rb') as f:\n", + " db = pickle.load(f)\n", + " return db['hist'], db['sys_msg']\n", + " except:\n", + " # 歷史檔不存在\n", + " print('無法開啟歷史檔')\n", + " return [], ''" + ], + "metadata": { + "id": "qp2tZiczidHG" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "hist, sys_msg = load_hist()\n", + "sys_msg_new = input(f\"你希望ㄟ唉扮演[{sys_msg}]:\")\n", + "if sys_msg_new: sys_msg = sys_msg_new\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_s(sys_msg, msg):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "save_hist(hist, sys_msg)" + ], + "metadata": { + "id": "s-Jjg7So0GU2" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 0c73adbbb7ebefc607448e4694215067020605a1 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:13:31 +0630 Subject: [PATCH 04/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...347\232\204\345\211\257\346\234\254.ipynb" | 383 ++++++++++++++++++ 1 file changed, 383 insertions(+) create mode 100644 "\343\200\214GPT4Dev_ch05\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" diff --git "a/\343\200\214GPT4Dev_ch05\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" "b/\343\200\214GPT4Dev_ch05\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" new file mode 100644 index 000000000..6b468285c --- /dev/null +++ "b/\343\200\214GPT4Dev_ch05\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" @@ -0,0 +1,383 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true, + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 5 突破時空限制–整合搜尋功能" + ], + "metadata": { + "id": "eNYngg3YPEp5" + } + }, + { + "cell_type": "markdown", + "source": [ + "**準備工作**\n", + "\n", + "安裝必要的套件與匯入相關模組後建立用戶端物件" + ], + "metadata": { + "id": "ccVUpuXM9dRz" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install openai\n", + "!pip install rich\n", + "import openai\n", + "from google.colab import userdata\n", + "from rich import print as pprint\n", + "client = openai.OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ], + "metadata": { + "id": "R1CAsaxl9_v4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 5-1 用搜尋網頁幫 AI 補充知識" + ], + "metadata": { + "id": "3O-zz4SSzPoF" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 使用 Google 搜尋" + ], + "metadata": { + "id": "f814Yd_fRBA0" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install googlesearch-python" + ], + "metadata": { + "id": "AtHx23DvU-T5" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from googlesearch import search" + ], + "metadata": { + "id": "Oj6I-4g6Vw7K" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "for item in search(\"2023 金馬獎影后\"):\n", + " print(item)" + ], + "metadata": { + "id": "I2IzKRzZWVjc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "使用進階搜尋選項" + ], + "metadata": { + "id": "xtPAfyxARHBn" + } + }, + { + "cell_type": "code", + "source": [ + "for item in search(\n", + " \"2023 金馬獎影后\", advanced=True, num_results=3):\n", + " print(item.title)\n", + " print(item.description)\n", + " print(item.url)\n", + " print()" + ], + "metadata": { + "id": "KMx3LddFXEHc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 5-2 整合搜尋結果讓 AI 跟上時代" + ], + "metadata": { + "id": "zjc6Vdv9zmSF" + } + }, + { + "cell_type": "markdown", + "source": [ + "加入網頁搜尋的聊天程式" + ], + "metadata": { + "id": "G2Ge034UfYDd" + } + }, + { + "cell_type": "code", + "source": [ + "def get_reply_s(messages):\n", + " try:\n", + " response = client.chat.completions.create(\n", + " model = \"gpt-4-1106-preview\",\n", + " messages = messages,\n", + " stream = True\n", + " )\n", + " for chunk in response:\n", + " yield chunk.choices[0].delta.content or ''\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.error.message}\"" + ], + "metadata": { + "id": "bpHqQNiMfemv" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_w(sys_msg, user_msg):\n", + " global hist\n", + " web_res = []\n", + " if user_msg[:3].lower() == '/w ': # /w 代表要搜尋網路\n", + " user_msg = user_msg[3:] # 移除指令留下實際的訊息\n", + " content = \"以下為已發生的事實:\\n\"\n", + " for res in search(user_msg, advanced=True,\n", + " num_results=5, lang='zh-TW'):\n", + " content += f\"標題:{res.title}\\n\" \\\n", + " f\"摘要:{res.description}\\n\\n\"\n", + " content += \"請依照上述事實回答以下問題:\\n\"\n", + " web_res = [{\"role\": \"user\", \"content\": content}]\n", + " web_res.append({\"role\": \"user\", \"content\": user_msg})\n", + " reply_full = \"\"\n", + " for reply in get_reply_s( # 使用串流版的函式\n", + " hist # 先提供歷史紀錄\n", + " + web_res # 再提供搜尋結果及目前訊息\n", + " + [{\"role\": \"system\", \"content\": sys_msg}]):\n", + " reply_full += reply # 記錄到目前為止收到的訊息\n", + " yield reply # 傳回本次收到的片段訊息\n", + " hist.append({\"role\": \"user\", \"content\": user_msg})\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新對話" + ], + "metadata": { + "id": "1UIcjOjqgffE" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_w(sys_msg, msg):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ], + "metadata": { + "id": "21fxgL_qg7r-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## 5-3 使用 Google Search JSON API" + ], + "metadata": { + "id": "uyyr2BH5z1hT" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 使用 HTTP API 取得搜尋結果" + ], + "metadata": { + "id": "7ItbggrERmJD" + } + }, + { + "cell_type": "code", + "source": [ + "import requests" + ], + "metadata": { + "id": "KdvaiFxmE80a" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "r = requests.get(\n", + " 'https://www.googleapis.com/customsearch/v1?' \\\n", + " 'key={}&' \\\n", + " 'cx={}&' \\\n", + " 'num={}&' \\\n", + " 'q={}'.format(\n", + " '你的 Google API 金鑰',\n", + " '你的搜尋引擎 ID',\n", + " 2,\n", + " '2023 金馬獎影后是誰?'\n", + " )\n", + ")" + ], + "metadata": { + "id": "JsvHn7iTFAk4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "pprint(r.json())" + ], + "metadata": { + "id": "WAn52xf6r79l" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "for item in r.json()['items']:\n", + " print(item['title'])\n", + " print(item['snippet'])\n", + " print(item['link'])\n", + " print()" + ], + "metadata": { + "id": "hJGAftvPsned" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 使用客製模組" + ], + "metadata": { + "id": "GcsCqK-ERtTb" + } + }, + { + "cell_type": "code", + "source": [ + "!git clone https://github.com/codemee/customsearchapi.git \\\n", + "customsearchapi" + ], + "metadata": { + "id": "vtOV_E5P3Wbe" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# 預設會在匯入時從環境變數 GOOGLE_API_KEY 與 GOOGLE_CSE_ID\n", + "# 讀取你的 Google API 金鑰與搜尋引擎 ID,\n", + "# 如果沒有設定, 也可以直接透過模組內的變數設定:\n", + "import customsearchapi\n", + "customsearchapi.GOOGLE_API_KEY = userdata.get('GOOGLE_API_KEY')\n", + "customsearchapi.GOOGLE_CSE_ID = userdata.get('GOOGLE_CSE_ID')" + ], + "metadata": { + "id": "11hDo8Q7H15A" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "from customsearchapi import search" + ], + "metadata": { + "id": "_liR9utq3dvS" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "for item in search(\"2023 金馬獎影后是誰?\",\n", + " advanced=True,\n", + " num_results=3,\n", + " lang='lang_zh-TW'\n", + " ):\n", + " print(item.url)\n", + " print(item.title)\n", + " print(item.description)\n", + " print()" + ], + "metadata": { + "id": "8Ha1xFSm4VhA" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 7eff6e8bbe90c2ee4f33976afc7b1b5adae7b7c5 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:14:53 +0630 Subject: [PATCH 05/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch06.ipynb | 1095 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1095 insertions(+) create mode 100644 GPT4Dev_ch06.ipynb diff --git a/GPT4Dev_ch06.ipynb b/GPT4Dev_ch06.ipynb new file mode 100644 index 000000000..ec45add45 --- /dev/null +++ b/GPT4Dev_ch06.ipynb @@ -0,0 +1,1095 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eNYngg3YPEp5" + }, + "source": [ + "# 6 讓 AI 幫 AI-自動串接流程" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aQoeKOv30XwE" + }, + "source": [ + "## 6-1 從 ChatGPT 外掛得到的啟示" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ccVUpuXM9dRz" + }, + "source": [ + "### 準備工作\n", + "\n", + "安裝必要的套件與匯入相關模組後建立用戶端物件" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R1CAsaxl9_v4" + }, + "outputs": [], + "source": [ + "!pip install openai\n", + "!pip install rich\n", + "!pip install googlesearch-python\n", + "import openai\n", + "from googlesearch import search\n", + "from google.colab import userdata\n", + "from rich import print as pprint\n", + "client = openai.OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u7x3do07bs6c" + }, + "source": [ + "### 搭配串流/非串流模式的工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bpHqQNiMfemv" + }, + "outputs": [], + "source": [ + "def get_reply_g(messages, stream=False, json_format=False):\n", + " try:\n", + " json_msg = [{\n", + " 'role': 'system', 'content': '請用 JSON 回覆'\n", + " }] if json_format else []\n", + " response = client.chat.completions.create(\n", + " model = \"gpt-4-1106-preview\",\n", + " messages = messages + json_msg,\n", + " stream = stream,\n", + " response_format = {\n", + " 'type': \"json_object\" if json_format else 'text'\n", + " }\n", + " )\n", + " if stream: # 串留模式下交給輔助函式取得完整文字\n", + " for res in response:\n", + " yield res.choices[0].delta.content or ''\n", + " else: # 非串流模式下可直接取得完整回覆文字\n", + " yield response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"\n", + " print(reply)\n", + " yield reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Iugjm69PSbfF" + }, + "outputs": [], + "source": [ + "# 測試非串流模式的情況\n", + "for reply in get_reply_g([{'role':'user', 'content':'你好'}]):\n", + " print(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ec1Nb2keZlg_" + }, + "outputs": [], + "source": [ + "# 測試串流模式的情況\n", + "for msg in get_reply_g([{'role':'user', 'content':'你好'}], True):\n", + " print(msg, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "L1srs4bpRDwy" + }, + "outputs": [], + "source": [ + "# 測試 JSON 格式輸出\n", + "for reply in get_reply_g(\n", + " [{'role':'user', 'content':'你好'}],\n", + " json_format=True\n", + "):\n", + " print(reply)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6ontn4Qp0nDb" + }, + "source": [ + "## 6-2 由 AI 自動判斷要額外進行的工作" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Z4__qmyBb1sd" + }, + "source": [ + "### 撰寫判斷是否需要搜尋的工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-utgajNExsEE" + }, + "outputs": [], + "source": [ + "# 用來詢問是否需要搜尋才能回覆問題的樣板\n", + "# 要求 AI 以 JSON 格式回覆 Y/N 以及建議的搜尋關鍵字\n", + "template_google = '''\n", + "如果我想知道以下這件事, 請確認是否需要網路搜尋才做得到?\n", + "\n", + "```\n", + "{}\n", + "```\n", + "\n", + "如果需要, 請以下列 JSON 格式回答我, 除了 JSON 格式資料外,\n", + "不要加上額外資訊, 就算你知道答案, 也不要回覆:\n", + "\n", + "```\n", + "{{\n", + " \"search\":\"Y\",\n", + " \"keyword\":\"你建議的搜尋關鍵字\"\n", + "}}\n", + "```\n", + "如果不需要, 請以下列 JSON 格式回答我:\n", + "\n", + "```\n", + "{{\n", + " \"search\":\"N\",\n", + " \"keyword\":\"\"\n", + "}}\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_wBoRh5aMaUR" + }, + "outputs": [], + "source": [ + "# 利用目前歷史紀錄以及樣板內容詢問是否需要搜尋才能回覆問題\n", + "# 如果需要回覆, 也同時取得 AI 推薦的搜尋關鍵字\n", + "def check_google(hist, msg, verbose=False):\n", + " reply = get_reply_g(\n", + " hist + [{ # 加入歷史紀錄 AI 才能推薦正確的關鍵字\n", + " \"role\": \"user\",\n", + " \"content\": template_google.format(msg)\n", + " }], json_format=True)\n", + " for ans in reply:pass\n", + " if verbose: print(ans)\n", + " return ans" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x0HzE8hQM5sT" + }, + "outputs": [], + "source": [ + "# 測試需要搜尋的狀況\n", + "ans = check_google(\n", + " [], '2023 金馬影后是誰?', True\n", + ")\n", + "# 測試可能不需要搜尋的狀況\n", + "ans = check_google(\n", + " [], '新冠疫情是哪一年開始的?', True\n", + ")\n", + "# 測試沒有前文脈絡的狀況\n", + "ans = check_google(\n", + " [], '那台灣呢?', True\n", + ")\n", + "# 測試包含前文脈絡的狀況\n", + "ans = check_google(\n", + " [{'role':'assistant', 'content': '印度空污好嚴重'}],\n", + " '那台灣呢?', True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u3ng7lsCbH6D" + }, + "outputs": [], + "source": [ + "def google_res(user_msg, num_results=5, verbose=False):\n", + " content = \"以下為已發生的事實:\\n\" # 強調資料可信度\n", + " for res in search(user_msg, advanced=True, # 一一串接搜尋結果\n", + " num_results=num_results,\n", + " lang='zh-TW'):\n", + " content += f\"標題:{res.title}\\n\" \\\n", + " f\"摘要:{res.description}\\n\\n\"\n", + " content += \"請依照上述事實回答以下問題:\\n\" # 下達明確指令\n", + " if verbose:\n", + " print('------------')\n", + " print(content)\n", + " print('------------')\n", + " return content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_RtZC9BLuJpv" + }, + "outputs": [], + "source": [ + "res = google_res('2023 金馬獎影后是誰?', 2, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UilIxFrRSxYK" + }, + "source": [ + "### 可自行判斷是否進行網路搜尋的聊天程式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1UIcjOjqgffE" + }, + "outputs": [], + "source": [ + "import json\n", + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_g(sys_msg, user_msg, stream=False, verbose=False):\n", + " global hist\n", + " messages = [{'role':'user', 'content':user_msg}]\n", + " ans = json.loads(check_google(hist, user_msg,\n", + " verbose=verbose))\n", + " if ans['search'] == 'Y':\n", + " print(f'嘗試透過網路搜尋:{ans[\"keyword\"]}....')\n", + " res = google_res(ans['keyword'], verbose=verbose)\n", + " messages = [{'role':'user', 'content': res + user_msg}]\n", + "\n", + " replies = get_reply_g( # 使用搜尋版的函式\n", + " hist # 先提供歷史紀錄\n", + " + messages # 再提供搜尋結果及目前訊息\n", + " + [{\"role\": \"system\", \"content\": sys_msg}],\n", + " stream)\n", + " reply_full = ''\n", + " for reply in replies:\n", + " reply_full += reply\n", + " yield reply\n", + "\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "21fxgL_qg7r-" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " # 不論是字串或是生成器, 都可以適用 for...in 迴圈\n", + " for reply in chat_g(sys_msg, msg, stream=False):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iR66bWoL1MEG" + }, + "source": [ + "## 6-3 可建構外掛系統的 Function Calling 機制" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "byrjSV6Wv5i2" + }, + "source": [ + "**Function calling 機制**\n", + "\n", + "Function calling 機制可以讓我們提供可用函式的規格, 由 AI 幫我們判斷是否需要叫用其中的函式。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "33kQwvMXqnPO" + }, + "source": [ + "### 告知語言模型可用的外部工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MV2VyR2GoPpe" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model = \"gpt-4-1106-preview\",\n", + " messages = [{\"role\":\"user\", \"content\":\"2023 金馬獎影后是誰?\"}],\n", + " tools = [{ # 可用的函式清單\n", + " \"type\":\"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\", # 資料型別\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"], # 必要參數\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\") # 請 AI 判斷是否需要叫用函式" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DNOcLUYwq0Vu" + }, + "source": [ + "若 API 判斷需要叫用你描述的函式, 會在回覆中以 function_call 項目描述要叫用的函式名稱與參數值。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Cf9b8aoETuof" + }, + "source": [ + "### 取得語言模型的建議" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2qmxJli_qTxl" + }, + "outputs": [], + "source": [ + "pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LUSrFvHE0IM4" + }, + "outputs": [], + "source": [ + "tool_call = response.choices[0].message.tool_calls[0]\n", + "func_name = tool_call.function.name\n", + "import json\n", + "args = json.loads(tool_call.function.arguments)\n", + "arg_val = args.popitem()[1]\n", + "print(f'{func_name}(\"{arg_val}\")')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gwnrQzio3wQK" + }, + "source": [ + "### 執行函式並傳回結果\n", + "\n", + "你必須自行叫用函式, 並且將執行結果透過 function 角色的訊息傳回" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IaSwYSFETbHl" + }, + "outputs": [], + "source": [ + "# 用來過濾掉訊息中 function_call 欄位的函式\n", + "# 由於 function_call 在請求中和 tool_choice 是同樣的功能\n", + "# 如果一併傳回會造成錯誤, 所以我們這裡特別濾掉\n", + "# def make_tool_back_msg(tool_msg):\n", + "# msg_json = tool_msg.model_dump()\n", + "# tool_back_msg = {\n", + "# 'content': msg_json['content'],\n", + "# 'role': msg_json['role'],\n", + "# 'tool_calls': msg_json['tool_calls']\n", + "# }\n", + "# return tool_back_msg" + ] + }, + { + "cell_type": "code", + "source": [ + "# 用來過濾掉訊息中 function_call 欄位的函式\n", + "# 由於 function_call 在請求中和 tool_choice 是同樣的功能\n", + "# 如果一併傳回會造成錯誤, 所以我們這裡特別濾掉\n", + "def make_tool_back_msg(tool_msg):\n", + " msg_json = tool_msg.model_dump()\n", + " del msg_json['function_call']\n", + " return msg_json" + ], + "metadata": { + "id": "55I2kUUcR8e4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nybm9QfezrdT" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model='gpt-4-1106-preview',\n", + " messages=[\n", + " {\"role\":\"user\", \"content\":\"2023 金馬獎影后是誰?\"},\n", + " # 傳回 AI 傳給我們的 function calling 結果\n", + " make_tool_back_msg(response.choices[0].message),\n", + " { # 以 function 角色加上 name 屬性指定函式名稱傳回執行結果\n", + " \"tool_call_id\": tool_call.id, # 叫用函式的識別碼\n", + " \"role\": \"tool\", # 以工具角色送出回覆\n", + " \"name\": func_name, # 叫用的函式名稱\n", + " \"content\": eval(f'{func_name}(\"{arg_val}\")') # 函式傳回值\n", + " }\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gNzBfp03052c" + }, + "outputs": [], + "source": [ + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zF9ACHjdVbCV" + }, + "source": [ + "### 同時叫用多個函式 (parallel function calling)\n", + "\n", + "2023/11/06 之後的模型支援單次對話可以要求執行多個函式呼叫:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tJnRX6H_Um1J" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model = \"gpt-4-1106-preview\",\n", + " messages = [{\n", + " \"role\":\"user\",\n", + " \"content\":\"2023 金曲獎歌后和金馬獎影后各是誰?\"}],\n", + " tools = [{ # 可用的函式清單\n", + " \"type\":\"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\", # 資料類型\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"], # 必要參數\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\") # 請 AI 判斷是否需要叫用函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VhSZ_0UNWE_n" + }, + "outputs": [], + "source": [ + "pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Efx1DRycWM0g" + }, + "outputs": [], + "source": [ + "def make_func_messages(tool_calls):\n", + " messages = []\n", + " for tool_call in tool_calls:\n", + " func = tool_call.function\n", + " args_val = json.loads(func.arguments).popitem()[1]\n", + " print(f'{func.name}(\"{args_val}\")')\n", + " messages.append({\n", + " \"tool_call_id\": tool_call.id, # 叫用函式的識別碼\n", + " \"role\": \"tool\", # 以工具角色送出回覆\n", + " \"name\": func.name, # 叫用的函式名稱\n", + " \"content\": eval(f'{func.name}(\"{args_val}\")') # 傳回值\n", + " })\n", + " return messages" + ] + }, + { + "cell_type": "code", + "source": [ + "msges = make_func_messages(response.choices[0].message.tool_calls)\n", + "pprint(msges)" + ], + "metadata": { + "id": "he4MVmnr3Ya2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2rjyVICeWXxH" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model='gpt-4-1106-preview',\n", + " messages=[{\n", + " \"role\":\"user\",\n", + " \"content\":\"2023 金曲獎歌后和金馬獎影后各是誰?\"},\n", + " # 傳回 AI 傳給我們的 function calling 結果\n", + " make_tool_back_msg(response.choices[0].message),\n", + " ] + msges\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uvFRBoo9Wkul" + }, + "outputs": [], + "source": [ + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "M527Isb35K_m" + }, + "source": [ + "### 以串流方式使用 function calling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q8zKX0Nf3Es9" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model = \"gpt-4-1106-preview\",\n", + " messages = [{\"role\":\"user\",\n", + " \"content\":\"2023 金馬獎影后和金曲獎歌后各是誰?\"}],\n", + " tools = [{\n", + " \"type\": \"function\", # 工具類型\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\",\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"],\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\", # 請 AI 判斷是否需要使用工具\n", + " stream=True\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5G8g3ln85lgI" + }, + "source": [ + "傳回結果一樣是生成器" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "43D-blzoW9Rx" + }, + "outputs": [], + "source": [ + "for chunk in response:\n", + " pprint(chunk)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BLEYI9KL1amp" + }, + "source": [ + "## 6-4 建立 API 外掛系統" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5RRoqgB-UKOF" + }, + "source": [ + "### 建立外部工具函式參考表" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "50TGN5_lr226" + }, + "source": [ + "建立以 function calling 為基礎的外掛機制。
\n", + "建立結構化的函式表格。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WJtnKbIN6XYP" + }, + "outputs": [], + "source": [ + "tools_table = [ # 可用工具表\n", + " { # 每個元素代表一個工具\n", + " \"chain\": True, # 工具執行結果是否要再傳回給 API\n", + " \"func\": google_res, # 工具對應的函式\n", + " \"spec\": { # function calling 需要的工具規格\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\",\n", + " \"description\": \"取得 Google 搜尋結果\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"要搜尋的關鍵字\",\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"],\n", + " },\n", + " }\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J0GQMNX7sKCI" + }, + "source": [ + "### 建立協助 function calling 的工具函式\n", + "依據回應內容自動叫用對應函式:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nSaSvwtMWovK" + }, + "outputs": [], + "source": [ + "def call_tools(tool_calls, tools_table):\n", + " res = ''\n", + " msg = []\n", + " for tool_call in tool_calls:\n", + " func = tool_call.function\n", + " func_name = func.name\n", + " args = json.loads(func.arguments)\n", + " for f in tools_table: # 找出包含此函式的項目\n", + " if func_name == f['spec']['function']['name']:\n", + " print(f\"嘗試叫用:{func_name}(**{args})\")\n", + " val = f['func'](**args)\n", + " if f['chain']: # 要將結果送回模型\n", + " msg.append({\n", + " 'tool_call_id': tool_call.id,\n", + " 'role': 'tool',\n", + " 'name': 'func_name',\n", + " 'content': val\n", + " })\n", + " else:\n", + " res += str(val)\n", + " break\n", + " return msg, res" + ] + }, + { + "cell_type": "code", + "source": [ + "def get_tool_calls(messages, stream=False, tools_table=None,\n", + " **kwargs):\n", + " model = 'gpt-4-1106-preview' # 設定模型\n", + " if 'model' in kwargs: model = kwargs['model']\n", + "\n", + " tools = {}\n", + " if tools_table: # 加入工具表\n", + " tools = {'tools':[tool['spec'] for tool in tools_table]}\n", + "\n", + " response = client.chat.completions.create(\n", + " model = model,\n", + " messages = messages,\n", + " stream = stream,\n", + " **tools\n", + " )\n", + "\n", + " if not stream: # 非串流模式\n", + " msg = response.choices[0].message\n", + " if msg.content == None: # function calling 的回覆\n", + " return msg.tool_calls, None # 取出叫用資訊\n", + " return None, response # 一般回覆\n", + "\n", + " tool_calls = [] # 要呼叫的函式清單\n", + " prev = None\n", + " for chunk in response:\n", + " delta = chunk.choices[0].delta\n", + " if delta.content != None: # 一般回覆 (非 function calling)\n", + " return None, response # 直接返回結果\n", + " if delta.tool_calls: # 不是頭/尾的 chunk\n", + " curr = delta.tool_calls[0]\n", + " if curr.function.name: # 單一 call 開始\n", + " prev = curr # 取得工具名稱\n", + " tool_calls.append(curr) # 加入串列\n", + " else: # 串接引數內容\n", + " prev.function.arguments += curr.function.arguments\n", + " return tool_calls, None" + ], + "metadata": { + "id": "QlHZ56XkNTL4" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5ySOFG6OeH0_" + }, + "outputs": [], + "source": [ + "tool_calls, reply = get_tool_calls(\n", + " messages = [{'role':'user', 'content':'2023 金曲歌王是哪位?'}]\n", + ")\n", + "\n", + "print(tool_calls)\n", + "print(reply and reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PqLCfWNPUvi_" + }, + "source": [ + "### 建立 function_calling 版的 get_reply_f() 函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6_HyReryakU5" + }, + "outputs": [], + "source": [ + "def get_reply_f(messages, stream=False, tools_table=None, **kwargs):\n", + " try:\n", + " tool_calls, response = get_tool_calls(\n", + " messages, stream, tools_table, **kwargs)\n", + " if tool_calls:\n", + " tool_messages, res = call_tools(tool_calls, tools_table)\n", + " tool_calls_messeges = []\n", + " for tool_call in tool_calls:\n", + " tool_calls_messeges.append(tool_call.model_dump())\n", + " if tool_messages: # 如果需要將函式執行結果送回給 AI 再回覆\n", + " messages += [ # 必須傳回原本 function_calling 的內容\n", + " {\n", + " \"role\": \"assistant\", \"content\": None,\n", + " \"tool_calls\": tool_calls_messeges\n", + " }]\n", + " messages += tool_messages\n", + " # pprint(messages)\n", + " yield from get_reply_f(messages, stream,\n", + " tools_table, **kwargs)\n", + " else: # chain 為 False, 以函式叫用結果當成模型生成內容\n", + " yield res\n", + " elif stream: # 不需叫用函式但使用串流模式\n", + " for chunk in response:\n", + " yield chunk.choices[0].delta.content or ''\n", + " else: # 不需叫用函式也沒有使用串流模式\n", + " yield response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"\n", + " print(reply)\n", + " yield reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fLO76nV4Ealo" + }, + "outputs": [], + "source": [ + "# 測試非串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬講影后是誰?\"}],\n", + " tools_table=tools_table):\n", + " print(chunk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tNioKT6Vodaf" + }, + "outputs": [], + "source": [ + "# 測試串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬講影后是誰?\"}],\n", + " stream=True,\n", + " tools_table=tools_table):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m23Ds5BOtVnS" + }, + "outputs": [], + "source": [ + "# 測試非串流、無 function calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬講影后是誰?\"}]):\n", + " print(chunk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ay2vtRj3td6q" + }, + "outputs": [], + "source": [ + "# 測試串流、無 function calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬講影后是誰?\"}],\n", + " stream=True):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "code", + "source": [ + "# 測試串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金曲獎歌后和金馬獎影后各是誰?\"}],\n", + " stream=True,\n", + " tools_table=tools_table):\n", + " print(chunk, end='')" + ], + "metadata": { + "id": "n-sRcTFbirzw" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ajlSC5PPVBnq" + }, + "source": [ + "### 建立 function calling 版本的 chat_f() 函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sSpzuVo7yo-M" + }, + "outputs": [], + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_f(sys_msg, user_msg, stream=False, **kwargs):\n", + " global hist\n", + "\n", + " replies = get_reply_f( # 使用函式功能版的函式\n", + " hist # 先提供歷史紀錄\n", + " + [{\"role\": \"user\", \"content\": user_msg}]\n", + " + [{\"role\": \"system\", \"content\": sys_msg}],\n", + " stream, tools_table, **kwargs)\n", + " reply_full = ''\n", + " for reply in replies:\n", + " reply_full += reply\n", + " yield reply\n", + "\n", + " hist += [{\"role\":\"user\", \"content\":user_msg},\n", + " {\"role\":\"assistant\", \"content\":reply_full}]\n", + " hist = hist[-2 * backtrace:] # 留下最新的對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1rR4R1uOuLhx" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_f(sys_msg, msg, stream=True):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "JPmHKc5t-YCC" + }, + "execution_count": null, + "outputs": [] + } + ], + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From d522c66a3dfd97cee7c08c5268ebe980cae314e8 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:15:33 +0630 Subject: [PATCH 06/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch07.ipynb | 1305 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1305 insertions(+) create mode 100644 GPT4Dev_ch07.ipynb diff --git a/GPT4Dev_ch07.ipynb b/GPT4Dev_ch07.ipynb new file mode 100644 index 000000000..597a3190e --- /dev/null +++ b/GPT4Dev_ch07.ipynb @@ -0,0 +1,1305 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eNYngg3YPEp5" + }, + "source": [ + "# 7 網頁版聊天程式與文字生圖 Image API" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 7-1 準備工作\n" + ], + "metadata": { + "id": "qdQ33kiZ4Gmg" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ccVUpuXM9dRz" + }, + "source": [ + "**準備工作**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "R1CAsaxl9_v4", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "61abbecb-00ea-428e-fe0d-eae19994de75" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting gradio\n", + " Downloading gradio-4.21.0-py3-none-any.whl (17.0 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.0/17.0 MB\u001b[0m \u001b[31m33.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting aiofiles<24.0,>=22.0 (from gradio)\n", + " Downloading aiofiles-23.2.1-py3-none-any.whl (15 kB)\n", + "Requirement already satisfied: altair<6.0,>=4.2.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (4.2.2)\n", + "Collecting fastapi (from gradio)\n", + " Downloading fastapi-0.110.0-py3-none-any.whl (92 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m92.1/92.1 kB\u001b[0m \u001b[31m10.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting ffmpy (from gradio)\n", + " Downloading ffmpy-0.3.2.tar.gz (5.5 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting gradio-client==0.12.0 (from gradio)\n", + " Downloading gradio_client-0.12.0-py3-none-any.whl (310 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m310.7/310.7 kB\u001b[0m \u001b[31m26.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting httpx>=0.24.1 (from gradio)\n", + " Downloading httpx-0.27.0-py3-none-any.whl (75 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.6/75.6 kB\u001b[0m \u001b[31m9.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: huggingface-hub>=0.19.3 in /usr/local/lib/python3.10/dist-packages (from gradio) (0.20.3)\n", + "Requirement already satisfied: importlib-resources<7.0,>=1.3 in /usr/local/lib/python3.10/dist-packages (from gradio) (6.1.2)\n", + "Requirement already satisfied: jinja2<4.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (3.1.3)\n", + "Requirement already satisfied: markupsafe~=2.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (2.1.5)\n", + "Requirement already satisfied: matplotlib~=3.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (3.7.1)\n", + "Requirement already satisfied: numpy~=1.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (1.25.2)\n", + "Collecting orjson~=3.0 (from gradio)\n", + " Downloading orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (138 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m138.5/138.5 kB\u001b[0m \u001b[31m14.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from gradio) (23.2)\n", + "Requirement already satisfied: pandas<3.0,>=1.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (1.5.3)\n", + "Requirement already satisfied: pillow<11.0,>=8.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (9.4.0)\n", + "Requirement already satisfied: pydantic>=2.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (2.6.3)\n", + "Collecting pydub (from gradio)\n", + " Downloading pydub-0.25.1-py2.py3-none-any.whl (32 kB)\n", + "Collecting python-multipart>=0.0.9 (from gradio)\n", + " Downloading python_multipart-0.0.9-py3-none-any.whl (22 kB)\n", + "Requirement already satisfied: pyyaml<7.0,>=5.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (6.0.1)\n", + "Collecting ruff>=0.2.2 (from gradio)\n", + " Downloading ruff-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.9/7.9 MB\u001b[0m \u001b[31m58.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting semantic-version~=2.0 (from gradio)\n", + " Downloading semantic_version-2.10.0-py2.py3-none-any.whl (15 kB)\n", + "Collecting tomlkit==0.12.0 (from gradio)\n", + " Downloading tomlkit-0.12.0-py3-none-any.whl (37 kB)\n", + "Requirement already satisfied: typer[all]<1.0,>=0.9 in /usr/local/lib/python3.10/dist-packages (from gradio) (0.9.0)\n", + "Requirement already satisfied: typing-extensions~=4.0 in /usr/local/lib/python3.10/dist-packages (from gradio) (4.10.0)\n", + "Collecting uvicorn>=0.14.0 (from gradio)\n", + " Downloading uvicorn-0.28.0-py3-none-any.whl (60 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m60.6/60.6 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from gradio-client==0.12.0->gradio) (2023.6.0)\n", + "Collecting websockets<12.0,>=10.0 (from gradio-client==0.12.0->gradio)\n", + " Downloading websockets-11.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (129 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m129.9/129.9 kB\u001b[0m \u001b[31m14.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: entrypoints in /usr/local/lib/python3.10/dist-packages (from altair<6.0,>=4.2.0->gradio) (0.4)\n", + "Requirement already satisfied: jsonschema>=3.0 in /usr/local/lib/python3.10/dist-packages (from altair<6.0,>=4.2.0->gradio) (4.19.2)\n", + "Requirement already satisfied: toolz in /usr/local/lib/python3.10/dist-packages (from altair<6.0,>=4.2.0->gradio) (0.12.1)\n", + "Requirement already satisfied: anyio in /usr/local/lib/python3.10/dist-packages (from httpx>=0.24.1->gradio) (3.7.1)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx>=0.24.1->gradio) (2024.2.2)\n", + "Collecting httpcore==1.* (from httpx>=0.24.1->gradio)\n", + " Downloading httpcore-1.0.4-py3-none-any.whl (77 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.8/77.8 kB\u001b[0m \u001b[31m8.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx>=0.24.1->gradio) (3.6)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from httpx>=0.24.1->gradio) (1.3.1)\n", + "Collecting h11<0.15,>=0.13 (from httpcore==1.*->httpx>=0.24.1->gradio)\n", + " Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.19.3->gradio) (3.13.1)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.19.3->gradio) (2.31.0)\n", + "Requirement already satisfied: tqdm>=4.42.1 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub>=0.19.3->gradio) (4.66.2)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (1.2.0)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (4.49.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (1.4.5)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (3.1.1)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.10/dist-packages (from matplotlib~=3.0->gradio) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas<3.0,>=1.0->gradio) (2023.4)\n", + "Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2.0->gradio) (0.6.0)\n", + "Requirement already satisfied: pydantic-core==2.16.3 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2.0->gradio) (2.16.3)\n", + "Requirement already satisfied: click<9.0.0,>=7.1.1 in /usr/local/lib/python3.10/dist-packages (from typer[all]<1.0,>=0.9->gradio) (8.1.7)\n", + "Collecting colorama<0.5.0,>=0.4.3 (from typer[all]<1.0,>=0.9->gradio)\n", + " Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB)\n", + "Collecting shellingham<2.0.0,>=1.3.0 (from typer[all]<1.0,>=0.9->gradio)\n", + " Downloading shellingham-1.5.4-py2.py3-none-any.whl (9.8 kB)\n", + "Requirement already satisfied: rich<14.0.0,>=10.11.0 in /usr/local/lib/python3.10/dist-packages (from typer[all]<1.0,>=0.9->gradio) (13.7.1)\n", + "Collecting starlette<0.37.0,>=0.36.3 (from fastapi->gradio)\n", + " Downloading starlette-0.36.3-py3-none-any.whl (71 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m71.5/71.5 kB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: attrs>=22.2.0 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=3.0->altair<6.0,>=4.2.0->gradio) (23.2.0)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=3.0->altair<6.0,>=4.2.0->gradio) (2023.12.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=3.0->altair<6.0,>=4.2.0->gradio) (0.33.0)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema>=3.0->altair<6.0,>=4.2.0->gradio) (0.18.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.7->matplotlib~=3.0->gradio) (1.16.0)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=10.11.0->typer[all]<1.0,>=0.9->gradio) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=10.11.0->typer[all]<1.0,>=0.9->gradio) (2.16.1)\n", + "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio->httpx>=0.24.1->gradio) (1.2.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->huggingface-hub>=0.19.3->gradio) (3.3.2)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->huggingface-hub>=0.19.3->gradio) (2.0.7)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=10.11.0->typer[all]<1.0,>=0.9->gradio) (0.1.2)\n", + "Building wheels for collected packages: ffmpy\n", + " Building wheel for ffmpy (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for ffmpy: filename=ffmpy-0.3.2-py3-none-any.whl size=5584 sha256=6b116ad857b783af6aa403911010909964861b534d464808caa99df1733cbfc3\n", + " Stored in directory: /root/.cache/pip/wheels/bd/65/9a/671fc6dcde07d4418df0c592f8df512b26d7a0029c2a23dd81\n", + "Successfully built ffmpy\n", + "Installing collected packages: pydub, ffmpy, websockets, tomlkit, shellingham, semantic-version, ruff, python-multipart, orjson, h11, colorama, aiofiles, uvicorn, starlette, httpcore, httpx, fastapi, gradio-client, gradio\n", + "Successfully installed aiofiles-23.2.1 colorama-0.4.6 fastapi-0.110.0 ffmpy-0.3.2 gradio-4.21.0 gradio-client-0.12.0 h11-0.14.0 httpcore-1.0.4 httpx-0.27.0 orjson-3.9.15 pydub-0.25.1 python-multipart-0.0.9 ruff-0.3.2 semantic-version-2.10.0 shellingham-1.5.4 starlette-0.36.3 tomlkit-0.12.0 uvicorn-0.28.0 websockets-11.0.3\n", + "Collecting openai\n", + " Downloading openai-1.13.3-py3-none-any.whl (227 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m227.4/227.4 kB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai) (1.7.0)\n", + "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from openai) (0.27.0)\n", + "Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (2.6.3)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.1)\n", + "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai) (4.66.2)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from openai) (4.10.0)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (3.6)\n", + "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (1.2.0)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (2024.2.2)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (1.0.4)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->openai) (0.6.0)\n", + "Requirement already satisfied: pydantic-core==2.16.3 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->openai) (2.16.3)\n", + "Installing collected packages: openai\n", + "Successfully installed openai-1.13.3\n", + "Collecting googlesearch-python\n", + " Downloading googlesearch-python-1.2.3.tar.gz (3.9 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: beautifulsoup4>=4.9 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (4.12.3)\n", + "Requirement already satisfied: requests>=2.20 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (2.31.0)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4>=4.9->googlesearch-python) (2.5)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (3.3.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (3.6)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (2.0.7)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (2024.2.2)\n", + "Building wheels for collected packages: googlesearch-python\n", + " Building wheel for googlesearch-python (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for googlesearch-python: filename=googlesearch_python-1.2.3-py3-none-any.whl size=4209 sha256=b41d5a529d752883f4ef9d84d49709429f5ed590c5ba7217a9653a8f8a53a22a\n", + " Stored in directory: /root/.cache/pip/wheels/98/24/e9/6c225502948c629b01cc895f86406819281ef0da385f3eb669\n", + "Successfully built googlesearch-python\n", + "Installing collected packages: googlesearch-python\n", + "Successfully installed googlesearch-python-1.2.3\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (13.7.1)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich) (2.16.1)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich) (0.1.2)\n" + ] + } + ], + "source": [ + "!pip install gradio\n", + "!pip install openai\n", + "!pip install googlesearch-python\n", + "!pip install rich\n", + "from rich import print as pprint\n", + "from google.colab import userdata\n", + "import openai\n", + "client = openai.OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ] + }, + { + "cell_type": "markdown", + "source": [ + "**從 github 下載套件**" + ], + "metadata": { + "id": "7fb60E5MvUYb" + } + }, + { + "cell_type": "code", + "source": [ + "!git clone https://github.com/FlagTech/flagchat4.git flagchat4" + ], + "metadata": { + "id": "Z3Dw2PpubJnD", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "e83a51fe-4605-41b2-bc9f-bf2d9092465c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Cloning into 'flagchat4'...\n", + "remote: Enumerating objects: 27, done.\u001b[K\n", + "remote: Counting objects: 100% (27/27), done.\u001b[K\n", + "remote: Compressing objects: 100% (19/19), done.\u001b[K\n", + "remote: Total 27 (delta 7), reused 27 (delta 7), pack-reused 0\u001b[K\n", + "Receiving objects: 100% (27/27), 8.94 KiB | 8.94 MiB/s, done.\n", + "Resolving deltas: 100% (7/7), done.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "**匯入相關函式與資料**" + ], + "metadata": { + "id": "EN00RCpFvXWy" + } + }, + { + "cell_type": "code", + "source": [ + "from flagchat4 import (\n", + " set_client, # 設定要使用的用戶端物件 (預設直接使用 openai 模組)\n", + " get_reply, # 輸入訊息串列傳回回覆\n", + " chat, # 輸入 system, user 發言取得回覆並會記錄對答歷史\n", + " tools_table, # 記錄可用工具函式的參考表, 預設有 Google 搜尋函式\n", + " set_backtrace, # 設定記錄幾組對答 (預設:2)\n", + " empty_history, # 清除對答歷史\n", + ")" + ], + "metadata": { + "id": "HuPwhogybOJd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "set_client(client)" + ], + "metadata": { + "id": "FgdIwjP1pB1H" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "使用方法可參考 [Colab 範例筆記本](https://colab.research.google.com/drive/1TvZ6Bpv8NQvz9woIFJX5yhlU517-zBRK?usp=sharing)" + ], + "metadata": { + "id": "I2xth-wFu_re" + } + }, + { + "cell_type": "code", + "source": [ + "for chunk in chat('繁體中文小助手', '2023 金馬獎最佳女配角'):\n", + " print(chunk, end='')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2P7FPzXUqINk", + "outputId": "c775cc3d-c347-4d1a-ad43-cba1f80e210b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:google_res(**{'user_msg': '2023 \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n金馬獎 \\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n采才\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n最佳女配角'})\n", + "The winner of the \"Best Supporting Actress\" award at the 2023 Golden Horse Awards is Fang Zhiyou for the film \"Day Off\" (《本日公休》)." + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 7-2 使用 gradio 套件快速建立網頁程式" + ], + "metadata": { + "id": "P0o41v7E4Ka1" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 安裝與使用 gradio" + ], + "metadata": { + "id": "fdxBFtLh83QD" + } + }, + { + "cell_type": "code", + "source": [ + "# 已經在第一個儲存格安裝過\n", + "# 目前要先安裝 gradio, 在安裝 openai 套件才不會有問題\n", + "# !pip install gradio\n", + "import gradio as gr" + ], + "metadata": { + "id": "CBotGWq1rH6G" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "建立基本的網頁介面" + ], + "metadata": { + "id": "jmBZZNCU88us" + } + }, + { + "cell_type": "code", + "source": [ + "web_chat = gr.Interface(\n", + " fn = chat,\n", + " inputs = ['text', 'text'],\n", + " outputs = ['text']\n", + ")" + ], + "metadata": { + "id": "fRkqv4VGrgYh" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat.queue()\n", + "web_chat.launch(share=True)" + ], + "metadata": { + "id": "6-kRoO1JsGq2", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 611 + }, + "outputId": "8445075d-02d4-4515-9c9e-49a0f65c3524" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://5692e09eb71801f9a4.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "u30T026Tshcg", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "fefc61f3-f8e7-4d2e-8083-9f07abfae97a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7860\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 使用串流方式顯示輸出" + ], + "metadata": { + "id": "h_QG2S899BBJ" + } + }, + { + "cell_type": "code", + "source": [ + "web_chat = gr.Interface(\n", + " fn = chat,\n", + " inputs = ['text', 'text', 'checkbox'],\n", + " outputs = ['text']\n", + ")" + ], + "metadata": { + "id": "4SEXqmplBFw0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "gU_McbmiBLvZ", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 646 + }, + "outputId": "f6dedafd-7aab-4f3a-ad39-92f220ab6895" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Setting queue=True in a Colab notebook requires sharing enabled. Setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", + "\n", + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://cf57f7ab9d849f04bd.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "570S8JdhBksT", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "a18905c1-c56c-45ef-c600-e18836a17fa3" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7861\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "利用包裝函式組合片段內容" + ], + "metadata": { + "id": "3hyVgEil9Fz8" + } + }, + { + "cell_type": "code", + "source": [ + "def wrapper_chat(sys_msg, user_msg, stream):\n", + " reply = ''\n", + " for chunk in chat(sys_msg, user_msg, stream):\n", + " reply += chunk\n", + " yield reply" + ], + "metadata": { + "id": "-dIxREtO1AZm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat = gr.Interface(\n", + " fn = wrapper_chat,\n", + " inputs = ['text', 'text', 'checkbox'],\n", + " outputs = ['text']\n", + ")" + ], + "metadata": { + "id": "ig1c9U2S1WVF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "uc_o6p5A1cQa", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 646 + }, + "outputId": "a89a9ddd-042e-45e3-883a-2f4070b42a27" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Setting queue=True in a Colab notebook requires sharing enabled. Setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", + "\n", + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://d6268dd073bd797147.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 18 + } + ] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Gq97FfZgvyMu", + "outputId": "b67b4971-f5b1-4e4a-e188-589aba287419" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7860\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 客製使用者介面" + ], + "metadata": { + "id": "6bZly9TQ9N8v" + } + }, + { + "cell_type": "code", + "source": [ + "messages = []\n", + "\n", + "def wrapper_chat_bot(sys_msg, user_msg, stream):\n", + " messages.append([user_msg, ''])\n", + " for chunk in chat(sys_msg, user_msg, stream):\n", + " messages[-1][1] += chunk\n", + " yield messages" + ], + "metadata": { + "id": "BndzGRcl2k_7" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False)],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")" + ], + "metadata": { + "id": "A5fVguj03bWg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "lLqvX5Bu4R99", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 646 + }, + "outputId": "5d9da667-8524-463a-d2ea-f0c0514fa149" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Setting queue=True in a Colab notebook requires sharing enabled. Setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", + "\n", + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://74c505e0ae276c6939.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "oP-v3wby4pDG", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "b3fab2d3-4829-4d71-fa52-b9ac9d9a7152" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7862\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "[連結文字](https://)## 7-3 使用 DALL‧E 的 Image API" + ], + "metadata": { + "id": "OA5OOLjP4Q0T" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 7-3 使用 DALL‧E 的 Image API" + ], + "metadata": { + "id": "BwYEvJb9uTR2" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Image API 用法" + ], + "metadata": { + "id": "G5NzRvvK80h2" + } + }, + { + "cell_type": "code", + "source": [ + "res = client.images.generate( # 文字生圖\n", + " model='dall-e-3',\n", + " prompt='夕陽下駛過海邊的火車', # 描述文字\n", + " n=1, # 生圖張數\n", + " quality='hd',\n", + " size='1024x1024', # 影像大小, 預設 1024x1024\n", + " style='natural', # 風格, 預設 'vivid'\n", + ")\n", + "pprint(res)" + ], + "metadata": { + "id": "UsJq8Hlw-fWT", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 289 + }, + "outputId": "ff1bdefe-69a9-425d-c912-4598995e2cca" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mImagesResponse\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mcreated\u001b[0m=\u001b[1;36m1710124919\u001b[0m,\n", + " \u001b[33mdata\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mImage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mb64_json\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mrevised_prompt\u001b[0m=\u001b[32m'A train traveling along the coastline, perfectly silhouetted against the breathtaking \u001b[0m\n", + "\u001b[32mdisplay of a sunset. The setting sun casts a beautiful shade of red and orange across the sky, reflecting \u001b[0m\n", + "\u001b[32mbrilliantly off the tranquil ocean. Coastal scenery accompanies the railroad, teeming with seagulls and waves \u001b[0m\n", + "\u001b[32mgently crashing onto the sands. The train, in motion, lends a sense of adventure to this tranquil scene.'\u001b[0m,\n", + " \u001b[33murl\u001b[0m=\u001b[32m'https://oaidalleapiprodscus.blob.core.windows.net/private/org-TnN5jDJWh2Gbe6gZ6C11q1fl/user-hwS8wM\u001b[0m\n", + "\u001b[32mY6Z8ZzjiE3tcFcl4mM/img-L0vqXRxWLeFOGPj2N3Tv8RVn.png?\u001b[0m\u001b[32mst\u001b[0m\u001b[32m=\u001b[0m\u001b[32m2024\u001b[0m\u001b[32m-03-11T01%3A41%3A59Z&\u001b[0m\u001b[32mse\u001b[0m\u001b[32m=\u001b[0m\u001b[32m2024\u001b[0m\u001b[32m-03-11T03%3A41%3A59Z&\u001b[0m\u001b[32msp\u001b[0m\u001b[32m=\u001b[0m\u001b[32mr\u001b[0m\u001b[32m&\u001b[0m\u001b[32msv\u001b[0m\n", + "\u001b[32m=\u001b[0m\u001b[32m2021\u001b[0m\u001b[32m-08-06&\u001b[0m\u001b[32msr\u001b[0m\u001b[32m=\u001b[0m\u001b[32mb\u001b[0m\u001b[32m&\u001b[0m\u001b[32mrscd\u001b[0m\u001b[32m=\u001b[0m\u001b[32minline\u001b[0m\u001b[32m&\u001b[0m\u001b[32mrsct\u001b[0m\u001b[32m=\u001b[0m\u001b[32mimage\u001b[0m\u001b[32m/png&\u001b[0m\u001b[32mskoid\u001b[0m\u001b[32m=\u001b[0m\u001b[32m6aaadede\u001b[0m\u001b[32m-4fb3-4698-a8f6-684d7786b067&\u001b[0m\u001b[32msktid\u001b[0m\u001b[32m=\u001b[0m\u001b[32ma48cca56\u001b[0m\u001b[32m-e6da-484e-a81\u001b[0m\n", + "\u001b[32m4-9c849652bcb3&\u001b[0m\u001b[32mskt\u001b[0m\u001b[32m=\u001b[0m\u001b[32m2024\u001b[0m\u001b[32m-03-10T20%3A49%3A48Z&\u001b[0m\u001b[32mske\u001b[0m\u001b[32m=\u001b[0m\u001b[32m2024\u001b[0m\u001b[32m-03-11T20%3A49%3A48Z&\u001b[0m\u001b[32msks\u001b[0m\u001b[32m=\u001b[0m\u001b[32mb\u001b[0m\u001b[32m&\u001b[0m\u001b[32mskv\u001b[0m\u001b[32m=\u001b[0m\u001b[32m2021\u001b[0m\u001b[32m-08-06&\u001b[0m\u001b[32msig\u001b[0m\u001b[32m=\u001b[0m\u001b[32myYwzIKUZJEoFWXlsh\u001b[0m\n", + "\u001b[32mPh70TZpwPZGJaqFvLGgbI7S\u001b[0m\u001b[32m/So%3D'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
ImagesResponse(\n",
+              "    created=1710124919,\n",
+              "    data=[\n",
+              "        Image(\n",
+              "            b64_json=None,\n",
+              "            revised_prompt='A train traveling along the coastline, perfectly silhouetted against the breathtaking \n",
+              "display of a sunset. The setting sun casts a beautiful shade of red and orange across the sky, reflecting \n",
+              "brilliantly off the tranquil ocean. Coastal scenery accompanies the railroad, teeming with seagulls and waves \n",
+              "gently crashing onto the sands. The train, in motion, lends a sense of adventure to this tranquil scene.',\n",
+              "            url='https://oaidalleapiprodscus.blob.core.windows.net/private/org-TnN5jDJWh2Gbe6gZ6C11q1fl/user-hwS8wM\n",
+              "Y6Z8ZzjiE3tcFcl4mM/img-L0vqXRxWLeFOGPj2N3Tv8RVn.png?st=2024-03-11T01%3A41%3A59Z&se=2024-03-11T03%3A41%3A59Z&sp=r&sv\n",
+              "=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a81\n",
+              "4-9c849652bcb3&skt=2024-03-10T20%3A49%3A48Z&ske=2024-03-11T20%3A49%3A48Z&sks=b&skv=2021-08-06&sig=yYwzIKUZJEoFWXlsh\n",
+              "Ph70TZpwPZGJaqFvLGgbI7S/So%3D'\n",
+              "        )\n",
+              "    ]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立文字生圖像網址的函式" + ], + "metadata": { + "id": "m9ZE2kKWXrxx" + } + }, + { + "cell_type": "code", + "source": [ + "def txt_to_img_url(prompt):\n", + " response = client.images.generate(\n", + " model='dall-e-3',\n", + " prompt=prompt,\n", + " n=1,\n", + " size='1024x1024',\n", + " style='vivid',\n", + " quality='hd'\n", + " )\n", + " return response.data[0].url" + ], + "metadata": { + "id": "ge1xqcrCBeWd" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "print(txt_to_img_url('田邊騎著腳踏車晃的少年'))" + ], + "metadata": { + "id": "OogZKOJ9B3f_", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "7b97c6ac-9485-4aa7-fcd9-c1a6bafd078d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "https://oaidalleapiprodscus.blob.core.windows.net/private/org-TnN5jDJWh2Gbe6gZ6C11q1fl/user-hwS8wMY6Z8ZzjiE3tcFcl4mM/img-1pUmu17WXOXEz7OqJdmEmN02.png?st=2024-03-11T01%3A42%3A19Z&se=2024-03-11T03%3A42%3A19Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-03-10T18%3A23%3A23Z&ske=2024-03-11T18%3A23%3A23Z&sks=b&skv=2021-08-06&sig=xlq2Xq%2BB7liM7FmQZN6BA6PyNi88GwtbssnGWWsvH2c%3D\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "tools_table.append(\n", + " { # 每個元素代表一個函式\n", + " \"chain\": False, # 生圖後不需要傳回給 API\n", + " \"func\": txt_to_img_url,\n", + " \"spec\": { # function calling 需要的函式規格\n", + " 'type': 'function',\n", + " 'function': {\n", + " \"name\": \"txt_to_img_url\",\n", + " \"description\": \"可由文字生圖並傳回圖像網址\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"prompt\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"描述要產生圖像內容的文字\",\n", + " }\n", + " },\n", + " \"required\": [\"prompt\"],\n", + " },\n", + " }\n", + " }\n", + " }\n", + ")" + ], + "metadata": { + "id": "wh0CFGhiC07Y" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "for chunk in chat('小助理', '我想要夕陽下海豚躍出海面的圖像', True):\n", + " print(chunk)" + ], + "metadata": { + "id": "Q42dWiLwDvNp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "21895474-f1b0-487c-da71-dff34d679fb8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:txt_to_img_url(**{'prompt': 'dolphins leaping out of the ocean at sunset'})\n", + "https://oaidalleapiprodscus.blob.core.windows.net/private/org-TnN5jDJWh2Gbe6gZ6C11q1fl/user-hwS8wMY6Z8ZzjiE3tcFcl4mM/img-UdIryHrIQhRAoIGtwiCxn4Or.png?st=2024-03-11T01%3A42%3A48Z&se=2024-03-11T03%3A42%3A48Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2024-03-10T18%3A32%3A49Z&ske=2024-03-11T18%3A32%3A49Z&sks=b&skv=2021-08-06&sig=t9gjbFaPJX9XupFHtDLZG3igR6zNXOnCdewhcis5hf0%3D\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "套用生圖功能到網頁聊天中" + ], + "metadata": { + "id": "HaYtdEdEKZ-M" + } + }, + { + "cell_type": "code", + "source": [ + "messages = []\n", + "\n", + "def wrapper_chat_bot(sys_msg, user_msg, stream):\n", + " messages.append([user_msg, ''])\n", + " for chunk in chat(sys_msg, user_msg, stream):\n", + " messages[-1][1] += chunk\n", + " yield messages\n", + "\n", + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False)],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")\n", + "\n", + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "Xn9BIkvjJxtL", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 654 + }, + "outputId": "00820536-9818-4084-b201-8493fbb82c0b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Setting queue=True in a Colab notebook requires sharing enabled. Setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", + "\n", + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://309a1992ec431c1b10.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "結果只會顯示連結, 對於 Chatbot 介面要提供 Mardown 格式才會顯示。" + ], + "metadata": { + "id": "CtVG7aqhKgF1" + } + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "Y1z5C4ghNS53", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "b7e92d3d-d866-4f5b-d3cd-59c397cab427" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7860\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 包裝成生成 markdown 語法的函式\n" + ], + "metadata": { + "id": "a3FlFSI6KqN4" + } + }, + { + "cell_type": "code", + "source": [ + "def txt_to_img_md(prompt):\n", + " return f'![{prompt}]({txt_to_img_url(prompt)})'" + ], + "metadata": { + "id": "Pn6ELd1-KV4B" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "tools_table.pop()\n", + "tools_table.append({ # 每個元素代表一個函式\n", + " \"chain\": False, # 生圖後不需要傳回給 API\n", + " \"func\": txt_to_img_md,\n", + " \"spec\": { # function calling 需要的函式規格\n", + " 'type': 'function',\n", + " 'function': {\n", + " \"name\": \"txt_to_img_md\",\n", + " \"description\": \"可由文字生圖並傳回 markdown 圖像元素\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"prompt\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"描述要產生圖像內容的文字\",\n", + " }\n", + " },\n", + " \"required\": [\"prompt\"],\n", + " },\n", + " }\n", + " }\n", + "})" + ], + "metadata": { + "id": "PSccxr-WLv6B" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "messages = []\n", + "\n", + "def wrapper_chat_bot(sys_msg, user_msg, stream):\n", + " messages.append([user_msg, ''])\n", + " for chunk in chat(sys_msg, user_msg, stream):\n", + " messages[-1][1] += chunk\n", + " yield messages\n", + "\n", + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False)],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")\n", + "\n", + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "WDD3T9_xMCSN" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "Flo93iUQND4-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "63d021b9-5d44-4239-ae96-31924690712a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7860\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "messages = []\n", + "\n", + "def wrapper_chat_bot(sys_msg, user_msg, stream, model):\n", + " messages.append([user_msg, ''])\n", + " for chunk in chat(sys_msg, user_msg, stream, model=model):\n", + " messages[-1][1] += chunk\n", + " yield messages\n", + "\n", + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False),\n", + " gr.Radio(label='模型', choices=['gpt-3.5-turbo-1106',\n", + " 'gpt-4-1106-preview'],\n", + " value='gpt-4-1106-preview')],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")\n", + "\n", + "web_chat.queue()\n", + "web_chat.launch()" + ], + "metadata": { + "id": "m6aiXt1W_Fkv", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 646 + }, + "outputId": "8108c914-a68b-4e41-8172-d8d10a9f62f1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Setting queue=True in a Colab notebook requires sharing enabled. Setting `share=True` (you can turn this off by setting `share=False` in `launch()` explicitly).\n", + "\n", + "Colab notebook detected. To show errors in colab notebook, set debug=True in launch()\n", + "Running on public URL: https://d5c46c86834125773e.gradio.live\n", + "\n", + "This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "
" + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [] + }, + "metadata": {}, + "execution_count": 26 + } + ] + }, + { + "cell_type": "code", + "source": [ + "web_chat.close()" + ], + "metadata": { + "id": "LdM6anxxAE0y", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "67d79e48-8a5e-4b45-e017-9636ff5f7bba" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Closing server running on port: 7860\n" + ] + } + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From 75e5743d3de51312a8b778329d7eb873d181ed93 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:16:04 +0630 Subject: [PATCH 07/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch08.ipynb | 3348 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3348 insertions(+) create mode 100644 GPT4Dev_ch08.ipynb diff --git a/GPT4Dev_ch08.ipynb b/GPT4Dev_ch08.ipynb new file mode 100644 index 000000000..945ba6738 --- /dev/null +++ b/GPT4Dev_ch08.ipynb @@ -0,0 +1,3348 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 8 Assistants API —快速開發助理應用程式" + ], + "metadata": { + "id": "bA7Hn1IiauRO" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 8-1 什麼是 Assistants-API" + ], + "metadata": { + "id": "Qm26sxiLQkV_" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Assistants API 的基本元件" + ], + "metadata": { + "id": "tgAM93YpapMO" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nMaGbwytUvwn", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "2e664774-c2ae-4fb0-c325-a6ffcf197bdd" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting openai\n", + " Downloading openai-1.6.1-py3-none-any.whl (225 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m225.4/225.4 kB\u001b[0m \u001b[31m2.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (13.7.0)\n", + "Collecting googlesearch-python\n", + " Downloading googlesearch-python-1.2.3.tar.gz (3.9 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai) (1.7.0)\n", + "Collecting httpx<1,>=0.23.0 (from openai)\n", + " Downloading httpx-0.26.0-py3-none-any.whl (75 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.9/75.9 kB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.10.13)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.0)\n", + "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai) (4.66.1)\n", + "Collecting typing-extensions<5,>=4.7 (from openai)\n", + " Downloading typing_extensions-4.9.0-py3-none-any.whl (32 kB)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich) (2.16.1)\n", + "Requirement already satisfied: beautifulsoup4>=4.9 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (4.11.2)\n", + "Requirement already satisfied: requests>=2.20 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (2.31.0)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (3.6)\n", + "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (1.2.0)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4>=4.9->googlesearch-python) (2.5)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n", + "Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)\n", + " Downloading httpcore-1.0.2-py3-none-any.whl (76 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.9/76.9 kB\u001b[0m \u001b[31m5.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->openai)\n", + " Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m5.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich) (0.1.2)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (3.3.2)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (2.0.7)\n", + "Building wheels for collected packages: googlesearch-python\n", + " Building wheel for googlesearch-python (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for googlesearch-python: filename=googlesearch_python-1.2.3-py3-none-any.whl size=4209 sha256=83af38f683c0eec6630116409c9ec62b72f5d608060664a7a518067069bdc308\n", + " Stored in directory: /root/.cache/pip/wheels/98/24/e9/6c225502948c629b01cc895f86406819281ef0da385f3eb669\n", + "Successfully built googlesearch-python\n", + "Installing collected packages: typing-extensions, h11, httpcore, googlesearch-python, httpx, openai\n", + " Attempting uninstall: typing-extensions\n", + " Found existing installation: typing_extensions 4.5.0\n", + " Uninstalling typing_extensions-4.5.0:\n", + " Successfully uninstalled typing_extensions-4.5.0\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "llmx 0.0.15a0 requires cohere, which is not installed.\n", + "llmx 0.0.15a0 requires tiktoken, which is not installed.\n", + "tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.9.0 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed googlesearch-python-1.2.3 h11-0.14.0 httpcore-1.0.2 httpx-0.26.0 openai-1.6.1 typing-extensions-4.9.0\n" + ] + } + ], + "source": [ + "!pip install openai rich googlesearch-python" + ] + }, + { + "cell_type": "code", + "source": [ + "from google.colab import userdata\n", + "from rich import print as pprint\n", + "from openai import OpenAI\n", + "client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ], + "metadata": { + "id": "3CIUUtb9U6dS" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立 Assistant" + ], + "metadata": { + "id": "rU3eBNKOcen-" + } + }, + { + "cell_type": "code", + "source": [ + "assistant = client.beta.assistants.create(\n", + " name=\"運動達人\",\n", + " instructions=\"你是一位熱愛運動的達人, 具有各種與運動相關的知識\",\n", + " model=\"gpt-3.5-turbo-1106\"\n", + ")\n", + "pprint(assistant)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 213 + }, + "id": "Czw4_IeDdo4w", + "outputId": "1d687cc0-3bf2-4323-f69f-853925e951f8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistant\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569838\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'運動達人'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Assistant(\n",
+              "    id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    created_at=1703569838,\n",
+              "    description=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    name='運動達人',\n",
+              "    object='assistant',\n",
+              "    tools=[]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立 Thread 與 Message" + ], + "metadata": { + "id": "3xhS_834e1MS" + } + }, + { + "cell_type": "code", + "source": [ + "thread = client.beta.threads.create()\n", + "pprint(thread)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 33 + }, + "id": "7ZoEIS8we4-a", + "outputId": "827cd99c-0bd7-468f-88b7-5194fed2a147" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThread\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'thread_1r6m1Agm9WkjWk9Eif5o3NOK'\u001b[0m, \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569840\u001b[0m, \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'thread'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Thread(id='thread_1r6m1Agm9WkjWk9Eif5o3NOK', created_at=1703569840, metadata={}, object='thread')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "message = client.beta.threads.messages.create(\n", + " thread_id=thread.id,\n", + " role=\"user\",\n", + " content=\"走路一小時相當於消耗多少熱量?\"\n", + ")\n", + "pprint(message)" + ], + "metadata": { + "id": "z6icPCPpfMZS", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 211 + }, + "outputId": "965000de-6377-4d82-bacb-e73edd39f75d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThreadMessage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'msg_aJVCCSuuU3ZutHzJkSx80BxQ'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcontent\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mMessageContentText\u001b[0m\u001b[1m(\u001b[0m\u001b[33mtext\u001b[0m=\u001b[1;35mText\u001b[0m\u001b[1m(\u001b[0m\u001b[33mannotations\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m, \u001b[33mvalue\u001b[0m=\u001b[32m'走路一小時相當於消耗多少熱量?'\u001b[0m\u001b[1m)\u001b[0m, \u001b[33mtype\u001b[0m=\u001b[32m'text'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569842\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.message'\u001b[0m,\n", + " \u001b[33mrole\u001b[0m=\u001b[32m'user'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_1r6m1Agm9WkjWk9Eif5o3NOK'\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
ThreadMessage(\n",
+              "    id='msg_aJVCCSuuU3ZutHzJkSx80BxQ',\n",
+              "    assistant_id=None,\n",
+              "    content=[MessageContentText(text=Text(annotations=[], value='走路一小時相當於消耗多少熱量?'), type='text')],\n",
+              "    created_at=1703569842,\n",
+              "    file_ids=[],\n",
+              "    metadata={},\n",
+              "    object='thread.message',\n",
+              "    role='user',\n",
+              "    run_id=None,\n",
+              "    thread_id='thread_1r6m1Agm9WkjWk9Eif5o3NOK'\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立 Run 執行任務" + ], + "metadata": { + "id": "OdzsHiQnfpbB" + } + }, + { + "cell_type": "code", + "source": [ + "run = client.beta.threads.runs.create(\n", + " thread_id=thread.id,\n", + " assistant_id=assistant.id\n", + ")\n", + "pprint(run)" + ], + "metadata": { + "id": "ALfwvucTfo6J", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 339 + }, + "outputId": "b4a47b0b-69a7-4f26-d15a-6ea5b2204265" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_lHXzcCHn8NSObn8ZtOudM6AA'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569843\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[1;36m1703570443\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'queued'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_1r6m1Agm9WkjWk9Eif5o3NOK'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Run(\n",
+              "    id='run_lHXzcCHn8NSObn8ZtOudM6AA',\n",
+              "    assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    cancelled_at=None,\n",
+              "    completed_at=None,\n",
+              "    created_at=1703569843,\n",
+              "    expires_at=1703570443,\n",
+              "    failed_at=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "    last_error=None,\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    object='thread.run',\n",
+              "    required_action=None,\n",
+              "    started_at=None,\n",
+              "    status='queued',\n",
+              "    thread_id='thread_1r6m1Agm9WkjWk9Eif5o3NOK',\n",
+              "    tools=[]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "import time\n", + "\n", + "def wait_on_run(run):\n", + " while run.status == \"queued\" or run.status == \"in_progress\":\n", + " run = client.beta.threads.runs.retrieve(\n", + " thread_id=run.thread_id,\n", + " run_id=run.id,\n", + " )\n", + " time.sleep(0.5)\n", + " return run\n", + "run = wait_on_run(run)\n", + "pprint(run)" + ], + "metadata": { + "id": "6wmi0qLSf4xa" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 顯示討論串內的所有訊息" + ], + "metadata": { + "id": "o0aUTDgQhwri" + } + }, + { + "cell_type": "code", + "source": [ + "messages = client.beta.threads.messages.list(thread_id=thread.id)\n", + "pprint(messages)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 776 + }, + "id": "qwfHpR2Hh128", + "outputId": "a3d34380-a8fb-499a-8967-f4884523694b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "SyncCursorPage\u001b[1m[\u001b[0mThreadMessage\u001b[1m]\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mThreadMessage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'msg_LAJdOG3zD2dcPp8jwRw7hZci'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcontent\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mMessageContentText\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtext\u001b[0m=\u001b[1;35mText\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mannotations\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mvalue\u001b[0m=\u001b[32m'消耗的熱量取決於走路的速度以及個人的體重和身體情況。一般而言,以5公里/小時的速度行走\u001b[0m\n", + "\u001b[32m一小時,一個65公斤的人大約會消耗250-300卡路里的熱量。然而,這些數字僅供參考,實際的消耗量還會受到其他因素的影響,例\u001b[0m\n", + "\u001b[32m如地形、風向、走路時是否攜帶負重等。'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'text'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569844\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.message'\u001b[0m,\n", + " \u001b[33mrole\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_lHXzcCHn8NSObn8ZtOudM6AA'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_1r6m1Agm9WkjWk9Eif5o3NOK'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[1;35mThreadMessage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'msg_aJVCCSuuU3ZutHzJkSx80BxQ'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcontent\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mMessageContentText\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtext\u001b[0m=\u001b[1;35mText\u001b[0m\u001b[1m(\u001b[0m\u001b[33mannotations\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m, \u001b[33mvalue\u001b[0m=\u001b[32m'走路一小時相當於消耗多少熱量?'\u001b[0m\u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'text'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569842\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.message'\u001b[0m,\n", + " \u001b[33mrole\u001b[0m=\u001b[32m'user'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_1r6m1Agm9WkjWk9Eif5o3NOK'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'list'\u001b[0m,\n", + " \u001b[33mfirst_id\u001b[0m=\u001b[32m'msg_LAJdOG3zD2dcPp8jwRw7hZci'\u001b[0m,\n", + " \u001b[33mlast_id\u001b[0m=\u001b[32m'msg_aJVCCSuuU3ZutHzJkSx80BxQ'\u001b[0m,\n", + " \u001b[33mhas_more\u001b[0m=\u001b[3;91mFalse\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
SyncCursorPage[ThreadMessage](\n",
+              "    data=[\n",
+              "        ThreadMessage(\n",
+              "            id='msg_LAJdOG3zD2dcPp8jwRw7hZci',\n",
+              "            assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "            content=[\n",
+              "                MessageContentText(\n",
+              "                    text=Text(\n",
+              "                        annotations=[],\n",
+              "                        value='消耗的熱量取決於走路的速度以及個人的體重和身體情況。一般而言,以5公里/小時的速度行走\n",
+              "一小時,一個65公斤的人大約會消耗250-300卡路里的熱量。然而,這些數字僅供參考,實際的消耗量還會受到其他因素的影響,例\n",
+              "如地形、風向、走路時是否攜帶負重等。'\n",
+              "                    ),\n",
+              "                    type='text'\n",
+              "                )\n",
+              "            ],\n",
+              "            created_at=1703569844,\n",
+              "            file_ids=[],\n",
+              "            metadata={},\n",
+              "            object='thread.message',\n",
+              "            role='assistant',\n",
+              "            run_id='run_lHXzcCHn8NSObn8ZtOudM6AA',\n",
+              "            thread_id='thread_1r6m1Agm9WkjWk9Eif5o3NOK'\n",
+              "        ),\n",
+              "        ThreadMessage(\n",
+              "            id='msg_aJVCCSuuU3ZutHzJkSx80BxQ',\n",
+              "            assistant_id=None,\n",
+              "            content=[\n",
+              "                MessageContentText(\n",
+              "                    text=Text(annotations=[], value='走路一小時相當於消耗多少熱量?'),\n",
+              "                    type='text'\n",
+              "                )\n",
+              "            ],\n",
+              "            created_at=1703569842,\n",
+              "            file_ids=[],\n",
+              "            metadata={},\n",
+              "            object='thread.message',\n",
+              "            role='user',\n",
+              "            run_id=None,\n",
+              "            thread_id='thread_1r6m1Agm9WkjWk9Eif5o3NOK'\n",
+              "        )\n",
+              "    ],\n",
+              "    object='list',\n",
+              "    first_id='msg_LAJdOG3zD2dcPp8jwRw7hZci',\n",
+              "    last_id='msg_aJVCCSuuU3ZutHzJkSx80BxQ',\n",
+              "    has_more=False\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "for part in messages:\n", + " print(f'{part.role}:{part.content[0].text.value}')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fWaoPSRCmy4R", + "outputId": "18f92ef1-643f-4887-e7dd-6deb6a1af71f" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "assistant:消耗的熱量取決於走路的速度以及個人的體重和身體情況。一般而言,以5公里/小時的速度行走一小時,一個65公斤的人大約會消耗250-300卡路里的熱量。然而,這些數字僅供參考,實際的消耗量還會受到其他因素的影響,例如地形、風向、走路時是否攜帶負重等。\n", + "user:走路一小時相當於消耗多少熱量?\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 8-2 Assistants API 物件用法" + ], + "metadata": { + "id": "x10yUhc0gufd" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 建立新討論串直接執行" + ], + "metadata": { + "id": "T6CUd6kAr3Q9" + } + }, + { + "cell_type": "code", + "source": [ + "run = client.beta.threads.create_and_run(\n", + " assistant_id=assistant.id,\n", + " thread={\n", + " \"messages\": [\n", + " {\"role\": \"user\",\n", + " \"content\": \"當小腿抽筋時最適當的處理是?, 用繁體中文回答\"}\n", + " ],\n", + " }\n", + ")\n", + "pprint(run)\n", + "run = wait_on_run(run)\n", + "messages = client.beta.threads.messages.list(thread_id=run.thread_id)\n", + "for part in messages:\n", + " print(f'{part.role}:{part.content[0].text.value}')" + ], + "metadata": { + "id": "M_z77Hhlrzc5", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 395 + }, + "outputId": "4e3ce010-beaf-47b0-b462-fbb9ab2f4adb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_zxEUhKJrJNJMQk10w9xx2MmV'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570052\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[1;36m1703570652\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'queued'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Run(\n",
+              "    id='run_zxEUhKJrJNJMQk10w9xx2MmV',\n",
+              "    assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    cancelled_at=None,\n",
+              "    completed_at=None,\n",
+              "    created_at=1703570052,\n",
+              "    expires_at=1703570652,\n",
+              "    failed_at=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "    last_error=None,\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    object='thread.run',\n",
+              "    required_action=None,\n",
+              "    started_at=None,\n",
+              "    status='queued',\n",
+              "    thread_id='thread_KtqImOv443wCrhGngaLblots',\n",
+              "    tools=[]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "assistant:當小腿抽筋時,最好的處理方法是停止運動,放鬆緊繃的肌肉,輕輕按摩或拉伸受影響的區域,同時進行深呼吸放鬆身體。另外,可透過按摩或應用熱敷來緩解疼痛感。若疼痛持續或頻繁發生,建議尋求專業醫療建議。\n", + "user:當小腿抽筋時最適當的處理是?, 用繁體中文回答\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "new_thread = client.beta.threads.retrieve(run.thread_id)\n", + "pprint(new_thread)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 33 + }, + "id": "MdirW4Jmotwg", + "outputId": "40e12dea-5439-42cd-af1a-c8b5a8465c79" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThread\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m, \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570052\u001b[0m, \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'thread'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Thread(id='thread_KtqImOv443wCrhGngaLblots', created_at=1703570052, metadata={}, object='thread')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立對話函式" + ], + "metadata": { + "id": "YAEvRJuY0CYc" + } + }, + { + "cell_type": "code", + "source": [ + "def input_and_run(input, thread_id, assistant_id):\n", + " message = client.beta.threads.messages.create(\n", + " thread_id=thread_id,\n", + " role=\"user\",\n", + " content=input\n", + " )\n", + " run = client.beta.threads.runs.create(\n", + " thread_id=thread_id,\n", + " assistant_id=assistant_id,\n", + " )\n", + "\n", + " return message, run" + ], + "metadata": { + "id": "cIeeftdpxLko" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "message, run = input_and_run(\"燙傷時第一步該怎麼做?\",\n", + " new_thread.id, assistant.id)\n", + "wait_on_run(run)\n", + "messages = client.beta.threads.messages.list(thread_id=run.thread_id)\n", + "for part in messages:\n", + " print(f'{part.role}:{part.content[0].text.value}')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZWgfhkTavgvh", + "outputId": "84bb3737-97f6-4b77-8f3e-2f5d672a3027" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "assistant:燙傷時,第一步是立即將受傷部位放置在冷水下沖洗,持續約 10 至 15 分鐘,以降低受傷部位的溫度。切記不要使用冰塊直接觸碰受傷部位,以免造成更多傷害。冷水可以有效減輕疼痛、減少紅腫和防止燙傷加重。如果是擦傷燙傷的情況,可以輕輕擦拭傷口後再進行冷水沖洗。如果傷口擦傷面積較大或有疑慮,建議儘快就醫處理。\n", + "user:燙傷時第一步該怎麼做?\n", + "assistant:當小腿抽筋時,最好的處理方法是停止運動,放鬆緊繃的肌肉,輕輕按摩或拉伸受影響的區域,同時進行深呼吸放鬆身體。另外,可透過按摩或應用熱敷來緩解疼痛感。若疼痛持續或頻繁發生,建議尋求專業醫療建議。\n", + "user:當小腿抽筋時最適當的處理是?, 用繁體中文回答\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 取消執行" + ], + "metadata": { + "id": "b_1nXQoNroqp" + } + }, + { + "cell_type": "code", + "source": [ + "message, run = input_and_run('蒙娜麗莎是誰的作品?',\n", + " new_thread.id, assistant.id)\n", + "\n", + "run = client.beta.threads.runs.cancel(\n", + " thread_id=new_thread.id,\n", + " run_id=run.id\n", + ")\n", + "print(f\"取消中:{run.status}\")\n", + "while run.status == \"cancelling\":\n", + " run = client.beta.threads.runs.retrieve(\n", + " thread_id=new_thread.id,\n", + " run_id=run.id,\n", + " )\n", + " if run.status != \"cancelling\":\n", + " print(f\"已取消:{run.status}\")\n", + " break" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "H2P26pBsroFA", + "outputId": "be5d8bd2-58e9-45c5-c310-e009ff2ffa2d" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "取消中:cancelling\n", + "已取消:cancelled\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 檢視執行記錄" + ], + "metadata": { + "id": "joBddAAEPRa3" + } + }, + { + "cell_type": "code", + "source": [ + "runs = client.beta.threads.runs.list(\n", + " new_thread.id\n", + ")\n", + "pprint(runs)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "C2Iv5ALXrbNg", + "outputId": "b1cf63a7-5fec-4702-9ba1-151e6a9a4b80" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "SyncCursorPage\u001b[1m[\u001b[0mRun\u001b[1m]\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_oNEohwsNB800aPqcX0XmzrV0'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[1;36m1703570065\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570063\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[1;36m1703570064\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'cancelled'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_p8szAQiAIcm3yd3zvXnpo2Bl'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570062\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570057\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[1;36m1703570058\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_zxEUhKJrJNJMQk10w9xx2MmV'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570056\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570052\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[1;36m1703570053\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'list'\u001b[0m,\n", + " \u001b[33mfirst_id\u001b[0m=\u001b[32m'run_oNEohwsNB800aPqcX0XmzrV0'\u001b[0m,\n", + " \u001b[33mlast_id\u001b[0m=\u001b[32m'run_zxEUhKJrJNJMQk10w9xx2MmV'\u001b[0m,\n", + " \u001b[33mhas_more\u001b[0m=\u001b[3;91mFalse\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
SyncCursorPage[Run](\n",
+              "    data=[\n",
+              "        Run(\n",
+              "            id='run_oNEohwsNB800aPqcX0XmzrV0',\n",
+              "            assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "            cancelled_at=1703570065,\n",
+              "            completed_at=None,\n",
+              "            created_at=1703570063,\n",
+              "            expires_at=None,\n",
+              "            failed_at=None,\n",
+              "            file_ids=[],\n",
+              "            instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "            last_error=None,\n",
+              "            metadata={},\n",
+              "            model='gpt-3.5-turbo-1106',\n",
+              "            object='thread.run',\n",
+              "            required_action=None,\n",
+              "            started_at=1703570064,\n",
+              "            status='cancelled',\n",
+              "            thread_id='thread_KtqImOv443wCrhGngaLblots',\n",
+              "            tools=[]\n",
+              "        ),\n",
+              "        Run(\n",
+              "            id='run_p8szAQiAIcm3yd3zvXnpo2Bl',\n",
+              "            assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "            cancelled_at=None,\n",
+              "            completed_at=1703570062,\n",
+              "            created_at=1703570057,\n",
+              "            expires_at=None,\n",
+              "            failed_at=None,\n",
+              "            file_ids=[],\n",
+              "            instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "            last_error=None,\n",
+              "            metadata={},\n",
+              "            model='gpt-3.5-turbo-1106',\n",
+              "            object='thread.run',\n",
+              "            required_action=None,\n",
+              "            started_at=1703570058,\n",
+              "            status='completed',\n",
+              "            thread_id='thread_KtqImOv443wCrhGngaLblots',\n",
+              "            tools=[]\n",
+              "        ),\n",
+              "        Run(\n",
+              "            id='run_zxEUhKJrJNJMQk10w9xx2MmV',\n",
+              "            assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "            cancelled_at=None,\n",
+              "            completed_at=1703570056,\n",
+              "            created_at=1703570052,\n",
+              "            expires_at=None,\n",
+              "            failed_at=None,\n",
+              "            file_ids=[],\n",
+              "            instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "            last_error=None,\n",
+              "            metadata={},\n",
+              "            model='gpt-3.5-turbo-1106',\n",
+              "            object='thread.run',\n",
+              "            required_action=None,\n",
+              "            started_at=1703570053,\n",
+              "            status='completed',\n",
+              "            thread_id='thread_KtqImOv443wCrhGngaLblots',\n",
+              "            tools=[]\n",
+              "        )\n",
+              "    ],\n",
+              "    object='list',\n",
+              "    first_id='run_oNEohwsNB800aPqcX0XmzrV0',\n",
+              "    last_id='run_zxEUhKJrJNJMQk10w9xx2MmV',\n",
+              "    has_more=False\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 刪除討論串" + ], + "metadata": { + "id": "TQg_wOSupr3p" + } + }, + { + "cell_type": "code", + "source": [ + "response = client.beta.threads.delete(thread_id=new_thread.id)\n", + "pprint(response)" + ], + "metadata": { + "id": "xmVgVLt5pvgJ", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 33 + }, + "outputId": "ded634c8-9988-459a-d4e9-a636d67e2b9b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThreadDeleted\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'thread_KtqImOv443wCrhGngaLblots'\u001b[0m, \u001b[33mdeleted\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'thread.deleted'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
ThreadDeleted(id='thread_KtqImOv443wCrhGngaLblots', deleted=True, object='thread.deleted')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 上傳文件增添知識庫" + ], + "metadata": { + "id": "XfeUq-JNPxDX" + } + }, + { + "cell_type": "code", + "source": [ + "!curl -L \"https://flagtech.github.io/F4762/Hello.py\" -o \"Hello.py\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "HjzlXA7eNpDM", + "outputId": "f7fef4dd-31b6-4d79-cca8-4fb6b529896b" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + "100 894 100 894 0 0 5146 0 --:--:-- --:--:-- --:--:-- 5137\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "file = client.files.create(\n", + " file=open(\"Hello.py\", \"rb\"),\n", + " purpose=\"assistants\"\n", + ")" + ], + "metadata": { + "id": "n4xvnTWmP2Xg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 修改助理的檔案清單設定" + ], + "metadata": { + "id": "AUNtsmrtQk4w" + } + }, + { + "cell_type": "code", + "source": [ + "assistant_file = client.beta.assistants.update(\n", + " assistant.id,\n", + " tools=[{\"type\": \"retrieval\"}],\n", + " file_ids=[file.id]\n", + ")\n", + "pprint(assistant_file)" + ], + "metadata": { + "id": "jb7w8FBYQPjx", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 213 + }, + "outputId": "2048c6eb-b81b-4975-8ceb-4ddd57284e3e" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistant\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569838\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'運動達人'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mToolRetrieval\u001b[0m\u001b[1m(\u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'retrieval'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Assistant(\n",
+              "    id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    created_at=1703569838,\n",
+              "    description=None,\n",
+              "    file_ids=['file-Q0qnb55P5WATrtbvhMGxcpJS'],\n",
+              "    instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    name='運動達人',\n",
+              "    object='assistant',\n",
+              "    tools=[ToolRetrieval(type='retrieval')]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "assistant_file = client.beta.assistants.files.retrieve(\n", + " assistant_id=assistant.id,\n", + " file_id=file.id\n", + ")\n", + "pprint(assistant_file)" + ], + "metadata": { + "id": "i-J19ywMRRQY", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 113 + }, + "outputId": "d3e4cf78-47ef-48e0-f858-fa5b60b6ad02" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistantFile\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570087\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant.file'\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
AssistantFile(\n",
+              "    id='file-Q0qnb55P5WATrtbvhMGxcpJS',\n",
+              "    assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    created_at=1703570087,\n",
+              "    object='assistant.file'\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "assistant_files = client.beta.assistants.files.list(\n", + " assistant_id=assistant.id\n", + ")\n", + "pprint(assistant_files)" + ], + "metadata": { + "id": "kV689gMkRYp2", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 241 + }, + "outputId": "868ca225-e043-4533-c33f-df949ffde3cc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "SyncCursorPage\u001b[1m[\u001b[0mAssistantFile\u001b[1m]\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mAssistantFile\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570087\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant.file'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'list'\u001b[0m,\n", + " \u001b[33mfirst_id\u001b[0m=\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m,\n", + " \u001b[33mlast_id\u001b[0m=\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m,\n", + " \u001b[33mhas_more\u001b[0m=\u001b[3;91mFalse\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
SyncCursorPage[AssistantFile](\n",
+              "    data=[\n",
+              "        AssistantFile(\n",
+              "            id='file-Q0qnb55P5WATrtbvhMGxcpJS',\n",
+              "            assistant_id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "            created_at=1703570087,\n",
+              "            object='assistant.file'\n",
+              "        )\n",
+              "    ],\n",
+              "    object='list',\n",
+              "    first_id='file-Q0qnb55P5WATrtbvhMGxcpJS',\n",
+              "    last_id='file-Q0qnb55P5WATrtbvhMGxcpJS',\n",
+              "    has_more=False\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "deleted_assistant_file = client.beta.assistants.files.delete(\n", + " assistant_id=assistant.id,\n", + " file_id=file.id\n", + ")\n", + "pprint(deleted_assistant_file)" + ], + "metadata": { + "id": "lYDk_dZDR1zo", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 33 + }, + "outputId": "ebd231df-9876-4167-d321-2cc445ff7f58" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mFileDeleteResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'file-Q0qnb55P5WATrtbvhMGxcpJS'\u001b[0m, \u001b[33mdeleted\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'assistant.file.deleted'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
FileDeleteResponse(id='file-Q0qnb55P5WATrtbvhMGxcpJS', deleted=True, object='assistant.file.deleted')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 刪除文件" + ], + "metadata": { + "id": "xR26IYo9wmwa" + } + }, + { + "cell_type": "code", + "source": [ + "file_list = client.files.list(\n", + " purpose='assistants'\n", + ")\n", + "for i in file_list:\n", + " pprint(i)" + ], + "metadata": { + "id": "wvzwoVZdrCUc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "deleted_file = client.files.delete(file_list.data[0].id)\n", + "pprint(deleted_file)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 201 + }, + "id": "3z-FBEIUBnsW", + "outputId": "9adc35b5-8cd4-4581-c74e-2ae6615ac4b0" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "error", + "ename": "IndexError", + "evalue": "ignored", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIndexError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mdeleted_file\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mclient\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfiles\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdelete\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile_list\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0mpprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdeleted_file\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mIndexError\u001b[0m: list index out of range" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 列出所有助理" + ], + "metadata": { + "id": "zydW4bxAxTo4" + } + }, + { + "cell_type": "code", + "source": [ + "my_assistants = client.beta.assistants.list(\n", + " limit = 1\n", + ")\n", + "for i in my_assistants.data:\n", + " pprint(i)\n", + " response = client.beta.assistants.delete(i.id)\n", + " pprint(response)" + ], + "metadata": { + "id": "lZKveQaf4F97", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 229 + }, + "outputId": "9c8ff6a0-7a07-49e7-ace6-bd81495c9469" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistant\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703569838\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位熱愛運動的達人, 具有各種與運動相關的知識'\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'運動達人'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mToolRetrieval\u001b[0m\u001b[1m(\u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'retrieval'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Assistant(\n",
+              "    id='asst_EHR4omyzzojIfQ8spmohkvaB',\n",
+              "    created_at=1703569838,\n",
+              "    description=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='你是一位熱愛運動的達人, 具有各種與運動相關的知識',\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    name='運動達人',\n",
+              "    object='assistant',\n",
+              "    tools=[ToolRetrieval(type='retrieval')]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistantDeleted\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'asst_EHR4omyzzojIfQ8spmohkvaB'\u001b[0m, \u001b[33mdeleted\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'assistant.deleted'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
AssistantDeleted(id='asst_EHR4omyzzojIfQ8spmohkvaB', deleted=True, object='assistant.deleted')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 8-3 使用內建工具" + ], + "metadata": { + "id": "uR6fYOQHsZxr" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Code interpreter" + ], + "metadata": { + "id": "PrpizHmVh3iu" + } + }, + { + "cell_type": "code", + "source": [ + "assistant = client.beta.assistants.create(\n", + " name=\"網頁設計助理\",\n", + " instructions=\"你是一位網頁設計師, 能依照對話內容設計和修改出網頁, \"\n", + " \"請生成HTML檔\",\n", + " tools=[{\"type\": \"code_interpreter\"}],\n", + " model=\"gpt-3.5-turbo-1106\",\n", + ")\n", + "assistant_id = assistant.id\n", + "pprint(assistant)" + ], + "metadata": { + "id": "80MWnalHWsDS", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 213 + }, + "outputId": "48389e1e-893a-4100-d614-ab68f2c18e99" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistant\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570178\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位網頁設計師, 能依照對話內容設計和修改出網頁, 請生成HTML檔'\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-3.5-turbo-1106'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'網頁設計助理'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mToolCodeInterpreter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'code_interpreter'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Assistant(\n",
+              "    id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "    created_at=1703570178,\n",
+              "    description=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='你是一位網頁設計師, 能依照對話內容設計和修改出網頁, 請生成HTML檔',\n",
+              "    metadata={},\n",
+              "    model='gpt-3.5-turbo-1106',\n",
+              "    name='網頁設計助理',\n",
+              "    object='assistant',\n",
+              "    tools=[ToolCodeInterpreter(type='code_interpreter')]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "thread = client.beta.threads.create()\n", + "thread_id = thread.id\n", + "pprint(thread)" + ], + "metadata": { + "id": "FyTOj4IiFCba", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 33 + }, + "outputId": "c66f0b4e-389e-495c-c447-016bf53a06cf" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThread\u001b[0m\u001b[1m(\u001b[0m\u001b[33mid\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m, \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570221\u001b[0m, \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m, \u001b[33mobject\u001b[0m=\u001b[32m'thread'\u001b[0m\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Thread(id='thread_HVlKuweQ5Ryug70ePoPqN65R', created_at=1703570221, metadata={}, object='thread')\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "message, run = input_and_run(\"請幫我製作一個精美的天氣預報網頁, \"\n", + " \"先設置相關欄位, 有地點、日期、溫度和天氣狀況, \"\n", + " \"其他設定由你決定。\",\n", + " thread_id, assistant_id)\n", + "\n", + "run = wait_on_run(run)\n", + "\n", + "messages = client.beta.threads.messages.list(\n", + " thread_id=thread_id, order=\"asc\", after=message.id)\n", + "for part in messages:\n", + " print(f'訊息識別碼:{part.id}')\n", + " print(f'AI 回覆:{part.content[0].text.value}')" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Lt8nTkEC5eYE", + "outputId": "a2815dfc-92a0-4761-d6c1-297d84a305ce" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "訊息識別碼:msg_rLFIzt9Eut1DJEcRg1Wh0nfy\n", + "AI 回覆:好的,我們可以加上一些圖示和圖表來使網頁更生動。讓我們先設計一個基本的網頁,包含地點、日期、溫度和天氣狀況的欄位。接著再加入圖示和圖表。\n", + "\n", + "請稍等,我會開始設計網頁。\n", + "訊息識別碼:msg_yMf1lNmc1G6HLozV5yF7MWqy\n", + "AI 回覆:網頁已經設計完成並儲存為HTML檔案。您可以從以下連結下載:\n", + "\n", + "[weather_forecast.html](sandbox:/mnt/data/weather_forecast.html)\n", + "\n", + "請下載檔案並在瀏覽器中開啟,檢查是否符合您的期望。接下來,如果有需要進行修改或添加其他功能,請告訴我。\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "run_steps = client.beta.threads.runs.steps.list(\n", + " thread_id=thread_id,\n", + " run_id=run.id,\n", + " order=\"asc\"\n", + ")\n", + "pprint(run_steps)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "6xMndhzVsjVA", + "outputId": "b1a12bb9-a6e8-48f6-9475-f5fb89c4d948" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "SyncCursorPage\u001b[1m[\u001b[0mRunStep\u001b[1m]\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mdata\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mRunStep\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'step_qE5FZ2bYtyn2lldxRsa7LKw7'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570227\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570224\u001b[0m,\n", + " \u001b[33mexpired_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run.step'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_KzBPRWBji8fBP0TSpffa4Y44'\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mstep_details\u001b[0m=\u001b[1;35mMessageCreationStepDetails\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mmessage_creation\u001b[0m=\u001b[1;35mMessageCreation\u001b[0m\u001b[1m(\u001b[0m\u001b[33mmessage_id\u001b[0m=\u001b[32m'msg_rLFIzt9Eut1DJEcRg1Wh0nfy'\u001b[0m\u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'message_creation'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'message_creation'\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[1;35mRunStep\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'step_NTfW0jpqSpgqRPjDaVVyYSqi'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570236\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570227\u001b[0m,\n", + " \u001b[33mexpired_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run.step'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_KzBPRWBji8fBP0TSpffa4Y44'\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mstep_details\u001b[0m=\u001b[1;35mToolCallsStepDetails\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtool_calls\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mCodeToolCall\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'call_GGRVuCB3B0pqpMVtQfsZB4D7'\u001b[0m,\n", + " \u001b[33mcode_interpreter\u001b[0m=\u001b[1;35mCodeInterpreter\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33minput\u001b[0m=\u001b[32m'# 產生基本的HTML模板\\r\\nhtml_content = \"\"\"\\r\\n\u001b[0m\u001b[32m<\u001b[0m\u001b[32m!DOCTYPE html>\\r\\n\\r\\n\\r\\n \\r\\n \\r\\n 天氣預報\\r\\n \\r\\n\\r\\n\\r\\n
天氣預報
\\r\\n
\\r\\n \u001b[0m\n", + "\u001b[32m

地點: 台北市

\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n
日期溫度天氣狀況
10/01/202228°C
10/02/202229°C多雲
\\r\\n
\\r\\n\\r\\n\u001b[0m\u001b[32m\\r\\n\"\"\"\\r\\n\\r\\n# 將HTML內容存為檔案\\r\\nfile_path = \u001b[0m\n", + "\u001b[32m\\'/mnt/data/weather_forecast.html\\'\\r\\nwith open\u001b[0m\u001b[32m(\u001b[0m\u001b[32mfile_path, \\'w\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m as file:\\r\\n \u001b[0m\n", + "\u001b[32mfile.write\u001b[0m\u001b[32m(\u001b[0m\u001b[32mhtml_content\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\r\\n\\r\\nfile_path'\u001b[0m,\n", + " \u001b[33moutputs\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mCodeInterpreterOutputLogs\u001b[0m\u001b[1m(\u001b[0m\u001b[33mlogs\u001b[0m=\u001b[32m\"'/mnt/data/weather_forecast.html'\"\u001b[0m, \u001b[33mtype\u001b[0m=\u001b[32m'logs'\u001b[0m\u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'code_interpreter'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'tool_calls'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'tool_calls'\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[1;35mRunStep\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'step_eaAdvSBEByJFdjOB7OnhNcfZ'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570237\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570236\u001b[0m,\n", + " \u001b[33mexpired_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run.step'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_KzBPRWBji8fBP0TSpffa4Y44'\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mstep_details\u001b[0m=\u001b[1;35mMessageCreationStepDetails\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mmessage_creation\u001b[0m=\u001b[1;35mMessageCreation\u001b[0m\u001b[1m(\u001b[0m\u001b[33mmessage_id\u001b[0m=\u001b[32m'msg_yMf1lNmc1G6HLozV5yF7MWqy'\u001b[0m\u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'message_creation'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'message_creation'\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'list'\u001b[0m,\n", + " \u001b[33mfirst_id\u001b[0m=\u001b[32m'step_qE5FZ2bYtyn2lldxRsa7LKw7'\u001b[0m,\n", + " \u001b[33mlast_id\u001b[0m=\u001b[32m'step_eaAdvSBEByJFdjOB7OnhNcfZ'\u001b[0m,\n", + " \u001b[33mhas_more\u001b[0m=\u001b[3;91mFalse\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
SyncCursorPage[RunStep](\n",
+              "    data=[\n",
+              "        RunStep(\n",
+              "            id='step_qE5FZ2bYtyn2lldxRsa7LKw7',\n",
+              "            assistant_id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "            cancelled_at=None,\n",
+              "            completed_at=1703570227,\n",
+              "            created_at=1703570224,\n",
+              "            expired_at=None,\n",
+              "            failed_at=None,\n",
+              "            last_error=None,\n",
+              "            metadata=None,\n",
+              "            object='thread.run.step',\n",
+              "            run_id='run_KzBPRWBji8fBP0TSpffa4Y44',\n",
+              "            status='completed',\n",
+              "            step_details=MessageCreationStepDetails(\n",
+              "                message_creation=MessageCreation(message_id='msg_rLFIzt9Eut1DJEcRg1Wh0nfy'),\n",
+              "                type='message_creation'\n",
+              "            ),\n",
+              "            thread_id='thread_HVlKuweQ5Ryug70ePoPqN65R',\n",
+              "            type='message_creation',\n",
+              "            expires_at=None\n",
+              "        ),\n",
+              "        RunStep(\n",
+              "            id='step_NTfW0jpqSpgqRPjDaVVyYSqi',\n",
+              "            assistant_id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "            cancelled_at=None,\n",
+              "            completed_at=1703570236,\n",
+              "            created_at=1703570227,\n",
+              "            expired_at=None,\n",
+              "            failed_at=None,\n",
+              "            last_error=None,\n",
+              "            metadata=None,\n",
+              "            object='thread.run.step',\n",
+              "            run_id='run_KzBPRWBji8fBP0TSpffa4Y44',\n",
+              "            status='completed',\n",
+              "            step_details=ToolCallsStepDetails(\n",
+              "                tool_calls=[\n",
+              "                    CodeToolCall(\n",
+              "                        id='call_GGRVuCB3B0pqpMVtQfsZB4D7',\n",
+              "                        code_interpreter=CodeInterpreter(\n",
+              "                            input='# 產生基本的HTML模板\\r\\nhtml_content = \"\"\"\\r\\n<!DOCTYPE html>\\r\\n<html \n",
+              "lang=\"en\">\\r\\n<head>\\r\\n    <meta charset=\"UTF-8\">\\r\\n    <meta name=\"viewport\" content=\"width=device-width, \n",
+              "initial-scale=1.0\">\\r\\n    <title>天氣預報</title>\\r\\n    <style>\\r\\n        body {\\r\\n            font-family: \n",
+              "Arial, sans-serif;\\r\\n            background-color: #f4f4f4;\\r\\n            margin: 0;\\r\\n            padding: \n",
+              "0;\\r\\n        }\\r\\n        header {\\r\\n            background-color: #007bff;\\r\\n            color: white;\\r\\n     \n",
+              "padding: 1em;\\r\\n            text-align: center;\\r\\n            font-size: 1.5em;\\r\\n        }\\r\\n        \n",
+              ".container {\\r\\n            max-width: 800px;\\r\\n            margin: 20px auto;\\r\\n            padding: 20px;\\r\\n  \n",
+              "background-color: white;\\r\\n            border-radius: 5px;\\r\\n            box-shadow: 0 0 10px rgba(0, 0, 0, \n",
+              "0.1);\\r\\n        }\\r\\n        table {\\r\\n            width: 100%;\\r\\n            border-collapse: collapse;\\r\\n    \n",
+              "margin-bottom: 20px;\\r\\n        }\\r\\n        th, td {\\r\\n            padding: 12px 15px;\\r\\n            text-align:\n",
+              "left;\\r\\n        }\\r\\n        th {\\r\\n            background-color: #007bff;\\r\\n            color: white;\\r\\n      \n",
+              "}\\r\\n    </style>\\r\\n</head>\\r\\n<body>\\r\\n    <header>天氣預報</header>\\r\\n    <div class=\"container\">\\r\\n        \n",
+              "<h2>地點: 台北市</h2>\\r\\n        <table>\\r\\n            <tr>\\r\\n                <th>日期</th>\\r\\n                \n",
+              "<th>溫度</th>\\r\\n                <th>天氣狀況</th>\\r\\n            </tr>\\r\\n            <tr>\\r\\n                \n",
+              "<td>10/01/2022</td>\\r\\n                <td>28°C</td>\\r\\n                <td>晴</td>\\r\\n            </tr>\\r\\n       \n",
+              "<tr>\\r\\n                <td>10/02/2022</td>\\r\\n                <td>29°C</td>\\r\\n                <td>多雲</td>\\r\\n  \n",
+              "</tr>\\r\\n        </table>\\r\\n    </div>\\r\\n</body>\\r\\n</html>\\r\\n\"\"\"\\r\\n\\r\\n# 將HTML內容存為檔案\\r\\nfile_path = \n",
+              "\\'/mnt/data/weather_forecast.html\\'\\r\\nwith open(file_path, \\'w\\') as file:\\r\\n    \n",
+              "file.write(html_content)\\r\\n\\r\\nfile_path',\n",
+              "                            outputs=[\n",
+              "                                CodeInterpreterOutputLogs(logs=\"'/mnt/data/weather_forecast.html'\", type='logs')\n",
+              "                            ]\n",
+              "                        ),\n",
+              "                        type='code_interpreter'\n",
+              "                    )\n",
+              "                ],\n",
+              "                type='tool_calls'\n",
+              "            ),\n",
+              "            thread_id='thread_HVlKuweQ5Ryug70ePoPqN65R',\n",
+              "            type='tool_calls',\n",
+              "            expires_at=None\n",
+              "        ),\n",
+              "        RunStep(\n",
+              "            id='step_eaAdvSBEByJFdjOB7OnhNcfZ',\n",
+              "            assistant_id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "            cancelled_at=None,\n",
+              "            completed_at=1703570237,\n",
+              "            created_at=1703570236,\n",
+              "            expired_at=None,\n",
+              "            failed_at=None,\n",
+              "            last_error=None,\n",
+              "            metadata=None,\n",
+              "            object='thread.run.step',\n",
+              "            run_id='run_KzBPRWBji8fBP0TSpffa4Y44',\n",
+              "            status='completed',\n",
+              "            step_details=MessageCreationStepDetails(\n",
+              "                message_creation=MessageCreation(message_id='msg_yMf1lNmc1G6HLozV5yF7MWqy'),\n",
+              "                type='message_creation'\n",
+              "            ),\n",
+              "            thread_id='thread_HVlKuweQ5Ryug70ePoPqN65R',\n",
+              "            type='message_creation',\n",
+              "            expires_at=None\n",
+              "        )\n",
+              "    ],\n",
+              "    object='list',\n",
+              "    first_id='step_qE5FZ2bYtyn2lldxRsa7LKw7',\n",
+              "    last_id='step_eaAdvSBEByJFdjOB7OnhNcfZ',\n",
+              "    has_more=False\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "run_step = client.beta.threads.runs.steps.retrieve(\n", + " thread_id=thread_id,\n", + " run_id=run.id,\n", + " step_id=run_steps.data[1].id\n", + ")\n", + "pprint(run_step)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 799 + }, + "id": "XhA0I2dBsROW", + "outputId": "3cea9100-5059-4a2a-c7fd-e75aead65cd1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mRunStep\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'step_NTfW0jpqSpgqRPjDaVVyYSqi'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[1;36m1703570236\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570227\u001b[0m,\n", + " \u001b[33mexpired_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run.step'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_KzBPRWBji8fBP0TSpffa4Y44'\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'completed'\u001b[0m,\n", + " \u001b[33mstep_details\u001b[0m=\u001b[1;35mToolCallsStepDetails\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtool_calls\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mCodeToolCall\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'call_GGRVuCB3B0pqpMVtQfsZB4D7'\u001b[0m,\n", + " \u001b[33mcode_interpreter\u001b[0m=\u001b[1;35mCodeInterpreter\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33minput\u001b[0m=\u001b[32m'# 產生基本的HTML模板\\r\\nhtml_content = \"\"\"\\r\\n\u001b[0m\u001b[32m<\u001b[0m\u001b[32m!DOCTYPE html>\\r\\n\\r\\n\\r\\n \\r\\n \\r\\n 天氣預報\\r\\n \\r\\n\\r\\n\\r\\n
天氣預報
\\r\\n
\\r\\n \u001b[0m\n", + "\u001b[32m

地點: 台北市

\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n \\r\\n \\r\\n \\r\\n \u001b[0m\n", + "\u001b[32m\\r\\n
日期溫度天氣狀況
10/01/202228°C
10/02/202229°C多雲
\\r\\n
\\r\\n\\r\\n\u001b[0m\u001b[32m\\r\\n\"\"\"\\r\\n\\r\\n# 將HTML內容存為檔案\\r\\nfile_path = \u001b[0m\n", + "\u001b[32m\\'/mnt/data/weather_forecast.html\\'\\r\\nwith open\u001b[0m\u001b[32m(\u001b[0m\u001b[32mfile_path, \\'w\\'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m as file:\\r\\n \u001b[0m\n", + "\u001b[32mfile.write\u001b[0m\u001b[32m(\u001b[0m\u001b[32mhtml_content\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\r\\n\\r\\nfile_path'\u001b[0m,\n", + " \u001b[33moutputs\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mCodeInterpreterOutputLogs\u001b[0m\u001b[1m(\u001b[0m\u001b[33mlogs\u001b[0m=\u001b[32m\"'/mnt/data/weather_forecast.html'\"\u001b[0m, \u001b[33mtype\u001b[0m=\u001b[32m'logs'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'code_interpreter'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'tool_calls'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'tool_calls'\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
RunStep(\n",
+              "    id='step_NTfW0jpqSpgqRPjDaVVyYSqi',\n",
+              "    assistant_id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "    cancelled_at=None,\n",
+              "    completed_at=1703570236,\n",
+              "    created_at=1703570227,\n",
+              "    expired_at=None,\n",
+              "    failed_at=None,\n",
+              "    last_error=None,\n",
+              "    metadata=None,\n",
+              "    object='thread.run.step',\n",
+              "    run_id='run_KzBPRWBji8fBP0TSpffa4Y44',\n",
+              "    status='completed',\n",
+              "    step_details=ToolCallsStepDetails(\n",
+              "        tool_calls=[\n",
+              "            CodeToolCall(\n",
+              "                id='call_GGRVuCB3B0pqpMVtQfsZB4D7',\n",
+              "                code_interpreter=CodeInterpreter(\n",
+              "                    input='# 產生基本的HTML模板\\r\\nhtml_content = \"\"\"\\r\\n<!DOCTYPE html>\\r\\n<html \n",
+              "lang=\"en\">\\r\\n<head>\\r\\n    <meta charset=\"UTF-8\">\\r\\n    <meta name=\"viewport\" content=\"width=device-width, \n",
+              "initial-scale=1.0\">\\r\\n    <title>天氣預報</title>\\r\\n    <style>\\r\\n        body {\\r\\n            font-family: \n",
+              "Arial, sans-serif;\\r\\n            background-color: #f4f4f4;\\r\\n            margin: 0;\\r\\n            padding: \n",
+              "0;\\r\\n        }\\r\\n        header {\\r\\n            background-color: #007bff;\\r\\n            color: white;\\r\\n     \n",
+              "padding: 1em;\\r\\n            text-align: center;\\r\\n            font-size: 1.5em;\\r\\n        }\\r\\n        \n",
+              ".container {\\r\\n            max-width: 800px;\\r\\n            margin: 20px auto;\\r\\n            padding: 20px;\\r\\n  \n",
+              "background-color: white;\\r\\n            border-radius: 5px;\\r\\n            box-shadow: 0 0 10px rgba(0, 0, 0, \n",
+              "0.1);\\r\\n        }\\r\\n        table {\\r\\n            width: 100%;\\r\\n            border-collapse: collapse;\\r\\n    \n",
+              "margin-bottom: 20px;\\r\\n        }\\r\\n        th, td {\\r\\n            padding: 12px 15px;\\r\\n            text-align:\n",
+              "left;\\r\\n        }\\r\\n        th {\\r\\n            background-color: #007bff;\\r\\n            color: white;\\r\\n      \n",
+              "}\\r\\n    </style>\\r\\n</head>\\r\\n<body>\\r\\n    <header>天氣預報</header>\\r\\n    <div class=\"container\">\\r\\n        \n",
+              "<h2>地點: 台北市</h2>\\r\\n        <table>\\r\\n            <tr>\\r\\n                <th>日期</th>\\r\\n                \n",
+              "<th>溫度</th>\\r\\n                <th>天氣狀況</th>\\r\\n            </tr>\\r\\n            <tr>\\r\\n                \n",
+              "<td>10/01/2022</td>\\r\\n                <td>28°C</td>\\r\\n                <td>晴</td>\\r\\n            </tr>\\r\\n       \n",
+              "<tr>\\r\\n                <td>10/02/2022</td>\\r\\n                <td>29°C</td>\\r\\n                <td>多雲</td>\\r\\n  \n",
+              "</tr>\\r\\n        </table>\\r\\n    </div>\\r\\n</body>\\r\\n</html>\\r\\n\"\"\"\\r\\n\\r\\n# 將HTML內容存為檔案\\r\\nfile_path = \n",
+              "\\'/mnt/data/weather_forecast.html\\'\\r\\nwith open(file_path, \\'w\\') as file:\\r\\n    \n",
+              "file.write(html_content)\\r\\n\\r\\nfile_path',\n",
+              "                    outputs=[CodeInterpreterOutputLogs(logs=\"'/mnt/data/weather_forecast.html'\", type='logs')]\n",
+              "                ),\n",
+              "                type='code_interpreter'\n",
+              "            )\n",
+              "        ],\n",
+              "        type='tool_calls'\n",
+              "    ),\n",
+              "    thread_id='thread_HVlKuweQ5Ryug70ePoPqN65R',\n",
+              "    type='tool_calls',\n",
+              "    expires_at=None\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "pprint(messages.data[-1])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 502 + }, + "id": "5OkTiJ_i7Xzw", + "outputId": "93bf2903-9124-4721-bc6a-31561d9dbbdd" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mThreadMessage\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'msg_yMf1lNmc1G6HLozV5yF7MWqy'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_jRwS6xuOVrBr8SX6ALwNcdod'\u001b[0m,\n", + " \u001b[33mcontent\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mMessageContentText\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtext\u001b[0m=\u001b[1;35mText\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mannotations\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mTextAnnotationFilePath\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mend_index\u001b[0m=\u001b[1;36m95\u001b[0m,\n", + " \u001b[33mfile_path\u001b[0m=\u001b[1;35mTextAnnotationFilePathFilePath\u001b[0m\u001b[1m(\u001b[0m\u001b[33mfile_id\u001b[0m=\u001b[32m'file-qmlalTBl2yCmGG17UsqhKpRw'\u001b[0m\u001b[1m)\u001b[0m,\n", + " \u001b[33mstart_index\u001b[0m=\u001b[1;36m56\u001b[0m,\n", + " \u001b[33mtext\u001b[0m=\u001b[32m'sandbox:/mnt/data/weather_forecast.html'\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'file_path'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mvalue\u001b[0m=\u001b[32m'網頁已經設計完成並儲存為HTML檔案。您可以從以下連結下載:\\n\\n\u001b[0m\u001b[32m[\u001b[0m\u001b[32mweather_forecast.html\u001b[0m\u001b[32m]\u001b[0m\u001b[32m(\u001b[0m\u001b[32msandbox:\u001b[0m\n", + "\u001b[32m/mnt/data/weather_forecast.html\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\n\\n請下載檔案並在瀏覽器中開啟,檢查是否符合您的期望。接下來,如果有需要進行修改或\u001b[0m\n", + "\u001b[32m添加其他功能,請告訴我。'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'text'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570236\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m'file-qmlalTBl2yCmGG17UsqhKpRw'\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.message'\u001b[0m,\n", + " \u001b[33mrole\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mrun_id\u001b[0m=\u001b[32m'run_KzBPRWBji8fBP0TSpffa4Y44'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_HVlKuweQ5Ryug70ePoPqN65R'\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
ThreadMessage(\n",
+              "    id='msg_yMf1lNmc1G6HLozV5yF7MWqy',\n",
+              "    assistant_id='asst_jRwS6xuOVrBr8SX6ALwNcdod',\n",
+              "    content=[\n",
+              "        MessageContentText(\n",
+              "            text=Text(\n",
+              "                annotations=[\n",
+              "                    TextAnnotationFilePath(\n",
+              "                        end_index=95,\n",
+              "                        file_path=TextAnnotationFilePathFilePath(file_id='file-qmlalTBl2yCmGG17UsqhKpRw'),\n",
+              "                        start_index=56,\n",
+              "                        text='sandbox:/mnt/data/weather_forecast.html',\n",
+              "                        type='file_path'\n",
+              "                    )\n",
+              "                ],\n",
+              "                value='網頁已經設計完成並儲存為HTML檔案。您可以從以下連結下載:\\n\\n[weather_forecast.html](sandbox:\n",
+              "/mnt/data/weather_forecast.html)\\n\\n請下載檔案並在瀏覽器中開啟,檢查是否符合您的期望。接下來,如果有需要進行修改或\n",
+              "添加其他功能,請告訴我。'\n",
+              "            ),\n",
+              "            type='text'\n",
+              "        )\n",
+              "    ],\n",
+              "    created_at=1703570236,\n",
+              "    file_ids=['file-qmlalTBl2yCmGG17UsqhKpRw'],\n",
+              "    metadata={},\n",
+              "    object='thread.message',\n",
+              "    role='assistant',\n",
+              "    run_id='run_KzBPRWBji8fBP0TSpffa4Y44',\n",
+              "    thread_id='thread_HVlKuweQ5Ryug70ePoPqN65R'\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import HTML\n", + "\n", + "def show_html(messages):\n", + " for i in messages.data:\n", + " # 找出有文件內容的對話物件\n", + " if i.content[0].text.annotations:\n", + " file_index = i.content[0].text.annotations\n", + " # 找到文件識別碼\n", + " file_ids = file_index[0].file_path.file_id\n", + " content = client.files.content(file_ids)\n", + " # 儲存 HTML\n", + " content.stream_to_file('test.html')\n", + " # 顯示 HTML\n", + " html_content = content.content.decode('utf-8')\n", + " display(HTML(html_content))\n", + "\n", + "show_html(messages)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 340 + }, + "id": "IfriRgna7PhC", + "outputId": "8f446fc8-adf4-4167-a440-ef6ec6410635" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " 天氣預報\n", + " \n", + "\n", + "\n", + "
天氣預報
\n", + "
\n", + "

地點: 台北市

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
日期溫度天氣狀況
10/01/202228°C
10/02/202229°C多雲
\n", + "
\n", + "\n", + "\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " message, run = input_and_run(question, thread_id, assistant_id)\n", + "\n", + " run = wait_on_run(run)\n", + "\n", + " if run.status == 'completed':\n", + " messages = client.beta.threads.messages.list(\n", + " thread_id=thread_id,order=\"asc\", after=message.id)\n", + " for part in messages:\n", + " print(f'AI 回覆:{part.content[0].text.value}')\n", + " show_html(messages)\n", + " else:\n", + " print(run.status)" + ], + "metadata": { + "id": "aehyrFdzUjPr", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 922 + }, + "outputId": "093f67dd-754e-448b-e00a-bdbd4ad52cb5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "請輸入要求:請加入背景\n", + "AI 回覆:當然,我們可以加入一個動態的背景圖片來提升網頁的吸引力。讓我們立即加入背景圖片,請稍等片刻。\n", + "AI 回覆:背景圖片已成功加入並且網頁內容已經更新。您可以從以下連結下載新版本的HTML檔案,並在瀏覽器中開啟以檢查。 \n", + "\n", + "[weather_forecast_with_bg.html](sandbox:/mnt/data/weather_forecast_with_bg.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " 天氣預報\n", + " \n", + "\n", + "\n", + "
天氣預報
\n", + "
\n", + "

地點: 台北市

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
日期溫度天氣狀況
10/01/202228°C
10/02/202229°C多雲
\n", + "
\n", + "\n", + "\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "請輸入要求:增加下拉式表單\n", + "AI 回覆:理解了,您想加入一個下拉式表單供使用者選擇不同的地點以查詢天氣預報。讓我立即修改網頁,請稍等。\n", + "AI 回覆:下拉式表單已經成功加入並且網頁內容已經更新。您可以從以下連結下載新版本的HTML檔案,並在瀏覽器中開啟以檢查。\n", + "\n", + "[weather_forecast_with_dropdown.html](sandbox:/mnt/data/weather_forecast_with_dropdown.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " \n", + " 天氣預報\n", + " \n", + "\n", + "\n", + "
天氣預報
\n", + "
\n", + "
\n", + " \n", + " \n", + " \n", + "
\n", + "

地點: 台北市

\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
日期溫度天氣狀況
10/01/202228°C
10/02/202229°C多雲
\n", + "
\n", + "\n", + "\n" + ] + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Retrieval 檢索器 - RAG\n" + ], + "metadata": { + "id": "qafxX3H5mgr5" + } + }, + { + "cell_type": "code", + "source": [ + "!mkdir \"旅遊\"\n", + "!curl -L \"https://ppt.cc/fscClx\" -o \"旅遊/food.pdf\"\n", + "!curl -L \"https://ppt.cc/f2BqBx\" -o \"旅遊/history.pdf\"\n", + "!curl -L \"https://ppt.cc/fuarUx\" -o \"旅遊/nature.pdf\"" + ], + "metadata": { + "id": "rd2x5qTPbejG", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "e355d2e9-5651-4d09-a1fd-6c5225b9f9fd" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0\n", + "100 3576k 100 3576k 0 0 2139k 0 0:00:01 0:00:01 --:--:-- 17.4M\n", + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n", + "100 3357k 100 3357k 0 0 2429k 0 0:00:01 0:00:01 --:--:-- 2429k\n", + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\n", + "100 3803k 100 3803k 0 0 2758k 0 0:00:01 0:00:01 --:--:-- 2758k\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import os\n", + "ids = []\n", + "allfilelist = os.listdir('/content/旅遊')\n", + "for path in allfilelist:\n", + " file = client.files.create(\n", + " file=open(f\"/content/旅遊/{path}\", \"rb\"),\n", + " purpose='assistants'\n", + " )\n", + " ids.append(file.id)" + ], + "metadata": { + "id": "TeOyGD93nTfY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "assistant = client.beta.assistants.create(\n", + " name=\"旅遊助理\",\n", + " instructions=\"你是一位旅遊規劃者, 可以根據旅遊文件規劃旅遊行程\",\n", + " tools=[{\"type\": \"retrieval\"}],\n", + " model=\"gpt-4-1106-preview\",\n", + " file_ids=ids\n", + ")\n", + "assistant_id = assistant.id\n", + "pprint(assistant)\n", + "\n", + "thread = client.beta.threads.create()\n", + "thread_id = thread.id" + ], + "metadata": { + "id": "Z3WC3Y7_mnDX", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 213 + }, + "outputId": "097cf51e-b2cb-4824-eba3-ff75a419feac" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mAssistant\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'asst_TNZ60IGVpv2oBNtQdLz0ViKt'\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703570337\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[32m'file-7gOt18kaBgxzZw8zlqbp33Yc'\u001b[0m, \u001b[32m'file-BhyqYnwfjovD9DkC5bfGRBuX'\u001b[0m, \u001b[32m'file-q7xrzBOA27eF0k8iAIpnKGJR'\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'你是一位旅遊規劃者, 可以根據旅遊文件規劃旅遊行程'\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-4-1106-preview'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'旅遊助理'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'assistant'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mToolRetrieval\u001b[0m\u001b[1m(\u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'retrieval'\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Assistant(\n",
+              "    id='asst_TNZ60IGVpv2oBNtQdLz0ViKt',\n",
+              "    created_at=1703570337,\n",
+              "    description=None,\n",
+              "    file_ids=['file-7gOt18kaBgxzZw8zlqbp33Yc', 'file-BhyqYnwfjovD9DkC5bfGRBuX', 'file-q7xrzBOA27eF0k8iAIpnKGJR'],\n",
+              "    instructions='你是一位旅遊規劃者, 可以根據旅遊文件規劃旅遊行程',\n",
+              "    metadata={},\n",
+              "    model='gpt-4-1106-preview',\n",
+              "    name='旅遊助理',\n",
+              "    object='assistant',\n",
+              "    tools=[ToolRetrieval(type='retrieval')]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "from IPython.display import Markdown\n", + "\n", + "def output_text(thread_id, message):\n", + " messages = client.beta.threads.messages.list(\n", + " thread_id=thread_id, order=\"asc\", after=message.id)\n", + " for message in messages:\n", + " display(Markdown(message.content[0].text.value))" + ], + "metadata": { + "id": "5rF2SGWEp5wn" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "message, run = input_and_run(\"請規劃台中一日遊行程\",\n", + " thread_id, assistant.id)\n", + "run = wait_on_run(run)\n", + "pprint(run.status)\n", + "output_text(thread_id, message)" + ], + "metadata": { + "id": "X2uiV32Fm57O", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 242 + }, + "outputId": "78964a1c-d8ee-4129-b1ec-7b00d896c76c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "基於提供的資料,我為你規劃了一個台中一日遊行程,以下是建議的旅遊景點及順序:\n\n1. **彩虹眷村**【13†source】: 推薦先從富有色彩和歷史意義的彩虹眷村開始你的一日遊。\n\n2. **霧峰林家宮保第園區**【14†source】: 隨後參觀一下霧峰林家宮保第園區,體驗尊貴的清朝臺灣第一官宅氣勢。\n\n3. **亞洲大學現代美術館**【15†source】: 在霧峰區附近,你還可以順遊亞洲大學的現代美術館,感受國際大師安藤忠雄氛圍。\n\n4. **摘星山莊**【16†source】: 繼而前往摘星山莊,一睹被譽為「臺灣十大民宅之首」的豪宅之美。\n\n5. **筱雲山莊**【21†source】: 筱雲山莊同樣為百年官宅建築,感受保存良好的古蹟風貌。\n\n6. **東勢客家文化園區**【24†source】: 最後到東勢客家文化園區,體驗客家文化和山城的美。\n\n請注意開放時間及休息日,以免前往時遇到關閉的情況。希望這個行程能為你的台中之旅增添樂趣!有其他特別需求或想要更多建議,請告訴我。" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "!curl -L \"https://ppt.cc/fAzoVx\" -o \"Travel_In_Taoyuan.json\"" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jBLm7pwfhS16", + "outputId": "8f9e6b1e-1cc5-4e4e-ee09-d96e456e2601" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " % Total % Received % Xferd Average Speed Time Time Time Current\n", + " Dload Upload Total Spent Left Speed\n", + " 0 0 0 0 0 0 0 0 --:--:-- 0:00:01 --:--:-- 0\n", + "100 119k 100 119k 0 0 85758 0 0:00:01 0:00:01 --:--:-- 85758\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "file = client.files.create(\n", + " file=open(\"/content/Travel_In_Taoyuan.json\", \"rb\"),\n", + " purpose='assistants'\n", + ")\n", + "\n", + "message = client.beta.threads.messages.create(\n", + " thread_id=thread.id,\n", + " role=\"user\",\n", + " content=\"可以規劃去桃園兩天一夜的行程嗎?\",\n", + " file_ids=[file.id]\n", + ")" + ], + "metadata": { + "id": "q4Y5yZlbf7LF" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "run = client.beta.threads.runs.create(\n", + " thread_id=thread_id,\n", + " assistant_id=assistant.id,\n", + ")\n", + "run = wait_on_run(run)\n", + "pprint(run.status)\n", + "output_text(thread_id, message)" + ], + "metadata": { + "id": "0M8zGPadkx4m", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 397 + }, + "outputId": "6bd861c1-942a-442c-ed60-22f03c841be5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "根据提供的资料,为您规划了一个桃园两天一夜的行程如下:\n\n### 第一天:\n- 早餐后开始您的旅程\n- **新北市立黄金博物馤**:首先参观黄金博物馆,了解这里的黄金文化历史。\n- **黄金神社**:参观黄金神社,感受其特有的宁静和历史韵味。\n- **无耳茶壶山登山步道**:接着前往无耳茶壶山,进行一个轻松的登山之旅。\n- **报时山观景台**:午间在报时山观景台欣赏美景,并在此用午餐。\n- 返回**黄金神社**:下午返回黄金神社,进一步探索。\n- **基隆廟口夜市+晚餐**:傍晚,前往基隆廟口夜市品尝当地的美食。\n- **基隆旅宿**:晚餐后,前往基隆旅宿休息。\n\n### 第二天:\n- 早餐后退房准备出发\n- 参观**大漢溪山豬湖生態親水園區**:从中壢后火车站出发,去大漢溪的山豬湖生態親水园区。\n- **李鄭芳古厝**:继续探访李鄭芳古厝,领略古厝的精美雕刻和建筑美学。\n- 特色午餐:在**香满园花园餐厅**或**台湾砖窯鸡**或**大溪山水庭园餐厅**享用特色餐。\n\n以上行程需要根据实际开放时间和交通安排进行微调,同时建议早点开始一天的行程以避免错过任何景点。使用北北基桃好玩卡可以享受相关景点的优惠【30†source】。祝您在桃园的两天时光玩得开心!" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "如果要台中與桃園各玩一天, 該如何規劃?\n", + "\n", + "我想要去台中進行一趟美食之旅" + ], + "metadata": { + "id": "PeANi4aY80E-" + } + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " message, run = input_and_run(question, thread_id, assistant_id)\n", + "\n", + " run = wait_on_run(run)\n", + " pprint(run.status)\n", + " output_text(thread_id, message)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 631 + }, + "id": "h4h6fizLFEU5", + "outputId": "7b51595a-2adf-4c86-be12-32457d8fcdd9" + }, + "execution_count": null, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:如果要台中與桃園各玩一天, 該如何規劃?\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "當然可以。根據您的需求,這裡是一個簡單的行程規劃,台中與桃園各玩一天:\n\n### 台中一日遊行程\n- **上午**\n - **彩虹眷村**:開始您的台中之旅,參觀充滿藝術和歷史的彩虹眷村【11†source】。\n - **霧峰林家宮保第園區**:接著遊覽霧峰林家宮保第園區,感受清朝臺灣第一官宅的氣勢【12†source】。\n - **午餐**:在附近享用道地台中美食。\n- **下午**\n - **亞洲大學現代美術館**:體驗國際大師安藤忠雄設計的亞洲大學現代美術館【13†source】。\n - **摘星山莊**:參觀被譽為「臺灣十大民宅之首」的摘星山莊【14†source】。\n- **晚上**\n - **台中公園(湖心亭)**:在台中公園輕鬆散步,享受美麗的湖心亭景色【18†source】。\n - **晚餐**:品嘗台中當地特色晚餐。\n\n### 桃園一日遊行程\n- **上午**\n - **大漢溪山豬湖生態親水園區**:前往大漢溪欣賞當地的自然景觀與生態【30†source】。\n - **李騰芳古厝**:探訪歷史悠久的李騰芳古厝,體驗百年建築的魅力【30†source】。\n - **午餐**:選擇在香滿園花園餐廳、台灣磚窯雞或大溪山水庭園餐廳用餐【30†source】。\n- **下午**\n - **桃園其他景點**:可根據您的興趣,選擇參觀其他桃園的景點,例如大溪老街、角板山、藝文特區等。\n - **晚餐**:在桃園品嚐道地的晚餐,享受桃園的夜生活。\n\n這只是一個基本的行程建議,您可以根據自己的喜好和時間進行適當調整。請記得考慮各個旅遊景點的開放時間和所需的交通時間。祝您旅途愉快!" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:我想要去台中進行一趟美食之旅\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "很抱歉,似乎在操作文檔時發生了一些錯誤,我無法從您提供的文件中獲得台中的美食資訊。我可以重新嘗試獲取有關台中美食的資料,或者我可以根據以往的知識給您一些建議。您希望我如何協助您?" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "new_thread = client.beta.threads.create()\n", + "new_thread_id = new_thread.id\n", + "\n", + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " message, run = input_and_run(question, new_thread_id,\n", + " assistant_id)\n", + " run = wait_on_run(run)\n", + " pprint(run.status)\n", + " output_text(new_thread_id, message)" + ], + "metadata": { + "id": "StsTmK64fyrl", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 781 + }, + "outputId": "cfdb2eac-5987-425d-8559-828eab90b065" + }, + "execution_count": null, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:可以規劃去桃園兩天一夜的行程嗎?\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "根據提供的資訊,我為您規劃了兩天一夜遊覽桃園的行程。\n\n### 第一天:\n1. **臺中火車站 (舊站)** - 這是全臺唯一三代同堂的車站,您可以欣賞到由日式木造小站、巴洛克式建築舊站,到未來設計感新站,這樣跨時空的美景【14†source】。\n\n2. **文化部文化資產園區** - 這裡是遺址保存最完整的臺灣酒廠,現已打造成藝文特區,古蹟和藝術交織在一起【14†source】。\n\n3. **臺中州廳** - 來這裡可觀賞百年前總統府御用建築師的作品,建築以法式風格為主,您可以體驗到細膩的古建築風味【14†source】。\n\n4. **臺中刑務所演武場** - 這是日據時期留下的珍貴武道場,充滿日式禪風,相當適合靜下心來參觀【14†source】。\n\n### 第二天:\n5. **臺中公園(湖心亭)** - 臺灣首座百年地標公園,您可以選擇划船或是散步在園內,體驗典型的台中風情【14†source】。\n\n6. **柳川水岸步道** - 最後您可以在這條生態工法開發的景觀河岸散步,享受一份悠閒【14†source】。\n\n請根據這些景點的開放時間安排旅行的行程,希望這個安排能讓您在桃園的兩天一夜之旅中擁有美好的體驗!如果您需要住宿或餐飲建議,請告知我,我可以繼續為您規劃。" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:我是要去桃園不是台中\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "completed\n" + ], + "text/html": [ + "
completed\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "對不起,我之前提供的資訊是有關臺中而非桃園的景點,我會重新進行搜索和規劃。\n\n經過查閱目前提供的旅遊文件,似乎沒有特定的提及桃園兩天一夜的詳細行程。為了制定一個合適的行程,我將使用已知的資訊以及一些常見的觀光推薦,來為您規劃桃園的旅遊。\n\n### 桃園兩天一夜行程建議:\n\n#### 第一天:\n- **搭車前往桃園** - 確保您選擇的交通方式能在早上抵達桃園,以便有更多的觀光時間。\n- **登入窯文化園區** - 您可以參觀這個結合了文化與藝術的園區,了解桃園當地的手工藝以及傳統窯燒技藝。\n- **中壢夜市** - 晚上可以前往中壢夜市,享受當地的特色小吃,體驗台灣夜市的熱鬧氛圍。\n- **入住飯店** - 選擇一家舒適的飯店進行休息,以備次日的活動。\n\n#### 第二天:\n- **大溪老街** - 逛逛大溪老街,品嘗當地美食,欣賞老街上的傳統建築。\n- **角板山** - 前往角板山賞花遊憩區,你可以在此處遠眺大漢溪的美景,或是體驗該地區的戶外活動。\n- **大園區海邊** - 如果時間允許,可以去大園區的海邊去欣賞海景,也許還能偶遇到一些水上活動。\n- **返回** - 下午或傍晚搭乘交通工具返回家,結束兩天一夜的旅行。\n\n如果需要更加具體的行程規劃,例如特定的景點訊息、餐廳推薦或是住宿選擇,請提供更多細節或需求,我會進一步為您做出安排。" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Function calling" + ], + "metadata": { + "id": "mcpVFaJvmwW-" + } + }, + { + "cell_type": "code", + "source": [ + "from googlesearch import search\n", + "def google_res(user_input):\n", + " response = ''\n", + " for i in search(user_input, advanced=True, num_results=5):\n", + " response += f'{i.title}\\n{i.description}\\n'\n", + " return response" + ], + "metadata": { + "id": "li_CGBUsLFhE" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "assistant = client.beta.assistants.create(\n", + " name=\"搜尋助理\",\n", + " instructions=\"取得 Google 搜尋結果\",\n", + " model=\"gpt-3.5-turbo-1106\",\n", + " tools=[\n", + " {\"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\",\n", + " \"description\": \"如果問題為你不知道的事情需要網路搜尋, \"\\\n", + " \"才會使用這個搜尋函式, 使用繁體中文作回覆\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_input\": {\"type\": \"string\"}\n", + " },\n", + " \"required\": [\"user_input\"]\n", + " }\n", + " }\n", + " }]\n", + ")\n", + "assistant_id = assistant.id\n", + "\n", + "thread = client.beta.threads.create()\n", + "thread_id = thread.id" + ], + "metadata": { + "id": "wa2WA4wOmrzG" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "message, run = input_and_run(\"2023年金鐘獎男主角是?\",\n", + " thread_id, assistant_id)\n", + "run = wait_on_run(run)\n", + "pprint(run)" + ], + "metadata": { + "id": "TKLaS4Mx1Lie", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 973 + }, + "outputId": "9bb99a2b-98d2-4266-9b37-890a32cdd323" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1;35mRun\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'run_3WS96K5xKyhE0UJ9oxVtFwoI'\u001b[0m,\n", + " \u001b[33massistant_id\u001b[0m=\u001b[32m'asst_IH1ib1TQLfhsCtwRtr1KRZM9'\u001b[0m,\n", + " \u001b[33mcancelled_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcompleted_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mcreated_at\u001b[0m=\u001b[1;36m1703584541\u001b[0m,\n", + " \u001b[33mexpires_at\u001b[0m=\u001b[1;36m1703585141\u001b[0m,\n", + " \u001b[33mfailed_at\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mfile_ids\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + " \u001b[33minstructions\u001b[0m=\u001b[32m'取得 Google 搜尋結果'\u001b[0m,\n", + " \u001b[33mlast_error\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + " \u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[33mmodel\u001b[0m=\u001b[32m'gpt-4-1106-preview'\u001b[0m,\n", + " \u001b[33mobject\u001b[0m=\u001b[32m'thread.run'\u001b[0m,\n", + " \u001b[33mrequired_action\u001b[0m=\u001b[1;35mRequiredAction\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33msubmit_tool_outputs\u001b[0m=\u001b[1;35mRequiredActionSubmitToolOutputs\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mtool_calls\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mRequiredActionFunctionToolCall\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'call_cX5hqA8MtQ8yVLueBXfycVrd'\u001b[0m,\n", + " \u001b[33mfunction\u001b[0m=\u001b[1;35mFunction\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"user_input\":\"2023 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n踴 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 寅 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n獎男主角\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'google_res'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'function'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'submit_tool_outputs'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mstarted_at\u001b[0m=\u001b[1;36m1703584542\u001b[0m,\n", + " \u001b[33mstatus\u001b[0m=\u001b[32m'requires_action'\u001b[0m,\n", + " \u001b[33mthread_id\u001b[0m=\u001b[32m'thread_yWmunltK9QKaGB8gZjX2ISOw'\u001b[0m,\n", + " \u001b[33mtools\u001b[0m=\u001b[1m[\u001b[0m\n", + " \u001b[1;35mToolAssistantToolsFunction\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mfunction\u001b[0m=\u001b[1;35mFunctionDefinition\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mname\u001b[0m=\u001b[32m'google_res'\u001b[0m,\n", + " \u001b[33mdescription\u001b[0m=\u001b[32m'如果問題為你不知道的事情需要網路搜尋, 才會使用這個搜尋函式, 使用繁體中文作回覆'\u001b[0m,\n", + " \u001b[33mparameters\u001b[0m=\u001b[1m{\u001b[0m\n", + " \u001b[32m'type'\u001b[0m: \u001b[32m'object'\u001b[0m,\n", + " \u001b[32m'properties'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'user_input'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'type'\u001b[0m: \u001b[32m'string'\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[32m'required'\u001b[0m: \u001b[1m[\u001b[0m\u001b[32m'user_input'\u001b[0m\u001b[1m]\u001b[0m\n", + " \u001b[1m}\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'function'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" + ], + "text/html": [ + "
Run(\n",
+              "    id='run_3WS96K5xKyhE0UJ9oxVtFwoI',\n",
+              "    assistant_id='asst_IH1ib1TQLfhsCtwRtr1KRZM9',\n",
+              "    cancelled_at=None,\n",
+              "    completed_at=None,\n",
+              "    created_at=1703584541,\n",
+              "    expires_at=1703585141,\n",
+              "    failed_at=None,\n",
+              "    file_ids=[],\n",
+              "    instructions='取得 Google 搜尋結果',\n",
+              "    last_error=None,\n",
+              "    metadata={},\n",
+              "    model='gpt-4-1106-preview',\n",
+              "    object='thread.run',\n",
+              "    required_action=RequiredAction(\n",
+              "        submit_tool_outputs=RequiredActionSubmitToolOutputs(\n",
+              "            tool_calls=[\n",
+              "                RequiredActionFunctionToolCall(\n",
+              "                    id='call_cX5hqA8MtQ8yVLueBXfycVrd',\n",
+              "                    function=Function(\n",
+              "                        arguments='{\"user_input\":\"2023 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n踴 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 寅 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n獎男主角\"}',\n",
+              "                        name='google_res'\n",
+              "                    ),\n",
+              "                    type='function'\n",
+              "                )\n",
+              "            ]\n",
+              "        ),\n",
+              "        type='submit_tool_outputs'\n",
+              "    ),\n",
+              "    started_at=1703584542,\n",
+              "    status='requires_action',\n",
+              "    thread_id='thread_yWmunltK9QKaGB8gZjX2ISOw',\n",
+              "    tools=[\n",
+              "        ToolAssistantToolsFunction(\n",
+              "            function=FunctionDefinition(\n",
+              "                name='google_res',\n",
+              "                description='如果問題為你不知道的事情需要網路搜尋, 才會使用這個搜尋函式, 使用繁體中文作回覆',\n",
+              "                parameters={\n",
+              "                    'type': 'object',\n",
+              "                    'properties': {'user_input': {'type': 'string'}},\n",
+              "                    'required': ['user_input']\n",
+              "                }\n",
+              "            ),\n",
+              "            type='function'\n",
+              "        )\n",
+              "    ]\n",
+              ")\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "tool_calls = run.required_action.submit_tool_outputs.tool_calls\n", + "pprint(tool_calls)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 360 + }, + "id": "48qsoyMmrpXi", + "outputId": "62e002ad-e0d2-4b25-819e-0b9bd7f813b1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1m[\u001b[0m\n", + " \u001b[1;35mRequiredActionFunctionToolCall\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33mid\u001b[0m=\u001b[32m'call_cX5hqA8MtQ8yVLueBXfycVrd'\u001b[0m,\n", + " \u001b[33mfunction\u001b[0m=\u001b[1;35mFunction\u001b[0m\u001b[1m(\u001b[0m\n", + " \u001b[33marguments\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"user_input\":\"2023 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n踴 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\u001b[0m\n", + "\u001b[32m\\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \u001b[0m\n", + "\u001b[32m\\\\n \\\\n \\\\n \\\\n 寅 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n獎男主角\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + " \u001b[33mname\u001b[0m=\u001b[32m'google_res'\u001b[0m\n", + " \u001b[1m)\u001b[0m,\n", + " \u001b[33mtype\u001b[0m=\u001b[32m'function'\u001b[0m\n", + " \u001b[1m)\u001b[0m\n", + "\u001b[1m]\u001b[0m\n" + ], + "text/html": [ + "
[\n",
+              "    RequiredActionFunctionToolCall(\n",
+              "        id='call_cX5hqA8MtQ8yVLueBXfycVrd',\n",
+              "        function=Function(\n",
+              "            arguments='{\"user_input\":\"2023 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n踴 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n\n",
+              "\\\\n \\\\n 譯 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \n",
+              "\\\\n \\\\n \\\\n \\\\n 寅 \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n \\\\n獎男主角\"}',\n",
+              "            name='google_res'\n",
+              "        ),\n",
+              "        type='function'\n",
+              "    )\n",
+              "]\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "# 函式識別碼、名稱和參數\n", + "tool_id = tool_calls[0].id\n", + "func_name = tool_calls[0].function.name\n", + "func_args = json.loads(tool_calls[0].function.arguments)\n", + "\n", + "print(f'識別碼:{tool_id}\\n函式名稱:{func_name}\\n參數:{func_args}')\n", + "\n", + "# 將參數代入函式中\n", + "output = google_res(**func_args)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zjTfjHodTJRa", + "outputId": "54a9cf39-4691-49e4-d039-bbcc2e9e87bb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "識別碼:call_cX5hqA8MtQ8yVLueBXfycVrd\n", + "函式名稱:google_res\n", + "參數:{'user_input': '2023 \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n踴 \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n 譯 \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n 譯 \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n 寅 \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n \\n獎男主角'}\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "run = client.beta.threads.runs.submit_tool_outputs(\n", + " thread_id=thread_id,\n", + " run_id=run.id,\n", + " tool_outputs=[\n", + " {\n", + " \"tool_call_id\": tool_id,\n", + " \"output\": output,\n", + " }\n", + " ]\n", + ")\n", + "run = wait_on_run(run)\n", + "output_text(thread_id, message)" + ], + "metadata": { + "id": "JYHrvgbMnBpR", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 46 + }, + "outputId": "f098f08d-6501-4c57-b0bc-6a8d383b4155" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "很抱歉,我無法找到具體的資料來確認誰是2023年金鐘獎的男主角。建議您可以查看官方公告或娛樂新聞以獲得最準確的信息。" + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "def tool_outputs(tool_calls):\n", + " outputs = []\n", + " for tool in tool_calls:\n", + " func_name = tool.function.name\n", + " func_args = json.loads(tool.function.arguments)\n", + " print(f'{func_name}({func_args})')\n", + " reponse = google_res(**func_args)\n", + " outputs.append({\n", + " 'tool_call_id': tool.id,\n", + " 'output': reponse\n", + " })\n", + " return outputs" + ], + "metadata": { + "id": "x79_uUwB2Hkg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "亞洲空汙最嚴重的地區是?那台灣呢?\n", + "\n", + "2023年台灣金馬獎影帝和金曲獎歌王分別是?" + ], + "metadata": { + "id": "Bk6CDHCE9bwt" + } + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " message, run = input_and_run(question, thread_id, assistant_id)\n", + "\n", + " run = wait_on_run(run)\n", + "\n", + " if run.status == 'requires_action':\n", + " pprint('搜尋中...')\n", + " tool_calls = run.required_action.submit_tool_outputs.tool_calls\n", + " outputs = tool_outputs(tool_calls)\n", + " run = client.beta.threads.runs.submit_tool_outputs(\n", + " thread_id=thread_id,\n", + " run_id=run.id,\n", + " tool_outputs=outputs\n", + " )\n", + " run = wait_on_run(run)\n", + " output_text(thread_id, message)\n", + " else:\n", + " output_text(thread_id, message)" + ], + "metadata": { + "id": "Ukz82hm3Aj0i", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 280 + }, + "outputId": "62499dff-05b0-4f9f-e57d-d3b6cfc08f20" + }, + "execution_count": null, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:亞洲空汙最嚴重的地區是?那台灣呢?\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "搜尋中\u001b[33m...\u001b[0m\n" + ], + "text/html": [ + "
搜尋中...\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "google_res({'user_input': '亞洲空汙最嚴重的地區是'})\n", + "google_res({'user_input': '台灣空汙情況'})\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "亞洲空汙最嚴重的地區包括印度、中國、巴基斯坦、伊拉克等地,其中印度包括了八個城市在亞洲空氣污染排名中佔據前十名。\n\n而台灣的空氣污染情況可以通過Air Quality Index (AQI)等數據來評估,根據最近的報告,台灣的PM2.5平均值為13.4 μg/m³,優於前一年的16.2 μg/m³,顯示了空氣品質的改善。" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:2023年台灣金馬獎影帝和金曲獎歌王分別是?\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "搜尋中\u001b[33m...\u001b[0m\n" + ], + "text/html": [ + "
搜尋中...\n",
+              "
\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "google_res({'user_input': '2023年台灣金馬獎影帝是'})\n", + "google_res({'user_input': '2023年台灣金曲獎歌王是'})\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/markdown": "2023年台灣金馬獎影帝是吳慷仁,而金曲獎歌王則是由A-Lin榮獲。" + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:\n" + ] + } + ] + } + ] +} \ No newline at end of file From 838310ec360a25d1bc7929f65ac86c1c0eb74115 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:16:44 +0630 Subject: [PATCH 08/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch09.ipynb | 2043 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2043 insertions(+) create mode 100644 GPT4Dev_ch09.ipynb diff --git a/GPT4Dev_ch09.ipynb b/GPT4Dev_ch09.ipynb new file mode 100644 index 000000000..838e784a6 --- /dev/null +++ b/GPT4Dev_ch09.ipynb @@ -0,0 +1,2043 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 9 AI 助理開發實戰" + ], + "metadata": { + "id": "A1PcWGkKkU-w" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 9-1 看截圖就能生出類似網頁的小助理" + ], + "metadata": { + "id": "QHz_hf8Bk3oE" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VLJy06lgrGd0", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "4dca50d7-0c66-4ee0-dd9d-ef8728a97558" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting openai\n", + " Downloading openai-1.7.0-py3-none-any.whl (224 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m224.7/224.7 kB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (13.7.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai) (1.7.0)\n", + "Collecting httpx<1,>=0.23.0 (from openai)\n", + " Downloading httpx-0.26.0-py3-none-any.whl (75 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.9/75.9 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.10.13)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.0)\n", + "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai) (4.66.1)\n", + "Collecting typing-extensions<5,>=4.7 (from openai)\n", + " Downloading typing_extensions-4.9.0-py3-none-any.whl (32 kB)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich) (2.16.1)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (3.6)\n", + "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (1.2.0)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n", + "Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)\n", + " Downloading httpcore-1.0.2-py3-none-any.whl (76 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.9/76.9 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->openai)\n", + " Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich) (0.1.2)\n", + "Installing collected packages: typing-extensions, h11, httpcore, httpx, openai\n", + " Attempting uninstall: typing-extensions\n", + " Found existing installation: typing_extensions 4.5.0\n", + " Uninstalling typing_extensions-4.5.0:\n", + " Successfully uninstalled typing_extensions-4.5.0\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "llmx 0.0.15a0 requires cohere, which is not installed.\n", + "llmx 0.0.15a0 requires tiktoken, which is not installed.\n", + "tensorflow-probability 0.22.0 requires typing-extensions<4.6.0, but you have typing-extensions 4.9.0 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed h11-0.14.0 httpcore-1.0.2 httpx-0.26.0 openai-1.7.0 typing-extensions-4.9.0\n" + ] + } + ], + "source": [ + "!pip install openai rich\n", + "from rich import print as pprint\n", + "from google.colab import userdata\n", + "from IPython.display import display, HTML\n", + "from openai import OpenAI\n", + "client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ] + }, + { + "cell_type": "code", + "source": [ + "!git clone https://github.com/FlagTech/Assistant-API-Function.git assistant_function\n", + "import assistant_function as af\n", + "af.set_client(client)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I0eEzOoZV8Qc", + "outputId": "a802ae6a-9166-4af8-fc15-81e41239e2bc" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Cloning into 'assistant_function'...\n", + "remote: Enumerating objects: 52, done.\u001b[K\n", + "remote: Counting objects: 100% (52/52), done.\u001b[K\n", + "remote: Compressing objects: 100% (35/35), done.\u001b[K\n", + "remote: Total 52 (delta 16), reused 0 (delta 0), pack-reused 0\u001b[K\n", + "Receiving objects: 100% (52/52), 14.04 KiB | 7.02 MiB/s, done.\n", + "Resolving deltas: 100% (16/16), done.\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 使用 GPT-4-Vision 建立網頁模板" + ], + "metadata": { + "id": "VHfyVVS20Hok" + } + }, + { + "cell_type": "code", + "source": [ + "html_prompt = \"\"\"\n", + "您是一位專業的 Tailwind 開發者。您會根據用戶提供的網頁截圖,使用 Tailwind、\n", + "HTML 和 JS 建構網頁。\n", + "\n", + "-確保網頁的外觀與截圖完全一致。\n", + "-要特別注意文字顏色、字體大小、字體家族、內邊距、外邊距、邊框等。精確匹配顏色和尺寸。\n", + "-生成網頁大小需符合一般筆電螢幕。\n", + "-使用截圖中的準確文字。\n", + "-不要在程式碼中添加註解,例如 \"\" 或\n", + "\"\" 來代替完整的程式碼。請寫出完整的程式碼。\n", + "-根據需要重複元素以匹配截圖。例如,如果有 15 個項目,程式碼應該有 15 個項目。\n", + "不要留下像 \"\" 這樣的註解,否則會出現問題。\n", + "不要背景圖,但其他項目(如天氣圖片等)請使用 Font Awesome 生成,\n", + "並在 alt 文本中包括詳細的圖片描述。\n", + "\n", + "在使用的套件方面,\n", + "Tailwind:\n", + "字體 Font Awesome:\n", + "\n", + "請僅返回包含在 標籤中的完整程式碼。\n", + "不要在開頭或結尾處包含 markdown \"\" 或 \"html\"。\n", + "\"\"\"" + ], + "metadata": { + "id": "Ab6Ftz6qpNUm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "def html_generation(url):\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-4-vision-preview\",\n", + " messages=[\n", + " {\"role\": \"system\",\"content\": html_prompt},\n", + " {\"role\": \"user\",\"content\": [\n", + " {\"type\": \"text\",\"text\": \"請生成一個外觀與這張圖片\"\n", + " \"完全一致的網頁程式碼\"},\n", + " {\"type\": \"image_url\",\"image_url\": {\"url\": url}}],\n", + " }],\n", + " max_tokens=3000\n", + " )\n", + " display(HTML(response.choices[0].message.content))\n", + " return response.choices[0].message.content" + ], + "metadata": { + "id": "NXw0Sg9l0-GW" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立成 Function_calling" + ], + "metadata": { + "id": "x9CWZkKWTsuD" + } + }, + { + "cell_type": "code", + "source": [ + "tools_table = [{\n", + " \"function\": html_generation, # 實際的函式\n", + " \"spec\": {\n", + " \"name\": \"html_generation\",\n", + " \"description\": \"使用圖片連結產生網頁程式碼,使用繁體中文\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"url\": {\"type\": \"string\"}\n", + " },\n", + " \"required\": [\"url\"]\n", + " }\n", + " }\n", + "},]" + ], + "metadata": { + "id": "O752kpq2sFgM" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "tools = [{\"type\": \"code_interpreter\"}]\n", + "for tool in tools_table:\n", + " tools.append(\n", + " {\"type\": \"function\", \"function\": tool['spec']}\n", + " )\n", + "assistant = client.beta.assistants.create(\n", + " name=\"網頁設計助理\",\n", + " instructions=\"你是一位網頁設計師, 能依照對話內容設計和修改出網頁, \"\n", + " \"請生成 HTML 檔\",\n", + " tools=tools,\n", + " model=\"gpt-4-1106-preview\",\n", + ")\n", + "assistant_id = assistant.id\n", + "# 新建討論串\n", + "thread = client.beta.threads.create()\n", + "thread_id = thread.id" + ], + "metadata": { + "id": "V6qUD6yrsIAN" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "response = af.chat_with_functions('https://ppt.cc/fIxsSx'\n", + " '請使用繁體中文, 最後請給我 HTML 檔',\n", + " tools_table,\n", + " thread_id, assistant_id)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 765 + }, + "id": "ccvbMIGdvlHz", + "outputId": "99a61413-e5a0-4bd5-ac29-5b8d7823b741" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-----------------------------------\n", + "|\n", + "處理中...\n", + "html_generation({'url': 'https://ppt.cc/fIxsSx'})\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "```html\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + "\n", + " \n", + "

Mon Feb 15 2021 00:05:27

\n", + "\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "

晴時多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "\n", + " \n", + "
\n", + "
\n", + " \n", + "

Mon

\n", + "

晴時多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Tue

\n", + "

多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Wed

\n", + "

多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "
\n", + " \n", + "

Thu

\n", + "

晴時多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "
\n", + " \n", + "

Fri

\n", + "

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Sat

\n", + "

多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n", + "```\n", + "\n", + "Please note that in creating web pages, the appearance on different devices or browsers may vary slightly due to default styles and rendering. The above code is a starting point that will create a web page similar to the screenshot you provided, using Tailwind for styles and Font Awesome for weather icons." + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-----------------------------------\n", + "|\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "for data in response:\n", + " print(data.content[0].text.value)\n", + "af.show_html(response)" + ], + "metadata": { + "id": "VMYMHzjbKI6j", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 637 + }, + "outputId": "64b65e9a-b0c1-4a29-f422-52bf21955396" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "網頁設計已完成,並且已經存儲為 HTML 檔案。你可以通過以下連結下載該檔案:\n", + "\n", + "[下載 weather_page.html](sandbox:/mnt/data/weather_page.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + "
\n", + " \n", + "
\n", + "\n", + " \n", + "

Mon Feb 15 2021 00:05:27

\n", + "\n", + " \n", + "
\n", + "
\n", + " \n", + "
\n", + "

晴時多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "\n", + " \n", + "
\n", + "
\n", + " \n", + "

Mon

\n", + "

晴時多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Tue

\n", + "

多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Wed

\n", + "

多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "
\n", + " \n", + "

Thu

\n", + "

晴時多雲

\n", + "

溫度: 16 °C

\n", + "
\n", + "
\n", + " \n", + "

Fri

\n", + "

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + " \n", + "

Sat

\n", + "

多雲

\n", + "

溫度: 17 °C

\n", + "
\n", + "
\n", + "
\n", + "\n", + "\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 9-2 自動串接 API - 取得真正資料" + ], + "metadata": { + "id": "NnRvXttwjEga" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 串接 API 程式碼\n" + ], + "metadata": { + "id": "GMnSV7SN1OGm" + } + }, + { + "cell_type": "markdown", + "source": [ + "這裡的網址與書上不同是由於 replit 已經在 2024/01/01 取消免費帳戶的 always on 功能, 所以無法作為可隨時自動啟動的 API 服務, 因此改用 Google 的 Apps Script 服務建立同樣功能的專案, 以下網址也更改成 Apps Script 專案的 API, 並且改用 GET 方法取得資料。\n", + "\n", + "想要了解更多可以到 https://hackmd.io/_rMU5SRzSFOX4Sjk0uMnDQ?both 查看" + ], + "metadata": { + "id": "_fUgAZkZA7dk" + } + }, + { + "cell_type": "code", + "source": [ + "import requests\n", + "city = '臺北市'\n", + "response = requests.get(f'https://script.google.com/macros/s/AKfycbzmeU-mQXx7qjQSDjFCslQeT1OSU6HDRnRg9o3NmtZvD02DDhcO9RcK-K2oOn0ZigX5/exec?city={city}')\n", + "pprint(response.json())" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 194 + }, + "id": "5xwIfT2LVTlX", + "outputId": "b1f01053-c030-4fd1-c51e-1a700e3a5a9a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1m{\u001b[0m\n", + " \u001b[32m'臺北市'\u001b[0m: \u001b[1m[\u001b[0m\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-11'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'晴時多雲'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'17'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'11'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-12'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'晴時多雲'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'21'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'12'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-13'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'晴時多雲'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'20'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'15'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-14'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'多雲時晴'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'23'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'16'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-15'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'多雲短暫雨'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'18'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'16'\u001b[0m\u001b[1m}\u001b[0m,\n", + " \u001b[1m{\u001b[0m\u001b[32m'日期'\u001b[0m: \u001b[32m'2024-01-16'\u001b[0m, \u001b[32m'天氣狀態'\u001b[0m: \u001b[32m'多雲時陰短暫雨'\u001b[0m, \u001b[32m'最高溫'\u001b[0m: \u001b[32m'19'\u001b[0m, \u001b[32m'最低溫'\u001b[0m: \u001b[32m'16'\u001b[0m\u001b[1m}\u001b[0m\n", + " \u001b[1m]\u001b[0m\n", + "\u001b[1m}\u001b[0m\n" + ], + "text/html": [ + "
{\n",
+              "    '臺北市': [\n",
+              "        {'日期': '2024-01-11', '天氣狀態': '晴時多雲', '最高溫': '17', '最低溫': '11'},\n",
+              "        {'日期': '2024-01-12', '天氣狀態': '晴時多雲', '最高溫': '21', '最低溫': '12'},\n",
+              "        {'日期': '2024-01-13', '天氣狀態': '晴時多雲', '最高溫': '20', '最低溫': '15'},\n",
+              "        {'日期': '2024-01-14', '天氣狀態': '多雲時晴', '最高溫': '23', '最低溫': '16'},\n",
+              "        {'日期': '2024-01-15', '天氣狀態': '多雲短暫雨', '最高溫': '18', '最低溫': '16'},\n",
+              "        {'日期': '2024-01-16', '天氣狀態': '多雲時陰短暫雨', '最高溫': '19', '最低溫': '16'}\n",
+              "    ]\n",
+              "}\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 製作天氣預報網頁" + ], + "metadata": { + "id": "OpROO8Jm1XVP" + } + }, + { + "cell_type": "markdown", + "source": [ + "**第一步 輸入圖片連結:**
\n", + "https://ppt.cc/fIxsSx 請使用繁體中文, 最後請給我 HTML 檔\n", + "\n", + "**第二步 製作下拉表單:**
\n", + "請增加一個下拉式表單並填入以下選項['宜蘭縣', '花蓮縣', '臺東縣', '澎湖縣', '金門縣','連江縣', '臺北市', '新北市', '桃園市', '臺中市', '臺南市','高雄市', '基隆市', '新竹縣', '新竹市', '苗栗縣', '彰化縣','南投縣', '雲林縣', '嘉義縣', '嘉義市', '屏東縣'],請勿使用``\n", + "\n", + "**第三步 串接 API 程式:**
\n", + "使用繁體中文, 並將以下 API 程式加入到 HTML 檔案並修改 HTML 程式碼, 當選取下拉式表單時請將值代入到 city 中, import requests response = requests.get(f'https://script.google.com/macros/s/AKfycbzmeU-mQXx7qjQSDjFCslQeT1OSU6HDRnRg9o3NmtZvD02DDhcO9RcK-K2oOn0ZigX5/exec?city={city}') # 返回資料用 response.json()\n", + "\n", + "參考資料範例:{ '嘉義市': [ {'天氣狀態': '多雲', '日期': '2023-12-19', '最低溫': '17', '最高溫': '27'}, {'天氣狀態': '多雲', '日期': '2023-12-20', '最低溫': '15', '最高溫': '24'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-21', '最低溫': '14', '最高溫': '18'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-22', '最低溫': '10', '最高溫': '18'}, {'天氣狀態': '陰時多雲', '日期': '2023-12-23', '最低溫': '11', '最高溫': '21'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-24', '最低溫': '13', '最高溫': '21'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-25', '最低溫': '13', '最高溫': '22'} ] }\n", + "\n", + "最後可以動態顯示出 API 返回的資料(包含圖示), 請勿使用//省略程式碼, 請提供完整程式碼, 最後請給我 HTML 檔\n" + ], + "metadata": { + "id": "hJ-rrwiRS1Vo" + } + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " response = af.chat_with_functions(question, tools_table,\n", + " thread_id, assistant_id)\n", + " for data in response:\n", + " print(data.content[0].text.value)\n", + "\n", + " af.show_html(response)" + ], + "metadata": { + "id": "7_zeZWISRhsU", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "resources": { + "http://localhost:8080/your-clouds-background-image.jpg": { + "data": "CjwhRE9DVFlQRSBodG1sPgo8aHRtbCBsYW5nPWVuPgogIDxtZXRhIGNoYXJzZXQ9dXRmLTg+CiAgPG1ldGEgbmFtZT12aWV3cG9ydCBjb250ZW50PSJpbml0aWFsLXNjYWxlPTEsIG1pbmltdW0tc2NhbGU9MSwgd2lkdGg9ZGV2aWNlLXdpZHRoIj4KICA8dGl0bGU+RXJyb3IgNDA0IChOb3QgRm91bmQpISExPC90aXRsZT4KICA8c3R5bGU+CiAgICAqe21hcmdpbjowO3BhZGRpbmc6MH1odG1sLGNvZGV7Zm9udDoxNXB4LzIycHggYXJpYWwsc2Fucy1zZXJpZn1odG1se2JhY2tncm91bmQ6I2ZmZjtjb2xvcjojMjIyO3BhZGRpbmc6MTVweH1ib2R5e21hcmdpbjo3JSBhdXRvIDA7bWF4LXdpZHRoOjM5MHB4O21pbi1oZWlnaHQ6MTgwcHg7cGFkZGluZzozMHB4IDAgMTVweH0qID4gYm9keXtiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9lcnJvcnMvcm9ib3QucG5nKSAxMDAlIDVweCBuby1yZXBlYXQ7cGFkZGluZy1yaWdodDoyMDVweH1we21hcmdpbjoxMXB4IDAgMjJweDtvdmVyZmxvdzpoaWRkZW59aW5ze2NvbG9yOiM3Nzc7dGV4dC1kZWNvcmF0aW9uOm5vbmV9YSBpbWd7Ym9yZGVyOjB9QG1lZGlhIHNjcmVlbiBhbmQgKG1heC13aWR0aDo3NzJweCl7Ym9keXtiYWNrZ3JvdW5kOm5vbmU7bWFyZ2luLXRvcDowO21heC13aWR0aDpub25lO3BhZGRpbmctcmlnaHQ6MH19I2xvZ297YmFja2dyb3VuZDp1cmwoLy93d3cuZ29vZ2xlLmNvbS9pbWFnZXMvbG9nb3MvZXJyb3JwYWdlL2Vycm9yX2xvZ28tMTUweDU0LnBuZykgbm8tcmVwZWF0O21hcmdpbi1sZWZ0Oi01cHh9QG1lZGlhIG9ubHkgc2NyZWVuIGFuZCAobWluLXJlc29sdXRpb246MTkyZHBpKXsjbG9nb3tiYWNrZ3JvdW5kOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9sb2dvcy9lcnJvcnBhZ2UvZXJyb3JfbG9nby0xNTB4NTQtMngucG5nKSBuby1yZXBlYXQgMCUgMCUvMTAwJSAxMDAlOy1tb3otYm9yZGVyLWltYWdlOnVybCgvL3d3dy5nb29nbGUuY29tL2ltYWdlcy9sb2dvcy9lcnJvcnBhZ2UvZXJyb3JfbG9nby0xNTB4NTQtMngucG5nKSAwfX1AbWVkaWEgb25seSBzY3JlZW4gYW5kICgtd2Via2l0LW1pbi1kZXZpY2UtcGl4ZWwtcmF0aW86Mil7I2xvZ297YmFja2dyb3VuZDp1cmwoLy93d3cuZ29vZ2xlLmNvbS9pbWFnZXMvbG9nb3MvZXJyb3JwYWdlL2Vycm9yX2xvZ28tMTUweDU0LTJ4LnBuZykgbm8tcmVwZWF0Oy13ZWJraXQtYmFja2dyb3VuZC1zaXplOjEwMCUgMTAwJX19I2xvZ297ZGlzcGxheTppbmxpbmUtYmxvY2s7aGVpZ2h0OjU0cHg7d2lkdGg6MTUwcHh9CiAgPC9zdHlsZT4KICA8YSBocmVmPS8vd3d3Lmdvb2dsZS5jb20vPjxzcGFuIGlkPWxvZ28gYXJpYS1sYWJlbD1Hb29nbGU+PC9zcGFuPjwvYT4KICA8cD48Yj40MDQuPC9iPiA8aW5zPlRoYXTigJlzIGFuIGVycm9yLjwvaW5zPgogIDxwPiAgPGlucz5UaGF04oCZcyBhbGwgd2Uga25vdy48L2lucz4K", + "ok": false, + "headers": [ + [ + "content-length", + "1449" + ], + [ + "content-type", + "text/html; charset=utf-8" + ] + ], + "status": 404, + "status_text": "" + } + } + }, + "outputId": "6b1556cc-2bc6-4915-b373-848b0544183a" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "請輸入要求:https://ppt.cc/fIxsSx 請使用繁體中文, 最後請給我 HTML 檔\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "html_generation({'url': 'https://ppt.cc/fIxsSx'})\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "```html\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + " \n", + "
\n", + "
Mon Feb 15 2021 00:05:27
\n", + " \n", + "
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
Mon
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Tue
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Wed
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Thu
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Fri
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "\n", + "
\n", + "
Sat
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + "\n", + "\n", + "```\n", + "\n", + "請注意,背景圖片的URL需要替換為實際使用的雲朵背景圖片的URL。而且,上面的代碼假設雲朵背景圖片位於`your-clouds-background-image.jpg` 指向的位置。請確保將其替換為實際的路徑。在此代碼塊中,字體大小、顏色和圖標均已根據截圖對應的樣式進行調整,以符合Tailwind CSS的標準。" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "-----------------------------------\n", + "|\n", + "網頁已經設計完畢,並且已將HTML檔案保存。你可以點擊以下鏈接下載HTML檔案:\n", + "\n", + "[下載天氣網頁HTML檔案](sandbox:/mnt/data/weather_page.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + " \n", + "
\n", + "
Mon Feb 15 2021 00:05:27
\n", + " \n", + "
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
Mon
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Tue
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Wed
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Thu
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Fri
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "\n", + "
\n", + "
Sat
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + "\n", + "\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "請輸入要求:請增加一個下拉式表單並填入以下選項['宜蘭縣', '花蓮縣', '臺東縣', '澎湖縣', '金門縣','連江縣', '臺北市', '新北市', '桃園市', '臺中市', '臺南市','高雄市', '基隆市', '新竹縣', '新竹市', '苗栗縣', '彰化縣','南投縣', '雲林縣', '嘉義縣', '嘉義市', '屏東縣'],請勿使用\n", + "-----------------------------------\n", + "|\n", + "我已經增加了一個包含指定選項的下拉式表單到網頁中,並且已將更新後的HTML檔案儲存。您可以通過下面的鏈接下載新的HTML檔案:\n", + "\n", + "[下載包含下拉表單的天氣網頁HTML檔案](sandbox:/mnt/data/weather_page_with_dropdown.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + " \n", + "
\n", + "
Mon Feb 15 2021 00:05:27
\n", + " \n", + "
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
Mon
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Tue
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Wed
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Thu
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Fri
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "\n", + "
\n", + "
Sat
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + "\n", + "\n" + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "請輸入要求:使用繁體中文, 並將以下 API 程式加入到 HTML 檔案並修改 HTML 程式碼, 當選取下拉式表單時請將值代入到 city 中, import requests response = requests.get(f'https://script.google.com/macros/s/AKfycbzmeU-mQXx7qjQSDjFCslQeT1OSU6HDRnRg9o3NmtZvD02DDhcO9RcK-K2oOn0ZigX5/exec?city={city}') # 返回資料用 response.json() 參考資料範例:{ '嘉義市': [ {'天氣狀態': '多雲', '日期': '2023-12-19', '最低溫': '17', '最高溫': '27'}, {'天氣狀態': '多雲', '日期': '2023-12-20', '最低溫': '15', '最高溫': '24'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-21', '最低溫': '14', '最高溫': '18'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-22', '最低溫': '10', '最高溫': '18'}, {'天氣狀態': '陰時多雲', '日期': '2023-12-23', '最低溫': '11', '最高溫': '21'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-24', '最低溫': '13', '最高溫': '21'}, {'天氣狀態': '晴時多雲', '日期': '2023-12-25', '最低溫': '13', '最高溫': '22'} ] } 最後可以動態顯示出 API 返回的資料(包含圖示), 請勿使用//省略程式碼, 請提供完整程式碼, 最後請給我 HTML 檔\n", + "-----------------------------------\n", + "|\n", + "我已經將API程序加入到HTML文件中,並且修改了HTML程式碼,以便當選取下拉式表單時該值能夠傳入到 `city` 變量中。同時程式碼也支援動態顯示API返回的資料(包括圖示)。現在您可以通過以下鏈接下載包含API呼叫的HTML文件:\n", + "\n", + "[下載包含API呼叫的天氣網頁HTML檔案](sandbox:/mnt/data/weather_page_with_api.html)\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n", + "
\n", + " \n", + " \n", + "
\n", + "
Mon Feb 15 2021 00:05:27
\n", + " \n", + "
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
\n", + "
Mon
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Tue
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + " \n", + "
\n", + "
Wed
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Thu
\n", + " \n", + "
溫度: 16 °C
\n", + "
\n", + " \n", + "
\n", + "
Fri
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "\n", + "
\n", + "
Sat
\n", + " \n", + "
溫度: 17 °C
\n", + "
\n", + "
\n", + "
\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + "\n" + ] + }, + "metadata": {} + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 9-3 SQL 資料庫查詢助理" + ], + "metadata": { + "id": "4O9YrIt5k91M" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 下載資料" + ], + "metadata": { + "id": "nD2vEA_j1rpt" + } + }, + { + "cell_type": "code", + "source": [ + "import yfinance as yf\n", + "stock = [\"2330.TW\",\"2317.TW\"]\n", + "df = yf.download(stock,period='5y')\n", + "df.tail()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 287 + }, + "id": "LPzn9sTY_yrt", + "outputId": "5b980ff7-3816-4da7-9727-d8039596c780" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\r[ 0%% ]\r[*********************100%%**********************] 2 of 2 completed\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " Adj Close Close High Low \\\n", + " 2317.TW 2330.TW 2317.TW 2330.TW 2317.TW 2330.TW 2317.TW 2330.TW \n", + "Date \n", + "2023-12-26 104.0 586.0 104.0 586.0 104.0 586.0 103.5 582.0 \n", + "2023-12-27 103.5 592.0 103.5 592.0 104.0 592.0 103.0 586.0 \n", + "2023-12-28 104.0 593.0 104.0 593.0 104.0 593.0 103.0 589.0 \n", + "2023-12-29 104.5 593.0 104.5 593.0 105.0 593.0 103.5 589.0 \n", + "2024-01-02 105.0 593.0 105.0 593.0 105.0 593.0 104.0 589.0 \n", + "\n", + " Open Volume \n", + " 2317.TW 2330.TW 2317.TW 2330.TW \n", + "Date \n", + "2023-12-26 104.0 583.0 9920950 16094308 \n", + "2023-12-27 104.0 587.0 18875621 33401336 \n", + "2023-12-28 103.5 592.0 23057092 25514849 \n", + "2023-12-29 104.0 589.0 25969421 18416318 \n", + "2024-01-02 104.5 590.0 19865654 26409497 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Adj CloseCloseHighLowOpenVolume
2317.TW2330.TW2317.TW2330.TW2317.TW2330.TW2317.TW2330.TW2317.TW2330.TW2317.TW2330.TW
Date
2023-12-26104.0586.0104.0586.0104.0586.0103.5582.0104.0583.0992095016094308
2023-12-27103.5592.0103.5592.0104.0592.0103.0586.0104.0587.01887562133401336
2023-12-28104.0593.0104.0593.0104.0593.0103.0589.0103.5592.02305709225514849
2023-12-29104.5593.0104.5593.0105.0593.0103.5589.0104.0589.02596942118416318
2024-01-02105.0593.0105.0593.0105.0593.0104.0589.0104.5590.01986565426409497
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 15 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立 SQL 資料庫" + ], + "metadata": { + "id": "NWGb376pMNRx" + } + }, + { + "cell_type": "code", + "source": [ + "import sqlite3\n", + "conn = sqlite3.connect(\"stock.db\")\n", + "cursor = conn.cursor()" + ], + "metadata": { + "id": "qOd92z2Pk8_-" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "stock_name_list=['db_2330', 'db_2317']\n", + "for name in stock_name_list:\n", + " cursor.execute(f\"\"\"Create table if not exists {name} (\n", + " Date TEXT PRIMARY KEY,\n", + " Open REAL,\n", + " High REAL,\n", + " Low REAL,\n", + " Close REAL,\n", + " Adj_Close REAL,\n", + " Volume INTEGER\n", + " ); \"\"\")" + ], + "metadata": { + "id": "KjeHjkMq-yFz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "for i, name in zip(stock, stock_name_list):\n", + " stock_df = df.xs(i, level=1, axis=1)\n", + " stock_df.to_sql(name, con=conn,\n", + " if_exists='replace',\n", + " index=True)" + ], + "metadata": { + "id": "Rfp_jXelBAsn" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "table_data = cursor.execute(\"Select * from db_2330 limit 5\")\n", + "for data in table_data:\n", + " print(data)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8z9xMOhtBLcF", + "outputId": "4c57c223-5325-4b43-fac9-76f4bf19eb40" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "('2019-01-02 00:00:00', 191.67147827148438, 219.5, 226.5, 219.0, 226.5, 32900482)\n", + "('2019-01-03 00:00:00', 188.17857360839844, 215.5, 218.0, 214.0, 214.0, 34087620)\n", + "('2019-01-04 00:00:00', 181.62945556640625, 208.0, 211.5, 206.5, 211.5, 65943521)\n", + "('2019-01-07 00:00:00', 185.99554443359375, 213.0, 214.0, 211.0, 212.0, 35442176)\n", + "('2019-01-08 00:00:00', 184.2490997314453, 211.0, 212.5, 210.0, 212.0, 22694481)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "table_structure = cursor.execute(\n", + " \"SELECT sql FROM sqlite_master WHERE type='table';\")\n", + "for statement in table_structure:\n", + " print(statement[0])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Hxq2-eOMEgJt", + "outputId": "83bdfaf6-e4c9-490a-9698-37dd3b0e13a3" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CREATE TABLE \"db_2330\" (\n", + "\"Date\" TIMESTAMP,\n", + " \"Adj Close\" REAL,\n", + " \"Close\" REAL,\n", + " \"High\" REAL,\n", + " \"Low\" REAL,\n", + " \"Open\" REAL,\n", + " \"Volume\" INTEGER\n", + ")\n", + "CREATE TABLE \"db_2317\" (\n", + "\"Date\" TIMESTAMP,\n", + " \"Adj Close\" REAL,\n", + " \"Close\" REAL,\n", + " \"High\" REAL,\n", + " \"Low\" REAL,\n", + " \"Open\" REAL,\n", + " \"Volume\" INTEGER\n", + ")\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立相關函式" + ], + "metadata": { + "id": "rlPodX_tMfi3" + } + }, + { + "cell_type": "code", + "source": [ + "import csv\n", + "from bs4 import BeautifulSoup\n", + "import pandas as pd\n", + "import os\n", + "\n", + "def find_stock_info(search_term):\n", + " with open('stock.csv', mode='r', encoding='utf-8') as file:\n", + " csv_reader = csv.DictReader(file)\n", + " for row in csv_reader:\n", + " if row['股號'] == search_term or row['股名'] == search_term:\n", + " return row\n", + " return '無結果'\n", + "\n", + "def search_stock(stock):\n", + " if os.path.exists('stock.csv'):\n", + " row = find_stock_info(stock)\n", + " return row\n", + " else:\n", + " data=[]\n", + " response=requests.get(\n", + " 'https://isin.twse.com.tw/isin/C_public.jsp?strMode=2')\n", + " url_data=BeautifulSoup(response.text, 'html.parser')\n", + " stock_company=url_data.find_all('tr')\n", + " for i in stock_company[2:]:\n", + " j=i.find_all('td')\n", + " l=j[0].text.split('\\u3000')\n", + " if len(l[0].strip()) == 4:\n", + " stock_id,stock_name = l\n", + " data.append([stock_id.strip(),stock_name])\n", + " else:\n", + " break\n", + " df = pd.DataFrame(data, columns=['股號','股名'])\n", + " # 建立上市股票公司 CSV 檔\n", + " df.to_csv('stock.csv',index=False)\n", + " row = find_stock_info(stock)\n", + " return row\n", + "\n", + "def get_db_schema():\n", + " conn = sqlite3.connect('stock.db')\n", + " cursor = conn.cursor()\n", + " cursor.execute(\n", + " \"SELECT sql FROM sqlite_master WHERE type='table';\")\n", + " create_statements = cursor.fetchall()\n", + " conn.close()\n", + " return '\\n\\n'.join(\n", + " [statement[0] for statement in create_statements])\n", + "\n", + "def run_sql_query(query):\n", + " conn = sqlite3.connect('stock.db')\n", + " cursor = conn.cursor()\n", + " cursor.execute(query)\n", + " results = cursor.fetchall()\n", + " conn.close()\n", + " return '\\n'.join([str(result) for result in results])" + ], + "metadata": { + "id": "htv5lFouc4xz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### 建立 SQL 助理" + ], + "metadata": { + "id": "CfjikBVhMZZu" + } + }, + { + "cell_type": "code", + "source": [ + "tools_table = [{\n", + " 'function': get_db_schema,\n", + " 'spec': {\n", + " \"name\": \"get_db_schema\",\n", + " \"description\": \"取得 sqlite 資料庫的架構\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {}\n", + " }\n", + " }\n", + "},{\n", + " \"function\": run_sql_query,\n", + " \"spec\": {\n", + " \"name\": \"run_sql_query\",\n", + " \"description\": \"在 sqlite 資料庫上執行 SQL 查詢\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"query\": {\"type\": \"string\",\n", + " \"description\": \"SQL 語句\"}\n", + " },\n", + " \"required\": [\"query\"]\n", + " }\n", + " }\n", + "},{\n", + " \"function\": search_stock,\n", + " \"spec\": {\n", + " \"name\": \"search_stock\",\n", + " \"description\": \"如果不知道股票名稱或股票代碼可以使用這個工具\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"stock\": {\"type\": \"string\",\n", + " \"description\": \"為股票名稱或股票代碼\"}\n", + " },\n", + " \"required\": [\"stock\"]\n", + " }\n", + " }\n", + "},]" + ], + "metadata": { + "id": "O51kmL8pdAm0" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "tools = [{\"type\": \"code_interpreter\"}]\n", + "for tool in tools_table:\n", + " tools.append(\n", + " {\"type\": \"function\", \"function\": tool['spec']}\n", + " )\n", + "assistant = client.beta.assistants.create(\n", + " name=\"SQL 助理\",\n", + " instructions=\"您是 SQL 專家, 用戶向您詢問有關 sqlite 資料庫的問題。\"\n", + " \"首先請取得資料庫的架構,表格名稱都是使用 db_ + 股票代碼, \"\n", + " \"然後才產生 SQL 查詢來回答問題。\",\n", + " model=\"gpt-4-1106-preview\",\n", + " tools=tools\n", + ")\n", + "assistant_id = assistant.id\n", + "thread = client.beta.threads.create()\n", + "thread_id = thread.id" + ], + "metadata": { + "id": "lma0CCGVHmCh" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "台積電近一周的股價為何?\n", + "\n", + "找出台積電和鴻海三天內的股價並計算漲幅" + ], + "metadata": { + "id": "6w3PyT57z4kQ" + } + }, + { + "cell_type": "code", + "source": [ + "while True:\n", + " question = input('請輸入要求:')\n", + " if not question.strip():\n", + " break\n", + " response = af.chat_with_functions(question, tools_table,\n", + " thread_id, assistant_id)\n", + " for data in response:\n", + " print(data.content[0].text.value)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Z2k4nchWJorq", + "outputId": "e8b64209-8ad4-4a3e-90bf-25a297b4b33f" + }, + "execution_count": null, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "請輸入要求:台積電近一周的股價為何?\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "search_stock({'stock': '台積電'})\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "get_db_schema({})\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "run_sql_query({'query': \"SELECT Date, Close FROM db_2330 WHERE Date > datetime('now', '-7 days')\"})\n", + "-----------------------------------\n", + "|\n", + "台積電近一周的股價資訊如下:\n", + "\n", + "- 2023-12-27: 收盤價為 592.0\n", + "- 2023-12-28: 收盤價為 593.0\n", + "- 2023-12-29: 收盤價為 593.0\n", + "- 2024-01-02: 收盤價為 593.0\n", + "\n", + "請注意,正確的日期應該要是最近七天內的資訊,但這些資料看起來是未來的日期。請確認您的系統日期設定是否正確。如果需要最新的股市資訊,可能需要從股市的即時資料來源獲取。\n", + "請輸入要求:找出台積電和鴻海三天內的股價並計算漲幅\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "search_stock({'stock': '鴻海'})\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "run_sql_query({'query': \"SELECT Date, Close FROM db_2330 WHERE Date > datetime('now', '-3 days')\"})\n", + "run_sql_query({'query': \"SELECT Date, Close FROM db_2317 WHERE Date > datetime('now', '-3 days')\"})\n", + "-----------------------------------\n", + "|\n", + "台積電和鴻海過去三天的股價資訊如下:\n", + "\n", + "- 台積電 (2330)\n", + " - 2024-01-02: 收盤價為 593.0\n", + "- 鴻海 (2317)\n", + " - 2024-01-02: 收盤價為 105.0\n", + "\n", + "然而,由於返回的資料僅包含一個交易日的股價,我無法計算漲幅。為了計算漲幅,我需要至少兩個不同日期的收盤價格。這也可能表明這個時間範圍內可能只有一個交易日。請提供更多的日期範圍或確認股市交易日以進行進一步分析。\n", + "請輸入要求:找出台積電和鴻海一周內的股價並計算漲幅\n", + "-----------------------------------\n", + "|\n", + "處理中...\n", + "run_sql_query({'query': \"SELECT Date, Close FROM db_2330 WHERE Date > datetime('now', '-7 days') ORDER BY Date\"})\n", + "run_sql_query({'query': \"SELECT Date, Close FROM db_2317 WHERE Date > datetime('now', '-7 days') ORDER BY Date\"})\n", + "-----------------------------------\n", + "|\n", + "過去一周內,台積電和鴻海的股價資訊及漲幅如下:\n", + "\n", + "- 台積電 (2330):\n", + " - 2023-12-27: 收盤價 592.0\n", + " - 2023-12-28: 收盤價 593.0\n", + " - 2023-12-29: 收盤價 593.0\n", + " - 2024-01-02: 收盤價 593.0\n", + " - 漲幅計算: ((593.0 - 592.0) / 592.0) * 100 = 0.169% (12月27日至1月2日的漲幅)\n", + "\n", + "- 鴻海 (2317):\n", + " - 2023-12-27: 收盤價 103.5\n", + " - 2023-12-28: 收盤價 104.0\n", + " - 2023-12-29: 收盤價 104.5\n", + " - 2024-01-02: 收盤價 105.0\n", + " - 漲幅計算: ((105.0 - 103.5) / 103.5) * 100 = 1.449% (12月27日至1月2日的漲幅)\n", + "\n", + "請注意,正確的日期應該要是最近七天內的資訊,但這些資料看起來包含了未來的日期。這可能是因為系統日期設定不正確。如果需要最新且準確的股市資訊,應當從股市的即時資料來源獲取。\n", + "請輸入要求:\n" + ] + } + ] + } + ] +} \ No newline at end of file From 5ad91a8477811fe5c09ca269495b332b92a99adb Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:17:49 +0630 Subject: [PATCH 09/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...347\232\204\345\211\257\346\234\254.ipynb" | 369 ++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 "\343\200\214GPT4Dev_ch14\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" diff --git "a/\343\200\214GPT4Dev_ch14\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" "b/\343\200\214GPT4Dev_ch14\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" new file mode 100644 index 000000000..4481224c6 --- /dev/null +++ "b/\343\200\214GPT4Dev_ch14\343\200\215\347\232\204\345\211\257\346\234\254.ipynb" @@ -0,0 +1,369 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 14 GPTs Action 開發" + ], + "metadata": { + "id": "_iXaZWIv8-ck" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 14-2 使用TDX平台查詢台鐵資訊" + ], + "metadata": { + "id": "5_wzaoZd8-Do" + } + }, + { + "cell_type": "markdown", + "source": [ + "### 查詢所有車站基本資料" + ], + "metadata": { + "id": "lyad3p_ieDNp" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6cHx3KFGYSa7" + }, + "outputs": [], + "source": [ + "!pip install rich\n", + "from rich import print as pprint\n", + "import requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IEk2YBNJZb4t" + }, + "outputs": [], + "source": [ + "headers = {'user-agent':'Mozilla/5.0'}\n", + "api_base_url = (\n", + " 'https://tdx.transportdata.tw/api/basic/v3/'\n", + " 'Rail/TRA/'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7q-sU2qpYq8e" + }, + "outputs": [], + "source": [ + "res = stations = requests.get(\n", + " f\"{api_base_url}Station\",\n", + " headers=headers,\n", + ")\n", + "print(res.status_code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WZ9HqzWhY5e9" + }, + "outputs": [], + "source": [ + "json_data = res.json()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LkL5qno1ZCwr" + }, + "outputs": [], + "source": [ + "pprint(json_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mZf1_mfeZ8d7" + }, + "outputs": [], + "source": [ + "res = requests.get(\n", + " f\"{api_base_url}Station\"\n", + " \"?$select=stationName,stationID\",\n", + " headers=headers,\n", + ")\n", + "print(res.status_code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OVCyGww-a77p" + }, + "outputs": [], + "source": [ + "json_data = res.json()\n", + "stations = {}\n", + "for station in json_data['Stations']:\n", + " station_name = station['StationName']['Zh_tw']\n", + " station_id = station['StationID']\n", + " stations[station_name] = station_id\n", + "pprint(stations)" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 指定起迄站代號查詢時刻表" + ], + "metadata": { + "id": "q-y5M5nIeKS-" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JxWta5TBfJXa" + }, + "outputs": [], + "source": [ + "import time\n", + "now = time.localtime()\n", + "date = f\"{now.tm_year}-{now.tm_mon:02d}-{now.tm_mday:02d}\"\n", + "print(date)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rAqrrbN-dJ03" + }, + "outputs": [], + "source": [ + "start_station = '2240' # 龍井站代號\n", + "end_station = '2250' # 大肚站代號\n", + "res = stations = requests.get(\n", + " f\"{api_base_url}DailyTrainTimetable/OD/\"\n", + " f\"{start_station}\"\n", + " \"/to/\"\n", + " f\"{end_station}\"\n", + " \"/\"\n", + " f\"{date}\",\n", + " headers=headers,\n", + ")\n", + "print(res.status_code)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cj-uul3cm6Eb" + }, + "outputs": [], + "source": [ + "json_data = res.json()\n", + "pprint(json_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sqLmfC8LkI0M" + }, + "outputs": [], + "source": [ + "timetables = []\n", + "for timetable in json_data['TrainTimetables']:\n", + " train_no = timetable['TrainInfo']['TrainNo']\n", + " stop_times = timetable['StopTimes']\n", + " start_station = stop_times[0]['StationName']['Zh_tw']\n", + " departure_Time = stop_times[0]['DepartureTime']\n", + " end_station = stop_times[1]['StationName']['Zh_tw']\n", + " arrive_Time = stop_times[1]['ArrivalTime']\n", + " timetables.append({\n", + " 'train_no': train_no,\n", + " 'start_station': start_station,\n", + " 'departure_Time': departure_Time,\n", + " 'end_station': end_station,\n", + " 'arrive_Time': arrive_Time\n", + " })\n", + "pprint(timetables[0])" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### 以驗證身分方式使用 API\n" + ], + "metadata": { + "id": "JnosZbEeeZtk" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6hMNNoqNn0Uc" + }, + "outputs": [], + "source": [ + "!git clone https://github.com/FlagTech/tdx.git" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cGMqP2Oeqe6D" + }, + "outputs": [], + "source": [ + "from google.colab import userdata\n", + "from tdx import TDX\n", + "tdx_client = TDX(\n", + " userdata.get('TDX_ID'),\n", + " userdata.get('TDX_SECRET')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4pJFMvSCtMhz" + }, + "outputs": [], + "source": [ + "json_data = tdx_client.get_json(\n", + " f\"{api_base_url}Station\"\n", + " \"?$select=stationName,stationID\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HGsxSGMrvua5" + }, + "outputs": [], + "source": [ + "stations = {}\n", + "for station in json_data['Stations']:\n", + " station_name = station['StationName']['Zh_tw']\n", + " station_id = station['StationID']\n", + " stations[station_name] = station_id\n", + "pprint(stations)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zcH4bFW1v9ic" + }, + "outputs": [], + "source": [ + "start_station = '1000'\n", + "end_station = '2170'\n", + "json_data = tdx_client.get_json(\n", + " f\"{api_base_url}DailyTrainTimetable/OD/\"\n", + " f\"{start_station}\"\n", + " \"/to/\"\n", + " f\"{end_station}\"\n", + " \"/\"\n", + " f\"{date}\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mPehBG_OwZTv" + }, + "outputs": [], + "source": [ + "timetables = []\n", + "for timetable in json_data['TrainTimetables']:\n", + " train_no = timetable['TrainInfo']['TrainNo']\n", + " stop_times = timetable['StopTimes']\n", + " start_station = stop_times[0]['StationName']['Zh_tw']\n", + " departure_Time = stop_times[0]['DepartureTime']\n", + " end_station = stop_times[1]['StationName']['Zh_tw']\n", + " arrive_Time = stop_times[1]['ArrivalTime']\n", + " timetables.append({\n", + " 'train_no': train_no,\n", + " 'start_station': start_station,\n", + " 'departure_Time': departure_Time,\n", + " 'end_station': end_station,\n", + " 'arrive_Time': arrive_Time\n", + " })\n", + "\n", + "for train in timetables:\n", + " pprint(train)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XD-Odh42mT7G" + }, + "outputs": [], + "source": [ + "timetables = sorted(\n", + " timetables,\n", + " key=lambda timetable:timetable['departure_Time']\n", + ")\n", + "\n", + "for train in timetables:\n", + " pprint(train)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "toc_visible": true, + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From 128554bf4b343c6ab021d0bf790e0aae014af60d Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:19:04 +0630 Subject: [PATCH 10/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flagchat4_usage.ipynb | 707 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 flagchat4_usage.ipynb diff --git a/flagchat4_usage.ipynb b/flagchat4_usage.ipynb new file mode 100644 index 000000000..725a5114b --- /dev/null +++ b/flagchat4_usage.ipynb @@ -0,0 +1,707 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XeP8gN6eQZqo" + }, + "source": [ + "# flagchat4 套件用法示範" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SR-iKM00WuGE" + }, + "source": [ + "本套件主要將 OpenAI Chat API 抽象化, 納入串流、function calling 功能, 並且將介面統一使用生成器產生回覆, 不論是否啟用串流模式, 都可用一致的方式取得回覆。另外, 也搭配 function calling 設計一個簡易的外掛系統。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "hHfPJ7Y7QfaG" + }, + "source": [ + "## 事前準備" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "R1CAsaxl9_v4", + "outputId": "bfd8415c-4667-408f-9182-a077cf796727" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting openai\n", + " Downloading openai-1.3.7-py3-none-any.whl (221 kB)\n", + "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/221.4 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[91m━━━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[90m╺\u001b[0m\u001b[90m━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m122.9/221.4 kB\u001b[0m \u001b[31m3.6 MB/s\u001b[0m eta \u001b[36m0:00:01\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m221.4/221.4 kB\u001b[0m \u001b[31m4.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: anyio<4,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/lib/python3/dist-packages (from openai) (1.7.0)\n", + "Collecting httpx<1,>=0.23.0 (from openai)\n", + " Downloading httpx-0.25.2-py3-none-any.whl (74 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.0/75.0 kB\u001b[0m \u001b[31m8.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.10.13)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.0)\n", + "Requirement already satisfied: tqdm>4 in /usr/local/lib/python3.10/dist-packages (from openai) (4.66.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.5 in /usr/local/lib/python3.10/dist-packages (from openai) (4.5.0)\n", + "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.5.0->openai) (3.6)\n", + "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<4,>=3.5.0->openai) (1.2.0)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->openai) (2023.11.17)\n", + "Collecting httpcore==1.* (from httpx<1,>=0.23.0->openai)\n", + " Downloading httpcore-1.0.2-py3-none-any.whl (76 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m76.9/76.9 kB\u001b[0m \u001b[31m8.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting h11<0.15,>=0.13 (from httpcore==1.*->httpx<1,>=0.23.0->openai)\n", + " Downloading h11-0.14.0-py3-none-any.whl (58 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: h11, httpcore, httpx, openai\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "llmx 0.0.15a0 requires cohere, which is not installed.\n", + "llmx 0.0.15a0 requires tiktoken, which is not installed.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed h11-0.14.0 httpcore-1.0.2 httpx-0.25.2 openai-1.3.7\n", + "Collecting googlesearch-python\n", + " Downloading googlesearch-python-1.2.3.tar.gz (3.9 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: beautifulsoup4>=4.9 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (4.11.2)\n", + "Requirement already satisfied: requests>=2.20 in /usr/local/lib/python3.10/dist-packages (from googlesearch-python) (2.31.0)\n", + "Requirement already satisfied: soupsieve>1.2 in /usr/local/lib/python3.10/dist-packages (from beautifulsoup4>=4.9->googlesearch-python) (2.5)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (3.3.2)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (3.6)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (2.0.7)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests>=2.20->googlesearch-python) (2023.11.17)\n", + "Building wheels for collected packages: googlesearch-python\n", + " Building wheel for googlesearch-python (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for googlesearch-python: filename=googlesearch_python-1.2.3-py3-none-any.whl size=4209 sha256=d2cd613d52284bc7c2b698eabd2656e696b2d17c4b92f832bee299252ef95102\n", + " Stored in directory: /root/.cache/pip/wheels/98/24/e9/6c225502948c629b01cc895f86406819281ef0da385f3eb669\n", + "Successfully built googlesearch-python\n", + "Installing collected packages: googlesearch-python\n", + "Successfully installed googlesearch-python-1.2.3\n" + ] + } + ], + "source": [ + "!pip install openai\n", + "!pip install googlesearch-python\n", + "from googlesearch import search\n", + "from google.colab import userdata\n", + "import openai" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2JmHgsP-Qm56" + }, + "source": [ + "## 下載套件" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d2q3m6rtPvo4", + "outputId": "87c7432f-587a-4e36-c92d-a766627f7166" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Already up to date.\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Cloning into 'flagchat4'...\n" + ] + } + ], + "source": [ + "%%bash\n", + "git clone https://github.com/FlagTech/flagchat4.git flagchat4\n", + "cd flagchat4/\n", + "git pull" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BLXdCEVzQtSO" + }, + "source": [ + "## 從模組匯入工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HPp3JjAuImG7" + }, + "outputs": [], + "source": [ + "from flagchat4 import (\n", + " set_client, # 設定要使用的用戶端物件 (預設直接使用 openai 模組)\n", + " get_reply, # 輸入訊息串列傳回回覆\n", + " chat, # 輸入 system, user 發言取得回覆並會記錄對答歷史\n", + " tools_table, # 記錄可用工具函式的參考表, 預設有 Google 搜尋函式\n", + " set_backtrace, # 設定記錄幾組對答 (預設:2)\n", + " empty_history, # 清除對答歷史\n", + ")" + ] + }, + { + "cell_type": "markdown", + "source": [ + "flagchat4 預設為直接使用 openai 模組當用戶端物件, 你也可以透過 `set_client` 函式設定客製的用戶端物件。" + ], + "metadata": { + "id": "IIJ5LfKGe0_Y" + } + }, + { + "cell_type": "code", + "source": [ + "# 預設使用環境變數 OPENAI_API_KEY\n", + "openai.api_key = userdata.get('OPENAI_API_KEY')\n", + "\n", + "# 也可以使用客製的用戶端物件\n", + "# client = openai.OpenAI(userdata.get('OPENAI_API_KEY'))\n", + "# set_client(client)" + ], + "metadata": { + "id": "Y5h3e7JkLPKi" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "set_backtrace(2)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TxohD0nO0ptc", + "outputId": "02f09ec6-8223-4e74-c256-fb0ff714b6aa" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "2" + ] + }, + "metadata": {}, + "execution_count": 6 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HBMPPP5aQRE-" + }, + "source": [ + "## 單一問答測試\n", + "\n", + "get_reply 可以會透過 function calling 機制使用 func_table 傳入的函式表格傳回回覆。模組內預設的 tools_table 只有 Google 搜尋函式。\n", + "\n", + "```python\n", + "get_reply(\n", + " messages, # 訊息串列\n", + " stream=False # 是否啟用串流模式\n", + " tools_table=None # 工具函式參考表\n", + ")\n", + "```\n", + "另外有選用的參數:\n", + "\n", + "```python\n", + "model='gpt-3.5-turbo' # 指定模型\n", + "debug=False # 是否要顯示除錯訊息, 包含訊息串列內容\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fLO76nV4Ealo", + "outputId": "00efac55-4048-4f69-83f7-f0216e828afb" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:google_res(**{'user_msg': '2023金馬獎最佳男配角'})\n", + "2023年金馬獎最佳男配角的得主是陳慕義,他因在電影《老狐狸》中的表現獲此殊榮。\n" + ] + } + ], + "source": [ + "# 測試非串流方式 function_calling 功能\n", + "for chunk in get_reply( # 不論是否串流回覆, 都以生成器統一函式介面\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬獎最佳男配角?\"}], # 訊息串列\n", + " tools_table=tools_table): # 工具函式表\n", + " print(chunk) # 非串流模式只會生成一次" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tNioKT6Vodaf", + "outputId": "dc741309-51db-4dfd-9638-d0c65f33fec1" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:google_res(**{'user_msg': '2023金馬獎最佳導演'})\n", + "根據上述的搜尋結果摘要,2023年金馬獎最佳導演得主是《老狐狸》的蕭雅全。" + ] + } + ], + "source": [ + "# 測試串流方式 function_calling 功能\n", + "for chunk in get_reply( # 不論是否串流回覆, 都以生成器統一函式介面\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬獎最佳女配角?\"}], # 訊息串列\n", + " stream=True, # 啟用串流模式\n", + " tools_table=tools_table): # 工具函式表\n", + " print(chunk, end='') # 串流方式每次生成片段, 不換行才能接續內容" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "m23Ds5BOtVnS", + "outputId": "5bd55f56-31f2-4e8f-9563-344ace466cf9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "很抱歉,我無法提供當前或實時的數據信息,包括最新的獎項得主。截至我知识更新的時間在2023年之前,因此我沒有這一年度最佳導演獎的結果。要獲得最新的信息,建議查看最近的電影獎項結果,例如奧斯卡獎(Academy Awards)、金球獎(Golden Globe Awards)或其他電影節獎項的官方網站或可靠新聞來源。\n" + ] + } + ], + "source": [ + "# 測試非串流、無 function calling 功能\n", + "for chunk in get_reply(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬獎最佳導演是誰?\"}]):\n", + " print(chunk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Ay2vtRj3td6q", + "outputId": "91a133d0-b9f9-4f6d-c630-71ae1350b5c0" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "很抱歉,我無法提供即時信息或最新事件的更新,因為我的知識截止日期是2023年的初期。關於2023年金馬獎影帝的獲獎者,您可能需要查閱最新的新聞報導或官方金馬獎的公告以獲得最新資訊。" + ] + } + ], + "source": [ + "# 測試串流、無 function calling 功能\n", + "for chunk in get_reply(\n", + " [{\"role\":\"user\", \"content\":\"2023 金馬獎影帝是誰?\"}],\n", + " stream=True):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tgZAPh6uTUJQ" + }, + "source": [ + "## 歷史紀錄測試\n", + "\n", + "chat 是以 get_reply 函式為基礎, 加上對談歷史紀錄的功能, 可以使用 backtrace 設定要記錄的對談組數。\n", + "\n", + "```python\n", + "chat(\n", + " sys_msg, # system 角色發言\n", + " user_msg, # user 角色發言\n", + " stream=False, # 是否啟用串流模式\n", + " tools_table=tools_table # 工具函式參考表 (預設是模組內建的參考表)\n", + ")\n", + "```\n", + "一樣可以使用選用的參數:\n", + "\n", + "```python\n", + "model='gpt-3.5-turbo' # 指定模型\n", + "debug=False # 是否要顯示除錯訊息, 包含訊息串列內容\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rYFYP1YIUkS4", + "outputId": "078debad-4075-4dc6-f222-3895554d1e48" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:google_res(**{'user_msg': '2023 金馬獎 最佳女配角'})\n", + "2023年金馬獎最佳女配角是方志友,她憑藉在電影《本日公休》中的表演獲得該獎項。" + ] + } + ], + "source": [ + "for chunk in chat(\n", + " '小助理', # system 角色發言\n", + " '2023 金馬獎最佳女配角是誰?', # user 角色發言\n", + " True): # 使用串流模式\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iPB1ONTSVGDh" + }, + "source": [ + "底下會因為有歷史紀錄而影響建議的搜尋關鍵字:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "M0EXetr8U1-Z", + "outputId": "c62691aa-65db-4e2d-b1a6-98e4614d5d44" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:google_res(**{'user_msg': '2022金馬獎最佳女配角'})\n", + "2022年金馬獎最佳女配角得主是林詹珍妹,她因在電影《哈勇家》中的表演而獲此殊榮。" + ] + } + ], + "source": [ + "for chunk in chat(\n", + " '小助理', # system 角色發言\n", + " '那 2022 呢?', # user 角色發言 (會延續對答脈絡)\n", + " True): # 使用串流模式\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NHG1e0UbYvlX" + }, + "source": [ + "chat 會使用模組內預設的 func_table, 如果不想啟用, 可以加讓 func_table 參數值 None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TSk0vWjCV9Dw", + "outputId": "2050d72c-923a-4eaf-a07d-85b129dd118e" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "您好!看起来您只提供了一个年份 \"2021\" 但没有提供具体的问题或者背景信息。如果您需要关于2021年的信息,比如历史事件、科技发展、文化动态等,请提供更多的上下文或者具体问题,我将很乐意为您提供相关信息或者解答疑问。" + ] + } + ], + "source": [ + "for chunk in chat(\n", + " '小助理', # system 角色發言\n", + " '那 2021 呢?', # user 角色發言\n", + " True, # 串流模式\n", + " None): # 不使用工具函式參考表 (因此不會搜尋)\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UkWkIyA4VPNU" + }, + "source": [ + "## 連續交談測試\n", + "\n", + "以下是使用 chat 設計的聊天程式:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "1rR4R1uOuLhx", + "outputId": "f8b7333e-7911-4b3e-deaa-9fef0bc7a176" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "你希望ㄟ唉扮演:\n", + "\n", + "你說:2022 金馬獎影后是誰?他執導的電影有哪些?\n", + "使用繁體中文的小助理:嘗試叫用:google_res(**{'user_msg': '2022金馬獎影後'})\n", + "嘗試叫用:google_res(**{'user_msg': '張艇姊導演的電影'})\n", + "2022年的金馬獎影后是張艾嘉,她以電影《燈火闌珊》奪得該獎項。\n", + "\n", + "張艾嘉曾執導的電影包括《相愛相親》。由於提供的信息有限,如果您想了解更多關於張艾嘉執導的電影列表,可能需要更進一步的搜索或查詢。\n", + "\n", + "你說:\n" + ] + } + ], + "source": [ + "empty_history()\n", + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat(sys_msg, msg,\n", + " stream=True,\n", + " debug=True,\n", + " model='gpt-4-1106-preview',\n", + " ):\n", + " print(reply, end = \"\")\n", + " print('\\n')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3Y8v2zZQzIpC" + }, + "source": [ + "## 新增工具函式\n", + "\n", + "以文字生圖為例" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FbkO6IE_VkEn" + }, + "source": [ + "用 Image API 設計一個文生圖的工具函式:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ge1xqcrCBeWd" + }, + "outputs": [], + "source": [ + "def txt_to_img_url(prompt):\n", + " response = openai.images.generate(\n", + " prompt=prompt,\n", + " n=1,\n", + " size='1024x1024',\n", + " style='vivid',\n", + " quality='hd'\n", + " )\n", + " return response.data[0].url" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "14aHGxcmVoAG" + }, + "source": [ + "在工具函式表中新增項目, 生圖後不需要再送回給 AI 處理, 所以 chain 項目設為 False:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wh0CFGhiC07Y" + }, + "outputs": [], + "source": [ + "tools_table.append(\n", + " { # 每個元素代表一個函式\n", + " \"chain\": False, # 生圖後不需要傳回給 API\n", + " \"func\": txt_to_img_url,\n", + " \"spec\": { # function calling 需要的函式規格\n", + " 'type': 'function',\n", + " 'function': {\n", + " \"name\": \"txt_to_img_url\",\n", + " \"description\": \"可由文字生圖並傳回圖像網址\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"prompt\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"描述要產生圖像內容的文字\",\n", + " }\n", + " },\n", + " \"required\": [\"prompt\"],\n", + " },\n", + " }\n", + " }\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "source": [ + "tools_table.pop()\n", + "len(tools_table)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "OPQ647RRflKk", + "outputId": "1389fc19-3ba4-47ae-eef7-43c79deacadb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "1" + ] + }, + "metadata": {}, + "execution_count": 19 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "uIEEJYlYV-cS" + }, + "source": [ + "測試看看是不是可以正確生圖?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Q42dWiLwDvNp", + "outputId": "50728f7e-b77c-4fc5-b304-de06a514a9b9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "嘗試叫用:txt_to_img_url(**{'prompt': 'sunset with dolphin jumping out of the sea'})\n", + "https://oaidalleapiprodscus.blob.core.windows.net/private/org-TnN5jDJWh2Gbe6gZ6C11q1fl/user-hwS8wMY6Z8ZzjiE3tcFcl4mM/img-4VnFT09UiHTJ3bEXnUmj4cOH.png?st=2023-12-05T04%3A41%3A57Z&se=2023-12-05T06%3A41%3A57Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2023-12-04T23%3A04%3A53Z&ske=2023-12-05T23%3A04%3A53Z&sks=b&skv=2021-08-06&sig=KiMHVpzS%2B3FY55ZXz1Ze/A%2BwZh3IxAvEghrsb9kvZu8%3D\n" + ] + } + ], + "source": [ + "for chunk in chat('小助理', '我想要夕陽下海豚躍出海面的圖像', True):\n", + " print(chunk)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file From 3dfc3acac02cf57beefb4c4d1d7ff80e3db177ec Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:26:46 +0630 Subject: [PATCH 11/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_ch10.ipynb | 148 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 GPT4Dev_ch10.ipynb diff --git a/GPT4Dev_ch10.ipynb b/GPT4Dev_ch10.ipynb new file mode 100644 index 000000000..c74659d8f --- /dev/null +++ b/GPT4Dev_ch10.ipynb @@ -0,0 +1,148 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 10 設計 LINE AI 聊天機器人" + ], + "metadata": { + "id": "8BzuhxrU4s3w" + } + }, + { + "cell_type": "markdown", + "source": [ + "## 10-3 OpenAI 變化圖像的功能" + ], + "metadata": { + "id": "qYYVKyOK45e4" + } + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YueJ-b0UhVAt" + }, + "outputs": [], + "source": [ + "!pip install rembg\n", + "!pip install gradio\n", + "\n", + "from rembg import remove # 匯入移除背景的函式\n", + "from PIL import Image # 匯入 pillow 影像操作模組\n", + "import gradio as gr\n", + "\n", + "def remove_bg(img, x_off, y_off, scale):\n", + " x = int(x_off * img.width / 100) # 計算橫向位移像數\n", + " y = int(y_off * img.height / 100) # 計算縱向位移像數\n", + " width = img.width - x # 計算實際寬度\n", + " height = img.height - y # 計算實際高度\n", + " size = min(width, height) # 取寬高較短者\n", + " img = img.crop((x, y, x + size, y + size)) # 切割正方形區域\n", + " if scale < 100:\n", + " size = int(size * scale / 100)# 依照指定比例縮放\n", + " img = img.resize((size, size))\n", + " img_nb = remove(img) # 移除背景\n", + " img.save('img.png') # 儲存影像檔\n", + " img_nb.save('img_nb.png') # 儲存去背影像檔\n", + " return (img, img_nb)\n", + "\n", + "no_bg_if = gr.Interface(\n", + " fn=remove_bg,\n", + " inputs=[\n", + " gr.Image(label='輸入影像', type='pil'),\n", + " gr.Slider(label='橫向切割位移百分比'),\n", + " gr.Slider(label='縱向切割位移百分比'),\n", + " gr.Slider(label='縮放比例', value=100, step=5)],\n", + " outputs=[gr.Gallery(label='處理後影像')]\n", + ")\n", + "\n", + "no_bg_if.launch()" + ] + }, + { + "cell_type": "markdown", + "source": [ + "## 10-4 可控制變化內容的 create_edit 函式" + ], + "metadata": { + "id": "oa-jJHCr56jm" + } + }, + { + "cell_type": "code", + "source": [ + "!pip install openai\n", + "from PIL import Image\n", + "from io import BytesIO\n", + "import requests\n", + "from google.colab import userdata\n", + "\n", + "from openai import OpenAI\n", + "client = OpenAI(api_key=userdata.get('OPENAI_API_KEY'))" + ], + "metadata": { + "id": "cFbuFWZcy7qR" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "res = client.images.edit(\n", + " image=open(\"image_nb.png\", \"rb\"),\n", + " prompt='冬日下大雪的路上',\n", + " n=1,\n", + " size=\"1024x1024\"\n", + ")" + ], + "metadata": { + "id": "UODwhItOzVex" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "content = requests.get(res.data[0].url).content\n", + "img = Image.open(BytesIO(content))\n", + "from matplotlib import pyplot as plt\n", + "plt.axis(\"off\")\n", + "plt.imshow(img)" + ], + "metadata": { + "id": "HK4Tn7eB0Stj" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From bc05aee076c5905ad8fa144ba30c55a64c89e512 Mon Sep 17 00:00:00 2001 From: johnathan2012 Date: Fri, 12 Apr 2024 14:53:20 +0630 Subject: [PATCH 12/12] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20Colab=20=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GPT4Dev_chaa.ipynb | 3351 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3351 insertions(+) create mode 100644 GPT4Dev_chaa.ipynb diff --git a/GPT4Dev_chaa.ipynb b/GPT4Dev_chaa.ipynb new file mode 100644 index 000000000..eecfe6787 --- /dev/null +++ b/GPT4Dev_chaa.ipynb @@ -0,0 +1,3351 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "k0ujXQ_NIzTu" + }, + "source": [ + "# ChatGPT 開發實戰\n", + "\n", + "這是旗標科技《ChatGPT 開發實戰》新版本搭配 Azure OpenAI API 的範例檔案。" + ] + }, + { + "cell_type": "markdown", + "source": [ + "在建立資源時, 請選 sweden central, 會有最[多種的模型](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models)可以選用。" + ], + "metadata": { + "id": "G8bIvQmba3LD" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "a42M4mg1qNQ5" + }, + "source": [ + "## 使用 Python 呼叫 API\n", + "\n", + "OpenAI 官方提供有 openai 套件, 可以簡化直接使用 requests 模組的複雜度。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IQrw-pWsth0b" + }, + "source": [ + "### 使用官方 openai 套件" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rwGTl75BLNwu" + }, + "source": [ + "#### 安裝與使用 openai **套件**" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V9x1F86C4T9u" + }, + "outputs": [], + "source": [ + "!pip install gradio rich tiktoken openai" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nXBIuei0zDQB" + }, + "source": [ + "### 在 Colab 設定機密資料" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "k-X8C602xf2J" + }, + "outputs": [], + "source": [ + "from google.colab import userdata\n", + "import json\n", + "import base64" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6Duh4SCmLh7a" + }, + "source": [ + "### 使用 Azure OpenAI API\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_IgF-6qzLhje" + }, + "outputs": [], + "source": [ + "from openai import AzureOpenAI" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rdFV7NvKLoEU" + }, + "source": [ + "### 建立 Azure OpenAI API 用戶端" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v_2uHTPYOsgU" + }, + "source": [ + "以下各參數請參考 Playground 裡面顯示程式碼的部分, 這裡的 endpoint 是 Playground 裡面的 api_base。\n", + "\n", + "- api_version 請參考[這裡](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning)\n", + "- endpoint 請參考[這裡](https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tPszMBfDMIat" + }, + "outputs": [], + "source": [ + "client = AzureOpenAI(\n", + " api_version='2023-07-01-preview',\n", + " # api_version='2023-12-01-preview',\n", + " azure_endpoint='https://f4762-api.openai.azure.com/',\n", + " api_key=userdata.get('AZURE_OPENAI_KEY')\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f_8Ngm9QLvyQ" + }, + "source": [ + "### 測試 Chat Completions API" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "si-FTVTKNGty" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model='gpt41106', # 佈署名稱\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\",\n", + " },\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pyelatvvLiIn" + }, + "source": [ + "檢視傳回物件" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "e8C3ImwKD5a5" + }, + "outputs": [], + "source": [ + "print(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "aSjownHrGnde" + }, + "outputs": [], + "source": [ + "from rich import print as pprint\n", + "pprint(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oDHvn0VCGPzH" + }, + "outputs": [], + "source": [ + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ev4TvoQ3ps-z" + }, + "source": [ + "#### 直接使用模組叫用 API" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gvomwkY3pwNx" + }, + "outputs": [], + "source": [ + "# Azure OpenAI 似乎不能這樣用\n", + "import openai\n", + "openai.api_key = userdata.get('AZURE_OPENAI_KEY')\n", + "openai.api_version='2023-07-01-preview',\n", + "openai.azure_endpoint='https://swedencentralflag.openai.azure.com/',\n", + "\n", + "reply = openai.chat.completions.create(\n", + " model='gpt351106', # 佈署名稱\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "joNfGjzFJb5q" + }, + "outputs": [], + "source": [ + "pprint(reply)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nXM-kBtH2f5B" + }, + "source": [ + "#### 轉成 Python 字典" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ihiXl36g1z-u" + }, + "outputs": [], + "source": [ + "pprint(reply.model_dump())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BP5FkDxALp4e" + }, + "source": [ + "#### 傳遞多筆訊息" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "j1x0glPsNJe2" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = [\n", + " {\"role\":\"system\", \"content\":\"你是條住在深海、只會台灣中文的魚\"},\n", + " {\"role\":\"user\", \"content\": \"你住的地方很亮嗎?\"}\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "48eLL4VEQGza" + }, + "outputs": [], + "source": [ + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "65D0ie-qt8_d" + }, + "source": [ + "## 認識 token" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Y1BO-OuaFW0f" + }, + "source": [ + "### token 切割視覺化工具\n", + "\n", + "官方的[切割工具](https://platform.openai.com/encoder.encode)。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2Z-Ab0R2Kg4V" + }, + "source": [ + "### 使用 tiktoken 套件計算精確 token 數" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "L98OeqzKjJVm" + }, + "outputs": [], + "source": [ + "# !pip install tiktoken\n", + "import tiktoken" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SZxDdv76jrvO" + }, + "outputs": [], + "source": [ + "encoder = tiktoken.encoding_for_model('gpt-3.5-turbo')\n", + "print(encoder.name)\n", + "encoder = tiktoken.encoding_for_model('gpt-4')\n", + "print(encoder.name)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "O8SFWn0HHtsp" + }, + "outputs": [], + "source": [ + "tokens = encoder.encode(\"你好\")\n", + "print(tokens)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oChT24mGmKcZ" + }, + "outputs": [], + "source": [ + "print(encoder.decode(tokens))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0Pqpu0XGMDcD" + }, + "source": [ + "### ChatML 標記語言" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tWDiULI-My-x" + }, + "outputs": [], + "source": [ + "print(encoder.encode(\"user\"))\n", + "print(encoder.encode(\"assistant\"))\n", + "print(encoder.encode(\"system\"))\n", + "print(encoder.encode(\"\\n\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Pp8Dlbd8MNPL" + }, + "source": [ + "計算 message 總 tokens 數" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UTMcDBnLuz4Z" + }, + "outputs": [], + "source": [ + "def tokens_in_messages(messages):\n", + " totals = 0\n", + " for message in messages:\n", + " for k in message:\n", + " if k == \"content\":\n", + " totals += 4 # <|im_start|>user\\n{內容}<|im_end|>\n", + " totals += len(encoder.encode(message[k]))\n", + " totals += 3 # <|im_start|>assistant\\n\n", + " return totals" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bHO6ozY5wT4J" + }, + "outputs": [], + "source": [ + "print(tokens_in_messages([\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tZwKwOe8YMIv" + }, + "source": [ + "## 深入瞭解參數" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "v1-ga-Tyw5Ou" + }, + "source": [ + "### 控制生成訊息與 token 數量" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ODW5sx_XMSYG" + }, + "source": [ + "#### 指定生成的訊息數量 - n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kRgJDzMUilrz" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt351106\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " n=2\n", + ")\n", + "\n", + "pprint(reply)\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index,\n", + " choice.message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RqbMN9POMUkr" + }, + "source": [ + "#### 設定詞彙黑名單 - stop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "yXJqNS3fjRZx" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt351106\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " stop=['好'] # 最多 4 個\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)\n", + "print(reply.choices[0].finish_reason)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1KUb_1ELLvyc" + }, + "source": [ + "#### 設定回覆語句的 tokens 數量上限 - max_tokens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "EPrrVXy2Pf6M" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"您好!\"}\n", + " ],\n", + " max_tokens = 5\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)\n", + "print(reply.choices[0].finish_reason)\n", + "print(reply.usage.completion_tokens)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Lww8VuHe5TgP" + }, + "outputs": [], + "source": [ + "encoder.encode(\"您好!有什\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TPQbUVh3L9tf" + }, + "source": [ + "超過模型限制的 tokens 數" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7L-Bvt2NHDcB" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " # 用 0613 的模型示範比較節省 tokens 花費\n", + " model = \"gpt350613\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ],\n", + " max_tokens = 4090\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Bp3B7u-pw_qL" + }, + "source": [ + "### 控制回覆內容的變化性" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JQhZoMpINYAr" + }, + "source": [ + "#### 讓回覆更具彈性 - temperature" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ECWZd8GUkIfj" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " temperature=0,\n", + " n=2\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "xXAJhTiVkRYZ" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " temperature=2,\n", + " n=2,\n", + " max_tokens=400\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JM6fYpXYJ4P2" + }, + "source": [ + "#### 控制詞彙的豐富度 - top_p" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZC5i5Lt864EW" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\"role\": \"user\", \"content\": \"嗨!\"}],\n", + " top_p=0,\n", + " n=2\n", + ")\n", + "\n", + "for choice in reply.choices:\n", + " print(choice.index, choice.message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0XwQ69fZNsDX" + }, + "source": [ + "#### 控制詞彙的重複性 - presence_penalty 與 frequency_penalty" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "L-yFTbubsROQ" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " temperature=1,\n", + " presence_penalty=2,\n", + " max_tokens=400\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "XmsmGgpQ9oVB" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " temperature=1,\n", + " presence_penalty=-2,\n", + " max_tokens=400\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "T_VmWcN66DDv" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"}],\n", + " temperature=1, # 固定溫度會比較好測試比較\n", + " frequency_penalty=2,\n", + " max_tokens=400\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fusI2RpI90Yw" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"}],\n", + " temperature=1, # 固定溫度會比較好測試比較\n", + " frequency_penalty=-2,\n", + " max_tokens=400\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lc2KqtIVKBeB" + }, + "source": [ + "#### 調整特定 token 的分數 - logi-bias\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ecv9UTckKC0T" + }, + "outputs": [], + "source": [ + "encoder.encode('你好')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wVKafC6IKGnR" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " temperature=1,\n", + " logit_bias={\n", + " 53901: -100,\n", + " 57668: -100\n", + " },\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wfphuAh7KHA8" + }, + "outputs": [], + "source": [ + "encoder.encode('哈')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KpTnB5Itj1b_" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\"role\": \"user\", \"content\": \"你好\"}],\n", + " temperature=1,\n", + " logit_bias={\n", + " 99771: 100\n", + " },\n", + " max_tokens=400\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tinUei5sXLh8" + }, + "source": [ + "### 識別影像\n", + "\n", + "付費用戶才能使用 gpt-4-vision-preview 模型。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ixv6erkNhcfv" + }, + "source": [ + "#### 識別網路上的公開圖檔" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_TB7TsFdXRnH" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model=\"gpt4vision\",\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"圖片裡有什麼?\"},\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": \"https://flagtech.github.io/F3762/images/cat1.jpg\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " ],\n", + " }\n", + " ],\n", + " max_tokens=300,\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NqvmZOqbhXNf" + }, + "source": [ + "#### 辨識本機的圖檔" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4tngOeky_lc8" + }, + "outputs": [], + "source": [ + "!curl \"https://flagtech.github.io/F3762/images/cat2.jpg\" -o cat3.jpg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OQXov1_OcDZ8" + }, + "outputs": [], + "source": [ + "# Function to encode the image\n", + "def encode_image(image_path):\n", + " with open(image_path, \"rb\") as image_file:\n", + " return base64.b64encode(image_file.read()).decode('utf-8')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pNs9jSuKbxPI" + }, + "outputs": [], + "source": [ + "base64_image = encode_image('cat3.jpg')\n", + "\n", + "response = client.chat.completions.create(\n", + " model=\"gpt4vision\",\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": [\n", + " {\"type\": \"text\", \"text\": \"用中文告訴我圖片裡有什麼?\"},\n", + " {\n", + " \"type\": \"image_url\",\n", + " \"image_url\": {\n", + " \"url\": f\"data:image/jpeg;base64,{base64_image}\",\n", + " 'detail': 'high'\n", + " },\n", + " },\n", + " ],\n", + " }\n", + " ],\n", + " max_tokens=300,\n", + ")\n", + "\n", + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9oBB8v9idNQb" + }, + "source": [ + "### 串流輸出" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "p-798ckrMcjT" + }, + "source": [ + "#### 可循序傳回結果的生成器 (generator) - stream\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UKOBD3_WdMIf" + }, + "outputs": [], + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt41106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\"\n", + " }],\n", + " stream=True,\n", + ")\n", + "\n", + "for reply in replies:\n", + " pprint(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OSUKBv9hk9aX" + }, + "outputs": [], + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt351106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"台北是什麼樣的城市?\"\n", + " }],\n", + " stream=True\n", + ")\n", + "\n", + "for reply in replies:\n", + " if reply.choices:\n", + " print(reply.choices[0].delta.content or '', end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Lq6AgwlP9L-q" + }, + "source": [ + "### 控制回覆格式\n", + "\n", + "#### 強制 JSON 格式輸出" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Vs7Sa7yG9ORm" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"台灣最高的山高度是多少\"}\n", + " ]\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "kL3ax4Uz9s2o" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " # response_format 一定要用 1106 模型\n", + " model = \"gpt351106\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"台灣最高的山高度是多少\"},\n", + " {\"role\":\"system\", \"content\": \"請用 json 格式回覆\"}\n", + " ],\n", + " response_format={'type': 'json_object'} # or 'text'\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uDBGy-6_AdwW" + }, + "outputs": [], + "source": [ + "reply = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = [\n", + " {\"role\":\"system\", \"content\": \"請用 json 格式回覆\"},\n", + " {\n", + " \"role\":\"user\",\n", + " \"content\": \"台灣最高的山高度是多少, 請以如下格式回覆:\"\n", + " '{\"name\":\"山的名稱\", \"height\":高度}'\n", + " },\n", + " ],\n", + " response_format={'type': 'json_object'} # or 'text'\n", + ")\n", + "\n", + "print(reply.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "NZFLnM0qBSKU" + }, + "source": [ + "#### 固定輸出結果" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QPu-CP-5LYnY" + }, + "outputs": [], + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt350613\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\"\n", + " }],\n", + " temperature=1.6,\n", + " # 同樣的種子搭配同樣的參數可以固定輸出結果\n", + "# seed=1\n", + ")\n", + "\n", + "print(\n", + " replies.system_fingerprint,\n", + " replies.choices[0].message.content)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dRs8oH8qNjUB" + }, + "outputs": [], + "source": [ + "replies = client.chat.completions.create(\n", + " model=\"gpt351106\",\n", + " messages=[{\n", + " \"role\": \"user\",\n", + " \"content\": \"你好\"\n", + " }],\n", + " temperature=1.6,\n", + " seed=1\n", + ")\n", + "\n", + "print(\n", + " replies.system_fingerprint,\n", + " replies.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BblEBT0A5C-H" + }, + "source": [ + "## 取得底層 HTTP 的原始回覆" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RXHwp-NY1EFl" + }, + "outputs": [], + "source": [ + "# 取得原始 HTTP 回覆內容\n", + "reply = client.chat.completions.with_raw_response.create(\n", + " model = \"gpt351106\",\n", + " # model = \"gpt-4\",\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "3lxlCxYz1K1v" + }, + "outputs": [], + "source": [ + "import json\n", + "print(reply.status_code)\n", + "print(reply) # APIResponse 型別的物件\n", + "print('------')\n", + "print(reply.text) # JSON 格式文字\n", + "print('------')\n", + "reply_dic = json.loads(reply.text) # 轉成 Python 字典\n", + "pprint(reply_dic)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "F-fKo8FZCXLg" + }, + "outputs": [], + "source": [ + "print(reply_dic['choices'][0]['message']['content'])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "MzbgDbQCMInK" + }, + "source": [ + "## 錯誤處理與使用限制" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xnqfl7R5ugIO" + }, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bycmYTRICqUA" + }, + "outputs": [], + "source": [ + "'''\n", + "Exception\n", + " +--OpenAIError\n", + " +--APIError ◆ message: str\n", + " | ◆ request: httpx.Request\n", + " +--APIResponseValidationError ◆ response: httpx.Response\n", + " | ◆ status_code: int\n", + " +--APIStatusError ◆ response: httpx.Response\n", + " | | ◆ status_code: int\n", + " | +--BadRequestError (請求參數或是格式錯誤)\n", + " | +--AuthenticationError (金鑰認證有問題)\n", + " | +--PermissionDeniedError\n", + " | +--NotFoundError\n", + " | +--ConflictError\n", + " | +--UnprocessableEntityError\n", + " | +--RateLimitError (超過次數限制)\n", + " | +--InternalServerError\n", + " +--APIConnectionError (無法連線)\n", + " +--APITimeoutError (逾時)\n", + "'''" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1bV1BZvzMF06" + }, + "source": [ + "### 使用例外機制處理錯誤" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WHlx6477SQEo" + }, + "outputs": [], + "source": [ + "import openai\n", + "try:\n", + " reply = client.chat.completions.create(\n", + " model = \"gpt350613\", # 使用 0613 模型限制小減少浪費\n", + " messages = [\n", + " {\"role\":\"user\", \"content\": \"你好\"}\n", + " ],\n", + " max_tokens = 4096\n", + " )\n", + " print(reply.choices[0].message.content)\n", + "\n", + "except openai.APIError as err:\n", + " print(err.message)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kR8G5znxxuht" + }, + "source": [ + "## 文字模式簡易聊天程式" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "X021wDnuPYlx" + }, + "source": [ + "設計簡易對談程式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LRBgH2SzEjLr" + }, + "outputs": [], + "source": [ + "def get_reply(messages):\n", + " try:\n", + " response = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = messages\n", + " )\n", + " reply = response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"\n", + " return reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ptsDFS0mFZ_b" + }, + "outputs": [], + "source": [ + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " messages = [{\"role\":\"user\", \"content\":msg}]\n", + " reply = get_reply(messages)\n", + " print(f\"ㄟ唉:{reply}\\n\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "VyO0gDj7yD5M" + }, + "source": [ + "### 加入聊天記錄維持聊天脈絡" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iIsWBAoaPj1z" + }, + "source": [ + "把歷史紀錄加入 prompt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "V--0U28tI17U" + }, + "outputs": [], + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat(sys_msg, user_msg):\n", + " global hist\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " reply = get_reply(hist\n", + " + [{\"role\":\"system\", \"content\":sys_msg}])\n", + " hist.append({\"role\":\"assistant\", \"content\":reply})\n", + " hist = hist[-2 * backtrace:] # 保留新的對話\n", + " return reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uwhnfM-6JAvA" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '繁體中文小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " reply = chat(sys_msg, msg)\n", + " print(f\"{sys_msg}:{reply}\\n\")\n", + "hist = [] # 清除對話記錄以免影響後續測試" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Qqa3sGEDyMp1" + }, + "source": [ + "### 串流版本的聊天程式" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G2Ge034UfYDd" + }, + "source": [ + "串流版本的聊天程式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bpHqQNiMfemv" + }, + "outputs": [], + "source": [ + "def get_reply_s(messages):\n", + " try:\n", + " response = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = messages,\n", + " stream = True\n", + " )\n", + " for chunk in response:\n", + " if chunk.choices: # 略過第一個只有適合度資料的片段\n", + " yield chunk.choices[0].delta.content or ''\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KZciBFLufywW" + }, + "outputs": [], + "source": [ + "for reply in get_reply_s([{\n", + " \"role\":\"user\",\n", + " \"content\":\"請介紹台北市\"\n", + "}]):\n", + " print(reply, end='')\n", + "print('')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1UIcjOjqgffE" + }, + "outputs": [], + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_s(sys_msg, user_msg):\n", + " global hist\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " reply_full = \"\"\n", + " for reply in get_reply_s( # 使用串流版的函式\n", + " hist + [{\"role\":\"system\", \"content\":sys_msg}]):\n", + " reply_full += reply # 記錄到目前為止收到的訊息\n", + " yield reply # 傳回本次收到的片段訊息\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新的對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "21fxgL_qg7r-" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_s(sys_msg, msg):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + " # pprint(hist)\n", + "hist = []" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eNYngg3YPEp5" + }, + "source": [ + "## 突破時空限制–整合搜尋功能" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3O-zz4SSzPoF" + }, + "source": [ + "### 用搜尋網頁幫 AI 補充知識" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "f814Yd_fRBA0" + }, + "source": [ + "### 使用 Google 搜尋" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AtHx23DvU-T5" + }, + "outputs": [], + "source": [ + "!pip install googlesearch-python\n", + "from googlesearch import search" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "I2IzKRzZWVjc" + }, + "outputs": [], + "source": [ + "for item in search(\"2023 金曲獎歌后\"):\n", + " print(item)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xtPAfyxARHBn" + }, + "source": [ + "使用進階搜尋選項" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KMx3LddFXEHc" + }, + "outputs": [], + "source": [ + "for item in search(\n", + " \"2023 金曲獎歌后\", advanced=True, num_results=3):\n", + " print(item.title)\n", + " print(item.description)\n", + " print(item.url)\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zjc6Vdv9zmSF" + }, + "source": [ + "### 整合搜尋結果讓 AI 跟上時代" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IafW58_7IkpG" + }, + "source": [ + "加入網頁搜尋的聊天程式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AhiWJ8txIkpG" + }, + "outputs": [], + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_w(sys_msg, user_msg):\n", + " global hist\n", + " web_res = []\n", + " if user_msg[:3].lower() == '/w ': # /w 代表要搜尋網路\n", + " user_msg = user_msg[3:] # 移除指令留下實際的訊息\n", + " content = \"以下為已發生的事實:\\n\"\n", + " for res in search(user_msg, advanced=True,\n", + " num_results=5, lang='zh-TW'):\n", + " content += f\"標題:{res.title}\\n\" \\\n", + " f\"摘要:{res.description}\\n\\n\"\n", + " content += \"請依照上述事實回答以下問題:\\n\"\n", + " web_res = [{\"role\": \"user\", \"content\": content}]\n", + " web_res.append({\"role\": \"user\", \"content\": user_msg})\n", + " reply_full = \"\"\n", + " for reply in get_reply_s( # 使用串流版的函式\n", + " hist # 先提供歷史紀錄\n", + " + web_res # 再提供搜尋結果及目前訊息\n", + " + [{\"role\": \"system\", \"content\": sys_msg}]):\n", + " reply_full += reply # 記錄到目前為止收到的訊息\n", + " yield reply # 傳回本次收到的片段訊息\n", + " hist.append({\"role\": \"user\", \"content\": user_msg})\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "jIXa5pX2IkpG" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_w(sys_msg, msg):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GcsCqK-ERtTb" + }, + "source": [ + "### 使用客製模組" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vtOV_E5P3Wbe" + }, + "outputs": [], + "source": [ + "!git clone https://github.com/codemee/customsearchapi.git customsearchapi" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_liR9utq3dvS" + }, + "outputs": [], + "source": [ + "# 預設會在匯入時從環境變數 GOOGLE_API_KEY 與 GOOGLE_ID\n", + "# 讀取你的 API Key 與搜尋引擎 ID,\n", + "# 如果沒有設定, 也可以直接透過模組內的變數設定:\n", + "import customsearchapi\n", + "customsearchapi.GOOGLE_API_KEY = userdata.get('GOOGLE_API_KEY')\n", + "customsearchapi.GOOGLE_CSE_ID = userdata.get('GOOGLE_CSE_ID')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8Ha1xFSm4VhA" + }, + "outputs": [], + "source": [ + "from customsearchapi import search\n", + "\n", + "for item in search(\"2023 NBA 冠軍\",\n", + " advanced=True,\n", + " num_results=3,\n", + " lang='zh-TW'):\n", + " print(item.url)\n", + " print(item.title)\n", + " print(item.description)\n", + " print()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jVLUFQMKa3rc" + }, + "source": [ + "## 讓 AI 幫 AI-自動串接流程" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "aQoeKOv30XwE" + }, + "source": [ + "### 從 ChatGPT 外掛得到的啟示" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ONHyAgPZa3rd" + }, + "outputs": [], + "source": [ + "def get_reply_g(messages, stream=False, json_format=False):\n", + " try:\n", + " json_msg = ([{'role': 'system', 'content': '請用 JSON 回覆'}]\n", + " if json_format else [])\n", + " response = client.chat.completions.create(\n", + " model = \"gpt351106\",\n", + " messages = messages + json_msg,\n", + " stream = stream,\n", + " response_format = {\n", + " 'type': \"json_object\" if json_format else 'text'\n", + " }\n", + " )\n", + " if stream: # 串留模式下以生成器傳回片段內容\n", + " for res in response:\n", + " if res.choices:\n", + " yield res.choices[0].delta.content or ''\n", + " else: # 非串流模式下可直接取得完整回覆文字\n", + " yield response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"\n", + " print(reply)\n", + " yield reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Iugjm69PSbfF" + }, + "outputs": [], + "source": [ + "# 測試非串流模式\n", + "for reply in get_reply_g([{'role':'user', 'content':'你好'}]):\n", + " print(reply)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ec1Nb2keZlg_" + }, + "outputs": [], + "source": [ + "# 測試串流模式\n", + "for msg in get_reply_g([{'role':'user', 'content':'你好'}], stream=True):\n", + " print(msg, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LiyZiwM7RBdp" + }, + "outputs": [], + "source": [ + "# 測試 JSON 格式輸出\n", + "for reply in get_reply_g(\n", + " [{'role':'user', 'content':'你好'}],\n", + " json_format=True\n", + "):\n", + " print(reply)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6ontn4Qp0nDb" + }, + "source": [ + "### 由 AI 自動判斷要額外進行的工作" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Z4__qmyBb1sd" + }, + "source": [ + "#### 撰寫判斷是否需要搜尋的工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-utgajNExsEE" + }, + "outputs": [], + "source": [ + "# 用來詢問是否需要搜尋才能回覆問題的樣板\n", + "# 要求 AI 以 JSON 格式回覆 Y/N 以及建議的搜尋關鍵字\n", + "template_google = '''\n", + "如果我想知道以下這件事, 請確認是否需要網路搜尋才做得到?\n", + "\n", + "```\n", + "{}\n", + "```\n", + "\n", + "如果需要, 請以下列 JSON 格式回答我, 除了 JSON 格式資料外,\n", + "不要加上額外資訊, 就算你知道答案, 也不要回覆:\n", + "\n", + "```\n", + "{{\n", + " \"search\":\"Y\",\n", + " \"keyword\":\"你建議的搜尋關鍵字\"\n", + "}}\n", + "```\n", + "如果不需要, 請以下列 JSON 格式回答我:\n", + "\n", + "```\n", + "{{\n", + " \"search\":\"N\",\n", + " \"keyword\":\"\"\n", + "}}\n", + "'''" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_wBoRh5aMaUR" + }, + "outputs": [], + "source": [ + "# 利用目前歷史紀錄以及樣板內容詢問是否需要搜尋才能回覆問題\n", + "# 如果需要回覆, 也同時取得 AI 推薦的搜尋關鍵字\n", + "def check_google(hist, msg, verbose=False):\n", + " reply = get_reply_g(\n", + " hist + [{ # 加入歷史紀錄 AI 才能推薦正確的關鍵字\n", + " \"role\": \"user\",\n", + " \"content\": template_google.format(msg)\n", + " }], json_format=True)\n", + " for ans in reply:pass\n", + " if verbose: print(ans)\n", + " return ans" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "x0HzE8hQM5sT" + }, + "outputs": [], + "source": [ + "# 測試需要搜尋的狀況\n", + "ans = check_google(\n", + " [], '2023 NBA 冠軍是哪一隊?', True\n", + ")\n", + "# 測試可能不需要搜尋的狀況\n", + "ans = check_google(\n", + " [], '新冠疫情是哪一年開始的?', True\n", + ")\n", + "# 測試沒有前文脈絡的狀況\n", + "ans = check_google(\n", + " [], '那台灣呢?', True\n", + ")\n", + "# 測試包含前文脈絡的狀況\n", + "ans = check_google(\n", + " [{'role':'assistant', 'content': '印度空污好嚴重'}],\n", + " '那台灣呢?', True\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u3ng7lsCbH6D" + }, + "outputs": [], + "source": [ + "def google_res(user_msg, num_results=5, verbose=False):\n", + " content = \"以下為已發生的事實:\\n\" # 強調資料可信度\n", + " for res in search(user_msg, advanced=True, # 一一串接搜尋結果\n", + " num_results=num_results,\n", + " lang='zh-TW'):\n", + " content += f\"標題:{res.title}\\n\" \\\n", + " f\"摘要:{res.description}\\n\\n\"\n", + " # content += \"請依照上述事實回答以下問題:\\n\" # 下達明確指令\n", + " if verbose:\n", + " print('------------')\n", + " print(content)\n", + " print('------------')\n", + " return content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_RtZC9BLuJpv" + }, + "outputs": [], + "source": [ + "res = google_res('2023 NBA 冠軍隊', 2, verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UilIxFrRSxYK" + }, + "source": [ + "### 可自行判斷是否進行網路搜尋的聊天程式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tLRYkJ1IerqD" + }, + "outputs": [], + "source": [ + "import json\n", + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_g(sys_msg, user_msg, stream=False, verbose=False):\n", + " global hist\n", + " messages = [{'role':'user', 'content':user_msg}]\n", + " ans = json.loads(check_google(hist, user_msg,\n", + " verbose=verbose))\n", + " if ans['search'] == 'Y':\n", + " print(f'嘗試透過網路搜尋:{ans[\"keyword\"]}....')\n", + " res = google_res(ans['keyword'], verbose=verbose)\n", + " messages = [{'role':'user', 'content': res + user_msg}]\n", + "\n", + " replies = get_reply_g( # 使用搜尋版的函式\n", + " hist # 先提供歷史紀錄\n", + " + messages # 再提供搜尋結果及目前訊息\n", + " + [{\"role\": \"system\", \"content\": sys_msg}],\n", + " stream)\n", + " reply_full = ''\n", + " for reply in replies:\n", + " reply_full += reply\n", + " yield reply\n", + "\n", + " hist.append({\"role\":\"user\", \"content\":user_msg})\n", + " hist.append({\"role\":\"assistant\", \"content\":reply_full})\n", + " hist = hist[-2 * backtrace:] # 保留最新對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1JWrI77ierqD" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " # 不論是字串或是生成器, 都可以適用 for...in 迴圈\n", + " for reply in chat_g(sys_msg, msg, stream=False):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iR66bWoL1MEG" + }, + "source": [ + "## 可建構外掛系統的 Function Calling 機制" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "byrjSV6Wv5i2" + }, + "source": [ + "**Function calling 機制**\n", + "\n", + "Function calling 機制可以讓我們提供可用函式的規格, 由 AI 幫我們判斷是否需要叫用其中的函式。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "33kQwvMXqnPO" + }, + "source": [ + "### 告知語言模型可用的外部工具函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MV2VyR2GoPpe" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model = \"gpt41106\",\n", + " messages = [{\"role\":\"user\", \"content\":\"2023 金曲歌后?\"}],\n", + " tools = [{ # 可用的函式清單\n", + " \"type\":\"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\", # 資料類型\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"], # 必要參數\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\") # 請 AI 判斷是否需要叫用函式" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "DNOcLUYwq0Vu" + }, + "source": [ + "若 API 判斷需要叫用你描述的函式, 會在回覆中以 function_call 項目描述要叫用的函式名稱與參數值。" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Cf9b8aoETuof" + }, + "source": [ + "### 取得語言模型的建議" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7yI_HsqeBuYq" + }, + "outputs": [], + "source": [ + "pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "LUSrFvHE0IM4" + }, + "outputs": [], + "source": [ + "tool_call = response.choices[0].message.tool_calls[0]\n", + "func_name = tool_call.function.name\n", + "import json\n", + "args = json.loads(tool_call.function.arguments)\n", + "arg_val = args.popitem()[1]\n", + "print(f'{func_name}(\"{arg_val}\")')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gwnrQzio3wQK" + }, + "source": [ + "### 執行函式並傳回結果\n", + "\n", + "你必須自行叫用函式, 並且將執行結果透過 tool 角色的訊息傳回。\n", + "\n", + "要注意的是, 傳回時要一併送回原本模型送過來, 包含有 tool_calls 內容的訊息, 不過這個訊息因為考慮到相容性的關係, 所以除了 tool_calls 外, 還放了值為 None 的 function_call 欄位, 但這個欄位在請求中是 tool_choice 的功能, 如果連同這個欄位傳回, API 端會出錯, 認為這是不正確的參數, 因此目前的作法是透過自訂函式 maker_tool_back_msg 來客製一個訊息, 濾掉不需要傳回去的 function_call 欄位。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ynPG9RTJxaCU" + }, + "outputs": [], + "source": [ + "# 用來過濾掉訊息中 function_call 欄位的函式\n", + "def make_tool_back_msg(tool_msg):\n", + " msg_json = tool_msg.model_dump()\n", + " del msg_json['function_call']\n", + " return msg_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nybm9QfezrdT" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model='gpt41106',\n", + " messages=[\n", + " {\"role\":\"user\", \"content\":\"2023 金曲歌后?\"},\n", + " # 傳回 AI 傳給我們的 function calling 結果\n", + " make_tool_back_msg(response.choices[0].message),\n", + " { # 以 function 角色加上 name 屬性指定函式名稱傳回執行結果\n", + " \"tool_call_id\": tool_call.id, # 叫用函式的識別碼\n", + " \"role\": \"tool\", # 以工具角色送出回覆\n", + " \"name\": func_name, # 叫用的函式名稱\n", + " \"content\": eval(f'{func_name}(\"{arg_val}\")') # 函式傳回值\n", + " }\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gNzBfp03052c" + }, + "outputs": [], + "source": [ + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qActYIH0YeVY" + }, + "source": [ + "\n", + "2023/11/06 之後的模型支援單次對話可以要求執行多個函式呼叫:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "JxwScFYoVHnQ" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model = \"gpt41106\",\n", + " messages = [{\"role\":\"user\", \"content\":\"2023 金馬獎影后和金曲獎歌王各是誰?\"}],\n", + " tools = [{ # 可用的函式清單\n", + " \"type\":\"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\", # 資料類型\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"], # 必要參數\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\") # 請 AI 判斷是否需要叫用函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ivEmFRleXTPr" + }, + "outputs": [], + "source": [ + "pprint(response)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "iuCnmiOAVRfu" + }, + "outputs": [], + "source": [ + "for tool_call in response.choices[0].message.tool_calls:\n", + " func = tool_call.function\n", + " func_name = func.name\n", + " args_val = json.loads(func.arguments).popitem()[1]\n", + " print(f'{func.name}(\"{args_val}\")')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "HHqRidVVbMsB" + }, + "outputs": [], + "source": [ + "def make_func_messages(tool_calls):\n", + " messages = []\n", + " for tool_call in tool_calls:\n", + " func = tool_call.function\n", + " func_name = func.name\n", + " args_val = json.loads(func.arguments).popitem()[1]\n", + " print(f'{func.name}(\"{args_val}\")')\n", + " messages.append({\n", + " \"tool_call_id\": tool_call.id, # 叫用函式的識別碼\n", + " \"role\": \"tool\", # 以工具角色送出回覆\n", + " \"name\": func.name, # 叫用的函式名稱\n", + " \"content\": eval(f'{func_name}(\"{args_val}\")') # 函式傳回值\n", + " })\n", + " return messages\n", + "\n", + "func_messages = make_func_messages(response.choices[0].message.tool_calls)\n", + "pprint(func_messages)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UyzsNlibZ4bn" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " model='gpt41106',\n", + " messages=[\n", + " {\"role\":\"user\", \"content\":\"2023 金馬獎影后和金曲獎歌王各是誰?\"},\n", + " # 傳回 AI 傳給我們的 function calling 結果\n", + " make_tool_back_msg(response.choices[0].message),\n", + " ] + func_messages\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PwJCg4ujg1NA" + }, + "outputs": [], + "source": [ + "print(response.choices[0].message.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "M527Isb35K_m" + }, + "source": [ + "### 以串流方式使用 function calling" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "q8zKX0Nf3Es9" + }, + "outputs": [], + "source": [ + "response = client.chat.completions.create(\n", + " # model = \"gpt41106\",\n", + " model = \"gpt351106\",\n", + " messages = [{\"role\":\"user\", \"content\":\"宮崎駿和是枝裕和的最新作品各是哪一部?\"}],\n", + " tools = [{\n", + " \"type\": \"function\", # 工具類型\n", + " \"function\": {\n", + " \"name\": \"google_res\", # 函式名稱\n", + " \"description\": \"取得 Google 搜尋結果\", # 函式說明\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": { # 參數名稱\n", + " \"type\": \"string\",\n", + " \"description\": \"要搜尋的關鍵字\", # 參數說明\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"],\n", + " },\n", + " }\n", + " }],\n", + " tool_choice = \"auto\", # 請 AI 判斷是否需要使用工具\n", + " stream=True\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5G8g3ln85lgI" + }, + "source": [ + "傳回結果一樣是可走訪物件。\n", + "\n", + "注意, 1106 的模型第一個 chunk 沒有函式名稱, 第二個 chunk 之後才有 function calling 的資料。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "43D-blzoW9Rx" + }, + "outputs": [], + "source": [ + "for chunk in response:\n", + " pprint(chunk)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BLEYI9KL1amp" + }, + "source": [ + "## 建立 API 外掛系統" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5RRoqgB-UKOF" + }, + "source": [ + "### 建立外部工具函式參考表" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "50TGN5_lr226" + }, + "source": [ + "建立以 function calling 為基礎的外掛機制。
\n", + "建立結構化的函式表格。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WJtnKbIN6XYP" + }, + "outputs": [], + "source": [ + "tools_table = [ # 可用工具表\n", + " { # 每個元素代表一個工具\n", + " \"chain\": True, # 工具執行結果是否要再傳回給 API\n", + " \"func\": google_res, # 工具對應的函式\n", + " \"spec\": { # function calling 需要的工具規格\n", + " \"type\": \"function\",\n", + " \"function\": {\n", + " \"name\": \"google_res\",\n", + " \"description\": \"取得 Google 搜尋結果\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"user_msg\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"要搜尋的關鍵字\",\n", + " }\n", + " },\n", + " \"required\": [\"user_msg\"],\n", + " },\n", + " }\n", + " }\n", + " }\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "J0GQMNX7sKCI" + }, + "source": [ + "### 建立協助 function calling 的工具函式\n", + "依據回應內容自動叫用對應函式:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nSaSvwtMWovK" + }, + "outputs": [], + "source": [ + "def call_tools(tool_calls, tools_table):\n", + " res = ''\n", + " msg = []\n", + " for tool_call in tool_calls:\n", + " func = tool_call.function\n", + " func_name = func.name\n", + " args = json.loads(func.arguments)\n", + " for f in tools_table: # 找出包含此函式的項目\n", + " if func_name == f['spec']['function']['name']:\n", + " print(f\"嘗試叫用:{func_name}(**{args})\")\n", + " val = f['func'](**args)\n", + " if f['chain']: # 要將結果送回模型\n", + " msg.append({\n", + " 'tool_call_id': tool_call.id,\n", + " 'role': 'tool',\n", + " 'name': 'func_name',\n", + " 'content': val\n", + " })\n", + " else: res += str(val)\n", + " break\n", + " return msg, res" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bVa9UmFAbnI0" + }, + "outputs": [], + "source": [ + "def get_tool_calls(messages, stream=False, tools_table=None,\n", + " **kwargs):\n", + " model = 'gpt351106' # 設定模型\n", + " if 'model' in kwargs: model = kwargs['model']\n", + "\n", + " tools = {}\n", + " if tools_table: # 加入工具表\n", + " tools = {'tools':[tool['spec'] for tool in tools_table]}\n", + "\n", + " response = client.chat.completions.create(\n", + " model = model,\n", + " messages = messages,\n", + " stream = stream,\n", + " **tools\n", + " )\n", + "\n", + " if not stream: # 非串流模式\n", + " msg = response.choices[0].message\n", + " if msg.content == None: # function calling 的回覆\n", + " return msg.tool_calls, None # 取出叫用資訊\n", + " return None, response # 一般回覆\n", + "\n", + " tool_calls = [] # 要呼叫的函式清單\n", + " prev = None\n", + " for chunk in response:\n", + " if not chunk.choices: continue # 略過 Azure 串流的第一個片段\n", + " delta = chunk.choices[0].delta\n", + " if delta.content != None: # 一般回覆 (非 function calling)\n", + " return None, response # 直接返回結果\n", + " if delta.tool_calls: # 不是頭/尾的 chunk\n", + " curr = delta.tool_calls[0]\n", + " if curr.function.name: # 單一 call 開始\n", + " prev = curr # 取得工具名稱\n", + " tool_calls.append(curr) # 加入串列\n", + " else: # 串接引數內容\n", + " prev.function.arguments += curr.function.arguments\n", + " return tool_calls, None" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_VPoLmupNhtN" + }, + "outputs": [], + "source": [ + "pprint(get_tool_calls(\n", + " messages = [{'role':'user', 'content':'2023 金曲歌王是哪位?'}]\n", + "))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "IvGmInUSOW-O" + }, + "outputs": [], + "source": [ + "tool_calls, response = get_tool_calls(\n", + " messages = [{'role':'user', 'content':'2023 金曲歌王是哪位?'}],\n", + " stream=True\n", + ")\n", + "for chunk in response:\n", + " print(chunk.choices[0].delta.content or '', end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Btpkyh4WPepL" + }, + "outputs": [], + "source": [ + "tool_calls, response = get_tool_calls(\n", + " messages = [{'role':'user', 'content':'2023 金曲歌王是哪位?'}],\n", + " tools_table=tools_table\n", + ")\n", + "pprint(tool_calls)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "H8yvRhj3P_hw" + }, + "outputs": [], + "source": [ + "tool_calls, response = get_tool_calls(\n", + " messages = [{'role':'user', 'content':'2023 金曲歌王是哪位?'}],\n", + " stream=True,\n", + " tools_table=tools_table\n", + ")\n", + "pprint(tool_calls)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CpOlNDlcYQiK" + }, + "outputs": [], + "source": [ + "tool_calls, response = get_tool_calls(\n", + " messages = [{'role':'user', 'content':'宮崎駿和是枝裕和的最新作品各是哪一部?'}],\n", + " stream=True,\n", + " tools_table=tools_table,\n", + " # model='gpt41106'\n", + ")\n", + "pprint(tool_calls)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "PqLCfWNPUvi_" + }, + "source": [ + "### 建立 function_calling 版的 get_reply_f() 函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6_HyReryakU5" + }, + "outputs": [], + "source": [ + "def get_reply_f(messages, stream=False, tools_table=None, **kwargs):\n", + " try:\n", + " tool_calls, response = get_tool_calls(messages,\n", + " stream, tools_table, **kwargs)\n", + " if tool_calls:\n", + " tool_messages, res = call_tools(tool_calls, tools_table)\n", + " tool_calls_messeges = []\n", + " for tool_call in tool_calls:\n", + " tool_calls_messeges.append(tool_call.model_dump())\n", + " if tool_messages: # 如果需要將函式執行結果送回給 AI 再回覆\n", + " messages += [ # 必須傳回原本 function_calling 的內容\n", + " {\n", + " \"role\": \"assistant\", \"content\": None,\n", + " \"tool_calls\": tool_calls_messeges\n", + " }]\n", + " messages += tool_messages\n", + " # pprint(messages)\n", + " yield from get_reply_f(messages, stream,\n", + " tools_table, **kwargs)\n", + " else: # chain 為 False, 以函式叫用結果當成模型生成內容\n", + " yield res\n", + " elif stream: # 不需叫用函式但使用串流模式\n", + " for chunk in response:\n", + " if chunk.choices: # 略過 Azure 串流的第一個片段\n", + " yield chunk.choices[0].delta.content or ''\n", + " else: # 不需叫用函式也沒有使用串流模式\n", + " yield response.choices[0].message.content\n", + " except openai.APIError as err:\n", + " reply = f\"發生錯誤\\n{err.message}\"\n", + " print(reply)\n", + " yield reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fLO76nV4Ealo" + }, + "outputs": [], + "source": [ + "# 測試非串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金曲歌后是誰?\"}],\n", + " tools_table=tools_table):\n", + " print(chunk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tNioKT6Vodaf" + }, + "outputs": [], + "source": [ + "# 測試串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金曲歌后是誰?\"}],\n", + " stream=True,\n", + " tools_table=tools_table):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m23Ds5BOtVnS" + }, + "outputs": [], + "source": [ + "# 測試非串流、無 function calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金曲歌后是誰?\"}]):\n", + " print(chunk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Ay2vtRj3td6q" + }, + "outputs": [], + "source": [ + "# 測試串流、無 function calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"2023 金曲歌后是誰?\"}],\n", + " stream=True):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BNNLNn6pnkK7" + }, + "outputs": [], + "source": [ + "# 測試串流方式 function_calling 功能\n", + "for chunk in get_reply_f(\n", + " [{\"role\":\"user\", \"content\":\"宮崎駿和是枝裕和的最新作品各是哪一部?\"}],\n", + " stream=True,\n", + " tools_table=tools_table,\n", + " # model='gpt41106'\n", + "):\n", + " print(chunk, end='')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ajlSC5PPVBnq" + }, + "source": [ + "### 建立 function calling 版本的 chat_f() 函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sSpzuVo7yo-M" + }, + "outputs": [], + "source": [ + "hist = [] # 歷史對話紀錄\n", + "backtrace = 2 # 記錄幾組對話\n", + "\n", + "def chat_f(sys_msg, user_msg, stream=False, **kwargs):\n", + " global hist\n", + "\n", + " replies = get_reply_f( # 使用函式功能版的函式\n", + " hist # 先提供歷史紀錄\n", + " + [{\"role\": \"user\", \"content\": user_msg}]\n", + " + [{\"role\": \"system\", \"content\": sys_msg}],\n", + " stream, tools_table, **kwargs)\n", + " reply_full = ''\n", + " for reply in replies:\n", + " reply_full += reply\n", + " yield reply\n", + "\n", + " hist += [{\"role\":\"user\", \"content\":user_msg},\n", + " {\"role\":\"assistant\", \"content\":reply_full}]\n", + " hist = hist[-2 * backtrace:] # 留下最新的對話" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1rR4R1uOuLhx" + }, + "outputs": [], + "source": [ + "sys_msg = input(\"你希望ㄟ唉扮演:\")\n", + "if not sys_msg.strip(): sys_msg = '使用繁體中文的小助理'\n", + "print()\n", + "while True:\n", + " msg = input(\"你說:\")\n", + " if not msg.strip(): break\n", + " print(f\"{sys_msg}:\", end = \"\")\n", + " for reply in chat_f(sys_msg, msg, stream=True):\n", + " print(reply, end = \"\")\n", + " print('\\n')\n", + "hist = []\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OA5OOLjP4Q0T" + }, + "source": [ + "## 使用 DALL‧E 的 Image API" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G5NzRvvK80h2" + }, + "source": [ + "### Image API 用法" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "juK-91EWDgvw" + }, + "source": [ + "Dall-e-3 模型\n", + "\n", + "1024x1024, 1792x1024, or 1024x1792\n", + "\n", + "注意:Azure 中 api_version 要用 2023-12-01-preview 才行。" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "UsJq8Hlw-fWT" + }, + "outputs": [], + "source": [ + "res = client.images.generate( # 文字生圖\n", + " model='Dalle3',\n", + " prompt='夕陽下駛過海邊的火車', # 描述文字\n", + " n=1, # 生圖張數\n", + " quality='hd',\n", + " size='1024x1024', # 影像大小, 預設 1024x1024\n", + " style='vivid', # 風格, 預設 'vivid'\n", + ")\n", + "pprint(res)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9v43-3oQd3zc" + }, + "outputs": [], + "source": [ + "from IPython.display import Image, display, Markdown" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "cVROEax9fBGS" + }, + "outputs": [], + "source": [ + "display(Image(url=res.data[0].url, width=200))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "mcuHC9YmEpKY" + }, + "outputs": [], + "source": [ + "print(res.data[0].revised_prompt)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AwPVb7hceBHw" + }, + "outputs": [], + "source": [ + "display(Markdown(f\"![]({res.data[0].url})\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "m9ZE2kKWXrxx" + }, + "source": [ + "### 建立文字生圖像網址的函式" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ge1xqcrCBeWd" + }, + "outputs": [], + "source": [ + "def txt_to_img_url(prompt):\n", + " response = client.images.generate(\n", + " model='Dalle3',\n", + " prompt=prompt,\n", + " n=1,\n", + " size='1024x1024',\n", + " style='vivid',\n", + " quality='hd'\n", + " )\n", + " return response.data[0].url" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OogZKOJ9B3f_" + }, + "outputs": [], + "source": [ + "display(Image(url=txt_to_img_url('田邊騎著腳踏車晃的少年'), width=200))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wh0CFGhiC07Y" + }, + "outputs": [], + "source": [ + "tools_table.append({ # 每個元素代表一個函式\n", + " \"chain\": False, # 生圖後不需要傳回給 API\n", + " \"func\": txt_to_img_url,\n", + " \"spec\": { # function calling 需要的函式規格\n", + " 'type': 'function',\n", + " 'function': {\n", + " \"name\": \"txt_to_img_url\",\n", + " \"description\": \"可由文字生圖並傳回圖像網址\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"prompt\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"描述要產生圖像內容的文字\",\n", + " }\n", + " },\n", + " \"required\": [\"prompt\"],\n", + " },\n", + " }\n", + " }\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Q42dWiLwDvNp" + }, + "outputs": [], + "source": [ + "for chunk in chat_f('小助理', '請畫一張夕陽下海豚躍出海面的圖像', False):\n", + " if chunk.startswith('https'):\n", + " display(Image(url=chunk, width=300))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P0o41v7E4Ka1" + }, + "source": [ + "## 使用 gradio 套件快速建立網頁程式" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fdxBFtLh83QD" + }, + "source": [ + "### 安裝與使用 gradio" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "CBotGWq1rH6G" + }, + "outputs": [], + "source": [ + "#!pip install gradio" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jmBZZNCU88us" + }, + "source": [ + "建立基本的網頁介面" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "P0QYgtoxQEPH" + }, + "outputs": [], + "source": [ + "import gradio as gr" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fRkqv4VGrgYh" + }, + "outputs": [], + "source": [ + "hist = []\n", + "web_chat = gr.Interface(\n", + " fn = chat_f,\n", + " inputs = ['text', 'text'],\n", + " outputs = ['text'],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "6-kRoO1JsGq2" + }, + "outputs": [], + "source": [ + "web_chat.queue()\n", + "web_chat.launch(share=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "u30T026Tshcg" + }, + "outputs": [], + "source": [ + "web_chat.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "h_QG2S899BBJ" + }, + "source": [ + "### 使用串流方式顯示輸出" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4SEXqmplBFw0" + }, + "outputs": [], + "source": [ + "hist = []\n", + "web_chat = gr.Interface(\n", + " fn = chat_f,\n", + " inputs = ['text', 'text', 'checkbox'],\n", + " outputs = ['text']\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gU_McbmiBLvZ" + }, + "outputs": [], + "source": [ + "web_chat.queue()\n", + "web_chat.launch()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "570S8JdhBksT" + }, + "outputs": [], + "source": [ + "web_chat.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3hyVgEil9Fz8" + }, + "source": [ + "利用包裝函式組合片段內容" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-dIxREtO1AZm" + }, + "outputs": [], + "source": [ + "def wrapper_chat(sys_msg, user_msg, stream):\n", + " reply = ''\n", + " for chunk in chat_f(sys_msg, user_msg, stream):\n", + " reply += chunk\n", + " yield reply" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ig1c9U2S1WVF" + }, + "outputs": [], + "source": [ + "hist = []\n", + "web_chat = gr.Interface(\n", + " fn = wrapper_chat,\n", + " inputs = ['text', 'text', 'checkbox'],\n", + " outputs = ['text']\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uc_o6p5A1cQa" + }, + "outputs": [], + "source": [ + "web_chat.queue()\n", + "web_chat.launch()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "muY9Tmas5Sfc" + }, + "outputs": [], + "source": [ + "web_chat.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6bZly9TQ9N8v" + }, + "source": [ + "### 客製使用者介面" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BndzGRcl2k_7" + }, + "outputs": [], + "source": [ + "messages = []\n", + "\n", + "def wrapper_chat_bot(sys_msg, user_msg, stream):\n", + " messages.append([user_msg, ''])\n", + " for chunk in chat_f(sys_msg, user_msg, stream):\n", + " messages[-1][1] += chunk\n", + " yield messages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "A5fVguj03bWg" + }, + "outputs": [], + "source": [ + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False)],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lLqvX5Bu4R99" + }, + "outputs": [], + "source": [ + "hist = []\n", + "web_chat.queue()\n", + "web_chat.launch()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oP-v3wby4pDG" + }, + "outputs": [], + "source": [ + "web_chat.close()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Pn6ELd1-KV4B" + }, + "outputs": [], + "source": [ + "def txt_to_img_md(prompt):\n", + " return f'![{prompt}]({txt_to_img_url(prompt)})'" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "QUEQYL5GEOKp" + }, + "outputs": [], + "source": [ + "tools_table.pop()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PSccxr-WLv6B" + }, + "outputs": [], + "source": [ + "tools_table.append({ # 每個元素代表一個函式\n", + " \"chain\": False, # 生圖後不需要傳回給 API\n", + " \"func\": txt_to_img_md,\n", + " \"spec\": { # function calling 需要的函式規格\n", + " 'type': 'function',\n", + " 'function': {\n", + " \"name\": \"txt_to_img_md\",\n", + " \"description\": \"可由文字生圖並傳回 markdown 圖像元素\",\n", + " \"parameters\": {\n", + " \"type\": \"object\",\n", + " \"properties\": {\n", + " \"prompt\": {\n", + " \"type\": \"string\",\n", + " \"description\": \"描述要產生圖像內容的文字\",\n", + " }\n", + " },\n", + " \"required\": [\"prompt\"],\n", + " },\n", + " }\n", + " }\n", + "})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WDD3T9_xMCSN" + }, + "outputs": [], + "source": [ + "hist = []\n", + "messages = []\n", + "web_chat = gr.Interface(\n", + " fn=wrapper_chat_bot,\n", + " inputs=[\n", + " gr.Textbox(label='系統角色', value='使用繁體中文的小助理'),\n", + " gr.Textbox(label='使用者發言'),\n", + " gr.Checkbox(label='使用串流', value=False)],\n", + " outputs=[gr.Chatbot(label='AI 回覆')]\n", + ")\n", + "\n", + "web_chat.queue()\n", + "web_chat.launch()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gKI8uZIKdQgU" + }, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Flo93iUQND4-" + }, + "outputs": [], + "source": [ + "web_chat.close()" + ] + } + ], + "metadata": { + "colab": { + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file