NL ▾
API-sleutel ophalen

Wu Xianzhi APICodevoorbeelden

Compleet ongecensureerd AI API-voorbeeld: Python, Node.js en cURL

Een handleiding voor ontwikkelaars voor het ongecensureerde AI API. De endpoint is compatibel met OpenAI Chat Completions, dus je bekende openai SDK werkt na het aanpassen van twee configuratieregels. We behandelen basisverzoeken, streaming, function calling, foutafhandeling, kostenbeheersing via max_tokens en contextbehoud bij multi-turn. Alle code is direct bruikbaar en leest de sleutel uit een omgevingsvariabele.

Bijgewerkt op

Kernpunten

  1. Pas base_url aan naar https://api.wuxianzhiapi.com/v1,模型名写 uncensored; de officiële openai SDK vereist geen andere wijzigingen
  2. Het laatste blok van een streaming-response bevat verbruiksstatistieken met een lege choices-array; controleer dit eerst
  3. Gebruik alleen exponentiële back-off retry bij 429 en 503; retry bij 400/401/402/403 is zinloos
  4. De API is stateless; je moet de geschiedenis zelf opnieuw versturen en de kosten beheersen met max_tokens en trimmen

API-basisinformatie en omgevingsvariabelen

Onthoud eerst een paar vaste parameters die in alle voorbeelden worden gebruikt. De base URL is https://api.wuxianzhiapi.com/v1, de modelnaam is vast uncensored, en de authenticatie gebeurt via de header Authorization: Bearer <sleutel>. Er zijn slechts twee endpoints: POST /v1/chat/completions voor gesprekken en GET /v1/models om te controleren of het model beschikbaar is. De indeling van verzoeken en antwoorden komt overeen met die van OpenAI Chat Completions, dus je hoeft de officiële openai SDK alleen de base_url en de sleutel aan te passen.

Key shown immediately at /get-api-key/. Login with email and password. New accounts get $0.50 trial credit valid for 7 days. No card binding needed. All examples read the key from the environment variable WUXIANZHI_API_KEY. Do not commit keys to code repositories or put them in frontend pages. Limits: 100,000 token context window (prompt + output), request body max 8 MB, 300 requests per key per minute.

export WUXIANZHI_API_KEY="把你的密钥放这里"

# 确认连通性,应返回包含 uncensored 的模型列表
curl https://api.wuxianzhiapi.com/v1/models \
  -H "Authorization: Bearer $WUXIANZHI_API_KEY"

Als dit een 401 retourneert, is de sleutel onjuist of werkt de omgevingsvariabele niet. Controleer dit eerst voordat je doorgaat. Zie API-documentatie voor volledige parameterdetails.

cURL: minimaal werkend verzoek

Gebruik eerst cURL om je netwerk, sleutel en request-indeling te valideren, zodat je deze problemen kunt scheiden van je bedrijfscode. Dit is een standaardverzoek met max_tokens. De response bevat de tekst in choices[0].message.content en het tokenverbruik in usage; de kosten worden op basis van deze cijfers berekend.

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
  }'

Chinese tekst in JSON hoeft niet handmatig te worden geëscaped zolang je UTF-8 compatible JSON aangeeft in de headers; plakken in de meeste terminals werkt direct. Gebruik in Windows PowerShell bij voorkeur een body.json-bestand en verstuur het met -d @body.json vanwege aanhalingstekens.

Compleet Python- en Node.js-voorbeeld

Gebruik voor Python het officiële openai-pakket (v1+). Installeer eerst met pip install openai. Geef bij het aanmaken van de client de base_url en api_key op. De rest van de API is identiek aan OpenAI. Sla dit script op als chat.py en voer het uit.

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 gebruikt het openai npm-pakket (v4 en hoger), npm install openai. De onderstaande code gebruikt bovenliggend await, dus sla het bestand op als chat.mjs, of stel "type": "module" in in package.json.

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);

De structuur van beide scripts is identiek; het verschil zit alleen in de syntaxis. Als je al OpenAI-code hebt, hoef je meestal alleen de clientinitialisatie aan te passen en de modelnaam te wijzigen naar uncensored. Zie Migratiegids voor het migreren van andere endpoints.

Streaming (SSE) lezen

Gebruik streaming bij lange teksten of chatinterfaces, zodat gebruikers niet hoeven te wachten tot de volledige tekst gegenereerd is. Stel stream: true in. De server stuurt dan SSE-blokken (Server-Sent Events), elk als een regel data: {...}, afgesloten met data: [DONE]. De officiële SDK parseert dit al; je hoeft alleen te itereren.

Let op: de stream eindigt met een blok met usage waar choices leeg is. Controleer dit voordat je chunk.choices[0] leest om indexfouten te voorkomen.

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);

Gebruik cURL met de -N-flag om output buffering uit te schakelen. Zo zie je elk blok direct verschijnen, wat handig is om te debuggen of een proxy of gateway de streaming-response verbergt.

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":"数到五。"}]}'

Als je een reverse proxy zoals Nginx gebruikt, schakel dan buffering uit voor dit pad, anders ziet de frontend de tekst in één keer en niet karakter voor karakter.

Function calling: tools en terugkoppeling

Function calling volgt de OpenAI-indeling: definieer functies met tools (naam, beschrijving, JSON-schema). Als het model een functie wil aanroepen, krijg je de naam en parameters in message.tool_calls. Voer de functie uit in je code en stuur het resultaat terug met role: "tool". Vraag daarna opnieuw om het eindantwoord te genereren.

tool_choice is standaard "auto". Forceer een functie met {"type": "function", "function": {"name": "get_weather"}}, of verbied calls met "none". Het voorbeeld hieronder toont de volledige cyclus: eerst tool_calls ophalen, de functie lokaal uitvoeren, en het resultaat terugsturen in een tweede verzoek.

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)

Drie veelgemaakte fouten: 1) de assistant tool_calls-boodschap niet terugvoegen in de geschiedenis voordat je de tool-boodschap toevoegt; 2) een mismatch in tool_call_id; 3) de parameters zijn strings en moeten eerst met json.loads worden geparsed, met foutafhandeling om direct concatenatie in commando's of SQL te voorkomen. De Node.js-flow is identiek.

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);
}

Foutafhandeling en retry: back-off bij 429/503

Foutrespons is altijd gestandaardiseerd JSON: {"error":{"code":...,"message":...}}. In je code hoef je alleen te proberen opnieuw te verbinden bij twee soorten fouten: 429 (meer dan 300 verzoeken per minuut) en 503 (upstream_busy, model tijdelijk bezet, probeer het over een paar seconden opnieuw). Het is ook verstandig om opnieuw te verbinden bij netwerktimeouts. Voor andere fouten heeft opnieuw proberen geen zin: 400 betekent dat het verzoek zelf een probleem heeft (bijvoorbeeld dat de prompt max_tokens meer dan 100k overschrijdt), 401 betekent dat de sleutel ongeldig is, 402 no_credit betekent dat het saldo op is of de proefperiode is verlopen, en 403 content_blocked betekent dat de inhoud is geblokkeerd.

Gebruik exponentiële back-off met jitter: wacht ongeveer 1 seconde bij de eerste poging, 2 seconden bij de tweede, 4 bij de derde. Stel een limiet in om te voorkomen dat gelijktijdige taken de rate limit verergeren. De officiële SDK heeft max_retries die standaard 429 en 5xx retryt; pas deze waarde aan voor eenvoudige scenario's. Voor logging, circuit breakers of aangepaste wachttijden schrijf je een eigen lus.

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 kun je ook maxRetries verhogen; de SDK verwerkt de back-off voor 429 en 5xx. Controleer de foutcode met 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;
  }
}

Let op: bij onderbroken streaming-verzoeken moet je de ontvangen data zelf bewaren. Een retry begint opnieuw en wordt opnieuw gefactureerd. Voor lange teksten is het beter om in segmenten te vragen in plaats van één groot verzoek.

Kosten en lengte beheersen met max_tokens

De tarieven zijn eenvoudig: invoer $0,25 per miljoen tokens, uitvoer $1,00 per miljoen tokens, prepaid tegoed, geen maandelijkse kosten, het saldo verloopt niet. De uitvoerprijs is vier keer zo hoog als de invoerprijs, dus besparen doe je door de uitvoer te beperken. max_tokens is standaard 2048, met een maximum van 32.000 per keer. Als je maar een paar zinnen nodig hebt, stel dit dan expliciet in op 200 of 300 om te voorkomen dat het model te lang doorgaat en om de kosten per verzoek te beperken.

Laten we de kosten berekenen: stel max_tokens in op 1.000, dan is de maximale outputkosten $0,001; bij het maximum van 32.000 is dit $0,016. Met $0,50 trial credit kun je ongeveer 500.000 output tokens of 2.000.000 input tokens genereren, genoeg om alle voorbeelden in deze gids meerdere keren uit te voeren. Let op: de som van prompt en max_tokens mag niet hoger zijn dan 100.000, anders krijg je een 400-fout. Bij lange prompts moet je de outputlimiet dus verlagen. Zie de prijslijst voor meer details.

Een ander detail: als het antwoord wordt afgekapt, is de finish_reason in de respons "length", wat betekent dat de max_tokens te laag was, niet dat het model klaar was. Controleer dit veld bij lange teksten om te beslissen of je moet doorgaan.

Multi-turn gesprek: context zelf beheren

De API is stateless; de server onthoudt eerdere requests niet. Om het model door te laten praten, moet je de volledige geschiedenis van berichten in de juiste volgorde opnieuw versturen: eerst system, dan afwisselend user en assistant. Dit betekent ook dat elke extra conversatieronde de input-tokens verhoogt, wat de kosten accumuleert. De invoerkosten per ronde worden hoger naarmate het gesprek vordert.

De totale context is beperkt tot 100.000 tokens (inclusief deze uitvoer), dus lange gesprekken moeten worden afgekapt. De eenvoudigste manier is om de systemboodschap en de meest recente paar ronden te behouden; voor een iets complexere aanpak kun je eerdere inhoud samenvatten in één verzoek en de samenvatting in de systemboodschap plaatsen. De onderstaande klasse encapsuleert de logica voor het bijhouden van de geschiedenis en het afkappen op basis van het aantal items, en kan direct in je chatdienst worden opgenomen.

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("刚才说的第二个地方,适合带老人吗?"))  # 能接上上一轮

Knippen op basis van het aantal berichten is vaak voldoende, maar niet precies omdat de lengte van berichten sterk varieert. Voor strikte controle kun je de waarde usage.prompt_tokens uit de response gebruiken als maat voor het werkelijke verbruik; comprimeer de geschiedenis proactief zodra je dicht bij 50.000 komt. Voor applicaties die langdurige gesprekken nodig hebben, bekijk de voorbeelden voor contextontwerp in de toepassingsgebieden.

Veelgestelde vragen

Waarom bevat het laatste streaming-gegevensblok geen inhoud?

Dat is het automatisch toegevoegde verbruiksgegevensblok; de choices-array is leeg en de tokenaantallen staan in usage. Controleer bij het lezen eerst of choices leeg is en haal vervolgens de delta op; er hoeven geen extra parameters te worden meegegeven om dit in te schakelen.

Moet je bij 429 en 503 beide opnieuw proberen? Hoe lang moet je wachten?

Beide zijn het proberen waard. Een 429-fout betekent dat je de limiet van 300 verzoeken per minuut hebt overschreden; een upstream_busy 503-fout betekent dat het model tijdelijk bezet is. We raden exponentiële back-off met willekeurige jitter aan, beginnend bij 1 seconde, met een maximum aantal pogingen om oneindig opnieuw proberen te voorkomen.

Wat moet je doen als het model geen tool_calls retourneert bij function calling?

Dit betekent dat het model geen aanroep nodig acht; de message.content is dan het definitieve antwoord. Als een aanroep verplicht is, stel je tool_choice in op een specifieke functie en controleer je of de functiedescriptie en het parameterschema duidelijk zijn opgesteld.

Wordt multi-turn gesprek duurder naarmate het vordert?

Ja. De API is stateless en elke keer moet de volledige geschiedenis opnieuw worden verzonden, waardoor het aantal invoertokens met elke beurt toeneemt. Je kunt alleen de meest recente paar ronden bewaren of eerdere inhoud samenvatten tot een samenvatting, en het aantal uitvoertokens beperken met max_tokens.

Vul het formulier in om je sleutel te ontvangen

Maak een account aan, kopieer je sleutel en pas de Base URL aan. Zo eenvoudig is de configuratie.

API-sleutel verkrijgen