# 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