安裝與首次請求
建立虛擬環境並安裝 SDK。全文假設使用 openai 套件版本 1 或更高。
python -m venv .venv
source .venv/bin/activate
pip install --upgrade openai從 API 金鑰 頁面取得金鑰;新用戶可一鍵領取 7 天有效的 $0.50 試用額度,無需信用卡。將金鑰設為環境變數,然後進行首次呼叫:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.glmapi.cc/v1",
api_key=os.environ["GLM_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise writing assistant."},
{"role": "user", "content": "Suggest five titles for a space-western novel."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)這就是完整的整合方式。該服務是一個獨立的 OpenAI 相容端點,僅提供單一 open-weight 模型,因此並非 GLM 背後廠商或其他任何人的官方 API。僅存在 POST /v1/chat/completions 與 GET /v1/models。快速入門列出了支援的請求欄位。
兩個小習慣能從一開始就派上用場。首先,在煙霧測試中呼叫一次 client.models.list():它會觸發 GET /v1/models,藉此證明金鑰、base URL 與網路路徑均能運作,然後才在補全請求中消耗 token。其次,在開發期間列印 resp.usage。提示詞與補全的 token 計數能讓你早期發現系統提示詞是否比預期大,且每 1M 輸入 token 為 $0.15、每 1M 輸出 token 為 $0.50,你可以心算呼叫成本。
多輪聊天與取樣設定
API 是無狀態的,因此你需要自行維護對話列表並每次傳送。一個小型類別即可應付:
class Chat:
def __init__(self, client, system, max_turns=12):
self.client = client
self.system = system
self.max_turns = max_turns
self.history = []
def say(self, text):
self.history.append({"role": "user", "content": text})
window = self.history[-self.max_turns * 2:]
resp = self.client.chat.completions.create(
model="uncensored",
messages=[{"role": "system", "content": self.system}] + window,
temperature=0.9,
top_p=0.95,
frequency_penalty=0.3,
)
reply = resp.choices[0].message.content
self.history.append({"role": "assistant", "content": reply})
return reply切片 self.history[-self.max_turns * 2:] 保留最後 12 組使用者與助手的配對。實際應用程式可能會固定幾則早期訊息(如角色設定或關鍵事實),並摘要中間內容。將輸入限制為最後 12 輪可讓對話增長時輸入成本保持固定。上下文視窗為 100,000 token(提示詞與完成內容合計),因此這是成本考量而非必要。參數 temperature、top_p、stop、seed 及懲罰參數會原樣傳遞。測試提示詞時設定 seed 以獲得可重複結果,除非要限制長度,否則不要設定 max_tokens。
串流回應
串流輸出使用伺服器傳送事件,並在產生時提供 token,這對任何互動式工具都很重要。傳入 stream=True 並遍歷:
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Describe a harbour at dawn in 120 words."}],
stream=True,
)
parts = []
for chunk in stream:
if not chunk.choices:
continue
delta = chunk.choices[0].delta.content
if delta:
parts.append(delta)
print(delta, end="", flush=True)
text = "".join(parts)對 chunk.choices 的檢查很重要,因為最後一個 chunk 包含用量資訊,且可能為空的 choices 列表。一個運作重點:串流請求會佔用並行請求槽位直到串流結束。將迴圈包裹在 try 區塊中,並在 finally 子句中關閉串流,以防程式碼中途拋出異常(例如用戶端斷線時)。務必結束或關閉串流,否則放棄串流的迴圈會觸發並行限制卻未執行任何有用工作。
JSON 模式
當輸出結果供其他程式碼使用時,請要求 JSON 物件。在提示詞中提及 JSON,讓模型知道你預期的格式:
import json
resp = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with a JSON object only."},
{"role": "user", "content": "Give a character with keys name, age, motto."},
],
)
data = json.loads(resp.choices[0].message.content)
print(data["name"])在生產環境程式碼中,仍應將 json.loads 包裹在 try 區塊中。JSON 模式提高產生有效輸出的機率,但若因停止序列或自訂限制導致請求被截斷,仍可能留下截斷的物件。使用相同輸入重試一次是廉價的修正方式,且失敗請求不計費,因此重試的成本僅為成功的那次呼叫。
函式呼叫
函式呼叫遵循 OpenAI 的格式:你在 tools 中描述函式,模型會回傳 tool_calls,你執行它們並使用 role tool 回傳結果。完整的往返流程:
import json
tools = [{
"type": "function",
"function": {
"name": "roll_dice",
"description": "Roll a die with the given number of sides.",
"parameters": {
"type": "object",
"properties": {"sides": {"type": "integer"}},
"required": ["sides"],
},
},
}]
messages = [{"role": "user", "content": "Roll a twenty-sided die for me."}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = {"value": 17, "sides": args["sides"]} # replace with real logic
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result),
})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
else:
print(msg.content)迴圈比骰子的細節更重要。模型請求進行呼叫,你的程式碼執行它,然後將助手訊息與工具結果都附加後再次請求。若省略助手訊息,結果中的 tool_call_id 將無處匹配,這是導致第二次回覆混亂的常見原因。當 tool_choice 設定為 auto 時,模型會自行決定是否呼叫工具,因此務必處理它直接回覆的分支(如上方的程式碼所示)。串流輸出也可包含 tool_calls,以片段形式傳遞,需在解析參數前依索引將其拼接。
在執行動作前請驗證參數。模型可能會產生格式錯誤的呼叫,你的程式碼應將參數視為不可信的輸入,就像來自使用者的任何資料一樣。
限制內的非同步批次
對於一次處理許多提示詞的情況,請使用 AsyncOpenAI 並搭配與你的並行請求限制相符的訊號量。完整金鑰允許 8 個並行請求,試用金鑰允許 2 個:
import asyncio, os
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.glmapi.cc/v1",
api_key=os.environ["GLM_API_KEY"],
)
gate = asyncio.Semaphore(8) # use 2 on a trial key
async def one(prompt):
async with gate:
r = await client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content
async def main():
prompts = [f"Write a one-line tagline for product {i}." for i in range(40)]
results = await asyncio.gather(*(one(p) for p in prompts))
print(len(results))
asyncio.run(main())訊號量可處理並行請求,但無法處理每分鐘 300 次請求的上限。對於長時間批次,請加入少量延遲或 token 桶限制器。輸入 token 每 1M 為 $0.15,輸出 token 每 1M 為 $0.50,40 則標語請求(輸入約 60 token,輸出 25 token)總計費用為 $0.00036 + $0.0005 = $0.00086,因此限制條件而非價格決定了批次設計。請見 定價頁面以進行儲值。
速率限制、重試與標頭
超過限制時,API 會回傳 HTTP 429。錯誤程式碼為 rate_limited(每分鐘請求過多)與 concurrency(同時請求過多)。暫時過載也會回傳 429 並帶有 Retry-After: 5 標頭。SDK 會自行重試某些失敗,你可以在客戶端設定 max_retries,但明確處理更清晰:
import time
from openai import RateLimitError
def ask(prompt, tries=4):
for attempt in range(tries):
try:
raw = client.chat.completions.with_raw_response.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
print("balance:", raw.headers.get("X-Balance-USD"))
return raw.parse().choices[0].message.content
except RateLimitError as e:
wait = float(e.response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("still rate limited")此處 with_raw_response 可存取 X-Balance-USD、X-Request-Id 與速率限制標頭。記錄任何欲回報錯誤的請求 ID。切勿將金鑰放入會提交至版本控制的 Notebook 或前端程式碼中。
不消耗額度的測試與常見錯誤
Python 整合的大部分是基礎建設,而基礎建設不需要活躍的模型。將網路呼叫封裝在一個函式中,你的測試就可以替換它。僅使用標準函式庫:
from unittest.mock import MagicMock
def reply_text(client, prompt):
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
)
return r.choices[0].message.content.strip()
def test_reply_text_strips_whitespace():
fake = MagicMock()
fake.chat.completions.create.return_value.choices = [
MagicMock(message=MagicMock(content=" hello "))
]
assert reply_text(fake, "hi") == "hello"僅在邊緣處執行少量真實呼叫:每次部署執行一次冒煙測試,並偶爾執行已儲存的提示詞集。一次約 100 token 輸入、20 token 輸出的冒煙測試成本為 $0.000015 + $0.00001 = $0.000025,這微乎其微,且失敗請求完全不計費。$0.50 試用額度遠超過測試套件所需。
進行提示詞工作時,固定seed和temperature,以便在同等條件下比較系統提示詞的兩個版本。如果在每次執行之間同時更改提示詞和取樣設定,將導致結果難以解讀。請儲存每次執行的輸出結果,並附上來自X-Request-Id標頭的請求 ID,以便稍後追蹤令人驚訝的回答。
常見 Python 錯誤
這些是導致 Python 使用者提出支援問題的主要原因:
- 僅傳遞金鑰作為
OPENAI_API_KEY。 若在程式碼中設定了base_url但遺漏金鑰變數,SDK 會發送空值或錯誤的金鑰。請明確傳遞api_key。 - 遺漏
/v1。 base URL 以/v1結尾。SDK 會自行新增/chat/completions。 - 使用來自其他服務的模型名稱。 這裡僅
uncensored有效。 - 無限制的並行請求。 對超過一千個協同程式使用
asyncio.gather且沒有訊號量,會產生一堆concurrency錯誤。 - 將串流回覆視為單一字串。 請連接資料片段,並跳過具有空
choices列表的區塊。 - 忽略上下文限制。 歷史記錄加上補全必須符合 100,000 token 的限制。在達到限制前請截斷舊的輪次。
如需儲值與當前費率,請見 定價頁面。此服務僅支援文字,因此無需設定圖片、音訊或嵌入呼叫,且提示詞不會用於訓練。
更多指南
問答
我需要哪個 Python 套件?
標準 openai 套件,版本 1 或更高。將 base_url 設定為 https://api.glmapi.cc/v1,並將模型設定為 uncensored。
串流輸出有效嗎?
是的。傳遞 stream=True 並遍歷 chunks。串流結束時會包含用量資訊,且在完成前會佔用一個並行槽位。
我可以同時執行多少個請求?
完整金鑰允許 8 個並行請求與每分鐘 300 次。試用金鑰允許 2 個並行請求與每分鐘 60 次。
我可以改用 LangChain 或 LiteLLM 嗎?
兩者都可以連接到 OpenAI 相容的端點,只要提供 base URL、金鑰與模型名稱 uncensored。請查看各專案的文件以取得確切的參數名稱。
只差一張表單,即可取得金鑰
建立帳號、複製金鑰、更改基礎 URL。這就是整個設定過程。
