昨天維元老師的 ChatGPT 實戰內訓課程講到 OpenAI API 串接實作, 我因在前一篇測試中已經註冊 OpenAI 帳戶並建立了存取 GPT 模型的金鑰, 所以就能在課堂上邊聽邊在 Colab 上測試, 本篇記錄利用 openai 這個 Python 套件與模型的串接與對話.
本系列之前的文章參考 :
要串接 OpenAI API 必須先取得金鑰才能進行本篇測試.
一. 安裝 openai 套件 :
串接 OpenAI API 必須先安裝 openai 套件 :
pip install openai
如果是在 Colab 則指令前面要冠驚嘆號 :
!pip install openai
D:\python\test>pip install openai
Collecting openai
Downloading openai-1.14.0-py3-none-any.whl.metadata (18 kB)
Requirement already satisfied: anyio<5,>=3.5.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (3.7.1)
Collecting distro<2,>=1.7.0 (from openai)
Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
Requirement already satisfied: httpx<1,>=0.23.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (0.24.1)
Requirement already satisfied: pydantic<3,>=1.9.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (2.5.3)
Requirement already satisfied: sniffio in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (1.3.0)
Requirement already satisfied: tqdm>4 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (4.66.1)
Requirement already satisfied: typing-extensions<5,>=4.7 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from openai) (4.9.0)
Requirement already satisfied: idna>=2.8 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from anyio<5,>=3.5.0->openai) (3.4)
Requirement already satisfied: exceptiongroup in c:\users\tony1\appdata\roaming\python\python310\site-packages (from anyio<5,>=3.5.0->openai) (1.1.3)
Requirement already satisfied: certifi in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpx<1,>=0.23.0->openai) (2023.7.22)
Requirement already satisfied: httpcore<0.18.0,>=0.15.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpx<1,>=0.23.0->openai) (0.17.3)
Requirement already satisfied: annotated-types>=0.4.0 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pydantic<3,>=1.9.0->openai) (0.5.0)
Requirement already satisfied: pydantic-core==2.14.6 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from pydantic<3,>=1.9.0->openai) (2.14.6)
Requirement already satisfied: colorama in c:\users\tony1\appdata\local\programs\thonny\lib\site-packages (from tqdm>4->openai) (0.4.6)
Requirement already satisfied: h11<0.15,>=0.13 in c:\users\tony1\appdata\roaming\python\python310\site-packages (from httpcore<0.18.0,>=0.15.0->httpx<1,>=0.23.0->openai) (0.14.0)
Downloading openai-1.14.0-py3-none-any.whl (257 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 257.5/257.5 kB 933.1 kB/s eta 0:00:00
Downloading distro-1.9.0-py3-none-any.whl (20 kB)
Installing collected packages: distro, openai
Successfully installed distro-1.9.0 openai-1.14.0
先用 import openai 匯入套件, 然後用 dir() 檢視套件內容 :
>>> import openai
>>> print(openai.__version__)
1.14.0
>>> dir(openai)
['APIConnectionError', 'APIError', 'APIResponse', 'APIResponseValidationError', 'APIStatusError', 'APITimeoutError', 'AssistantEventHandler', 'AsyncAPIResponse', 'AsyncAssistantEventHandler', 'AsyncAzureOpenAI', 'AsyncClient', 'AsyncOpenAI', 'AsyncStream', 'Audio', 'AuthenticationError', 'AzureOpenAI', 'BadRequestError', 'BaseModel', 'ChatCompletion', 'Client', 'Completion', 'ConflictError', 'Customer', 'DEFAULT_MAX_RETRIES', 'DEFAULT_TIMEOUT', 'Deployment', 'Edit', 'Embedding', 'Engine', 'ErrorObject', 'File', 'FineTune', 'FineTuningJob', 'Image', 'InternalServerError', 'Model', 'Moderation', 'NOT_GIVEN', 'NoneType', 'NotFoundError', 'NotGiven', 'OpenAI', 'OpenAIError', 'PermissionDeniedError', 'ProxiesTypes', 'RateLimitError', 'RequestOptions', 'Stream', 'Timeout', 'Transport', 'UnprocessableEntityError', 'VERSION', '_AmbiguousModuleClientUsageError', '_ApiType', '_AzureModuleClient', '_ModuleClient', '__all__', '__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__locals', '__name', '__name__', '__package__', '__path__', '__spec__', '__title__', '__version__', '_azure', '_base_client', '_client', '_compat', '_constants', '_exceptions', '_extras', '_files', '_has_azure_ad_credentials', '_has_azure_credentials', '_has_openai_credentials', '_httpx', '_legacy_response', '_load_client', '_models', '_module_client', '_os', '_qs', '_reset_client', '_resource', '_response', '_setup_logging', '_streaming', '_t', '_te', '_types', '_utils', '_version', 'annotations', 'api_key', 'api_type', 'api_version', 'audio', 'azure_ad_token', 'azure_ad_token_provider', 'azure_endpoint', 'base_url', 'beta', 'chat', 'completions', 'default_headers', 'default_query', 'embeddings', 'file_from_path', 'files', 'fine_tuning', 'http_client', 'images', 'lib', 'max_retries', 'models', 'moderations', 'organization', 'override', 'pagination', 'resources', 'timeout', 'types', 'version']
用 dir() 看不出哪些是類別, 函式, 與模組, 這可用下列自訂模組 members 來達成 :
# members.py
import inspect
def varname(x):
return [k for k,v in inspect.currentframe().f_back.f_locals.items() if v is x][0]
def list_members(parent_obj):
members=dir(parent_obj)
parent_obj_name=varname(parent_obj)
for mbr in members:
child_obj=eval(parent_obj_name + '.' + mbr)
if not mbr.startswith('_'):
print(mbr, type(child_obj))
將此函式存成 members.py 模組, 放在目前供作目錄下, 然後匯入其 list_members() 函式來檢視 openai 套件 :
>>> from members import list_members
>>> list_members(openai)
APIConnectionError <class 'type'>
APIError <class 'type'>
APIResponse <class 'type'>
APIResponseValidationError <class 'type'>
APIStatusError <class 'type'>
APITimeoutError <class 'type'>
AssistantEventHandler <class 'type'>
AsyncAPIResponse <class 'type'>
AsyncAssistantEventHandler <class 'type'>
AsyncAzureOpenAI <class 'type'>
AsyncClient <class 'type'>
AsyncOpenAI <class 'type'>
AsyncStream <class 'type'>
Audio <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
AuthenticationError <class 'type'>
AzureOpenAI <class 'type'>
BadRequestError <class 'type'>
BaseModel <class 'pydantic._internal._model_construction.ModelMetaclass'>
ChatCompletion <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Client <class 'type'>
Completion <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
ConflictError <class 'type'>
Customer <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
DEFAULT_MAX_RETRIES <class 'int'>
DEFAULT_TIMEOUT <class 'openai.Timeout'>
Deployment <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Edit <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Embedding <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Engine <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
ErrorObject <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
File <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
FineTune <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
FineTuningJob <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Image <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
InternalServerError <class 'type'>
Model <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
Moderation <class 'openai.lib._old_api.APIRemovedInV1Proxy'>
NOT_GIVEN <class 'openai.NotGiven'>
NoneType <class 'type'>
NotFoundError <class 'type'>
NotGiven <class 'type'>
OpenAI <class 'type'>
OpenAIError <class 'type'>
PermissionDeniedError <class 'type'>
ProxiesTypes <class 'typing._UnionGenericAlias'>
RateLimitError <class 'type'>
RequestOptions <class 'typing_extensions._TypedDictMeta'>
Stream <class 'type'>
Timeout <class 'type'>
Transport <class 'type'>
UnprocessableEntityError <class 'type'>
VERSION <class 'str'>
annotations <class '__future__._Feature'>
api_key <class 'NoneType'>
api_type <class 'NoneType'>
api_version <class 'NoneType'>
audio <class 'openai._module_client.AudioProxy'>
azure_ad_token <class 'NoneType'>
azure_ad_token_provider <class 'NoneType'>
azure_endpoint <class 'NoneType'>
base_url <class 'NoneType'>
beta <class 'openai._module_client.BetaProxy'>
chat <class 'openai._module_client.ChatProxy'>
completions <class 'openai._module_client.CompletionsProxy'>
default_headers <class 'NoneType'>
default_query <class 'NoneType'>
embeddings <class 'openai._module_client.EmbeddingsProxy'>
file_from_path <class 'function'>
files <class 'openai._module_client.FilesProxy'>
fine_tuning <class 'openai._module_client.FineTuningProxy'>
http_client <class 'NoneType'>
images <class 'openai._module_client.ImagesProxy'>
lib <class 'module'>
max_retries <class 'int'>
models <class 'openai._module_client.ModelsProxy'>
moderations <class 'openai._module_client.ModerationsProxy'>
organization <class 'NoneType'>
override <class 'function'>
pagination <class 'module'>
resources <class 'module'>
timeout <class 'openai.Timeout'>
types <class 'module'>
version <class 'module'>
可見 openai 套件有非常多成員, 但串接 GPT 大模型只會用到 OpenAI 這個類別.
二. 串接 OpenAI API :
串接 API 的程式語法是很制式的, 首先從 openai 套件中匯入 OpenAI 類別 :
>>> from openai import OpenAI
接著定義一個字串變數儲存金鑰, 例如下列範例 (非真實金鑰) :
>>> api_key='sk-tonyIqevbZyyp3eWNr1966BlbkFJpo5ls6CxBcwvSyHslay2'
然後呼叫 OpenAI 類別的建構函式 OpenAI() 並將金鑰傳給 api_key 參數, 它會傳回一個 OpenAI 物件 :
>>> client=OpenAI(api_key=api_key)
>>> type(client)
<class 'openai.OpenAI'>
最後呼叫此 OpenAI 物件的 chat.completions.create() 方法, 並傳入 messages (詢問訊息) 與 model (語言模型) 這兩個參數, 它會傳回一個 ChatCompletion 物件 (即 GPT 生成之回應) :
>>> chat_completion=client.chat.completions.create(
messages=[
{"role": "user",
"content": "請說一個好笑的笑話",
}],
model="gpt-3.5-turbo",
)
>>> type(chat_completion)
<class 'openai.types.chat.chat_completion.ChatCompletion'>
參數 message 是一個字典串列, 其中 content 屬性就是我們提出的詢問, 因為我使用的是新註冊者的免費帳戶, 所以只能使用 GTP 3.5 模型, 無法使用 GPT 4.
關於 chat.completions.create() 方法的參數用法參考 OpenAI API 說明 :
回應物件 ChatCompletion 的內容如下 :
>>> print(chat_completion)
ChatCompletion(id='chatcmpl-92b7YN2M39rI6XvX8eyKlxuTU2SzK', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='為什麼警察不玩撲克牌?因為他們怕抓到一隻大老鼠!哈哈哈哈哈!', role='assistant', function_call=None, tool_calls=None))], created=1710406376, model='gpt-3.5-turbo-0125', object='chat.completion', system_fingerprint='fp_4f2ebda25a', usage=CompletionUsage(completion_tokens=49, prompt_tokens=20, total_tokens=69))
print() 的輸出是擠成一團的字串, 可讀性差, 可以用第三方套件 rich 的 print() 來輸出可讀性高的物件內容, 關於 rich 參考 :
>>> import rich
>>> rich.print(chat_completion)
ChatCompletion(
id='chatcmpl-92b7YN2M39rI6XvX8eyKlxuTU2SzK',
choices=[
Choice(
finish_reason='stop',
index=0,
logprobs=None,
message=ChatCompletionMessage(
content='為什麼警察不玩撲克牌?因為他們怕抓到一隻大老鼠!
哈哈哈哈哈!',
role='assistant',
function_call=None,
tool_calls=None
)
)
],
created=1710406376,
model='gpt-3.5-turbo-0125',
object='chat.completion',
system_fingerprint='fp_4f2ebda25a',
usage=CompletionUsage(
completion_tokens=49,
prompt_tokens=20,
total_tokens=69
)
)
觀察 ChatCompletion 物件結構, 可知 GPT 的回應內容放在 choices[0].message.content 屬性裡 :
>>> response_message=chat_completion.choices[0].message.content
>>> print(response_message)
為什麼警察不玩撲克牌?因為他們怕抓到一隻大老鼠!哈哈哈哈哈!
完整程式碼如下 :
from openai import OpenAI
api_key='sk-tonyIqevbZyyp3eWNr1966BlbkFJpo5ls6CxBcwvSyHslay2'
client=OpenAI(api_key=api_key)
chat_completion=client.chat.completions.create(
messages=[
{"role": "user",
"content": "請說一個好笑的笑話",
}],
model="gpt-3.5-turbo",
)
print(chat_completion)
response_message=chat_completion.choices[0].message.content
print(response_message)
關於 OpenAI API 的 Python 語法參考官網教學文件 :
三. 將金鑰儲存在環境變數中 :
上面直接將金鑰寫在程式中的作法並不妥當, 因為可能在分享時不經意地洩漏出去, 比較好的做法是將金鑰存在環境變數中, 參考 :
首先在目前工作目錄下建立一個檔名為 .env 的純文字隱藏檔, 用 = 把金鑰賦予一個變數名稱, 例如 OPENAI_KEY (注意, 金鑰不可用括號括起來) :
OPENAI_KEY=sk-tonyIqevbZyyp3eWNr1966BlbkFJpo5ls6CxBcwvSyHslay2
然後安裝第三方套件 python-decouple 或 python-dotenv, 此處以 python-dotenv 為例 :
pip install python-dotenv
這樣就可以匯入 dotenv 模組的 load_dotenv() 函式來載入環境變數檔 .env, 並配合使用 os.getenv() 來取得環境變數中的指定金鑰了 :
>>> from dotenv import load_dotenv
>>> import os
>>> load_dotenv()
True
>>> api_key=os.environ.get('OPENAI_API')
>>> print(api_key)
sk-tonyIqevbZyyp3eWNr1966BlbkFJpo5ls6CxBcwvSyHslay2
>>> client=OpenAI(api_key=api_key)
>>> chat_completion=client.chat.completions.create(
messages=[
{"role": "user",
"content": "請說一個好笑的笑話",
}],
model="gpt-3.5-turbo",
)
>>> response_message=chat_completion.choices[0].message.content
>>> print(response_message)
為什麼警察不玩撲克牌?因為他們怕抓到一隻大老鼠!哈哈哈哈哈!
完整程式碼如下 :
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
api_key=os.environ.get('OPENAI_API')
client=OpenAI(api_key=api_key)
chat_completion=client.chat.completions.create(
messages=[
{"role": "user",
"content": "請說一個好笑的笑話",
}],
model="gpt-3.5-turbo",
)
print(chat_completion)
response_message=chat_completion.choices[0].message.content
print(response_message)
這樣就可避免在程式中直接揭露金鑰的問題了.
沒有留言:
張貼留言