# 《OpenAI API 開發手冊》服務專區
{%youtube eEqi1Qxc5AE%}
- [本書範例檔案](#%E6%9C%AC%E6%9B%B8%E7%AF%84%E4%BE%8B%E6%AA%94%E6%A1%88)
- [旗標官網頁面](https://www.flag.com.tw/books/product/F5762)
- [勘誤](https://hackmd.io/cixa-XYvSW22UGcz6flVGA#%E5%8B%98%E8%AA%A4)
## 本書範例檔案
### 第 1 章
- [Colab 筆記本](https://colab.research.google.com/drive/1g2StUAFx5Ev3pKnr7xM66OFHTN0qm4X3?usp=sharing)
- [OpenAI API 註冊與申請金鑰流程](https://hackmd.io/@flagmaker/BJuxF7bkye)
- [OpenAI Tokenizer 網站](https://platform.openai.com/tokenizer)
- [tiktokenizer 網站](https://tiktokenizer.vercel.app/)
### 第 2 章
- [Colab 筆記本](https://colab.research.google.com/drive/1tZhG0mCG4s4S3DbgzqfSZPeEaC3QXvvb?usp=sharing)
- [驗證組織的網頁](https://platform.openai.com/settings/organization/general)
#### 輸入圖片的計費
計費方式相關資訊:
- [輸入圖片的計費方式](https://platform.openai.com/docs/guides/images#calculating-costs)
- [Pricing 頁面](https://openai.com/api/pricing/) FAQ 區最後有輸入圖片的費用計算機
- [驗算用的輔助函式](https://colab.research.google.com/drive/15UqEWFWyRKOSnm7UzgplW4pJ7xYV8t29?usp=sharing)
### 第 3 章
- [Colab 筆記本](https://colab.research.google.com/drive/1xREEYvkOMppizk5WorSF_nQU80A-LGrt?usp=sharing)
### 第 4 章
- [Colab 筆記本](https://colab.research.google.com/drive/1EXEpkyxETc1Sn7_2BMdKd6brasmINwDL?usp=sharing)
- [搜尋工具的計價方式](https://platform.openai.com/docs/pricing#web-search)
- [檔案檢索工具的計費方式](https://platform.openai.com/docs/pricing#built-in-tools)
### 第 5 章
- [Colab 筆記本](https://colab.research.google.com/drive/1GGomc4iwTq3ohX_cx16P5dT1mI0O6MV0?usp=sharing)
### 第 6 章
- [Colab 筆記本](https://colab.research.google.com/drive/1pVisiBOP3iCqWxhXvw_OHUP9NUq-gLvx?usp=sharing)
- [最後版本的 Chat 類別](https://colab.research.google.com/drive/1wqyPtoddd7xG-TC3JtNqee6aIJaiIXFc?usp=sharing)
### 第 7 章
- [範例下載](https://github.com/FlagTech/F5762_mcp/archive/refs/heads/master.zip)
- [uv 工具教學](https://reurl.cc/YYmamx)
- uv 工具安裝:
- Windows:
```
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
```
- Mac 或是 Linux:
```
curl -LsSf https://astral.sh/uv/install.sh | sh
```
#### 7-2 MCP 伺服器開發
:::info
請記得修改 MCP 伺服器程式檔路徑為你自己專案的路徑。
:::
##### 測試 MCP 伺服器:
```json
{
"mcpServers": {
"shell_helper": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_shell_helper.py"
]
}
}
}
```
##### 多個 MCP 伺服器共同運作
```json
{
"mcpServers": {
"shell_helper": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_shell_helper.py"
]
},
"google_search": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_google_search.py"
]
}
}
}
```
#### 7-3 MCP 用戶端
##### 使用單一 MCP 伺服器的應用程式
```json
{
"mcpServers": {
"shell_helper": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_shell_helper.py"
]
},
}
}
```
##### 同時使用多個 MCP 伺服器
```json
{
"mcpServers": {
"shell_helper": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_shell_helper.py"
]
},
"google_search": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_google_search.py"
]
}
}
}
```
##### 使用其他人設計的 MCP 伺服器
```json
{
"mcpServers": {
"shell_helper": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_shell_helper.py"
]
},
"google_search": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_google_search.py"
]
},
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"]
}
}
}
```
###### JSON Schema 相容問題
```json
{
"mcpServers": {
"pan-doc": {
"command": "uvx",
"args": ["pan-doc"]
}
}
}
```
### 第 8 章
本章範例與第 7 章是同一個專案,沿用即可。若有需要,再重新下載:
- [範例下載](https://github.com/FlagTech/F5762_mcp/archive/refs/heads/master.zip)
本章大部分都是在本機端執行的程式範例,但 8-2 節會使用到單獨的 Colab 筆記本:
- [Colab 筆記本](https://colab.research.google.com/drive/1qvaIlGVAZWEITFyW8WbgPYfQgA6dEJmW?usp=sharing)
#### 8-1 使用 SSE 將 MCP 伺服器部署在網路上
##### 使用 SSE 傳輸的 MCP 用戶端:
```json
{
"mcpServers": {
"sse_google_search": {
"url": "http://localhost:8000/sse"
}
}
}
```
##### 使用 Streamable HTTP 在網路上部署 MCP 伺服器:
```json
{
"mcpServers": {
"http_google_search": {
"type": "http",
"url": "http://localhost:8000/mcp"
}
}
}
```
#### 8-2 使用公開在網路上的 MCP 伺服器
##### GitMCP 使用方法
```json
{
"mcpServers": {
"sse_google_search": {
"url": "http://localhost:8000/sse"
},
"gitmcp": {
"type": "openai",
"url": "https://gitmcp.io/idosal/git-mcp"
}
}
}
```
##### 使用 OpenAI 內建工具連接部署在公開網路上的 MCP 伺服器
- [Colab 筆記本](https://colab.research.google.com/drive/1qvaIlGVAZWEITFyW8WbgPYfQgA6dEJmW?usp=sharing)
#### 8-3 使用環境變數傳遞機密資訊給 MCP 伺服器
:::warning
請記得修改 MCP 伺服器執行檔的路徑為你自己專案的路徑。
:::
##### 預設揭露給 MCP 伺服器的環境變數
```json
{
"mcpServers": {
"show environ": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_environ.py"
]
}
}
}
```
##### 使用 env 項目傳遞環境變數給 MCP 伺服器
```json
{
"mcpServers": {
"show environ": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_environ.py"
],
"env": {
"FAKE_API_KEY": "my_fake_api_key"
}
}
}
}
```
#### 8-4 操控其他應用程式的 MCP 伺服器
##### 測試控制 Spotify 的 MCP 伺服器
```json
{
"mcpServers": {
"spotify": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_spotify.py"
],
"env": {
"SPOTIFY_CLIENT_ID": "你的 Client id",
"SPOTIFY_CLIENT_SECRET": "你的 Client secret"
}
}
}
}
```
### 第 9 章
- [範例下載](https://github.com/FlagTech/F5762_realtime/archive/refs/heads/master.zip)
#### 9-3 進入 Realtime API 的語音世界
##### 用語音控制 MCP 伺服器
```json
{
"mcpServers": {
"spotify": {
"command": "uv",
"args": [
"--directory",
"C:/temp/mcp_test",
"run",
"server_spotify.py"
],
"env": {
"SPOTIFY_CLIENT_ID": "你的 Client id",
"SPOTIFY_CLIENT_SECRET": "你的 Client secret"
}
}
}
}
```
### 第 10 章
- [Colab 筆記本](https://colab.research.google.com/drive/1n1VkVBxfO54GEbJ6EIEOagopU3aMuxyy?usp=sharing)
- [範例檔案下載](https://github.com/FlagTech/F5762_agents_sdk/archive/refs/heads/master.zip)
## 勘誤
### 第 2 章
#### 2-29 頁輸入圖片的計費方式
- gpt-4.1 的計費方式比照 gpt-4o
- gpt-4.1 mini 與 gpt-4.1-nano 說明中『以短邊縮小到 32 的倍數』,實際上會以短邊及長邊都各試算一次,最後**以區塊數少**的為準,再轉換成 token 數
### 第 5 章
#### 5-23 頁最下方 FunctionCallingCommand 類別的 make_tool_msg 函式
原本的程式碼如下:
```python=25
# 叫用單一函式並且將函式執行結果組成訊息後傳回
def make_tool_msg(self, tool_call):
tool_info = f'{tool_call.name}(**{tool_call.arguments})'
if self.verbose: print(f'叫用:{tool_info}')
result = eval(tool_info)
return { # 建立可傳回函式執行結果的字典
"type": "function_call_output", # 以工具角色送出回覆
"call_id": tool_call.call_id, # 叫用函式的識別碼
"output": result # 函式傳回值
}
```
但因為 API 回覆的引數內容是 JSON 格式,會遇到像是 true/false 與 Python 的 True/False 大小寫不同的問題,最好先從 JSON 轉回 Python 後再執行函式,如下所示:
```python=25
# 叫用單一函式並且將函式執行結果組成訊息後傳回
def make_tool_msg(self, tool_call):
tool_info = f'{tool_call.name}(**{tool_call.arguments})'
if self.verbose: print(f'叫用:{tool_info}')
func = eval(tool_call.name)
args = json.loads(tool_call.arguments)
result = func(**args)
return { # 建立可傳回函式執行結果的字典
"type": "function_call_output", # 以工具角色送出回覆
"call_id": tool_call.call_id, # 叫用函式的識別碼
"output": result # 函式傳回值
}
```
### 第 6 章
#### 6-3 頁最下方儲存格程式碼
這裡程式碼與上一段重複,應該是如下[整理執行結果為 markdown 格式](https://colab.research.google.com/drive/1pVisiBOP3iCqWxhXvw_OHUP9NUq-gLvx?authuser=1#scrollTo=TAcv3QqeZPok&line=1&uniqifier=1):
```python=1
output_md = (
f'執行結果\n\n```\n'
f'{result.stdout.decode("utf8")}\n'
f'```\n\n錯誤訊息\n\n```\n'
f'{result.stderr.decode("utf8")}\n'
f'```\n\n結束碼:{result.returncode}'
)
print(output_md)
```
#### 6-32 頁
- 中間的程式碼:
```python=27
def handle_command(self, chat, cmd):
if not super().handle_command(chat, cmd):
return False
idx = chat.find_tool_index(self.tool_name)
if idx == -1:
chat.tools.append({
'type': self.tool_name,
'partial_images': 3 if self.verbose else 0
})
else:
chat.tools.pop(idx)
return True
```
中間第 34 行原本在非 verbose 模式時設定變化圖為 0 張,不過因為會採用串流方式,必須設定至少為 1 張,因此這裡改為 1:
```python=27
def handle_command(self, chat, cmd):
if not super().handle_command(chat, cmd):
return False
idx = chat.find_tool_index(self.tool_name)
if idx == -1:
chat.tools.append({
'type': self.tool_name,
'partial_images': 3 if self.verbose else 1
})
else:
chat.tools.pop(idx)
return True
```
- 最下方程式碼:
```python=40
def handle_event(self, chat, stream, event):
if event.type == 'response.completed':
b64_data = self.get_img_b64(event.response.output)
if not b64_data: return None
elif event.type == (
'response.image_generation_call.partial_image'
):
b64_data = event.partial_image_b64
else: return None
return get_img_obj(b64_data)
```
最後一行漏掉了 `self.`,應該為:
```python=40
def handle_event(self, chat, stream, event):
if event.type == 'response.completed':
b64_data = self.get_img_b64(event.response.output)
if not b64_data: return None
elif event.type == (
'response.image_generation_call.partial_image'
):
b64_data = event.partial_image_b64
else: return None
return self.get_img_obj(b64_data)
```