설치 및 첫 번째 요청
가상 환경을 생성하고 SDK를 설치하세요. openai 패키지의 버전 1 이상이 기본으로 가정됩니다.
python -m venv .venv
source .venv/bin/activate
pip install --upgrade openaiAPI 키 페이지에서 키를 얻으세요. 신규 사용자는 카드 없이 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)이제 전체 통합이 완료되었습니다. 이 서비스는 GLM이나 기타 벤더의 공식 API가 아닌, 단일 오픈 웨이트 모델을 위한 독립적인 OpenAI 호환 엔드포인트입니다. POST /v1/chat/completions와 GET /v1/models만 존재합니다. 빠른 시작 페이지에서 지원되는 요청 필드를 확인하세요.
초기 단계에서 두 가지 작은 습관이 도움이 됩니다. 먼저 스모크 테스트에서 client.models.list()를 한 번 호출하세요. 이는 GET /v1/models에 접근하므로, 토큰을 소모하기 전에 키, base URL 및 네트워크 경로가 모두 작동하는지 증명합니다. 둘째, 개발 중 resp.usage를 출력하세요. 프롬프트 및 completion 토큰 수는 시스템 프롬프트가 예상보다 큰지 조기에 알려주며, 입력 100만 토큰당 $0.15, 출력 100만 토큰당 $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 토큰이므로, 이는 필요보다는 비용 선택의 문제입니다. temperature, top_p, stop, seed 및 패널티 파라미터가 전달됩니다. 프롬프트 테스트 중 반복 가능한 실행을 원할 때만 seed을 설정하고, 길이 제한을 원하지 않으면 max_tokens을 생략하세요.
스트리밍 응답
스트리밍은 서버 전송 이벤트를 사용하며 생성되는 대로 토큰을 제공하므로 인터랙티브 도구에서 중요합니다. 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에 대한 가드는 중요합니다. 마지막 청크에는 사용량 정보가 포함되어 있으며 choices 목록이 비어 있을 수 있습니다. 운영상 주의할 점: 스트리밍 요청은 스트리밍이 끝날 때까지 동시 요청 슬롯을 유지합니다. 클라이언트가 연결을 끊는 등 코드 중간에 예외가 발생할 경우 스트림을 닫는 finally 절이 있는 try 블록으로 루프를 감싸세요. 스트리밍을 반드시 종료하거나 닫지 않으면, 스트리밍을 포기하는 루프는 유용한 작업을 수행하지 못하면서 동시 요청 제한에 도달합니다.
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 모드는 유효한 출력이 나올 가능성을 높이지만, stop 시퀀스나 자체 제한으로 인해 요청이 중단되면 객체가 잘릴 수 있습니다. 동일한 입력으로 한 번 재시도하는 것은 저렴한 해결책이며, 실패한 요청에는 요금이 청구되지 않으므로 재시도 비용은 성공한 호출 비용만 발생합니다.
함수 호출
함수 호출은 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개 요청의 제한은 처리하지 않습니다. 긴 배치의 경우 작은 지연 시간이나 토큰 버킷 제한자를 추가하세요. 입력 토큰 100만 개당 $0.15, 출력 토큰 100만 개당 $0.50이므로, 입력 60 토큰, 출력 25 토큰의 슬로건 요청 40건의 비용은 총 $0.00036 + $0.0005 = $0.00086입니다. 따라서 배치를 설계할 때 가격보다 제한이 더 중요합니다. 충전은 가격 책정 페이지를 참조하세요.
속도 제한, 재시도 및 헤더
제한을 초과하면 API는 HTTP 429를 반환합니다. 오류 코드는 분당 요청이 너무 많으면 rate_limited, 동시 요청이 너무 많으면 concurrency입니다. 일시적인 과부하도 Retry-After: 5 헤더와 함께 429를 반환합니다. 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를 로그에 남기세요. 키를 커밋되는 노트북이나 프론트엔드 코드에 넣지 마세요.
크레딧 소모 없이 테스트하고 흔한 실수 피하기
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 토큰, 출력 20 토큰의 스모크 테스트 하나당 비용은 $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 토큰을 초과하면 안 됩니다. 도달하기 전에 이전 대화를 줄이세요.
충전 및 현재 요금표는 가격 책정 페이지를 참조하세요. 이 서비스는 텍스트 전용이므로 이미지, 오디오 또는 임베딩 호출을 연결할 필요가 없으며 프롬프트는 학습에 사용되지 않습니다.
더 많은 가이드
질문과 답변
어떤 Python 패키지가 필요한가요?
표준 openai 패키지 (버전 1 이상)를 사용하세요. base_url을 https://api.glmapi.cc/v1로 설정하고 모델을 uncensored로 지정하세요.
스트리밍이 지원되나요?
네. stream=True를 전달하고 청크를 반복하세요. 스트림은 사용량 정보로 끝나며, 완료될 때까지 동시성 슬롯을 차지합니다.
동시 요청은 몇 개까지 실행할 수 있나요?
전체 키는 동시 요청 8개와 분당 300개를 허용합니다. 체험 키는 각각 2개와 60개를 허용합니다.
LangChain이나 LiteLLM을 대신 사용할 수 있나요?
base URL, API 키 및 모델 이름 uncensored를 제공하면 둘 다 OpenAI 호환 엔드포인트와 통신할 수 있습니다. 정확한 파라미터 이름은 각 프로젝트의 문서를 참조하세요.
키는 양식 하나만 작성하면 받을 수 있습니다
계정을 생성하고 키를 복사한 후 기본 URL을 변경하세요. 설정은 이것으로 끝입니다.
