KO ▾
지금 시작하기: 체험 잔액

ChatGPT 무검열 API: 스트리밍, 도구 및 JSON

실제 앱에 필요한 스트리밍 텍스트, 도구 호출 및 구조화된 출력을 지원하는 경우에만 ChatGPT 무검열 API가 유용합니다. 이 엔드포인트는 OpenAI 형식으로 세 가지 모두를 지원합니다. 아래는 각 항목에 대한 작동 코드와 지원되지 않는 항목의 명확한 목록입니다.

마지막 업데이트:

스트리밍 도구 및 JSON을 위한 컨베이어 벨트 위의 빛 입자 스트림과 로봇 팔

요약

  1. 지원: SSE를 통한 스트리밍, 스트리밍된 tool_calls를 사용한 도구 호출, JSON 모드 및 일반적인 샘플링 설정.
  2. 미지원: 이미지, 오디오 또는 비디오 생성, 임베딩, 미세 조정.
  3. 스트림은 완료될 때까지 동시 요청 슬롯을 점유합니다: 전체 키는 8개, 체험 키는 2개입니다.
  4. 모델이 잘못된 출력을 반환할 수 있으므로 도구 인수와 JSON 응답을 방어적으로 구문 분석하세요.

지원 및 미지원 항목

항목상태참고
채팅 완료예POST /v1/chat/completions
응답예POST /v1/responses
스트리밍 (SSE)예stream: true, 끝에 사용량 정보 포함
함수 호출예tools, tool_choice, 스트리밍된 tool_calls, 결과를 role: "tool" 형식으로 반환
JSON 모드예response_format: {"type":"json_object"}
샘플링 제어예temperature, top_p, stop, seed, 페널티
이미지, 오디오, 비디오아니요텍스트 전용
임베딩, 파인튜닝아니요제공되지 않음

이 모델은 ID가 uncensored인 독립 제공업체가 운영하는 오픈 웨이트 텍스트 모델입니다. 합법적인 성인 사용자에게 콘텐츠 거부 없이 응답하며, OpenAI 또는 기타 벤더의 공식 API가 아닙니다. 아래 모든 예제는 베이스 URL https://api.chatgptunfiltered.com/v1를 사용합니다.

이 목록에 없는 항목은 문서에서 달리 명시될 때까지 사용 불가로 간주해야 합니다. 여기에는 아래에 보이는 함수 호출과는 다른 벤더별 호스팅 도구가 포함됩니다.

스트리밍

stream: true를 설정하고 청크를 반복합니다. 텍스트 조각이 도착할 때마다 출력하고 마지막 청크에서 사용량을 확인합니다.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.chatgptunfiltered.com/v1",
    api_key="YOUR_KEY",
)

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Write a four-line poem about a night train."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\nusage:", chunk.usage)

두 가지 기억할 점입니다. 첫째, 스트리밍 요청은 스트림이 끝날 때까지 동시 요청 슬롯을 점유하므로, 중단된 스트림은 슬롯을 계속 차지합니다. 사용자가 떠날 때 스트림을 닫습니다. 둘째, 마지막 청크에는 사용량 데이터가 포함될 수 있지만 텍스트는 없을 수 있으므로, 위의 예시처럼 choices에 접근할 때 주의합니다.

웹 앱에서는 청크를 도착하는 대로 브라우저로 전달합니다. 예를 들어 자체 SSE 라우트를 통해 전달하고, 페이지가 각 텍스트 조각을 메시지에 추가하도록 합니다. 스트림이 종료된 후 서버에서 사용량 계산을 수행합니다. 마지막 청크에서 요청 비용을 확인할 수 있습니다.

도구 호출, 전체 과정

함수를 선언하고 모델이 해당 함수를 요청하도록 한 후, 직접 실행하여 결과를 role: "tool"로 다시 전송합니다.

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Look up the status of an order by its ID.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]

def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Where is order A-1042?"}]
r = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = r.choices[0].message

if msg.tool_calls:
    messages.append(msg.model_dump(exclude_none=True))
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        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_choice를 사용하여 이를 조정할 수 있습니다. 함수 자체는 서버가 아닌 귀하의 코드에서 실행되므로, 함수가 접근할 수 있는 데이터를 귀하가 제어합니다. 모델이 잘못된 형식의 인수를 보내거나 누락된 필드를 보낼 수 있으므로, 실행 전에 인수를 검증합니다.

도구 호출 스트리밍

stream: true를 tools와 함께 사용하면 호출이 여러 조각으로 도착합니다. 함수 이름이 먼저 오고, 인수는 index로 연결해야 하는 조각으로 도착합니다.

calls = {}

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Where is order A-1042?"}],
    tools=tools,
    stream=True,
)

for chunk in stream:
    if not chunk.choices:
        continue
    for tc in chunk.choices[0].delta.tool_calls or []:
        slot = calls.setdefault(tc.index, {"id": "", "name": "", "args": ""})
        if tc.id:
            slot["id"] = tc.id
        if tc.function and tc.function.name:
            slot["name"] = tc.function.name
        if tc.function and tc.function.arguments:
            slot["args"] += tc.function.arguments

for slot in calls.values():
    print(slot["name"], json.loads(slot["args"]))

스트리밍이 끝날 때까지 args를 구문 분석하지 마세요. 부분적으로 수신된 문자열은 유효한 JSON이 아닙니다. 이후 함수를 실행하고 비스트리밍 예제와 정확히 동일한 방식으로 결과를 다시 보내세요.

JSON 모드

구조화된 응답의 경우 JSON 모드를 활성화하고 프롬프트에 원하는 형상을 설명하세요.

r = client.chat.completions.create(
    model="uncensored",
    temperature=0.2,
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": "Reply with JSON only: {\"mood\": string, \"intensity\": integer from 1 to 5}."},
        {"role": "user", "content": "Rate this line: The door slammed and nobody spoke for an hour."},
    ],
)

try:
    data = json.loads(r.choices[0].message.content)
except json.JSONDecodeError:
    data = None  # retry once, or fall back
print(data)

JSON 모드는 유효한 JSON일 가능성을 크게 높이지만 스키마를 강제하지는 않습니다. 키와 형식을 직접 확인하고 재시도 경로를 유지하세요. 추출에는 낮은 temperature가 도움이 됩니다. temperature, top_p, stop, seed 및 페널티와 같은 샘플링 설정이 전달됩니다.

유용한 패턴은 JSON 모드를 명확한 오류 메시지를 반환하는 작은 검증 함수와 결합하는 것입니다. 그런 다음 해당 메시지를 단일 재시도로 모델에 피드백하세요: "마지막 응답에 intensity 키가 누락되었습니다. 유효한 JSON으로 다시 응답하세요." 모델은 보통 두 번째 시도에서 스스로 수정하며, 추가 요청 비용은 1센트의 일부에 불과입니다.

스트리밍 중 오류 처리

스트리밍은 일반 요청이 아닌 방식으로 실패합니다: 텍스트의 절반 이후에 연결이 끊어질 수 있습니다. 이에 대비하세요.

import time
from openai import RateLimitError, APIConnectionError

def stream_reply(messages):
    for attempt in range(3):
        parts = []
        try:
            stream = client.chat.completions.create(
                model="uncensored", messages=messages, stream=True
            )
            for chunk in stream:
                if chunk.choices and chunk.choices[0].delta.content:
                    parts.append(chunk.choices[0].delta.content)
            return "".join(parts)
        except RateLimitError:
            time.sleep(5)
        except APIConnectionError:
            time.sleep(1)
    raise RuntimeError("stream failed after retries")

여기서 429는 한도 초과 또는 일시적 과부하를 의미하며, API가 보내는 Retry-After: 5에 맞춰 5초 동안 대기하는 것이 올바른 조치입니다. 스트림이 중간에 끊기면 부분적인 답변을 받게 됩니다. 이를 버리고 재시도할지, 아니면 유지하고 모델에 계속하도록 요청할지 결정합니다. 채팅 UI의 경우 버리고 재시도하는 것이 일반적으로 사용자에게 덜 혼란스럽습니다.

계획할 한도 및 비용

  • 속도 제한: 전체 키 기준 분당 300개 요청, 체험 키 기준 분당 60개 요청.
  • 동시 요청: 전체 키 기준 8개, 체험 키 기준 2개. 모든 열린 스트림이 포함됨을 기억하세요.
  • 크기: 요청 본문 최대 8 MB; 토큰 100,000개의 컨텍스트 창, 프롬프트 및 완성 포함.
  • 429 처리: 코드 rate_limited 및 concurrency은 한도를 초과했음을 의미합니다. 일시적 과부하는 Retry-After: 5와 함께 429를 반환합니다.

비용 예시: 두 개의 요청이 포함된 함수 호출 교환. 첫 번째 요청은 입력 1,200 토큰, 출력 80 토큰, 두 번째 요청은 입력 1,400 토큰, 출력 150 토큰입니다. 입력 비용은 2,600 x $0.15 / 1M = $0.00039입니다. 출력 비용은 230 x $0.50 / 1M = $0.000115입니다. 총 비용은 $0.000505입니다. 실패하거나 거부된 요청에는 요금이 부과되지 않습니다. 속도는 가격 책정 페이지를 참조하세요. 전체 메서드 목록은 문서에 있습니다. API 키 페이지에서 $0.50 체험 크레딧으로 시작하세요. 모델 배경 지식은 무검열 ChatGPT 모델 이해하기를 참조하세요. 모든 모드에서 다음 한도가 적용됩니다: 미성년자가 관련된 성적 콘텐츠는 금지됩니다.

출시 전 체크리스트

  • 도구 인수는 try 블록에서 json.loads로 구문 분석되며, 실행 전에 예상 키와 형식과 비교됩니다.
  • 노출된 함수는 익명 사용자에게 허용할 수 있는 작업만 수행할 수 있습니다. 호출 시기는 모델이 결정하며 귀하가 결정하지 않습니다.
  • JSON 응답은 검증되며, 실패 시 재시도가 한 번 허용됩니다.
  • 스트림은 사용자가 페이지를 떠날 때 닫히므로 동시 요청 슬롯을 점유하지 않습니다.
  • 코드 내 동시 요청 한도는 키의 한도보다 작거나 같아야 합니다. 전체 키의 경우 8입니다.
  • X-Request-Id의 요청 ID가 기록됩니다.
  • 키는 서버에 저장되며 재생성할 수 있으며, 이전 키는 즉시 작동하지 않게 됩니다.

서비스는 텍스트 전용이므로 이미지나 임베딩이 필요한 기능은 다른 제공업체에서 제공해야 합니다.

자주 묻는 질문

엔드포인트는 응답을 스트림합니까?

네. stream: true를 전송하여 Server-Sent Events를 받을 수 있으며, 스트림 끝에 사용량 정보가 보고됩니다.

함수 호출을 지원하나요?

네. tools와 tool_choice를 사용하고, 응답에서 tool_calls를 읽은 후 role: tool인 메시지로 결과를 다시 전송합니다. 스트리밍되는 tool_calls도 지원됩니다.

JSON 모드는 항상 유효한 JSON을 반환합니까?

유효한 JSON이 반환될 가능성을 크게 높이지만 보장하지는 않으며 스키마를 강제하지도 않습니다. try 블록 내에서 파싱하고 실패 시 재시도하세요.

동일한 요청에서 스트리밍과 도구 사용이 가능한가요?

네. 도구 호출 이름과 인수가 조각으로 전달되므로 인덱스로 연결하고 스트림이 끝난 후 인수를 파싱하세요.

API 키 받기

회원가입, 토큰 복사, 기본 URL 업데이트. 그뿐입니다.

지금 시작하기: 체험 잔액