PL ▾
Pobierz klucz API

NsfwAiApiPrzewodnik

NSFW LLM API w Pythonie: strumieniowanie, JSON i narzędzia

Aby korzystać z API LLM NSFW w stylu Pythona, potrzebujesz tylko standardowego pakietu openai i dwóch zmian: adresu bazowego i klucza. Ten przewodnik pokazuje pierwsze zapytanie, strumieniowanie, tryb JSON, wywoływanie funkcji i czystą obsługę limitów zapytań.

Zaktualizowano

Okno terminala emitujące strumień świecących tokenów owiniętych skręconą wstęgą światła

Konfiguracja: instalacja i pierwsze wywołanie

Potrzebujesz aktualnego Pythona 3, pakietu openai (pip install openai) i klucza API. Nowe konta mogą doładować kredyt próbny na kwotę $0.50 na stronie klucza API jednym kliknięciem po zalogowaniu się; nie wymagają podania danych do płatności. Przechowuj klucz w zmiennej środowiskowej, a nie w kodzie źródłowym.

Usługa obsługuje protokół chat completions OpenAI: POST /v1/chat/completions do generowania i GET /v1/models do listy modeli. Autoryzacja to token Bearer, a ID modelu to uncensored. To najmniejszy działający skrypt:

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)

Jeśli wolisz najpierw sprawdzić połączenie z powłoki, to samo zapytanie działa z 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."}]}'

Model jest dostrojony do odpowiadania bez odrzucania treści dla legalnego użytku dorosłych. Wpływa to na ton tego, co napisze, a nie na kształt API, więc reszta Twojego kodu wygląda jak dowolna integracja z OpenAI. Przechowuj prompty do fikcji, czatu z postaciami i innych legalnych projektów dla dorosłych oraz upewnij się, że każda postać jest dorosła.

Kilka nawyków pomoże od pierwszego dnia. Umieść instrukcje, które się nie zmieniają, w wiadomości systemowej, a wiadomość użytkownika zostaw na część, która zmienia się w każdej turze. Wybierz jedną temperaturę dla zadania i nie zmieniaj jej podczas porównywania promptów, ponieważ zmiana dwóch rzeczy naraz niczego Cię nie nauczy. Loguj identyfikator zapytania i liczbę tokenów z resp.usage przy każdym wywołaniu, aby mieć zapis do weryfikacji, gdy rachunek lub wynik Cię zaskoczą.

Strumieniowanie odpowiedzi z SSE

Ustaw stream=True, a serwer wysyła tokeny jako zdarzenia wysyłane przez serwer (SSE). Dla aplikacji czatowych ma to ogromne znaczenie, ponieważ czytelnik widzi pierwsze słowa szybko, zamiast czekać na pełną odpowiedź. Całkowite zużycie pojawia się na końcu strumienia, więc zapytanie strumieniowe może być nadal rozliczane i logowane dokładnie.

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)

Dwa szczegóły oszczędzają czas debugowania. Po pierwsze, ostatni kawałek może mieć pustą listę choices, dlatego pętla sprawdza ją przed indeksowaniem. Po drugie, zapytanie strumieniowe zajmuje slot równoległości do końca strumienia, więc jeśli Twój kod porzuci strumień bez jego zamknięcia, ten slot pozostaje zajęty. Użyj with client.chat.completions.create(..., stream=True) as stream: lub wywołaj stream.close(), gdy przestajesz czytać wcześniej.

Tryb JSON dla strukturalnego outputu

Gdy Twój program potrzebuje danych, a nie tekstu, np. arkusza postaci, listy punktów fabularnych lub tagów do opowiadania, użyj trybu JSON z response_format={"type": "json_object"}. W promptie również napisz „JSON” i opisz klucze, których oczekujesz. Ustawienie żąda obiektu JSON, ale nie gwarantuje, że Twoje klucze będą obecne, więc je sprawdź.

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"])

Jeśli długa odpowiedź zostanie ucięta przez max_tokens, JSON będzie niekompletny, a json.loads zawiedzie. Nie ustawiaj max_tokens, ponieważ jest opcjonalny, a uzupełnienie może użyć reszty okna, lub ustaw go hojnie.

Wywoływanie funkcji

Wywoływanie funkcji pozwala modelowi poprosić Twój kod o wykonanie czegoś, np. rzutu kostką, wyszukania notatki o postaci lub pobrania ceny. Przekazujesz tools i, jeśli chcesz, tool_choice. Odpowiedź zawiera tool_calls; wykonujesz je i zwracasz wyniki jako wiadomości z role: "tool". Odpowiedzi strumieniowane zawierają również tool_calls w fragmentach.

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)

Traktuj argumenty jako niezaufane dane. Zweryfikuj je przed wykonaniem, dokładnie tak, jak w przypadku formularza internetowego. Modele czasem wysyłają brakujące pole lub błędny typ, a mała ochrona wokół json.loads zapobiega zakończeniu sesji przez jedno złe wywołanie.

Limity zapytań, ponowne próby i Twój stan konta

Limity chronią usługę i zapewniają przewidywalne zachowanie. Pełny klucz pozwala na 8 równoległych zapytań i 300 zapytań na minutę. Klucz próbny pozwala na 2 i 60. Maksymalny rozmiar body to 8 MB. Dwa rodzaje błędów limitu zwracane są jako HTTP 429: kod rate_limited oznacza zbyt wiele zapytań na minutę, a concurrency — zbyt wiele naraz. Tymczasowe przeciążenie również zwraca 429, z nagłówkiem Retry-After: 5.

SDK OpenAI domyślnie ponawia kilka błędów. Dla kontroły wyłącz to i napisz małą pętlę, która respektuje 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)

Każda odpowiedź zawiera X-Request-Id, który podajesz, kontaktując się z pomocą techniczną, oraz X-Balance-USD, który pokazuje pozostały kredyt. Nagłówki X-RateLimit-Limit-Requests i X-RateLimit-Limit-Concurrency informują Cię, jakie limity dotyczą Twojego klucza. Aby uruchomić wiele zadań jednocześnie, ogranicz równoległość we własnym kodzie za pomocą semafora o rozmiarze dostosowanym do Twojego klucza: 8 dla pełnego klucza i 2 dla klucza próbnego, dzięki czemu rzadko będziesz widzieć błąd 429.

Uruchamianie wielu zapytań równolegle

Zadania wsadowe, np. generowanie tagów dla kilkuset fragmentów opowieści, to miejsce, w którym limity równoległości mają znaczenie. Pełny klucz akceptuje 8 zapytań naraz, więc uruchom pracę przez klienta asynchronicznego i semafor o tym rozmiarze. Jeśli masz klucz próbny, użyj 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)

Semafor to cała sztuczka. Bez niego lista 200 fragmentów wysyła 200 zapytań jednocześnie, a większość z nich zwraca błędy 429 z kodem concurrency. Dzięki niemu praca płynie z prędkością, na jaką pozwala Twój klucz, i nic nie marnuje się. Jeśli dodatkowo ustawisz limit budżetu na minutę, 300 zapytań na minutę przy pełnym kluczu daje pięć na sekundę, więc bardzo krótkie zapytania mogą trafić w limit minutowy zanim wyczerpią limit równoległości.

Powszechne problemy i rozwiązania

  • 401 lub błędy uwierzytelniania. Brakuje klucza, jest obcięty lub wysłany do niewłaściwego hosta. Wypisz client.base_url i sprawdź zmienną środowiskową. Nagłówek musi brzmieć Authorization: Bearer, po którym następuje Twój klucz.
  • Model nie znaleziony. Jedynym ID modelu jest uncensored. Skopiowany przykład z inną nazwą zawiedzie, więc wywołaj GET /v1/models, jeśli nie jesteś pewien.
  • Puste lub krótkie odpowiedzi w pętli opowieści. Sprawdź, czy dodajesz odpowiedź asystenta do listy wiadomości przed następną turą. Bez tego model widzi tylko Twoją ostatnią wiadomość.
  • Odpowiedzi powtarzające się. Podnieś frequency_penalty nieco, np. do 0,3, lub obniż temperature, jeśli tekst się rozjeżdża. Kary top_p, stop i seed są przekazywane dalej.
  • Różne wyniki przy każdym uruchomieniu. Ustaw seed i niskie temperature, gdy potrzebujesz wyników możliwie powtarzalnych podczas debugowania.
  • Zapytanie zbyt duże. Limit rozmiaru body to 8 MB, a prompt plus uzupełnienie muszą zmieścić się w 100 000 tokenach. Obetnij lub podsumuj stare konteksty, zanim osiągną którykolwiek z limitów.

Gdy coś wygląda na błędnie po stronie serwera, zapisz nagłówek X-Request-Id z odpowiedzi i wyślij go na stronę pomocy wraz z krótkim opisem. Pozwoli to operatorom znaleźć Twoje dokładne zapytanie.

Koszty i ograniczenia

Ceny wynoszą $0.15 za 1M tokenów wejściowych i $0.50 za 1M tokenów wyjściowych, pobierane z przedpłaconego kredytu bez subskrypcji. Nieudane lub odrzucone zapytania nie są rozliczane, a kredyt nigdy nie wygasa. Przykładowo: skrypt wysyłający 3 000 tokenów i otrzymujący 800 zapłaci $0.00045 za wejście plus $0.0004 za wyjście, czyli $0.00085 za wywołanie, więc 1 000 wywołań kosztuje $0.85. Doładowania wynoszą od $20 do $500, z bonusami +5% przy wpłacie od $50, +10% od $100 i +15% od $200. Ta strona przyjmuje tylko kryptowaluty: USDT w sieci TRC20 lub USDC na Base. Pełną tabelę cen znajdziesz na stronie z cenami, a referencję endpointów w dokumentacji.

Granice są jasne. To pojedynczy model o otwartych wagach, obsługiwany przez niezależnego dostawcę, a nie oficjalne API żadnego dostawcy modeli. Okno kontekstu to 100 000 tokenów dla promptu plus uzupełnienia. Nie generuje obrazów, audio ani wideo, nie dostarcza embeddingów i nie obsługuje dostrajania. Używaj go tylko do legalnego użytku dorosłych i pamiętaj o ścisłym limicie: brak treści seksualnych z udziałem nieletnich. Prompty nie są używane do trenowania.

Pytania i odpowiedzi

Czy potrzebujesz specjalnego SDK do wywoływania tego API z Pythona?

Nie. Działa oficjalny pakiet openai. Ustaw base_url na https://api.nsfwaiapi.com/v1, przekaż swój klucz i użyj ID modelu uncensored.

Czy strumieniowanie działa tak samo jak w OpenAI?

Tak. Przekaż stream=True i czytaj zdarzenia wysyłane przez serwer (SSE) po kawałkach. Użycie jest raportowane na końcu strumienia.

Co zrobić, gdy otrzymasz błąd 429?

Poczekaj i spróbuj ponownie. Jeśli odpowiedź zawiera nagłówek Retry-After, zastosuj się do niego. Kod błędu rate_limited oznacza zbyt wiele zapytań na minutę, a concurrency — zbyt wiele naraz.

Czy mogę przetestować to bez płacenia?

Tak. Po rejestracji możesz odebrać $0,50 kredytu próbnego na stronie klucza API. Nie wymaga podania danych płatności, jest ważny przez 7 dni i przysługuje jednej osobie.

Twój klucz jest o jeden formularz stąd

Utwórz konto, skopiuj klucz, zmień bazowy URL. To cała konfiguracja.

Uzyskaj klucz API

NsfwAiApiAPI LLM bez cenzury do generowania tekstu NSFW