# AIoT班上課紀錄IV ## AIoT班上課紀錄 Part I網址 https://hackmd.io/@aaronlife/uch-aiot-2024 ## AIoT班上課紀錄 Part II網址 https://hackmd.io/@aaronlife/uch-aiot-2024-2 ## AIoT班上課紀錄 Part III網址 https://hackmd.io/@aaronlife/uch-aiot-2024-3 ## AIoT班上課紀錄 Part IV網址 https://hackmd.io/@aaronlife/uch-aiot-2024-4 ## AIoT班上課紀錄 Part V(測驗) 網址 https://hackmd.io/@aaronlife/uch-aiot-2024-5 ## 2024-07-19 #### 從Python執行作業系統指令 ```python= import subprocess # 打開檔案總管 subprocess.run('explorer') # 打開Chrome並前往Yahoo subprocess.run('C:\Program Files\Google\Chrome\Application\chrome.exe https://www.yahoo.com.tw') # 關機 subprocess.run('shutdown /s') ``` #### index.html ```html= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Material Messaging App Concept</title> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'> <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css'> <link rel="stylesheet" href="./style.css"> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <!-- partial:index.partial.html --> <body> <div class="container"> <div class="row"> <nav class="menu"> <ul class="items"> <li class="item"> <i class="fa fa-home" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-user" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-pencil" aria-hidden="true"></i> </li> <li class="item item-active"> <i class="fa fa-commenting" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-file" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-cog" aria-hidden="true"></i> </li> </ul> </nav> <section class="discussions"> <div class="discussion search"> <div class="searchbar"> <i class="fa fa-search" aria-hidden="true"></i> <input type="text" placeholder="Search..."></input> </div> </div> <div class="discussion message-active"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Megan Leib</p> <p class="message">dsfsdkflnsdlkfnsldknflskdnflksdnfklndsflke 😳</p> </div> <div class="timer">12 sec</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://e0.365dm.com/16/08/16-9/20/theirry-henry-sky-sports-pundit_3766131.jpg?20161212144602);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Dave Corlew</p> <p class="message">Let's meet for a coffee or something today ?</p> </div> <div class="timer">3 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1497551060073-4c5ab6435f12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=667&q=80);"> </div> <div class="desc-contact"> <p class="name">Jerome Seiber</p> <p class="message">I've sent you the annual report</p> </div> <div class="timer">42 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://thomasdaubenton.xyz/portfolio/images/photo.jpg);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Thomas Dbtn</p> <p class="message">See you tomorrow ! 🙂</p> </div> <div class="timer">2 hour</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1553514029-1318c9127859?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=80);"> </div> <div class="desc-contact"> <p class="name">Elsie Amador</p> <p class="message">What the f**k is going on ?</p> </div> <div class="timer">1 day</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1541747157478-3222166cf342?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=967&q=80);"> </div> <div class="desc-contact"> <p class="name">Billy Southard</p> <p class="message">Ahahah 😂</p> </div> <div class="timer">4 days</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1435348773030-a1d74f568bc2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Paul Walker</p> <p class="message">You can't see me</p> </div> <div class="timer">1 week</div> </div> </section> <section class="chat"> <div class="header-chat"> <i class="icon fa fa-user-o" aria-hidden="true"></i> <p class="name">Megan Leib</p> <i class="icon clickable fa fa-ellipsis-h right" aria-hidden="true"></i> </div> <!-- 將訊息限制在聊天區域內,超過的話會可以上下捲動 --> <div id="chat" class="messages-chat" style="height: 530px; overflow-y: auto;"> <!-- 左邊訊息 --> <!-- <div class="message"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <p class="text"> Hi, how are you ?</p> </div> <p class="time"> 14:58</p> --> <!-- 右邊訊息 --> <!-- <div class="message text-only"> <div class="response"> <p class="text"> Hey Megan ! It's been a while 😃</p> </div> </div> <p class="response-time time"> 15:04</p> --> </div> <div class="footer-chat"> <i class="icon fa fa-smile-o clickable" style="font-size:25pt;" aria-hidden="true"></i> <!-- 這是輸入框 --> <input id="message" type="text" class="write-message" placeholder="Type your message here11111"></input> <!-- 這是送出按鈕, 新增id屬性 --> <i id="send" class="icon send fa fa-paper-plane-o clickable" aria-hidden="true"></i> </div> </section> </div> </div> </body> <!-- partial --> <script src="./script.js"></script> <script> // 新增網頁載入完成事件傾聽器,確認網頁載入完成才有DOM可以用 document.addEventListener('DOMContentLoaded', function () { // 打/v1/chat API跟OpenAI聊天 function chat(msg) { // 以GET方式呼叫API axios.get('/v1/chat?msg=' + msg) .then(function (response) { // handle success console.log('你好'); console.log(response); if(response.data.indexOf('https://') == 0) addLeftImage(response.data) else addLeftMessage(response.data); }) .catch(function (error) { // handle error console.log(error); }) .finally(function () { // always executed }); } function addLeftImage(addr) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let img = document.createElement('img'); img.src = addr; img.style = "width: 100%; margin-bottom: 10px;"; chat.appendChild(img); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } function addLeftMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message') let div2 = document.createElement('div'); div2.classList.add('photo') // 幫新的元素設定樣式 div2.style = "min-width: 45px; background-image: url(https://static-00.iconduck.com/assets.00/openai-icon-2021x2048-4rpe5x7n.png);" let div3 = document.createElement('div'); div3.classList.add('online') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div2.appendChild(div3); div1.appendChild(div2); div1.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } function addRightMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message'); div1.classList.add('text-only'); let div2 = document.createElement('div'); div2.classList.add('response') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('response-time'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div1.appendChild(div2); div2.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } // 取得id=messgage這個標籤 let message_input = document.getElementById('message'); // 幫message標籤加上鍵盤按下事件傾聽器,處理Enter被按下 message_input.addEventListener('keydown', function (event) { if (event.key == 'Enter') { // 透過message標籤物件的value屬性來取得使用者輸入的文字 chat(message_input.value); addRightMessage(message_input.value); } }); // 取得id=send這個標籤 let send_btn = document.getElementById('send'); // 幫send標籤加上點擊事件傾聽器,處理點擊要做的事情 send_btn.addEventListener('click', function () { chat(message_input.value); addRightMessage(message_input.value); }); }); </script> </body> </html> ``` #### my-server.py ```python= from flask import Flask, request, send_file import concert_ai # 初始化flask app = Flask(__name__) @app.route('/v1/hello') def hello(): return 'Hello Flask' @app.route('/v1/chat') def chat(): msg = request.args.get('msg') return concert_ai.chat(msg) @app.route('/<path>') def static_file(path): return send_file(f'web/{path}') @app.route('/') def home(): return send_file('web/index.html') # 啟動flask server app.run(debug=True, port=80) ``` #### concert-ai.py ```python= import requests from bs4 import BeautifulSoup import pymysql import time from apscheduler.schedulers.background import BackgroundScheduler from openai import OpenAI import datetime import json import subprocess print('初始化OpenAI ...') client = OpenAI() time.sleep(1) # 將年代售票網的網頁爬下來並存成my-concert-01.html def crawl_web_1(): url = 'https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'} response = requests.get(url, headers=headers) print(response.status_code) print(response.text) with open('my-concert-01.html', 'w', encoding='utf-8') as file: file.write(response.text) def parse_web_1(): result = [] with open('my-concert-01.html', 'r', encoding='utf-8') as file: soup = BeautifulSoup(file.read(), 'html.parser') all_concerts = soup.find_all(class_='caption') for c in all_concerts: start = c.p.span.text.split('-')[0] start = start[:-3].replace('/', '-') end = c.p.span.text.split('-')[1] if len(c.p.span.text.split('-')) > 1 else c.p.span.text.split('-')[0] end = end[:-3].replace('/', '-') result.append( { 'name': c.h4.text, 'description': c.div.text, 'start_date': start, 'end_date': end } ) return result def update_db(concert_data): try: conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() for c in concert_data: curosr.execute(f'''INSERT INTO my_concert (name, description, start_date, end_date) SELECT * FROM (SELECT '{c['name']}' name, '{c['description']}' description, '{c['start_date']}' start_date, '{c['end_date']}' end_date) a WHERE NOT EXISTS ( SELECT name, description, start_date, end_date FROM my_concert mc WHERE name='{c['name']}' and description='{c['description']}' AND start_date='{c['start_date']}' AND end_date = '{c['end_date']}' );''') conn.commit() except Exception as e: print('資料庫失敗:', e) return False return True def schedule_job_1(): print('爬取年代售票網 .....') crawl_web_1() print('解析年代售票網 .....') result = parse_web_1() print('更新資料庫 .....') update_db(result) # 詳細描述有哪些function call可以用給OpenAI tools = [ { "type": "function", "function": { "name": "get_concert_next_month", "description": "取得下個月的演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "integer", "description": "Current UTC timestamp", }, }, } }, }, { "type": "function", "function": { "name": "get_concert_available", "description": "取得指定歌星最近的演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "歌星名字", }, }, "required": ["num"], } }, }, { "type": "function", "function": { "name": "open_explorer", "description": "打開檔案總管", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "沒事", }, }, } }, }, { "type": "function", "function": { "name": "open_yahoo", "description": "打開yahoo", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "沒事", }, }, } }, }, { "type": "function", "function": { "name": "generate_image", "description": "文生圖", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "圖片描述", }, }, "required": ["num"], } }, } ] def open_explorer(num): subprocess.run('explorer') return '已打開' def open_yahoo(num): subprocess.run('C:\Program Files\Google\Chrome\Application\chrome.exe https://www.yahoo.com.tw') return '已前往Yahoo' def generate_image(num): response = client.images.generate( model="dall-e-3", prompt=num, n=1, size="1024x1024" ) print(response.data[0].url) return response.data[0].url def get_concert_next_month(num): today = datetime.datetime.today() datem_s = datetime.datetime(today.year, today.month + 1, 1).strftime('%Y-%m-%d') datem_e = datetime.datetime(today.year, today.month + 2, 1).strftime('%Y-%m-%d') conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() curosr.execute(f'SELECT * FROM my_concert mc WHERE start_date >= \'{datem_s}\' AND end_date < \'{datem_e}\';') result = '' for c in curosr: result += c[1] + ',' return result def get_concert_available(num): conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() curosr.execute(f'SELECT start_date FROM my_concert mc WHERE name LIKE \'%{num}%\';') result = '' for c in curosr: result += c[0].strftime('%Y-%m-%d') + ',' return result def chat(user): messages = [ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": user} ] completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, tools=tools, tool_choice="auto", ) response_message = completion.choices[0].message result = completion.choices[0].message.content tool_calls = completion.choices[0].message.tool_calls if tool_calls: available_functions = { "get_concert_next_month": get_concert_next_month, "get_concert_available": get_concert_available, "open_explorer": open_explorer, "open_yahoo": open_yahoo, "generate_image": generate_image } messages.append(response_message) for tool_call in tool_calls: function_name = tool_call.function.name function_to_call = available_functions[function_name] function_args = json.loads(tool_call.function.arguments) # 涵式呼叫 function_response = function_to_call( num=function_args.get("num"), ) # 需要文生圖 if function_name == 'generate_image': return function_response messages.append( { "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, ) result = completion.choices[0].message.content print(result) return result ``` ## 2024-07-18 #### axios 安裝文件: https://axios-http.com/docs/intro 範例文件: https://axios-http.com/docs/example #### 新增網頁伺服器 ##### my-server.py ```python= from flask import Flask, request, send_file import concert_ai # 初始化flask app = Flask(__name__) @app.route('/v1/hello') def hello(): return 'Hello Flask' @app.route('/v1/chat') def chat(): msg = request.args.get('msg') return concert_ai.chat(msg) @app.route('/<path>') def static_file(path): return send_file(f'web/{path}') @app.route('/') def home(): return send_file('web/index.html') # 啟動flask server app.run(debug=True, port=80) ``` ##### index.html ```html= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>CodePen - Material Messaging App Concept</title> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css'> <link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Montserrat'> <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.min.css'> <link rel="stylesheet" href="./style.css"> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> </head> <body> <!-- partial:index.partial.html --> <body> <div class="container"> <div class="row"> <nav class="menu"> <ul class="items"> <li class="item"> <i class="fa fa-home" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-user" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-pencil" aria-hidden="true"></i> </li> <li class="item item-active"> <i class="fa fa-commenting" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-file" aria-hidden="true"></i> </li> <li class="item"> <i class="fa fa-cog" aria-hidden="true"></i> </li> </ul> </nav> <section class="discussions"> <div class="discussion search"> <div class="searchbar"> <i class="fa fa-search" aria-hidden="true"></i> <input type="text" placeholder="Search..."></input> </div> </div> <div class="discussion message-active"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Megan Leib</p> <p class="message">dsfsdkflnsdlkfnsldknflskdnflksdnfklndsflke 😳</p> </div> <div class="timer">12 sec</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://e0.365dm.com/16/08/16-9/20/theirry-henry-sky-sports-pundit_3766131.jpg?20161212144602);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Dave Corlew</p> <p class="message">Let's meet for a coffee or something today ?</p> </div> <div class="timer">3 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1497551060073-4c5ab6435f12?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=667&q=80);"> </div> <div class="desc-contact"> <p class="name">Jerome Seiber</p> <p class="message">I've sent you the annual report</p> </div> <div class="timer">42 min</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(http://thomasdaubenton.xyz/portfolio/images/photo.jpg);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Thomas Dbtn</p> <p class="message">See you tomorrow ! 🙂</p> </div> <div class="timer">2 hour</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1553514029-1318c9127859?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=80);"> </div> <div class="desc-contact"> <p class="name">Elsie Amador</p> <p class="message">What the f**k is going on ?</p> </div> <div class="timer">1 day</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1541747157478-3222166cf342?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=967&q=80);"> </div> <div class="desc-contact"> <p class="name">Billy Southard</p> <p class="message">Ahahah 😂</p> </div> <div class="timer">4 days</div> </div> <div class="discussion"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1435348773030-a1d74f568bc2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <div class="desc-contact"> <p class="name">Paul Walker</p> <p class="message">You can't see me</p> </div> <div class="timer">1 week</div> </div> </section> <section class="chat"> <div class="header-chat"> <i class="icon fa fa-user-o" aria-hidden="true"></i> <p class="name">Megan Leib</p> <i class="icon clickable fa fa-ellipsis-h right" aria-hidden="true"></i> </div> <!-- 將訊息限制在聊天區域內,超過的話會可以上下捲動 --> <div id="chat" class="messages-chat" style="height: 530px; overflow-y: auto;"> <!-- 左邊訊息 --> <!-- <div class="message"> <div class="photo" style="background-image: url(https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1050&q=80);"> <div class="online"></div> </div> <p class="text"> Hi, how are you ? </p> </div> <p class="time"> 14:58</p> --> <!-- 右邊訊息 --> <!-- <div class="message text-only"> <div class="response"> <p class="text"> Hey Megan ! It's been a while 😃</p> </div> </div> <p class="response-time time"> 15:04</p> --> </div> <div class="footer-chat"> <i class="icon fa fa-smile-o clickable" style="font-size:25pt;" aria-hidden="true"></i> <!-- 這是輸入框 --> <input id="message" type="text" class="write-message" placeholder="Type your message here11111"></input> <!-- 這是送出按鈕, 新增id屬性 --> <i id="send" class="icon send fa fa-paper-plane-o clickable" aria-hidden="true"></i> </div> </section> </div> </div> </body> <!-- partial --> <script src="./script.js"></script> <script> // 新增網頁載入完成事件傾聽器,確認網頁載入完成才有DOM可以用 document.addEventListener('DOMContentLoaded', function () { // 打/v1/chat API跟OpenAI聊天 function chat(msg) { // 以GET方式呼叫API axios.get('/v1/chat?msg=' + msg) .then(function (response) { // handle success console.log('你好'); console.log(response); addLeftMessage(response.data); }) .catch(function (error) { // handle error console.log(error); }) .finally(function () { // always executed }); } function addLeftMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message') let div2 = document.createElement('div'); div2.classList.add('photo') // 幫新的元素設定樣式 div2.style = "min-width: 45px; background-image: url(https://static-00.iconduck.com/assets.00/openai-icon-2021x2048-4rpe5x7n.png);" let div3 = document.createElement('div'); div3.classList.add('online') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div2.appendChild(div3); div1.appendChild(div2); div1.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } function addRightMessage(msg) { // 取得聊天室父元素 let chat = document.getElementById('chat'); // 建立新的元素 let div1 = document.createElement('div'); // 幫新的元素加上class屬性 div1.classList.add('message'); div1.classList.add('text-only'); let div2 = document.createElement('div'); div2.classList.add('response') let p1 = document.createElement('p'); p1.classList.add('text'); // 建立標籤內的文字node let msg_node = document.createTextNode(msg); // 將文字node設定給需要的標籤 p1.appendChild(msg_node); let p2 = document.createElement('p'); p2.classList.add('response-time'); p2.classList.add('time'); let time_node = document.createTextNode("14:00"); p2.appendChild(time_node); // 整合 div1.appendChild(div2); div2.appendChild(p1); // 將元素新增到網頁上 chat.appendChild(div1); chat.appendChild(p2); // 捲動訊息最新一則 chat.scrollTo(0, chat.scrollHeight); } // 取得id=messgage這個標籤 let message_input = document.getElementById('message'); // 幫message標籤加上鍵盤按下事件傾聽器,處理Enter被按下 message_input.addEventListener('keydown', function (event) { if (event.key == 'Enter') { // 透過message標籤物件的value屬性來取得使用者輸入的文字 chat(message_input.value); addRightMessage(message_input.value); } }); // 取得id=send這個標籤 let send_btn = document.getElementById('send'); // 幫send標籤加上點擊事件傾聽器,處理點擊要做的事情 send_btn.addEventListener('click', function () { chat(message_input.value); addRightMessage(message_input.value); }); }); </script> </body> </html> ``` ## 2024-07-17 ### 建立自己的 ChatBot Server #### 1. 安裝Flask模組 ```python= $ pip3 install flask ``` #### 2. Python建立伺服器 ##### my-server.py ```python= from flask import Flask, request import concert_ai # 初始化flask app = Flask(__name__) @app.route('/v1/hello') def hello(): return 'Hello Flask' @app.route('/v1/chat') def chat(): msg = request.args.get('msg') return concert_ai.chat(msg) # 啟動flask server app.run(debug=True) ``` ### 完整系統程式碼 ##### concert_ai.py ```python= import requests from bs4 import BeautifulSoup import pymysql import time from apscheduler.schedulers.background import BackgroundScheduler from openai import OpenAI import datetime import json print('初始化OpenAI ...') client = OpenAI() time.sleep(1) # 將年代售票網的網頁爬下來並存成my-concert-01.html def crawl_web_1(): url = 'https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'} response = requests.get(url, headers=headers) print(response.status_code) print(response.text) with open('my-concert-01.html', 'w', encoding='utf-8') as file: file.write(response.text) def parse_web_1(): result = [] with open('my-concert-01.html', 'r', encoding='utf-8') as file: soup = BeautifulSoup(file.read(), 'html.parser') all_concerts = soup.find_all(class_='caption') for c in all_concerts: start = c.p.span.text.split('-')[0] start = start[:-3].replace('/', '-') end = c.p.span.text.split('-')[1] if len(c.p.span.text.split('-')) > 1 else c.p.span.text.split('-')[0] end = end[:-3].replace('/', '-') result.append( { 'name': c.h4.text, 'description': c.div.text, 'start_date': start, 'end_date': end } ) return result def update_db(concert_data): try: conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() for c in concert_data: curosr.execute(f'''INSERT INTO my_concert (name, description, start_date, end_date) SELECT * FROM (SELECT '{c['name']}' name, '{c['description']}' description, '{c['start_date']}' start_date, '{c['end_date']}' end_date) a WHERE NOT EXISTS ( SELECT name, description, start_date, end_date FROM my_concert mc WHERE name='{c['name']}' and description='{c['description']}' AND start_date='{c['start_date']}' AND end_date = '{c['end_date']}' );''') conn.commit() except Exception as e: print('資料庫失敗:', e) return False return True def schedule_job_1(): print('爬取年代售票網 .....') crawl_web_1() print('解析年代售票網 .....') result = parse_web_1() print('更新資料庫 .....') update_db(result) # 詳細描述有哪些function call可以用給OpenAI tools = [ { "type": "function", "function": { "name": "get_concert_next_month", "description": "取得下個月的演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "integer", "description": "Current UTC timestamp", }, }, } }, }, { "type": "function", "function": { "name": "get_concert_available", "description": "取得指定歌星最近的演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "歌星名字", }, }, } }, } ] def get_concert_next_month(num): today = datetime.datetime.today() datem_s = datetime.datetime(today.year, today.month + 1, 1).strftime('%Y-%m-%d') datem_e = datetime.datetime(today.year, today.month + 2, 1).strftime('%Y-%m-%d') conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() curosr.execute(f'SELECT * FROM my_concert mc WHERE start_date >= \'{datem_s}\' AND end_date < \'{datem_e}\';') result = '' for c in curosr: result += c[1] + ',' return result def get_concert_available(num): conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') curosr = conn.cursor() curosr.execute(f'SELECT start_date FROM my_concert mc WHERE name LIKE \'%{num}%\';') result = '' for c in curosr: result += c[0].strftime('%Y-%m-%d') + ',' return result def chat(user): messages = [ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": user} ] completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, tools=tools, tool_choice="auto", ) response_message = completion.choices[0].message result = completion.choices[0].message.content tool_calls = completion.choices[0].message.tool_calls if tool_calls: available_functions = { "get_concert_next_month": get_concert_next_month, "get_concert_available": get_concert_available, } messages.append(response_message) for tool_call in tool_calls: function_name = tool_call.function.name function_to_call = available_functions[function_name] function_args = json.loads(tool_call.function.arguments) # 涵式呼叫 function_response = function_to_call( num=function_args.get("num"), ) messages.append( { "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, ) result = completion.choices[0].message.content return result if __name__ == '__main__': scheduler = BackgroundScheduler(timezone='Asia/Taipei') scheduler.add_job(schedule_job_1, 'cron', hour=4, minute=0, second=0) print('開始聊天 ...') while True: user = input('=> ') messages = [ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": user} ] completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, tools=tools, tool_choice="auto", ) response_message = completion.choices[0].message result = completion.choices[0].message.content tool_calls = completion.choices[0].message.tool_calls if tool_calls: available_functions = { "get_concert_next_month": get_concert_next_month, "get_concert_available": get_concert_available, } messages.append(response_message) for tool_call in tool_calls: function_name = tool_call.function.name function_to_call = available_functions[function_name] function_args = json.loads(tool_call.function.arguments) # 涵式呼叫 function_response = function_to_call( num=function_args.get("num"), ) messages.append( { "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, ) result = completion.choices[0].message.content print(result) ``` ## 2024-07-16 ### 專題暖身 寫一AI ChatBot功能,以演唱會資訊為主,接受使用者詢問,可以回答題型如下: 1. 下個月有甚麼演唱會? 2. 周杰倫最近會開演唱會嗎? 3. 沈文程演唱會結束了嗎? #### 系統架構 ![演唱會ChatBot架構圖](https://hackmd.io/_uploads/BykfEtQ_A.png) #### SQL Schema ![concert_data_schema](https://hackmd.io/_uploads/H1tlNiQdC.png) ```sql= CREATE TABLE `concert_data` ( `id` integer PRIMARY KEY, `name` integer, `description` integer, `start_date` date, `end_date` date ); ``` #### Python原始碼 ```python= import pymysql import requests from bs4 import BeautifulSoup from apscheduler.schedulers.background import BackgroundScheduler from openai import OpenAI import json import pprint from datetime import datetime import time TARGET_FILE_NAME_01 = 'ticket_web_01.html' def crawlWeb1(): url = 'https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205' headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'} response = requests.get(url, headers=headers) if response.status_code == 200: with open(TARGET_FILE_NAME_01, 'w', encoding='utf-8') as file: file.write(response.text) print('爬取年代售票網成功') else: print('爬取年代售票網失敗') return False # 解析網頁 concert_data = parseWeb1() # 更新到資料庫 update_db(concert_data) return True def parseWeb1(): result = [] with open(TARGET_FILE_NAME_01, 'r', encoding='utf-8') as file: soup = BeautifulSoup(file.read(), 'html.parser') all_concert = soup.find_all(class_='caption') for c in all_concert: start = c.p.span.text.split('-')[0] end = c.p.span.text.split('-')[1] if (end := len(c.p.span.text.split('-')) > 1) else c.p.span.text.split('-')[0] start = start[:-3].replace('/', '-') end = end[:-3].replace('/', '-') result.append( { 'name': c.h4.text, 'description': c.div.text, 'start_date': start, 'end_date': end } ) return result def update_db(concert_data): try: conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') cursor = conn.cursor() for c in concert_data: cursor.execute(f'''insert into concert_data (name, description, start_date, end_date) SELECT * FROM (SELECT '{c['name']}' name, '{c['description']}' description , '{c['start_date']}' start_date , '{c['end_date']}' end_date) AS a WHERE NOT EXISTS(SELECT name, description, start_date, end_date FROM concert_data WHERE name='{c['name']}' and description='{c['description']}' AND start_date='{c['start_date']}' AND end_date='{c['end_date']}');''') conn.commit() except Exception as e: print('資料庫連線失敗: ', e) return False return True # 取得下個月演唱會 def get_concert_next_month(num): today = datetime.today() datem_s = datetime(today.year, today.month + 1, 1).strftime('%Y-%m-%d') datem_e = datetime(today.year, today.month + 2, 1).strftime('%Y-%m-%d') conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') cursor = conn.cursor() cursor.execute(f'select * from concert_data where start_date >= \'{datem_s}\' and end_date < \'{datem_e}\'') result = '' for i in cursor: result += i[1] + ',' return result # 取得指定歌星最近的演唱會 def get_concert_available(num): conn = pymysql.connect(host='localhost', port=3306, user='root', password='3939889', database='new_db') cursor = conn.cursor() cursor.execute(f'select start_date from concert_data where name like \'%{num}%\'') result = '' for i in cursor: result += i[0].strftime('%Y-%m-%d') + ',' return result # 詳細描述有哪些function call可以用給OpenAI tools = [ { "type": "function", "function": { "name": "get_concert_next_month", "description": "取得下個月演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "integer", "description": "next month", }, }, "required": ["num"], } }, }, { "type": "function", "function": { "name": "get_concert_available", "description": "取得指定歌星最近的演唱會", "parameters": { "type": "object", "properties": { "num": { "type": "string", "description": "歌星名字", }, }, "required": ["num"], } }, } ] if __name__ == '__main__': print('=========================================') print('=') print('= 演唱會資料查詢AI') print('=') print('=========================================') time.sleep(1) # 初始化OpenAI print('初始化OpenAI .....') client = OpenAI() time.sleep(1) # 指定時區(一定要指定,否則會失敗) print('啟動爬蟲排程(Daily) .....') scheduler = BackgroundScheduler(timezone="Asia/Taipei") # 啟動排程 scheduler.add_job(crawlWeb1, 'cron', hour=4, minute=0, second=0) time.sleep(1) print('開始聊天 .....') while True: # 使用者詢問 user = input('=> ') messages = [ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": user} ] completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, tools=tools, tool_choice="auto", ) response_message = completion.choices[0].message tool_calls = completion.choices[0].message.tool_calls if tool_calls: available_functions = { "get_concert_next_month": get_concert_next_month, "get_concert_available": get_concert_available, } messages.append(response_message) for tool_call in tool_calls: function_name = tool_call.function.name function_to_call = available_functions[function_name] function_args = json.loads(tool_call.function.arguments) # 涵式呼叫 function_response = function_to_call( num=function_args.get("num"), ) messages.append( { "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, ) print(f'{completion.choices[0].message.content}') ``` #### 節目清單 年代售票: https://ticket.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205 拓元售票: https://tixcraft.com/activity 寬宏售票: https://kham.com.tw/application/UTK01/UTK0101_06.aspx?TYPE=1&CATEGORY=205 #### 爬蟲常識 https://hackmd.io/@aaronlife/python-topic-crawler-knowledge?utm_source=preview-mode&utm_medium=rec #### user-agent https://www.zenrows.com/blog/python-requests-user-agent#set-user-agent ## 2024-07-15 #### Fine-Tuning model 微調你的模型 ##### 步驟一: 準備資料 準備訓練資料: https://platform.openai.com/docs/guides/fine-tuning/preparing-your-dataset ##### 步驟二: 上傳訓練資料 參考: https://platform.openai.com/docs/api-reference/files/create?lang=python ```python= from openai import OpenAI client = OpenAI() # 上傳訓練資料 response = client.files.create( file=open("example.jsonl", "rb"), purpose="fine-tune" ) print(response.model_dump()) ``` ##### 開始訓練 參考: https://platform.openai.com/docs/guides/fine-tuning/create-a-fine-tuned-model ```python= # 建立訓練任務: 開始訓練 client.fine_tuning.jobs.create( training_file="xxxxxxxxxxxx", model="gpt-3.5-turbo" ) ``` > 開始訓練後,可以到dashboard觀察訓練進度 > https://platform.openai.com/finetune ##### Function Call 文件: https://platform.openai.com/docs/guides/function-calling ```python= import json import pprint from openai import OpenAI client = OpenAI() def get_zhongli_weather(num): return f'下大雨({str(num)})' # 詳細描述有哪些function call可以用給OpenAI tools = [ { "type": "function", "function": { "name": "get_zhongli_weather", "description": "取得今天中壢天氣", "parameters": { "type": "object", "properties": { "num": { "type": "integer", "description": "Current UTC timestamp", }, }, "required": ["num"], } }, } ] messages = [ {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": "今天中壢天氣如何?"} ] completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, tools=tools, tool_choice="auto", ) pprint.pprint(completion.model_dump()) response_message = completion.choices[0].message tool_calls = completion.choices[0].message.tool_calls if tool_calls: print(f'需要呼叫: {tool_calls[0].function.name} 函式') # parameter = tool_calls[0].function.arguments # parameter = json.loads(parameter)['num'] # 呼叫自己的function # result = get_zhongli_weather(parameter) available_functions = { "get_zhongli_weather": get_zhongli_weather, } messages.append(response_message) for tool_call in tool_calls: function_name = tool_call.function.name function_to_call = available_functions[function_name] function_args = json.loads(tool_call.function.arguments) # 涵式呼叫 function_response = function_to_call( num=function_args.get("num"), ) messages.append( { "tool_call_id": tool_call.id, "role": "tool", "name": function_name, "content": function_response, } ) # 進行第二次聊天 completion = client.chat.completions.create( model="gpt-3.5-turbo", messages=messages, ) print(f'{completion.choices[0].message.content}') ``` ## 2024-07-12 文字產生mp3: https://ttsmaker.com/zh-hk 語音轉文字: https://platform.openai.com/docs/api-reference/audio/createTranscription 文字轉語音: https://platform.openai.com/docs/api-reference/audio/createSpeech #### 跟AI聊天 ```python= from openai import OpenAI client = OpenAI() audio_file = open("ttsmaker-file-2024-7-12-11-17-24.mp3", "rb") transcript = client.audio.transcriptions.create( model="whisper-1", file=audio_file ) print(transcript.text) completion = client.chat.completions.create( model="gpt-3.5-turbo-0125", messages=[ {"role": "system", "content": "請用繁體中文回答"}, {"role": "system", "content": "翻譯成中文"}, {"role": "user", "content": transcript.text} ], ) print(f'ChatGPT({completion.choices[0].message.role}): {completion.choices[0].message.content}') print(f'輸入花了Token: {completion.usage.prompt_tokens}, 費用: {float(completion.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') print(f'輸出花了Token: {completion.usage.completion_tokens}, 費用: {float(completion.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') from pathlib import Path import openai response = openai.audio.speech.create( model="tts-1", voice="shimmer", input=completion.choices[0].message.content ) response.write_to_file("speech.mp3") from pygame import mixer # Load the popular external library import time mixer.init() mixer.music.load('speech.mp3') mixer.music.play() while mixer.music.get_busy(): # wait for music to finish playing time.sleep(1) ``` #### 文生圖 ```python= from openai import OpenAI client = OpenAI() response = client.images.generate( model="dall-e-3", prompt="李知恩在雨中散步", n=1, size="1024x1024" ) print(response.data[0].url) ``` #### Streaming ```python= # from openai import OpenAI # client = OpenAI() # while True: # user = input('=> ') # completion = client.chat.completions.create( # model="gpt-3.5-turbo-0125", # messages=[ # # {"role": "system", "content": "請用可愛的語氣回答"}, # {"role": "system", "content": "翻譯成中文"}, # {"role": "user", "content": user} # ], # ) # print(f'ChatGPT({completion.choices[0].message.role}): {completion.choices[0].message.content}') # print(f'輸入花了Token: {completion.usage.prompt_tokens}, 費用: {float(completion.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') # print(f'輸出花了Token: {completion.usage.completion_tokens}, 費用: {float(completion.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') # from openai import OpenAI # client = OpenAI() # response = client.chat.completions.create( # model="gpt-4-turbo", # messages=[ # {"role": "system", "content": "請用中文回答"}, # { # "role": "user", # "content": [ # {"type": "text", "text": "請直接回答有甚麼文字"}, # { # "type": "image_url", # "image_url": {'url': "https://cart.books.com.tw/member/captcha_login?9082d52892c7f1002fbafbf95258fd79"}, # }, # ], # } # ], # max_tokens=300, # ) # print(f'ChatGPT({response.choices[0].message.role}): {response.choices[0].message.content}') # print(f'輸入花了Token: {response.usage.prompt_tokens}, 費用: {float(response.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') # print(f'輸出花了Token: {response.usage.completion_tokens}, 費用: {float(response.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') from openai import OpenAI import time client = OpenAI() completion = client.chat.completions.create( model="gpt-4o", messages=[ {"role": "system", "content": "請用繁體中文回答"}, {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "logprobs參數是做甚麼用的"} ], # stream=True, temperature=1, # max_tokens=20, # n=5 logprobs=True ) # print('-----------------------') # for chunk in completion: # print(chunk.choices[0].delta.content, end='') print(f'ChatGPT({completion.to_json()}') print(f'輸入花了Token: {completion.usage.prompt_tokens}, 費用: {float(completion.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') print(f'輸出花了Token: {completion.usage.completion_tokens}, 費用: {float(completion.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') ``` ```python= # from openai import OpenAI # client = OpenAI() # while True: # user = input('=> ') # completion = client.chat.completions.create( # model="gpt-3.5-turbo-0125", # messages=[ # # {"role": "system", "content": "請用可愛的語氣回答"}, # {"role": "system", "content": "翻譯成中文"}, # {"role": "user", "content": user} # ], # ) # print(f'ChatGPT({completion.choices[0].message.role}): {completion.choices[0].message.content}') # print(f'輸入花了Token: {completion.usage.prompt_tokens}, 費用: {float(completion.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') # print(f'輸出花了Token: {completion.usage.completion_tokens}, 費用: {float(completion.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') from openai import OpenAI client = OpenAI() response = client.chat.completions.create( model="gpt-4-turbo", messages=[ {"role": "system", "content": "請用中文回答"}, { "role": "user", "content": [ {"type": "text", "text": "請直接回答有甚麼文字"}, { "type": "image_url", "image_url": {'url': "https://cart.books.com.tw/member/captcha_login?9082d52892c7f1002fbafbf95258fd79"}, }, ], } ], max_tokens=300, ) print(f'ChatGPT({response.choices[0].message.role}): {response.choices[0].message.content}') print(f'輸入花了Token: {response.usage.prompt_tokens}, 費用: {float(response.usage.prompt_tokens) / 1000000 * 5 * 32}新台幣') print(f'輸出花了Token: {response.usage.completion_tokens}, 費用: {float(response.usage.completion_tokens) / 1000000 * 15 * 32}新台幣') ``` ## 2024-07-11 #### OpenAI https://platform.openai.com/docs/overview ## OpenAI官方定價 > **注意:** > > - 所有在OpenAI內的價格,其單位都是美金,在付款請特別注意,結帳的幣別也都是以美金來做計價。 > > - OpenAI並沒有退款機制,一經付款都無法再退款:https://openai.com/policies/service-credit-terms #### 計量制 以使用量來計費方式,根據不同的modal會有不一樣的價格 ##### GPT-4 | | 價格 | Tokens | | ------------- | ----- | --------- | | 使用輸入(8K) | $0.03 | 1K Tokens | | 使用輸出(8K) | $0.06 | 1K Tokens | | 使用輸入(32K) | $0.06 | 1K Tokens | | 使用輸出(32K) | $0.12 | 1K Tokens | ##### GPT-3.5 Turbo | | 價格 | Tokens | | ------------- | ------- | --------- | | 使用輸入(4K) | $0.0015 | 1K Tokens | | 使用輸出(4K) | $0.02 | 1K Tokens | | 使用輸入(16K) | $0.003 | 1K Tokens | | 使用輸出(16K) | $0.004 | 1K Tokens | ##### Fine-tuning models | | 價格 | Tokens | | -------- | ------ | --------- | | 訓練階段 | $0.008 | 1K Tokens | | 使用輸入 | $0.012 | 1K Tokens | | 使用輸出 | $0.016 | 1K Tokens | ##### Embedding models Embedding model用來將內容轉換為向量表示。 | | 價格 | Tokens | | ---- | ------- | --------- | | 使用 | $0.0001 | 1K Tokens | > 資料來源:https://openai.com/pricing#language-models > **注意:** > > 儲值後,儲值當月時間不算,從下個月開始,使用期限為一年。 ## 何謂Token Token 是 GPT 處理內容的基本單位。Token 可以是一個字、一個詞語或特定語言中的一個字元。它們負責將輸入的內容資料轉換為 GPT 可以處理的資料格式。 通常1個Token大約等於4個英文字元,或者四分之三個中文字。 每個 GPT 模型都有一個預設的最大 Tokens 數量。例如,GPT-3.5 每次調用允許處理的最大 Tokens 數量約為 4000(4K),而16K版本則代表可以處理16000個Token。 GPT-4 32K版本則允許處理32000個Token, 這個數量包括用戶輸入和GPT輸出的所有 Tokens。 > Tokene官方計算機:https://platform.openai.com/tokenizer > 資料來源:https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them ## 速度限制 ChatGPT API不同的model有設定不同的速度限制,單位有三種: - TPM(Token per minute): - RPD(Request per day): - RPM(Request per minute): 當呼叫API的速度超過上限時會出現如下錯誤: ``` Rate limit reached for default-text-davinci-002 in organization org-{id} on requests per min. Limit: 20.000000 / min. Current: 24.000000 / min. ``` > 參考:https://platform.openai.com/account/rate-limits ## API介紹 #### 官方文件 https://platform.openai.com/docs/api-reference #### API Endpoint種類 | 名稱 | 說明 | | ----------- | ---------------- | | Audio | 語音辨識 | | Chat |聊天API | |Completions|聊天API(舊版)| | Embeddeding | 取得文字的向量值 | | Fine-tuning | 微調自己的模型 | | Files | 檔案操作 | | Images | 圖片生成 | | Models | 取得可用的模組相關資訊 | #### 使用OpenAI API #### 憑證 https://platform.openai.com/docs/api-reference/authentication ```python= curl https://api.openai.com/v1/models -H "Authorization: Bearer xxxxxxxx" ``` 使用環境變數(for Windows) ```python= curl https://api.openai.com/v1/models -H "Authorization: Bearer %OPENAI_API_KEY%" ``` 使用環境變數(for Linux) ```python= curl https://api.openai.com/v1/models -H "Authorization: Bearer $OPENAI_API_KEY" ``` #### Python初始化(無環境變數) ```python= from openai import OpenAI key = 'xxxxx馬賽克xxxxxxx' client = OpenAI(api_key=key) ``` #### Python初始化(有環境變數) ```python= from openai import OpenAI client = OpenAI() ``` #### 角色(role)介紹 OpenAI 為預先定義了三種角色,分別是:「system(系統)」、「assistant(助理)」以及「user(使用者)」。 - 系統角色(或 "system" 角色)主要是為了定義 Chat Completion API 在整個對話中的全域行為,並且允許我們把角色扮演、任務指令、背景資料等提示放在系統訊息裡。。使用這個角色來為整個對話定下基準和規則。 - user(使用者)角色代表使用者的問題或發表的言論。 - assistant(助理)角色負責存放機器人的回應。 #### 使用Chat API ```python= from openai import OpenAI client = OpenAI() completion = client.chat.completions.create( model="gpt-3.5-turbo-0125", messages=[ {"role": "system", "content": "請用可愛的語氣回答"}, {"role": "system", "content": "請用繁體中文回答"}, {"role": "user", "content": "Hello!"} ] ) print(f'ChatGPT({completion.choices[0].message.role}): {completion.choices[0].message.content}') ``` > Chat API參考: https://platform.openai.com/docs/api-reference/chat > 範例: https://platform.openai.com/docs/examples