如何使用OpenAI API X Flask 建立網路服務-以範例為例說明-3

Sean Yeh
Python Everywhere -from Beginner to Advanced
23 min readNov 10, 2023

--

HAKONE , Japan , photo by Sean Yeh

在今天的數位時代,程式設計與AI人工智能技術成為現代學習的重要一環。尤其是對於現代的年輕人而言,了解這些先進技術的基礎知識,就像學會閱讀和寫作一樣必要。在過去兩篇文章中,我們已經帶領大家初步接觸到了OpenAI的「Quickstart Sample App」,這個簡易的範例程式不僅讓我們一窺Flask Web應用的基本操作,更開啟了與人工智能對話的大門。

然而,技術的世界永遠充滿著更多的探索與學習。今天,將會進一步深入探討如何與OpenAI的API溝通:從發送問題(prompt)到接收回答,每一步都是通往生成式人工智能深海的重要腳步。對於尚未接觸過的人來說,這或許是一次全新的挑戰,但別擔心,我會試著用最淺顯易懂的方式,帶領大家一探究竟。讓我們一起打開這扇知識的大門,繼續AI的探險之旅。

進一步了解OpenAI API的使用,可以讓我們更靈活地與這項強大技術互動。為了讓API能準確地執行我們的指令,了解如何正確設定「標頭(Header)」和「內容(Body Content)」是非常關鍵的。

標頭(Header)

首先來談談標頭部分。在標頭中,「Authorization」這個字段確保了您的身份得到API的確認,就像是進入一個只有特定成員才能進入的俱樂部,API金鑰是您的專屬會員卡。

"Authorization": "Bearer (API金鑰)"
"Content-Type": "application/json",

不但如此,我們從上面的程式碼看到在金鑰前面需要加上「Bearer」這個文字。「Bearer」一詞是用來表示這是一種 OAuth 2.0 等認證機制使用的 Bearer Token。可以把「Bearer」想像成是卡片的稱號,沒有它,API就像是個不認識您的門衛,不會讓您通行。所以,每次發送請求時,千萬不要忘了帶上這個重要的「入場券」。

再來是「Content-Type」,它告訴API你準備傳送的資料型態。就像你去郵局寄信,需要告訴郵局這是掛號還是平信,API也需要透過「Content-Type」來了解你發送的是什麼格式的資料包裹。比較常被指定的格式是「application/json」。

內容(Body Content)

至於「內容」的部分,它就像是你寄出去的信件內容,其中「model」、「prompt」和「max_tokens」這三項設定(如下面的JSON格式),決定了你要發送的問題的精確度和深度。

{
"model": "模型名稱",
"prompt": "提示指令",
"max_tokens": 整數
}

在JSON的model裡面,需要指定要用的AI模型名稱;prompt則是填入您想要問的問題;而max_tokens則是限制可容許的最大Token使用數量,這個數量要使用「整數」來表示。這些細節一點都不能馬虎,因為它們直接關係到你會得到什麼樣的回應。

這些設定都搞定後,我們就可以進入下一階段:使用程式碼來建立與API的連線,並將問題寄出去,等待OpenAI的回答。讓我們繼續往下看。

單純使用request 連結API

當我們在Python裡使用OpenAI的API,有幾種不同的方式可以進行連結。大家可能都知道OpenAI官方提供了一個Python套件叫做openai,這是一個很方便的工具,不過,今天我們先要來講的是另一種方法 — — 使用requests套件。

requests套件在Python裡頭算是非常受歡迎的,因為它簡單易用,幾行程式碼就能完成HTTP請求。這對於開發者來說,是一大福音,它讓程式開發者不需要處理繁瑣的底層網路通訊協議,直接用幾個簡單的函數就能跟OpenAI API溝通。

實際上,透過HTTP協議與OpenAI API溝通,只需要準備好URL、所使用的HTTP方法(像是GET或POST),以及正確的標頭(Headers)和請求主體(Body Content)。這就像是你去便利商店買東西,知道要去哪裡(URL),要買什麼(HTTP方法),然後拿出錢(Headers)和購物清單(Body Content),就可以順利結帳了。

至於OpenAI API提供的模型(Model)種類,它其實有好幾種(例如:chat、Completion、Edit等等),而在這裡我們主要關注的是 Completion模型。這個模型在AI的內容生成方面相當基礎,但也非常強大。這就像是學校裡的基礎課程一樣,雖然簡單,但卻是建立知識大樓的重要基石。

接下來,我們將會用一個簡單的例子,來示範如何使用requests套件來與OpenAI API進行連結,並且發送一個簡單的請求。準備好了嗎?

Completions API介紹

在OpenAI,有一個模型叫做「Completions API」。想像一下,當我們玩文字接龍遊戲,每個人依序加上一個字或一句話,故事就這樣一步步堆疊而豐富起來。ChatGPT也是運作在這樣的原理上。只要給它一段話,它就根據這段話,依照機率決定最可能接上去的文字。這過程中,它會計算出各種可能性,嘗試找到最合適、最有意義的回答來回應你。

這就是「Completions API」的魅力所在 — — 我們開始一個話題,AI則扮演著那個接話的夥伴,繼續延伸我們的故事。這不僅僅是單純的回答問題,更是一種創造性的對話過程。

如果要使用這個Completions API,就須要透過POST方法,把文字送給它。這個API的存取網址為:

# POST 方法
<https://api.openai.com/v1/completions>

使用 requests 套件1: 安裝

在Python世界中,若想與OpenAI API進行交流,除了OpenAI官方的openai套件之外,我們還有另一個選擇——那就是requests套件,它提供了一種簡潔且直觀的方法來發送HTTP請求。

首先,確保您的開發環境中已經安裝了requests。方式很簡單,只需在終端機輸入pip install requests,或者在Jupyter Notebook中加上驚嘆號變為!pip install requests即可。

# 終端機
pip install requests

# Jupyter Notebook
!pip install requests**

如果您在Google Colab這樣的雲端開發環境中工作,那麼requests已經為您準備好了,Colab 預設上已經安裝 requests 套件,無需額外安裝。

使用 requests 套件2: 匯入並使用post方法

安裝好requests後,就可以導入它,並利用post方法來向API發出請求。在這過程中,我們會設定必要的HTTP標頭,如授權和內容類型,並將訊息打包為JSON格式發送。post()傳回的是 Reponse 類別的物件,可透過 text 屬性取得 API 傳回的內容。由於回傳的內容是JSON格式的文字資料,我們需要對這個資料進行轉換。

import requests
import os
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())

response = requests.post('<https://api.openai.com/v1/chat/completions>',
headers = {
'Authorization': f'Bearer {os.getenv("OPENAI_API_KEY")}',
'Content-Type': 'application/json'
},
json = {'model': 'gpt-3.5-turbo',
"messages":[{"role": "user", "content":"Hello"}]}
)

另外,您需確保在發送請求前,環境變數中已經設置了API金鑰,這一步是必不可少的,因為沒有金鑰,API就像是一扇您無法打開的門。

使用 requests 套件3: 取json

發出POST請求後,requests會回傳一個包含API回應的Response物件。要獲取這些資料,我們可以直接透過Response物件的json()方法,將JSON格式的回應轉化為Python字典,這樣就能更方便地操作了。

reply = response.json()

撰寫至此,我們可以先執行看看,到底會得到什麼樣的結果。

print(reply)

以下是直接顯示出來reply的結果。我們可以發現這是一串JSON格式的資料。

{'id': 'chatcmpl-8H6LZzOqxyVrahCLEZFjcn3RpD6CZ',
'object': 'chat.completion',
'created': 1699086665,
'model': 'gpt-3.5-turbo-0613',
'choices': [
{'index': 0,
'message': {
'role': 'assistant',
'content': 'Hello! How can I assist you today?'},
'finish_reason': 'stop'}
],
'usage': {
'prompt_tokens': 10,
'completion_tokens': 9,
'total_tokens': 19}
}

這份JSON格式的資料中,有下面幾個部分值得特別說明的:

# model

這裡告訴我們這次API使用什麼樣的模型。不過,有趣的是您可以看到雖然我們在request 的header資訊中指定了 gpt-3.5-turbo作為模型,但實際上reply回來,卻用的是 gpt-3.5-turbo-0613模型,這是因為API會自動提供您該版本下最優的模型。

# choices

這是我們最需要的部分。它是個list串列,代表 API 回覆訊息的內容。串列中可以有很多組,每一組元素({})的 message 項目就是一個訊息。由於預設的 API 只會回覆一個訊息,因此串列中就只有一個元素({})。

# usage

是表示本次 API 的使用量,會以字典的形式呈現給我們。其中:

  • prompt_tokens表示傳送給 API 的訊息換算為 token 的數量;
  • completion_tokens表示API 回覆的訊息換算為 token 的數量;
  • total_tokens則表示這次詢問所用到的總 token 數。也就是前兩項的加總。

綜上所述,我們從回應中獲得了一些有用的信息,比如API所使用的模型、回應的token數量,以及這次互動的總token數。這些訊息不僅幫助我們了解AI的運作,也是控制成本和使用效率的重要指標。

使用 requests 套件4:顯示覆結果

接著, 就可以取得回覆訊息的內容了,也就是choices的部分。

print(reply["choices"][0]["message"]["content"])

以下是執行的結果:

只顯示出回覆的結果:『Hello! How can I assist you today?』。

使用openai 套件

Python的世界裡,當我們想要和OpenAI的AI模型進行交流,還有一個專門的工具可以使這個過程變得簡單許多,那就是官方提供的openai套件。這個套件將複雜的HTTP通訊細節打包在內,讓我們可以專注於和AI對話的內容本身。

首先,您需要在Python程式碼中匯入openai套件,就像這樣:

import openai

這行程式碼可以讓我們取用openai提供的所有功能。接著,我們會用到Completion這個模式,這是一個非常基礎,卻也是非常強大的API模式。它讓我們可以給AI模型一個開端,AI模型會在這個基礎上生成接續的文字。

使用openai套件時,我們會這樣寫程式碼:

變數 = openai.Completion.create(
model=模型名稱,
prompt=提示文字,
max_tokens=Token數量
)

上面程式碼的create方法會建立一個「Generator」的物件。雖然我們可以選擇不同的參數放在「Generator」裡面,但至少要放以下三個:

model

跟前面討論的一樣,這裡需要設定這次API將使用哪一種模型。我們可以指定 gpt-3.5-turbotext-davinci-003等,當然如果您的口袋深,也可以指定gpt-4作為使用的模型。

prompt

這裡要填入您想要問的問題,也就是「提示文字」或「prompt」。我們會將這裡面的文字送到AI模型。OpenAI 的 AI模型會根據這文字來生成回應。

max_tokens

這是生成文字的最大Token數。您可以限制可容許的最大Token使用數量,這個數量要使用「整數」來表示。

這樣一來,我們就不需要直接處理HTTP請求的複雜性,可以更直接地與AI進行互動。這對於想要迅速獲得結果的開發者來說,無疑是一個非常方便的選擇。

關於access_openai 函式

在Python中,透過openai套件的幫助,我們可以輕鬆地發送問題與OpenAI API進行溝通。接下來,我們可以來上一篇文章中提到的簡化型範例「精簡啟動應用程式(Simplified Sample App)」。

大家還記得在「精簡啟動應用程式(Simplified Sample App)」裡面有一個access_openai函式嗎?當時我們稱這個函式為「只要傳入prompt指令,就會返回GPT結果的一個百寶箱」,只要您給它一段prompt指令,它就會回給您GPT模型生成的文字。今天我們就要來打開百寶箱一探究竟。

以下程式碼就是「精簡啟動應用程式(Simplified Sample App)」中的access_openai函式:

import openai

# 這裡是你的OpenAI API金鑰
api_key = "你的API金鑰"

def access_openai(prompt_value):
openai.api_key = api_key
response = openai.Completion.create(
model="text-davinci-003",
prompt=prompt_value,
max_tokens=100,
n=2,
stop=None,
temperature=0.5
)
return response.choices[0].text.strip()

access_openai函式定義完成後,我們可以將它放到實際的使用情境中。為了要單獨測試這段程式碼,我們需要在前後加上別的程式碼。在access_openai函式的前面要加上:

import openai
api_key = "... 這裡填你的API金鑰 ..."

access_openai函式的後面則需要加上下面這一段。它可以讓使用者輸入一段文字,然後將這段文字作為access_openai函式的參數來獲得AI的回答:

if __name__ == "__main__":
input_text = input("請輸入文字:")
access_openai(input_text)

在前面 api_key 的地方,需要填入自己拿到的API金鑰。接著,儲存為python檔案(副檔名.py)後,就可以將寫好的程式碼,拿來執行看看。

整個程式會從 if __name__ == "__main__": 這一行以下開始執行,當這段程式碼被執行時,會提示使用者輸入prompt指令,然後將這些指令文字發送給OpenAI API,API會根據您的輸入生成一段文字,最後將這段文字顯示出來。不過,依照目前的程式寫法,從送出問題到顯示結果之間,會需要稍微等一下。

Generator物件的參數

在Python程式設計中,當我們要和OpenAI API交流,openai套件提供了一個方便的連接方式,這個接觸點就是openai.Completion.create。透過這個方法,可以讓我們方便地與AI模型進行溝通,只需要給它一些基本參數,它就會返回AI生成的內容。

access_openai函式中,我們使用了這個接觸點。來看看我們用了哪些參數:

response = openai.Completion.create(
model="text-davinci-003",
prompt=prompt_value,
max_tokens=100,
n=2,
stop=None,
temperature=0.5
)

其中,model參數決定了我們使用哪個AI模型,這裡我們使用的是text-davinci-003prompt參數則是我們給AI的提示文字,這個文字會影響AI生成的內容。而max_tokens則限定了AI回應的長度。

除此之外,我們還有另外三個進階參數:nstoptemperature

  • n參數讓我們可以獲得多個回應選項,這裡設定為2(n=2),意味著AI會生成兩個可能的回應。
  • stop參數可以指定一個或多個終止符號,當AI在生成內容時遇到這些符號,就會停止生成。stop參數的預設值為空的list串列。
  • temperature參數則控制生成文字內的隨機性,數值越高,生成的內容樣化越多;數值越低,內容則越可以預測。

這些參數組合起來,就像是給AI模型的一個設計藍圖,告訴它我們的需求和期望。當然,要精準地把握這些參數對生成結果的影響,可能需要一些實驗和經驗累積。但這正是程式設計的樂趣所在,透過不斷試驗,可以更深入地了解AI模型的運作,並有效地利用它來完成我們的任務。

解釋完「Generator」物件的參數後,我們來看一下access_openai 函式的運作。其中最重要的部分實際上是在使用後它所回傳的結果。

Generator 的回傳值

在Python程式中,我們通常會這樣取得模型生成的結果:

result = response.choices[0].text.strip()

這裡的「Generator」物件所回傳的response,其實是一個包含了豐富資訊的物件。它不僅承載了我們先前所見的那串JSON格式的資料,還有更多與產出結果相關的細節。這個response物件大致包含了以下結構:modelchoicesusage等欄位。

{
"choices": [
{
"finish_reason": "stop",
"index": 0,
"logprobs": null,
"text": "\\\\n\\\\nHello World is a common phrase used as a simple greeting to someone or a group of people. It is commonly used to test the functionality of a new computer program or other technology."
},
{
"finish_reason": "stop",
"index": 1,
"logprobs": null,
"text": "\\\\n\\\\nHello! My name is Ryan. It is nice to meet you."
}
],
"created": 1699267267,
"id": "cmpl-8HrKV6jhaTprLtEOeo5421DViJPNB",
"model": "text-davinci-003",
"object": "text_completion",
"usage": {
"completion_tokens": 54,
"prompt_tokens": 2,
"total_tokens": 56
},

關於 choices 部分

choices 就是 AI 模型根據輸入的資料所生成的結果,我們可以從 choices[0] 中取出結果。一般情況下,系統預設只會產生一個結果,但如果我們設置了n 參數為2,那麼就會產生兩個結果。這也解釋了為什麼這裡會有兩個不同的index"index" 是指在陣列中的位置。而"text"中就包含了 AI 模型的回應,這通常是我們最關心的部分。

此外,"finish_reason" 告訴我們,AI生成在什麼狀況下結束了。它可能會因為以下幾種情況而終止:

  • "stop":達到了設定的停止條件,AI生成作業就此結束。
  • "length":生成的 token 數量已超過了最大限制,因此停止AI生成。
  • "content_filter":部分內容因為「內容過濾規則」而被排除。

這些細節有助於我們了解 AI 如何工作,並適當地調整我們的請求以獲取最佳結果。

「Quickstart Sample App」範例程式

前面,我們已經看過簡化型範例「精簡啟動應用程式(Simplified Sample App)」中的access_openai,接下來我們再來看看 OpenAI 提供的範例程式「Quickstart Sample App」,這裡我們將一探究竟。

首先,來看看這個範例應用程式的第一部分(Part 1):

@app.route("/", methods=("GET", "POST"))
def index():
if request.method == "POST":
animal = request.form["animal"]
response = openai.Completion.create(
model="text-davinci-003",
prompt=generate_prompt(animal),
temperature=0.6,
)
return redirect(url_for("index", result=response.choices[0].text))
result = request.args.get("result")
return render_template("index.html", result=result)

在這段程式碼中,當使用者透過表單送出資料時,後端會根據送出的動物名稱(animal),使用generate_prompt函式產生一個prompt指令,再藉由OpenAI的API生成一段文字內容。

這裡的response變數就是一個包含API回應的「Generator」物件。它主要包含三個參數:必填的modelprompt,以及選填的temperature

生成的文字內容會透過response.choices[0].text取得,然後重新指向至首頁,並將生成的內容作為結果參數。

再來看看generate_prompt函式的內容:

def generate_prompt(animal):
return """Suggest three names for an animal that is a superhero.
Animal: Cat
Names: Captain Sharpclaw, Agent Fluffball, The Incredible Feline
Animal: Dog
Names: Ruff the Protector, Wonder Canine, Sir Barks-a-Lot
Animal: {}
Names:""".format(animal.capitalize())

這個函式利用使用者輸入的動物種類(透過animal參數),自動產生一串格式化的prompt指令。這種結構清晰地分開了API設定與prompt提示指令,使得開發者可以簡便地修改generate_prompt來調整提示內容,或者直接在index函式中調整API的其他設定,而不會影響到prompt指令的結構。

透過這樣的結構,我們可以清晰地管理程式碼,方便後續的維護與更新。

從 Completion 跨越到Chat Completion

提到OpenAI的API使用,我們之前都是圍繞著Completion API進行討論。不過來到了2023年7月,OpenAI 推出了專為 Chat Completion 設計的強大新模型「GPT-4」,它在多方面都超越了前代的 GPT-3.5。隨著這項進展,OpenAI 也決定將逐步淘汰Completions API

有人可能會問:「既然Completions API終將退出歷史舞台,我們還需要學習它嗎?」

這是一個好問題。其實,無論是Completion還是Chat Completion,它們在設計Prompt指令的基本理念上是相通的。只是Chat Completion加入了「角色(Role)」這個新功能,允許我們更精確地設計Prompt指令。所以,你在Completion上學到的技能,在Chat Completion上依然適用,甚至可以發揮得更淋漓盡致。

因此,對於有志於掌握最新AI技術的開發者來說,即使Completions API將被新技術取代,學習它的經驗仍然是寶貴的資產。畢竟,技術的演進永遠都是站在巨人的肩膀上,過去的知識為我們提供了更高的起點。在這個快速變遷的時代,積累的知識和經驗,將成為我們應對未來挑戰的堅實基石。

結論

走過一連串對OpenAI API的探索後,終於來到了這系列文章的尾聲,讓我們在此做個總結。

在這趟學習旅程中,我們理解到,利用OpenAI提供的API,就能夠輕鬆地將先進的人工智慧技術融入平常的應用情境中。正如同打開一道通往未來的門,OpenAI API成為我們實現創新與解決問題的利器。然而,要有效地揮舞這把魔法棒,並非只是了解其表象,而是需要深入其內層的實踐與實作。

這次,透過實際操作OpenAI範例「Quickstart Sample App」,開啟了我們如何啟動AI的力量。期間並透過Flask框架,讓我們體驗到從接收用戶輸入的指令,到發起對OpenAI API的請求,再到呈現結果於網頁的全流程,這個過程的簡潔與直覺,令人印象深刻。

此外,我們也探討了如何精準地從OpenAI獲取所需資料,並深入範例應用程式的結構與邏輯,洞察其中的精妙。

有句俗諺說:「工欲善其事,必先利其器。」OpenAI API就是那把鋒利的瑞士刀,在這生成式AI的浪潮中,我們不僅要學會如何使用這些工具,更要學會如何創造、如何創新。這樣,在這日新月異的時代裡,我們才能夠更自在地前進、邁向未來。

--

--

Sean Yeh
Python Everywhere -from Beginner to Advanced

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。