Wu Xianzhi APICode-Beispiele
Unzensierte KI-API-Aufrufe: Python, Node.js und cURL – Vollständiger Code
Dieses Handbuch für Entwickler erklärt die unzensierte KI-API. Die Schnittstelle ist mit OpenAI Chat Completions kompatibel, sodass du das vertraute openai-SDK mit zwei Konfigurationszeilen nutzen kannst. Wir behandeln alle wichtigen Punkte: Basisanfragen, Streaming, Function Calling, Fehler-Retry, Kostenkontrolle via max_tokens und Kontextpflege bei Mehrfachdialogen. Alle Codes sind lauffähig, der API-Schlüssel wird einheitlich aus Umgebungsvariablen gelesen.
Aktualisiert am
Wichtige Punkte
- Ändere base_url zu https://api.wuxianzhiapi.com/v1,模型名写 unzensiert, das offizielle openai SDK benötigt keine weiteren Änderungen
- Der letzte Block der Streaming-Antwort enthält die Usage-Statistik. choices ist leer; prüfe dies vor dem Lesen
- Führe exponentiellen Backoff nur bei 429 und 503 durch. Retry bei 400/401/402/403 ist sinnlos
- Die Schnittstelle ist zustandslos. Bei Mehrfachdialogen musst du den Verlauf selbst neu senden und Kosten mit max_tokens und Beschneidung kontrollieren
Schnittstellen-Grundlagen und Umgebungsvariablen
Merke dir zunächst die festen Parameter, die du in allen Beispielen benötigst. Die Base URL ist https://api.wuxianzhiapi.com/v1, der Modellname ist fest uncensored, und die Authentifizierung erfolgt über den Header Authorization: Bearer <Schlüssel>. Es gibt nur zwei Endpunkte: POST /v1/chat/completions für Konversationen und GET /v1/models zur Überprüfung der Modellverfügbarkeit. Das Anfrage- und Antwortformat entspricht dem OpenAI Chat Completions, sodass du im offiziellen openai SDK nur die base_url und den Schlüssel ändern musst; der Anwendungscode bleibt im Wesentlichen unverändert.
Der API-Schlüssel wird nach der Registrierung unter /get-api-key/ angezeigt (E-Mail und Passwort reichen). Neue Konten erhalten $0,50 Testguthaben, das 7 Tage lang gültig ist und keine Kreditkarte erfordert. Alle Beispiele lesen den Schlüssel aus der Umgebungsvariablen WUXIANZHI_API_KEY. Speichere den Schlüssel nicht im Code-Repository oder im Frontend. Wichtige Limits: Das Kontextfenster umfasst 100.000 Token (Prompt und Ausgabe zusammen), der Request-Body darf 8 MB nicht überschreiten, und pro Schlüssel sind 300 Anfragen pro Minute erlaubt.
export WUXIANZHI_API_KEY="把你的密钥放这里"
# 确认连通性,应返回包含 uncensored 的模型列表
curl https://api.wuxianzhiapi.com/v1/models \
-H "Authorization: Bearer $WUXIANZHI_API_KEY"Wenn hier 401 zurückgegeben wird, ist der Schlüssel falsch oder die Umgebungsvariable wurde nicht geladen. Prüfe dies zuerst, bevor du den Code weiter unten ansiehst. Vollständige Parameterbeschreibungen findest du in der Schnittstellendokumentation.
cURL: Minimale funktionale Anfrage
Egal welche Sprache du am Ende nutzt, teste zunächst mit cURL. So isolierst du Probleme in den Bereichen „Netzwerk, Schlüssel, Request-Format“ von deinem Business-Code. Das folgende Beispiel ist eine normale Anfrage mit max_tokens. Im zurückgegebenen JSON ist choices[0].message.content der Antworttext. usage enthält die verbrauchten Input- und Output-Token. Die Abrechnung erfolgt basierend auf diesen Werten.
curl https://api.wuxianzhiapi.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $WUXIANZHI_API_KEY" \
-d '{
"model": "uncensored",
"messages": [
{"role": "system", "content": "你是一个直来直去的写作助手。"},
{"role": "user", "content": "用三句话描述一场暴雨前的小镇。"}
],
"max_tokens": 300
}'Chinesische Zeichen im JSON müssen nicht manuell escaped werden. Solange im Header UTF-8-kompatibles JSON deklariert ist, funktioniert das direkte Einfügen in den meisten Terminals. Bei der Fehlersuche in Windows PowerShell ist die Handhabung von Anführungszeichen schwieriger. Speichere den Request-Body besser in body.json und sende ihn mit -d @body.json.
Vollständige Aufrufe in Python und Node.js
Python: Nutze das offizielle openai Paket (v1 oder höher). Installiere es zuerst mit pip install openai. Übergebe beim Erstellen des Clients base_url und api_key. Die Aufrufweise unterscheidet sich nicht von OpenAI. Das folgende Skript kann direkt als chat.py gespeichert und ausgeführt werden.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "你是一个直来直去的写作助手。"},
{"role": "user", "content": "用三句话描述一场暴雨前的小镇。"},
],
max_tokens=300,
)
print(resp.choices[0].message.content)
print("输入 tokens:", resp.usage.prompt_tokens, "输出 tokens:", resp.usage.completion_tokens)Node.js: Nutze das npm-Paket openai (v4 oder höher). Installiere es mit npm install openai. Die folgende Schreibweise nutzt top-level await. Speichere die Datei daher als chat.mjs oder setze in package.json "type": "module".
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.wuxianzhiapi.com/v1",
apiKey: process.env.WUXIANZHI_API_KEY,
});
const resp = await client.chat.completions.create({
model: "uncensored",
messages: [
{ role: "system", content: "你是一个直来直去的写作助手。" },
{ role: "user", content: "用三句话描述一场暴雨前的小镇。" },
],
max_tokens: 300,
});
console.log(resp.choices[0].message.content);
console.log("用量:", resp.usage);Die Struktur beider Codes ist identisch, der Unterschied liegt nur in der Syntax. Wenn du bereits OpenAI-Aufrufe in deinem Projekt hast, musst du meist nur die Client-Initialisierung ändern und den Modellnamen auf uncensored setzen. Schritte zur Migration von anderen Schnittstellen findest du in der Migrationsanleitung.
Streaming (SSE) korrekt lesen
Bei langen Texten oder Chat-Oberflächen ist Streaming essenziell, damit der Nutzer nicht auf die vollständige Generierung warten muss. Setze stream: true. Der Server sendet Daten dann blockweise via SSE (Server-Sent Events). Jeder Block ist eine Zeile data: {...}, beendet mit data: [DONE]. Das offizielle SDK hat die Parsing-Logik bereits implementiert; du musst nur iterieren.
Ein häufiger Stolperstein: Am Ende des Streams wird automatisch ein Block mit usage angehängt. Das choices-Array ist in diesem Block leer. Du musst nicht extra einen Parameter setzen, um dies zu aktivieren. Prüfe im Code jedoch, ob choices leer ist, bevor du auf chunk.choices[0] zugreifst, sonst wirft das Programm am Ende des Streams einen Index-Out-of-Bounds-Fehler.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
)
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "写一段 200 字左右的悬疑小说开头。"}],
max_tokens=600,
stream=True,
)
usage = None
for chunk in stream:
if chunk.usage: # 最后一块:用量统计
usage = chunk.usage
if not chunk.choices: # 用量块没有 choices
continue
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
print()
if usage:
print("输入", usage.prompt_tokens, "输出", usage.completion_tokens)import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.wuxianzhiapi.com/v1",
apiKey: process.env.WUXIANZHI_API_KEY,
});
const stream = await client.chat.completions.create({
model: "uncensored",
messages: [{ role: "user", content: "写一段 200 字左右的悬疑小说开头。" }],
max_tokens: 600,
stream: true,
});
let usage = null;
for await (const chunk of stream) {
if (chunk.usage) usage = chunk.usage;
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
console.log("\n用量:", usage);Um die rohen SSE-Daten zu sehen, nutze cURL mit dem Parameter -N, um die Ausgabe-Pufferung zu deaktivieren. Jeder Block wird sofort ausgegeben. Das ist nützlich, um zu prüfen, ob ein Proxy oder Gateway Streaming-Antworten schluckt.
curl -N https://api.wuxianzhiapi.com/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $WUXIANZHI_API_KEY" \
-d '{"model":"uncensored","stream":true,"max_tokens":100,
"messages":[{"role":"user","content":"数到五。"}]}'Wenn du Streaming-Antworten über einen Reverse-Proxy wie Nginx weiterleitest, deaktiviere die Response-Pufferung für diesen Pfad. Andernfalls sieht der Nutzer die Antwort als Block statt Zeichen für Zeichen.
Function Calling: Tools und Rückgabe von Tool-Ergebnissen
Function Calling nutzt das OpenAI-Format. Definiere im Request mit tools Name, Beschreibung und JSON-Schema der Parameter. Wenn das Modell einen Aufruf plant, gibt es den Funktionsnamen und die Parameter als JSON-String in message.tool_calls zurück. Deine Anwendung führt die Funktion aus und sendet das Ergebnis als Nachricht mit role: "tool" zurück. Erst dann generiert das Modell die finale Antwort basierend auf dem Ergebnis.
tool_choice ist standardmäßig "auto". Um einen bestimmten Funktionsaufruf zu erzwingen, übergebe {"type": "function", "function": {"name": "get_weather"}}. Mit "none" werden Aufrufe unterbunden. Das folgende Beispiel zeigt den vollständigen Ablauf: Erster Request liefert tool_calls, lokale Funktion wird ausgeführt, zweiter Request enthält das Tool-Ergebnis.
import json, os
from openai import OpenAI
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
)
def get_weather(city: str) -> dict:
# 这里用假数据代替真实的天气接口
return {"city": city, "temp_c": 18, "condition": "多云"}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "城市名"}},
"required": ["city"],
},
},
}]
messages = [{"role": "user", "content": "杭州现在天气怎么样?"}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto", max_tokens=500
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg) # 必须把带 tool_calls 的 assistant 消息原样放回历史
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = get_weather(**args)
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": json.dumps(result, ensure_ascii=False),
})
final = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, max_tokens=500
)
print(final.choices[0].message.content)
else:
print(msg.content)Drei häufige Fehler: 1. Die assistant-Nachricht mit tool_calls wird nicht wieder in den Verlauf eingefügt, sondern nur tool-Nachrichten angehängt. Das macht den Request ungültig. 2. Die tool_call_id stimmt nicht überein. 3. Die vom Modell zurückgegebenen Parameter sind Strings und müssen erst mit json.loads geparst werden. Prüfe die Fehlerbehandlung, um sicherzustellen, dass Modell-Ausgaben nicht direkt in Befehle oder SQL injiziert werden. Der Node.js-Ablauf ist identisch, hier ist die äquivalente Schreibweise.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.wuxianzhiapi.com/v1",
apiKey: process.env.WUXIANZHI_API_KEY,
});
const getWeather = (city) => ({ city, temp_c: 18, condition: "多云" });
const tools = [{
type: "function",
function: {
name: "get_weather",
description: "查询指定城市的当前天气",
parameters: {
type: "object",
properties: { city: { type: "string", description: "城市名" } },
required: ["city"],
},
},
}];
const messages = [{ role: "user", content: "杭州现在天气怎么样?" }];
const first = await client.chat.completions.create({
model: "uncensored", messages, tools, tool_choice: "auto", max_tokens: 500,
});
const msg = first.choices[0].message;
if (msg.tool_calls?.length) {
messages.push(msg);
for (const call of msg.tool_calls) {
const args = JSON.parse(call.function.arguments);
messages.push({
role: "tool",
tool_call_id: call.id,
content: JSON.stringify(getWeather(args.city)),
});
}
const final = await client.chat.completions.create({
model: "uncensored", messages, tools, max_tokens: 500,
});
console.log(final.choices[0].message.content);
} else {
console.log(msg.content);
}
Fehlerbehandlung und Retry: Backoff bei 429 und 503
Fehlerantworten sind einheitlich als JSON formatiert: {"error":{"code":...,"message":...}}. Im Code sind nur zwei Fehlerarten für ein Retry sinnvoll: 429 (Überschreitung des Limits von 300 Anfragen pro Minute) und 503 (upstream_busy, das Modell ist vorübergehend beschäftigt, versuche es nach einigen Sekunden erneut). Ein Retry bei Netzwerk-Verbindungs-Timeouts ist ebenfalls ratsam. Bei anderen Fehlern ist ein Retry sinnlos: 400 bedeutet ein Problem mit der Anfrage (z. B. Prompt plus max_tokens übersteigt 100k), 401 bedeutet ein ungültiger Schlüssel, 402 no_credit bedeutet, dass Guthaben oder Testguthaben aufgebraucht sind, und 403 content_blocked bedeutet, dass der Inhalt blockiert wurde – hundertmaliges Wiederholen ändert das Ergebnis nicht.
Verwende eine Backoff-Strategie mit exponentieller Zunahme und Jitter: Warte beim 1. Versuch ca. 1 Sekunde, beim 2. Versuch ca. 2 Sekunden, beim 3. Versuch ca. 4 Sekunden. Setze eine Obergrenze und eine maximale Anzahl an Wiederholungen, um zu vermeiden, dass parallele Anfragen zur gleichen Zeit gleichzeitig erneut versuchen, die Anfrage zu stellen, was das Ratenlimit noch stärker ausreizt. Das offizielle SDK enthält max_retries, das standardmäßig 429- und 5xx-Fehler nur wenige Male wiederholt. Für einfache Szenarien erhöhe diesen Wert einfach. Wenn du Protokoll, Circuit Breaking oder individuelle Wartezeiten benötigst, schreibe deine eigene Schleife.
import os, random, time
import openai
from openai import OpenAI
# max_retries=0:关闭 SDK 自带重试,完全由下面的函数控制
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
max_retries=0,
timeout=60,
)
def chat_with_retry(messages, max_attempts=5, **kwargs):
for attempt in range(max_attempts):
try:
return client.chat.completions.create(
model="uncensored", messages=messages, **kwargs
)
except (openai.RateLimitError, openai.InternalServerError,
openai.APIConnectionError, openai.APITimeoutError) as e:
if attempt == max_attempts - 1:
raise
wait = min(30, 2 ** attempt) + random.uniform(0, 1)
print(f"{type(e).__name__},{wait:.1f} 秒后重试(第 {attempt + 1} 次)")
time.sleep(wait)
except openai.APIStatusError as e:
# 400 / 401 / 402 / 403 / 404:重试无效,直接交给上层处理
print("不可重试:", e.status_code, e.response.text)
raise
resp = chat_with_retry([{"role": "user", "content": "你好"}], max_tokens=100)
print(resp.choices[0].message.content)In Node.js kannst du maxRetries direkt erhöhen; das SDK behandelt 429 und 5xx. Unterscheide Fehler über error.status.
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.wuxianzhiapi.com/v1",
apiKey: process.env.WUXIANZHI_API_KEY,
maxRetries: 5,
timeout: 60_000,
});
try {
const resp = await client.chat.completions.create({
model: "uncensored",
messages: [{ role: "user", content: "你好" }],
max_tokens: 100,
});
console.log(resp.choices[0].message.content);
} catch (err) {
if (err instanceof OpenAI.APIError) {
if (err.status === 402) console.error("余额不足,请充值后再试");
else if (err.status === 403) console.error("内容被拦截:", err.message);
else console.error("请求失败:", err.status, err.message);
} else {
throw err;
}
}Ein weiterer Hinweis: Wenn ein Streaming-Request unterbrochen wird, musst du die bereits empfangenen Daten selbst speichern. Ein Retry beginnt von vorne und wird erneut abgerechnet. Für lange Textgenerierungen ist es daher ratsam, in Segmenten zu arbeiten, statt alles auf einmal anzufordern.
Kosten und Länge mit max_tokens steuern
Die Abrechnungsregeln sind klar: Input kostet $0.25 pro Million Tokens, Output kostet $1.00 pro Million Tokens. Du hast Prepaid-Guthaben, keine monatlichen Gebühren, und das Guthaben verfällt nie. Da die Output-Preise viermal so hoch sind wie die Input-Preise, sparst du am meisten durch die Ausgabe. max_tokens beträgt standardmäßig 2048, maximal sind 32.000 möglich. Wenn deine Anwendung nur eine kurze Antwort benötigt, setze den Wert explizit auf 200 oder 300. Das verhindert, dass das Modell ausschweift, und begrenzt die Kosten pro Anfrage.
Rechnen wir einmal: Bei einem Request mit max_tokens = 1.000 beträgt die maximale Output-Kostenbelastung $0,001. Bei der Obergrenze von 32.000 wären es maximal $0,016. Das Testguthaben von $0,50 entspricht etwa 500.000 Output-Token oder 2.000.000 Input-Token. Das reicht, um alle Beispiele auf dieser Seite mehrfach durchzuspielen. Beachte: Die Summe aus Prompt und max_tokens darf 100.000 nicht überschreiten, sonst erhältst du einen 400-Fehler. Bei langen Inputs musst du also die Output-Obergrenze senken. Detaillierte Preisinformationen findest du auf der Preisseite.
Ein weiterer Punkt: Wenn die Antwort abgeschnitten wird, ist finish_reason im Response auf "length" gesetzt. Das bedeutet, dass max_tokens nicht ausgereicht haben, nicht dass das Modell fertig war. Bei langen Texten kannst du dieses Feld prüfen, um zu entscheiden, ob du fortfahren sollst.
Mehrfachdialog: Kontext selbst verwalten
Die Schnittstelle ist zustandslos. Der Server merkt sich den vorherigen Request nicht. Damit das Modell weiterplaudern kann, musst du die vollständige Historie bei jeder Anfrage sequenziell neu senden: Zuerst system, dann abwechselnd user und assistant. Das bedeutet auch, dass mit jeder neuen Runde die Anzahl der Input-Token steigt. Die Kosten summieren sich. Die Input-Kosten pro Runde werden im Verlauf immer höher.
Die Gesamtmenge des Kontextfensters ist auf 100.000 Token begrenzt (inklusive der aktuellen Ausgabe), daher muss bei langen Dialogen geschnitten werden. Die einfachste Methode ist, die Systemnachricht und die letzten paar Runden zu behalten. Für komplexere Fälle kannst du frühere Inhalte mit einer Anfrage zu einer kurzen Zusammenfassung zusammenfassen und diese in die Systemnachricht einfügen. Die folgende Klasse kapselt die Logik zum Speichern des Verlaufs und zum Schneiden nach Anzahl der Nachrichten und kann direkt in deinen Chat-Dienst integriert werden.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.wuxianzhiapi.com/v1",
api_key=os.environ["WUXIANZHI_API_KEY"],
)
class Chat:
def __init__(self, system: str, keep_last: int = 20):
self.system = {"role": "system", "content": system}
self.history = [] # 只存 user / assistant 消息
self.keep_last = keep_last
def say(self, text: str, max_tokens: int = 500) -> str:
self.history.append({"role": "user", "content": text})
recent = self.history[-self.keep_last:]
resp = client.chat.completions.create(
model="uncensored",
messages=[self.system] + recent,
max_tokens=max_tokens,
)
reply = resp.choices[0].message.content
self.history.append({"role": "assistant", "content": reply})
return reply
bot = Chat("你是一位说话简短的旅行顾问。")
print(bot.say("我想去云南玩五天,有什么建议?"))
print(bot.say("刚才说的第二个地方,适合带老人吗?")) # 能接上上一轮Das Beschneiden nach Nachrichtenanzahl reicht oft aus, ist aber ungenau, da die Nachrichtenlängen stark variieren. Wenn du eine strenge Kontrolle benötigst, nutze usage.prompt_tokens aus der Antwort als Referenz für den tatsächlichen Verbrauch: Wenn du dich der Grenze von 50.000 nähern, komprimiere die Historie aktiv. Für Langzeit-Anwendungen kannst du dir die Beispiele zur Kontextgestaltung im Anwendungsszenario ansehen.
Häufig gestellte Fragen
Warum hat das letzte Streaming-Datenblock keinen Inhalt?
Dies ist der automatisch angehängte Nutzungsstatistik-Block; choices ist ein leeres Array und usage enthält die Token-Anzahl. Lies den delta-Wert aus, nachdem du geprüft hast, ob choices leer ist. Du musst keine zusätzlichen Parameter übergeben, um dies zu aktivieren.
Sollten 429 und 503 beide erneut versucht werden? Wie lange soll man warten?
Alle sind retry-würdig. 429 bedeutet, dass das Limit von 300 Anfragen pro Minute überschritten wurde; 503 upstream_busy bedeutet, dass das Modell vorübergehend ausgelastet ist. Wir empfehlen exponentielles Backoff mit Jitter, beginnend bei 1 Sekunde, mit einer maximalen Anzahl von Versuchen, statt endlos zu wiederholen.
Was tun, wenn das Modell bei der Function Calling keine tool_calls zurückgibt?
Das bedeutet, dass das Modell keine Aufrufe benötigt, und message.content ist die endgültige Antwort. Wenn du einen Aufruf erzwingen musst, kannst du tool_choice auf eine bestimmte Funktion festlegen und prüfen, ob die Funktionsbeschreibung und das Parameter-Schema klar formuliert sind.
Werden Mehrfachdialoge mit der Zeit teurer?
Ja. Die Schnittstelle ist zustandslos, daher muss der gesamte Verlauf erneut gesendet werden, und die Input-Token summieren sich pro Runde. Du kannst nur die letzten paar Runden behalten oder frühere Inhalte zu einer Zusammenfassung komprimieren. Nutze zudem max_tokens, um die Ausgabe zu begrenzen.
Fülle einfach das Formular aus, um deinen Schlüssel zu erhalten
Erstelle ein Konto, kopiere den Schlüssel und ändere die Base URL. Die Konfiguration ist so einfach.
API-Schlüssel erhalten