Wu Xianzhi APIPrzykłady kodu
Kompletny kod przykładów wywoływania API AI bez cenzury: Python, Node.js i cURL
To przewodnik dla programistów dotyczący wywoływania API AI bez cenzury. Interfejs jest kompatybilny z Chat Completions OpenAI, więc znane Ci SDK openai wystarczy skonfigurować, zmieniając dwie linie. W tym dokumencie omawiamy najważniejsze funkcje: podstawowe zapytania, strumieniowanie, wywoływanie funkcji, retry błędów, kontrolę kosztów przez max_tokens oraz utrzymanie kontekstu w rozmowach wieloetapowych. Wszystkie przykłady kodu można skopiować i uruchomić; klucze są odczytywane z zmiennych środowiskowych.
Aktualizacja:
Kluczowe informacje
- Zmień base_url na https://api.wuxianzhiapi.com/v1,模型名写 uncensored; oficjalne SDK openai nie wymaga innych zmian
- Ostatni blok odpowiedzi strumieniowej zawiera statystyki zużycia; choices jest puste — należy to sprawdzić przed odczytem
- Powtarzaj zapytania z eksponencjalnym backoff tylko dla 429 i 503; retry dla 400/401/402/403 nie ma sensu
- Interfejs jest bezstanowy; w rozmowach wieloetapowych musisz samodzielnie wysyłać historię i kontrolować koszty za pomocą max_tokens oraz przycinania
Podstawowe informacje o interfejsie i zmienne środowiskowe
Zapamiętaj stałe parametry. Base URL to https://api.wuxianzhiapi.com/v1, model to uncensored, autoryzacja: Authorization: Bearer <klucz>. Dostępne są dwa endpointy: POST /v1/chat/completions (dialog) i GET /v1/models (sprawdzenie modelu). Format jest zgodny z OpenAI, więc w SDK openai wystarczy zmienić base_url i klucz.
Klucz jest widoczny od razu na /get-api-key/. Nowe konto ma $0.50 kredytu próbnego ważnego 7 dni, bez konieczności podawania karty. Przykłady używają zmiennej WUXIANZHI_API_KEY. Okno kontekstu: 100 000 tokenów. Limit: 8 MB na zapytanie, 300 zapytań na minutę.
export WUXIANZHI_API_KEY="把你的密钥放这里"
# 确认连通性,应返回包含 uncensored 的模型列表
curl https://api.wuxianzhiapi.com/v1/models \
-H "Authorization: Bearer $WUXIANZHI_API_KEY"Jeśli zwróci 401, sprawdź klucz i zmienną środowiskową. Szczegóły parametrów: dokumentacja interfejsu.
cURL: minimalne poprawne zapytanie
Zalecamy przetestowanie przez cURL. Izoluje to problemy sieciowe od kodu. Przykład z max_tokens: odpowiedź JSON zawiera choices[0].message.content oraz usage do naliczania kosztów.
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
}'Nie trzeba ręcznie escapować UTF-8 w JSON. W PowerShell użyj pliku body.json z -d @body.json.
Pełne wywołanie w Pythonie i Node.js
Użyj pakietu openai (v1+). Zainstaluj pip install openai. Podaj base_url i api_key. Zapisz skrypt jako chat.py.
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)Użyj pakietu openai (v4+). Zainstaluj: npm install openai. Skrypt używa await, więc zapisz go jako chat.mjs lub dodaj w 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);Zmień inicjalizację klienta i ustaw model uncensored. Zobacz przewodnik migracji.
Jak odczytać strumieniowanie (SSE)
Ustaw stream: true dla strumieniowania. Serwer wysyła bloki SSE data: {...}, kończąc data: [DONE]. SDK parsuje dane automatycznie.
Ostatni blok zawiera usage i pusty choices. Sprawdź, czy nie jest puste, zanim odwołasz się do chunk.choices[0].
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);Użyj cURL z -N, aby zobaczyć surowe dane SSE bez buforowania.
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":"数到五。"}]}'Jeśli przesyłasz odpowiedzi strumieniowe przez reverse proxy (np. Nginx), pamiętaj o wyłączeniu buforowania odpowiedzi dla tej ścieżki — w przeciwnym razie frontend zobaczy dane w jednym kawałku, a nie stopniowo.
Wywoływanie funkcji: tools i zwracanie wyników
Użyj tools do deklaracji funkcji. Model zwraca message.tool_calls. Wyślij wynik z role: "tool", aby uzyskać odpowiedź.
tool_choice: "auto" (domyślnie), {"type": "function", "function": {"name": "get_weather"}} (wymuszenie), "none" (blokada). Przykład: pobierz tool_calls, wykonaj funkcję, wyślij wynik.
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)Błędy: 1. Niezapisanie tool_calls w historii. 2. Błędny tool_call_id. 3. Parametry jako string – użyj json.loads. Node.js działa analogicznie.
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);
}
Obsługa błędów i retry: backoff dla 429 i 503
Wszystkie błędne odpowiedzi mają jednolity format JSON: {"error":{"code":...,"message":...}}. W kodzie należy zretrykować tylko dwa typy: 429 (przekroczenie limitu 300 zapytań na minutę) i 503 (upstream_busy, model jest chwilowo zajęty, spróbuj ponownie za kilka sekund). Warto zretrykować również timeouty połączenia warstwy sieciowej. Pozostałe błędy nie wymagają retry: 400 oznacza problem z samym zapytaniem (np. prompt plus max_tokens przekraczają 100k), 401 to nieprawidłowy klucz, 402 no_credit oznacza brak środków lub wygaśnięcie kredytu, a 403 content_blocked to zablokowanie treści — setki ponownych prób dadzą ten sam wynik.
Strategia wycofania się wykorzystuje wykładniczy wzrost z losowym jitterem: 1. próba trwa około 1 sekundy, 2. próba około 2 sekundy, 3. próba około 4 sekundy. Ustawia się górny limit i maksymalną liczbę powtórzeń, aby uniknąć sytuacji, w której wiele równoległych zadań ponawia zapytania w tym samym momencie, co nasila limit zapytań. Oficjalny SDK zawiera max_retries, który domyślnie wykonuje kilka ponowień dla kodów 429 i 5xx; w prostych przypadkach wystarczy zwiększyć tę wartość, a przy potrzebie logowania, wyłączania obciążenia lub niestandardowych czasów oczekiwania należy napisać własną pętlę.
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)W Node.js zwiększ maxRetries. SDK obsługuje backoff dla 429 i 5xx. Sprawdzaj status przez 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;
}
}Dodatkowa uwaga: jeśli strumieniowe zapytanie zostanie przerwane, odebrane dane należy zapisać; retry zacznie generować od nowa i ponownie naliczy opłaty. Dlatego przy generowaniu długich tekstów zalecamy dzielenie na mniejsze zapytania, a nie wysyłanie jednego dużego żądania.
Kontrola kosztów i długości odpowiedzi za pomocą max_tokens
Ceny: $0.25 / 1M tokenów input, $1.00 / 1M output. Przedpłacony kredyt, bez opłat miesięcznych, nie wygasa. Output jest 4x droższy. Domyślny max_tokens to 2048, max 32 000. Ustaw 200-300 dla krótkich odpowiedzi.
Zróbmy rachunek sumowania: przy zapytaniu z max_tokens ustawionym na 1 000, koszt w najgorszym scenariuszu to $0,001; przy limicie 32 000 wynosi $0,016. Darmowy kredyt próbny w wysokości $0,50 to odpowiednio ok. 500 000 tokenów wyjściowych lub 2 000 000 tokenów wejściowych — wystarczy to na wielokrotne uruchomienie wszystkich przykładów z tego artykułu. Pamiętaj, że suma promptu i max_tokens nie może przekroczyć 100 000, w przeciwnym razie otrzymasz błąd 400, więc przy długich promptach obniż limit wyjściowy. Szczegółowy opis cen znajdziesz na stronie z cenami.
Kolejna uwaga: jeśli odpowiedź zostanie ucięta, pole finish_reason w odpowiedzi przyjmie wartość "length", co oznacza, że zabrakło max_tokens, a nie że model sam skończył pisać. Przy generowaniu długich tekstów sprawdź to pole, aby zdecydować, czy kontynuować pisanie.
Rozmowy wieloetapowe: samodzielne utrzymanie kontekstu
Interfejs jest bezstanowy — serwer nie zapamiętuje poprzednich zapytań. Aby kontynuować rozmowę, musisz za każdym razem wysyłać pełną historię wiadomości w kolejności: system, user, assistant, user itd. Oznacza to, że każda kolejna tura zwiększa liczbę tokenów wejściowych, a koszty się sumują — im dalej w rozmowie, tym wyższy koszt wejścia w każdej kolejnej turze.
Całkowita ilość kontekstu jest ograniczona do 100 000 tokenów (włącznie z tą odpowiedzią), więc długie rozmowy wymagają przycinania. Najprostszym rozwiązaniem jest zachowanie wiadomości systemowych oraz kilku ostatnich tur; w bardziej zaawansowanych przypadkach można zsumować wcześniejsze treści w jednym zapytaniu i umieścić krótkie streszczenie w wiadomości systemowej. Poniższa klasa encapsuluje logikę zapisu historii i przycinania według liczby wiadomości, gotowa do użycia w Twojej usłudze czatu.
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("刚才说的第二个地方,适合带老人吗?")) # 能接上上一轮Przycinanie według liczby wiadomości wystarcza, ale nie jest precyzyjne, ponieważ długość każdej wiadomości może się znacznie różnić. Jeśli potrzebujesz ścisłej kontroli, użyj usage.prompt_tokens z odpowiedzi jako wskaźnika rzeczywistego zużycia: aktywuj kompresję historii, gdy zbliżysz się do 50 000. Jeśli tworzysz produkt typu long-term companion, zapoznaj się z przykładami projektowania kontekstu w scenariuszach użycia.
Najczęściej zadawane pytania
Dlaczego ostatni blok danych strumieniowych jest pusty?
To automatyczny blok statystyk: choices to pusta tablica, usage zawiera tokeny. Sprawdź czy choices nie są puste, zanim odczytasz delta.
Czy należy ponawiać zapytania przy kodach 429 i 503? Jak długo czekać?
Warto ponawiać w obu przypadkach. 429 oznacza przekroczenie limitu 300 zapytań na minutę, a 503 z upstream_busy oznacza tymczasowe zajęcie modelu. Zalecamy eksponencjalne wycofywanie z losowym jitterem, zaczynając od 1 sekundy, z ustawionym limitem liczby prób, aby unikać nieskończonego ponawiania.
Co zrobić, jeśli model nie zwraca tool_calls podczas wywoływania funkcji?
Oznacza to, że model uznał wywołanie za niepotrzebne; w takim przypadku message.content stanowi ostateczną odpowiedź. Jeśli wywołanie jest konieczne, ustaw tool_choice na konkretną funkcję i sprawdź, czy opisy funkcji oraz schemat parametrów są jasno zdefiniowane.
Czy wieloetapowe rozmowy stają się coraz droższe?
Tak. Interfejs jest bezstanowy, więc każda tura wymaga ponownego wysłania historii, a liczba tokenów wejściowych rośnie z każdą turą. Możesz zachować tylko kilka ostatnich tur lub skompresować wcześniejsze treści w streszczenie, jednocześnie ograniczając output za pomocą max_tokens.
Wypełnij formularz, aby uzyskać klucz
Utwórz konto, skopiuj klucz i zmień Base URL. Konfiguracja jest taka prosta.
Uzyskaj klucz API