NsfwAiApi/가이드
Python용 NSFW LLM API: 스트리밍, JSON 및 도구
Python 스타일의 NSFW LLM API를 사용하려면 표준 openai 패키지와 두 가지 변경 사항(base URL 및 키)만 필요합니다. 이 가이드는 첫 번째 요청, 스트리밍, JSON 모드, 도구 호출 및 속도 제한의 깔끔한 처리 방법을 보여줍니다.

설정: 설치 및 첫 번째 호출
현재 버전의 Python 3, openai 패키지(pip install openai) 및 API 키가 필요합니다. 새 계정은 가입 후 한 번의 클릭으로 API 키 페이지에서 $0.50의 체험 크레딧을 청구할 수 있습니다. 결제 정보가 필요하지 않습니다. 키를 환경 변수에 저장하고 소스 코드에는 저장하지 마십시오.
이 서비스는 OpenAI 채팅 완성 프로토콜을 사용합니다: 생성을 위한 POST /v1/chat/completions 및 모델 목록을 위한 GET /v1/models. 인증은 Bearer 토큰이며 모델 ID는 uncensored입니다. 다음은 최소한의 작동 스크립트입니다:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a fiction co-writer for adult readers. Write vivid, non-explicit scenes."},
{"role": "user", "content": "Open a noir story in a rainy harbor town, three sentences."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)
먼저 셸에서 연결을 확인하고 싶다면 동일한 요청이 curl로도 작동합니다:
curl https://api.nsfwaiapi.com/v1/chat/completions \
-H "Authorization: Bearer $NSFWAIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "uncensored", "messages": [{"role": "user", "content": "Say hello in five words."}]}'
이 모델은 합법적인 성인 용도로 콘텐츠 거부 없이 답변하도록 조정되었습니다. 이는 모델이 작성할 내용의 어조에 영향을 미치지만 API의 형태에는 영향을 주지 않으므로 나머지 코드는 다른 모든 OpenAI 통합과 동일하게 보입니다. 픽션, 캐릭터 채팅 및 기타 합법적인 성인 프로젝트에 대한 프롬프트를 유지하고 모든 캐릭터를 성인으로 유지하십시오.
몇 가지 습관은 첫날부터 도움이 됩니다. 변경되지 않는 지침을 시스템 메시지에 넣고, 매 턴마다 변경되는 부분에 대해 사용자 메시지를 유지하십시오. 작업에 대한 temperature를 하나 선택하고 프롬프트를 비교하는 동안 그대로 유지하십시오. 동시에 두 가지를 변경하면 아무것도 배우지 못합니다. resp.usage에서 요청 ID와 토큰 수를 각 호출 옆에 기록하십시오. 그러면 청구금이나 출력에 놀랐을 때 확인할 기록이 있습니다.
SSE를 통한 스트리밍 응답
stream=True를 설정하면 서버가 서버 전송 이벤트로 토큰을 보냅니다. 채팅 앱의 경우 전체 응답을 기다리는 대신 독자가 첫 단어를 빠르게 볼 수 있으므로 이는 매우 중요합니다. 사용량 총계는 스트림 끝에 도착하므로 스트리밍된 요청도 정확히 청구 및 기록할 수 있습니다.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a short scene: two rival smugglers share a lantern during a storm."}],
stream=True,
)
text = ""
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
piece = chunk.choices[0].delta.content
text += piece
print(piece, end="", flush=True)
usage = getattr(chunk, "usage", None)
if usage:
print("\n[usage]", usage)
디버깅 시간을 아끼는 두 가지 팁이 있습니다. 첫 번째로, 마지막 청크는 빈 choices 목록을 가질 수 있으므로 루프는 인덱싱 전에 이를 확인합니다. 두 번째로, 스트리밍 요청은 스트림이 끝날 때까지 동시성 슬롯을 유지하므로, 코드가 스트림을 닫지 않고 중단하면 해당 슬롯이 사용 중 상태로 남습니다. with client.chat.completions.create(..., stream=True) as stream:를 사용하거나 조기에 읽기를 중단할 때 stream.close()를 호출하세요.
구조화된 출력을 위한 JSON 모드
캐릭터 시트, 장면 시퀀스 목록 또는 스토리 태그와 같이 산문이 아닌 데이터가 필요할 때 response_format={"type": "json_object"}와 함께 JSON 모드를 사용하세요. 프롬프트에 "JSON"이라고 명시하고 원하는 키를 설명하세요. 이 설정은 JSON 객체를 요청하지만 키의 존재를 보장하지는 않으므로 확인해야 합니다.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "Reply with a JSON object with keys: name, age, goal, secret. All characters are adults."},
{"role": "user", "content": "Create a ship's cook for a pirate adventure."},
],
temperature=0.7,
)
sheet = json.loads(resp.choices[0].message.content)
missing = [k for k in ("name", "age", "goal", "secret") if k not in sheet]
if missing:
raise ValueError(f"model skipped keys: {missing}")
print(sheet["name"], "-", sheet["goal"])
max_tokens로 인해 긴 응답이 잘리면 JSON이 불완전해지고 json.loads이 실패합니다. max_tokens은 선택 사항이므로 설정하지 않고 창을 모두 사용하게 하거나, 넉넉하게 설정하세요.
도구 호출
도구 호출은 모델이 주사위 굴리기, 캐릭터 메모 조회 또는 가격 조회와 같은 작업을 수행하도록 코드를 호출할 수 있게 합니다. tools와 필요시 tool_choice을 전달합니다. 응답에는 tool_calls이 포함되며, 이를 실행한 후 결과를 role: "tool" 메시지로 다시 전송합니다. 스트리밍 응답에는 델타에도 tool_calls가 포함됩니다.
import json
import os
import random
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
tools = [{
"type": "function",
"function": {
"name": "roll_die",
"description": "Roll one die with the given number of sides",
"parameters": {
"type": "object",
"properties": {"sides": {"type": "integer", "minimum": 2}},
"required": ["sides"],
},
},
}]
messages = [
{"role": "system", "content": "You run a tabletop fiction game. Use roll_die for any chance-based outcome."},
{"role": "user", "content": "I try to pick the lock on the captain's cabin. Roll a d20."},
]
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 = random.randint(1, args["sides"])
messages.append({"role": "tool", "tool_call_id": call.id, "content": str(result)})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools
)
print(final.choices[0].message.content)
else:
print(msg.content)
인수를 신뢰할 수 없는 입력으로 취급하세요. 웹 폼에 대해 수행하듯 실행 전에 유효성을 검사하세요. 모델은 누락된 필드나 잘못된 타입을 전송할 수 있으며, json.loads 주위에 작은 보호 장치를 두면 하나의 잘못된 호출로 세션이 종료되는 것을 방지할 수 있습니다.
속도 제한, 재시도 및 잔액
제한은 서비스를 보호하고 예측 가능한 동작을 제공합니다. 전체 키는 동시 요청 8개와 분당 300개의 요청을 허용합니다. 체험 키는 각각 2개와 60개를 허용합니다. 최대 본문 크기는 8 MB입니다. 두 가지 유형의 제한 오류가 HTTP 429로 반환됩니다. 코드 rate_limited은 분당 요청 수가 많음을 의미하고, concurrency은 동시 요청 수가 많음을 의미합니다. 일시적인 과부하도 429를 반환하며 Retry-After: 5 헤더를 포함합니다.
OpenAI SDK는 기본적으로 몇 가지 오류를 재시도합니다. 제어를 위해 이를 끄고 Retry-After을 준수하는 작은 루프를 작성하세요:
import os
import time
from openai import OpenAI, RateLimitError
client = OpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
max_retries=0,
)
def ask(messages, tries=5):
for attempt in range(tries):
try:
raw = client.chat.completions.with_raw_response.create(
model="uncensored", messages=messages
)
print("balance:", raw.headers.get("X-Balance-USD"), "request id:", raw.headers.get("X-Request-Id"))
return raw.parse()
except RateLimitError as err:
wait = float(err.response.headers.get("Retry-After", 2 ** attempt))
time.sleep(wait)
raise RuntimeError("still rate limited after retries")
reply = ask([{"role": "user", "content": "Describe a market at dawn in two sentences."}])
print(reply.choices[0].message.content)
모든 응답에는 X-Request-Id가 포함되어 있으며, 이는 지원팀에 문의할 때 참조해야 하는 값입니다. 또한 X-Balance-USD는 남은 크레딧을 보여줍니다. X-RateLimit-Limit-Requests 및 X-RateLimit-Limit-Concurrency 헤더는 귀하의 키에 적용되는 한도를 알려줍니다. 여러 작업을 동시에 실행하려면 자체 코드에서 세마포어를 사용하여 병렬성을 제한해야 합니다. 세마포어 크기를 키에 맞추면(전체 키는 8, 체험 키는 2) 429 오류를 거의 보지 않게 됩니다.
여러 요청을 병렬로 실행
수백 개의 이야기 단락에 태그를 생성하는 것과 같은 배치 작업에서는 동시성 한도가 중요합니다. 전체 키는 동시에 8개의 요청을 처리할 수 있으므로 비동기 클라이언트와 해당 크기의 세마포어를 사용하여 작업을 실행해야 합니다. 체험 키를 사용하는 경우 2를 사용하십시오.
import asyncio
import os
from openai import AsyncOpenAI
client = AsyncOpenAI(
base_url="https://api.nsfwaiapi.com/v1",
api_key=os.environ["NSFWAIAPI_KEY"],
)
gate = asyncio.Semaphore(8)
async def summarize(fragment):
async with gate:
resp = await client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Summarize the passage in one sentence."},
{"role": "user", "content": fragment},
],
temperature=0.3,
)
return resp.choices[0].message.content
async def main(fragments):
return await asyncio.gather(*(summarize(f) for f in fragments))
results = asyncio.run(main(["The tide came in early.", "Marta hid the ledger under the floor."]))
print(results)
세마포어가 핵심입니다. 이것이 없으면 200개의 조각 목록이 200개의 요청을 동시에 발생시켜 대부분 코드 concurrency와 함께 429 오류로 반환됩니다. 이것이 있으면 작업은 키가 허용하는 속도로 흐르고 낭비되지 않습니다. 분당 예산도 실행하는 경우, 전체 키의 분당 300개 요청은 초당 5개에 해당하므로 매우 짧은 요청은 동시성 제한보다 분당 제한에 먼저 도달할 수 있습니다.
일반적인 문제 및 해결 방법
- 401 또는 인증 오류. 키가 누락되었거나 잘렸거나 잘못된 호스트로 전송되었습니다.
client.base_url를 출력하고 환경 변수를 확인하세요. 헤더는Authorization: Bearer로 시작해야 하며 그 뒤에 키가 옵니다. - 모델을 찾을 수 없음. 유일한 모델 ID는
uncensored입니다. 다른 이름을 가진 복사된 예시는 실패하므로, 확신이 없다면GET /v1/models를 호출하세요. - 이야기 루프에서 빈 또는 짧은 응답. 다음 턴 전에 어시스턴트 응답을 메시지 목록에 추가했는지 확인하십시오. 그렇지 않으면 모델은 마지막 메시지만 봅니다.
- 반복되는 응답.
frequency_penalty를 0.3과 같이 약간 높이거나 텍스트가 어긋날 경우temperature를 낮추십시오.top_p,stop및seed와 같은 매개변수는 모두 API로 전달됩니다. - 매번 다른 출력. 디버깅 중 가능한 한 반복 가능한 결과가 필요할 때
seed과 낮은temperature을 설정하세요. - 요청이 너무 큼. 본문 제한은 8 MB이며, 프롬프트와 완성을 합쳐 100,000 토큰 안에 들어와야 합니다. 한도에 도달하기 전에 이전 컨텍스트를 자르거나 요약하십시오.
서버 측에서 문제가 있는 것으로 보이면 응답에서 X-Request-Id 헤더를 저장하고 짧은 설명과 함께 지원 페이지 연락처로 보내세요. 이를 통해 운영자가 정확한 요청을 찾을 수 있습니다.
비용 및 제한 사항
가격은 선불 크레딧 기준 입력 토큰 100만 개당 $0.15, 출력 토큰 100만 개당 $0.50입니다. 구독이 없습니다. 실패하거나 거부된 요청은 요금이 부과되지 않으며 크레딧은 만료되지 않습니다. 예시로, 3,000 토큰을 보내고 800 토큰을 받는 스크립트는 입력에 $0.00045, 출력에 $0.0004를 지불하여 호출당 $0.00085가 들며, 1,000회 호출에는 $0.85가 듭니다. 충전은 $20부터 $500까지 가능하며, $50부터 +5%, $100부터 +10%, $200부터 +15% 보너스가 제공됩니다. 이 사이트는 TRC20의 USDT 또는 Base의 USDC 암호화폐만 받습니다. 전체 표는 가격 페이지에서, 엔드포인트 참조는 문서에서 확인하세요.
경계는 명확합니다. 이는 독립적인 제공자가 실행하는 단일 오픈 웨이트 모델이며, 어떤 모델 벤더의 공식 API도 아닙니다. 컨텍스트 창은 프롬프트와 완성을 합쳐 100,000 토큰입니다. 이미지, 오디오 또는 비디오를 생성하지 않으며, 임베딩을 제공하지 않고 파인 튜닝을 지원하지 않습니다. 합법적인 성인 용도로만 사용하십시오. 중요한 제한 사항: 미성년자가 포함된 성 콘텐츠는 없습니다. 프롬프트는 학습에 사용되지 않습니다.
더 많은 가이드
질문과 답변
Python에서 이 API를 호출하려면 특수 SDK가 필요합니까?
아니요. 공식 openai 패키지로 작동합니다. base_url을 https://api.nsfwaiapi.com/v1로 설정하고 키를 전달한 후 모델 ID uncensored를 사용하십시오.
스트리밍은 OpenAI와 동일한 방식으로 작동합니까?
네. stream=True를 전달하고 서버 전송 이벤트를 청크 단위로 읽으십시오. 사용량은 스트림 끝에 보고됩니다.
429 오류가 발생하면 어떻게 해야 합니까?
대기했다가 다시 시도하십시오. 응답에 Retry-After 헤더가 있으면 이를 준수하십시오. rate_limited 오류 코드는 분당 요청 수가 많음을 의미하고, concurrency는 동시 요청 수가 많음을 의미합니다.
결제 없이 테스트할 수 있습니까?
네. 가입 후 API 키 페이지에서 $0.50의 체험 크레딧을 청구할 수 있습니다. 결제 정보가 필요하지 않으며, 7일 동안 유효하고 사람당 하나만 허용됩니다.
키는 양식 하나만 작성하면 받을 수 있습니다
계정을 생성하고 키를 복사한 후 기본 URL을 변경하세요. 설정은 이것으로 끝입니다.
API 키 받기NsfwAiApiNSFW 텍스트 생성용 무검열 LLM API