Instal dan permintaan pertama
Buat lingkungan virtual dan instal SDK. Versi 1 atau lebih baru dari paket openai diasumsikan di seluruh halaman.
python -m venv .venv
source .venv/bin/activate
pip install --upgrade openaiDapatkan kunci dari halaman kunci API; pengguna baru dapat mengklaim uji coba senilai $0,50 dengan satu klik, berlaku selama 7 hari dan tanpa kartu. Simpan kunci dalam variabel lingkungan, lalu buat panggilan pertama:
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)Itulah seluruh integrasi. Layanan ini adalah endpoint yang kompatibel dengan OpenAI untuk satu model bobot terbuka, sehingga ini bukan API resmi dari vendor di balik GLM atau pihak lain. Hanya POST /v1/chat/completions dan GET /v1/models yang ada. Panduan awal mencantumkan bidang permintaan yang didukung.
Dua kebiasaan kecil membantu sejak awal. Pertama, panggil client.models.list() sekali dalam smoke test: ini mengakses GET /v1/models, sehingga membuktikan bahwa kunci, base URL, dan jalur jaringan semuanya berfungsi sebelum Anda menghabiskan satu token pun untuk penyelesaian. Kedua, cetak resp.usage selama pengembangan. Hitungan token prompt dan penyelesaian menunjukkan kepada Anda apakah prompt sistem lebih besar dari yang Anda kira, dan pada $0,15 per 1 juta input dan $0,50 per 1 juta output, Anda dapat memperkirakan biaya panggilan di kepala Anda.
Chat multi-gilir dan pengaturan sampling
API ini stateless, jadi Anda menyimpan daftar percakapan sendiri dan mengirimkannya setiap kali. Kelas kecil sudah cukup:
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 replyPotongan self.history[-self.max_turns * 2:] menyimpan dua belas pasangan pengguna dan asisten terakhir. Aplikasi nyata mungkin juga mengunci beberapa pesan awal, seperti lembar karakter atau fakta kunci, dan meringkas bagian tengah. Memotong hingga 12 putaran terakhir menjaga biaya input tetap stabil seiring pertumbuhan percakapan. Jendela konteks adalah 100.000 token untuk prompt dan completion bersama-sama, jadi ini adalah pilihan biaya lebih daripada kebutuhan. Parameter temperature, top_p, stop, seed dan penalti diteruskan. Atur seed saat Anda ingin menjalankan ulang yang dapat direproduksi saat menguji prompt, dan biarkan max_tokens tidak diatur kecuali Anda ingin membatasi panjangnya.
Respons streaming
Streaming menggunakan server-sent events dan memberikan token kepada Anda saat dihasilkan, yang penting untuk alat interaktif. Berikan stream=True dan iterasi:
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)Pemeriksaan pada chunk.choices penting karena chunk terakhir membawa penggunaan dan mungkin memiliki daftar choices kosong. Satu poin operasional: permintaan yang dialirkan memegang slot konkurensinya hingga stream berakhir. Bungkus loop dalam blok try dengan klausa finally yang menutup stream jika kode Anda menimbulkan kesalahan di tengah jalan, misalnya ketika klien terputus. Selalu selesaikan atau tutup stream, jika tidak loop yang meninggalkan stream akan mencapai batas konkurensi sambil melakukan pekerjaan yang tidak berguna.
Mode JSON
Ketika output menjadi masukan bagi kode lain, minta objek JSON. Sebutkan JSON dalam prompt juga, agar model mengetahui bentuk yang Anda harapkan:
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"])Tetap bungkus json.loads dalam blok try dalam kode produksi. Mode JSON membuat output yang valid menjadi lebih mungkin, dan permintaan yang terpotong oleh urutan berhenti atau batas Anda sendiri masih dapat meninggalkan objek yang terpotong. Mencoba lagi sekali dengan input yang sama adalah perbaikan murah, dan karena permintaan yang gagal tidak dibebankan biaya, biaya percobaan ulang hanyalah panggilan yang berhasil.
Pemanggilan alat
Pemanggilan alat mengikuti bentuk OpenAI: Anda mendeskripsikan fungsi dalam tools, model mengembalikan tool_calls, Anda menjalankannya dan mengirimkan kembali hasil dengan role tool. Perjalanan bolak-balik lengkap:
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)Loop lebih penting daripada detail dadu. Model meminta panggilan, kode Anda menjalankannya, dan Anda menambahkan pesan asisten dan hasil alat sebelum meminta lagi. Jika Anda melewatkan pesan asisten, tool_call_id dalam hasil tidak akan memiliki pasangan yang cocok, yang merupakan penyebab umum balasan kedua yang bingung. Dengan tool_choice diatur ke auto model memutuskan apakah akan memanggil alat sama sekali, jadi selalu tangani cabang di mana ia menjawab langsung, seperti yang dilakukan kode di atas. Respons yang dialirkan dapat membawa tool_calls juga, yang dikirimkan dalam potongan yang Anda gabungkan berdasarkan indeks sebelum menguraikan argumennya.
Validasi argumen sebelum bertindak. Model dapat menghasilkan panggilan yang tidak valid, dan kode Anda harus memperlakukan argumen sebagai input yang tidak dipercaya, sama seperti apa pun dari pengguna.
Batch asinkron dalam batas
Untuk banyak prompt sekaligus, gunakan AsyncOpenAI dengan semaphore yang sesuai dengan batas konkurensi Anda. Kunci penuh mengizinkan 8 permintaan konkuren, kunci uji coba 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())Semaphore menangani konkurensi tetapi bukan batas per menit 300 permintaan. Untuk batch panjang, tambahkan jeda kecil atau pembatas token-bucket. Pada $0,15 per 1 juta token input dan $0,50 per 1 juta token output, 40 permintaan slogan dengan sekitar 60 token masuk dan 25 keluar per permintaan menelan biaya $0,00036 + $0,0005 = $0,00086 secara total, jadi batas, bukan harga, membentuk desain batch. Lihat halaman harga untuk top-up.
Batas laju, percobaan ulang, dan header
Di atas batas, API mengembalikan HTTP 429. Kode kesalahan adalah rate_limited untuk terlalu banyak permintaan per menit dan concurrency untuk terlalu banyak sekaligus. Beban berlebih sementara juga mengembalikan 429 dengan header Retry-After: 5. SDK mencoba lagi beberapa kegagalan secara mandiri dan Anda dapat mengatur max_retries pada klien, tetapi penanganan eksplisit lebih jelas:
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")Di sini with_raw_response memberikan akses ke X-Balance-USD, X-Request-Id dan header batas laju. Log ID permintaan dengan kesalahan apa pun yang ingin Anda laporkan. Jangan masukkan kunci ke notebook yang Anda komit atau ke kode front-end.
Pengujian tanpa menghabiskan saldo prabayar, dan kesalahan umum
Sebagian besar integrasi Python adalah kode penghubung, dan kode penghubung tidak memerlukan model langsung. Simpan panggilan jaringan di balik satu fungsi, dan pengujian Anda dapat menggantinya. Hanya dengan pustaka standar:
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"Jalankan sejumlah panggilan nyata hanya di tepi: satu smoke test per deploy, ditambah menjalankan secara berkala set prompt yang Anda simpan. Satu smoke test dengan sekitar 100 token masuk dan 20 keluar biaya $0,000015 + $0,00001 = $0,000025, yang tidak ada apa-apanya, dan permintaan yang gagal tidak dibebankan biaya sama sekali. Kredit uji coba $0,50 jauh lebih banyak daripada yang dibutuhkan oleh suite pengujian.
Untuk pekerjaan prompt, tetapkan seed dan temperature agar Anda dapat membandingkan dua versi prompt sistem dalam kondisi yang setara. Mengubah prompt dan pengaturan pengambilan sampel antara menjalankan ulang membuat hasil sulit dibaca. Simpan output setiap menjalankan ulang dengan ID permintaan dari header X-Request-Id, dan Anda dapat melacak jawaban yang mengejutkan nanti.
Kesalahan Python umum
Ini menyebabkan sebagian besar pertanyaan dukungan yang berasal dari pengguna Python:
- Meneruskan kunci sebagai
OPENAI_API_KEYsaja. Jika Anda mengaturbase_urldalam kode tetapi lupa variabel kunci, SDK mengirimkan kunci kosong atau salah. Teruskanapi_keysecara eksplisit. - Melupakan
/v1. Base URL berakhir dengan/v1. SDK menambahkan/chat/completionssendiri. - Menggunakan nama model dari layanan lain. Hanya
uncensoredyang valid di sini. - Konkurensi tak terbatas.
asyncio.gatherlebih dari seribu koroutine tanpa semaphore menghasilkan dinding kesalahanconcurrency. - Memperlakukan balasan yang dialirkan sebagai satu string. Gabungkan delta, dan lewati chunk dengan daftar
choiceskosong. - Mengabaikan batas konteks. Riwayat plus completion harus muat dalam 100.000 token. Potong putaran lama sebelum Anda mencapainya.
Untuk top-up dan daftar harga saat ini, lihat halaman harga. Layanan ini hanya teks, jadi tidak ada panggilan gambar, audio, atau embedding untuk dipasang, dan prompt tidak digunakan untuk pelatihan.
Panduan lainnya
Tanya jawab
Paket Python apa yang saya butuhkan?
Paket openai standar, versi 1 atau lebih tinggi. Atur base_url ke https://api.glmapi.cc/v1 dan model ke uncensored.
Apakah streaming berfungsi?
Ya. Berikan stream=True dan iterasi melalui potongan data. Aliran berakhir dengan informasi penggunaan, dan ia menempati slot konkurensi hingga selesai.
Berapa banyak permintaan yang dapat saya jalankan secara paralel?
Kunci penuh mengizinkan 8 permintaan paralel dan 300 per menit. Kunci uji coba mengizinkan 2 dan 60.
Bisakah saya menggunakan LangChain atau LiteLLM sebagai gantinya?
Keduanya dapat berkomunikasi dengan endpoint yang kompatibel dengan OpenAI ketika Anda memberikan base URL, kunci, dan nama model uncensored. Periksa dokumentasi setiap proyek untuk nama parameter yang tepat.
Kunci Anda hanya selangkah lagi dari satu formulir
Buat akun, salin kunci, ubah URL dasar. Itu saja seluruh proses pengaturannya.
