ES ▾
Obtener clave de API

Wu Xianzhi APIEjemplos de código

Código completo de ejemplos de API de IA sin censura: Python, Node.js y cURL

Manual de API de IA sin censura para desarrolladores. Compatible con Chat Completions de OpenAI; tu SDK openai conocido solo necesita cambiar dos líneas de configuración. Cubrimos: peticiones básicas, streaming, llamadas a funciones, reintentos de errores, control de costos con max_tokens y mantenimiento de contexto en conversaciones multironda. Código listo para copiar; la clave se lee de variables de entorno.

Actualizado el

Puntos clave

  1. Cambia base_url a https://api.wuxianzhiapi.com/v1,模型名写 uncensored; el SDK openai oficial no necesita otros cambios
  2. El último bloque de streaming contiene estadísticas de uso con choices vacío; verifica antes de leer
  3. Reintenta solo 429 y 503 con retroceso exponencial; reintentar 400/401/402/403 no tiene sentido
  4. API sin estado; reenvía el historial tú mismo y usa max_tokens y recorte para controlar costos

Información básica de la API y variables de entorno

Anota primero estos parámetros fijos, que se usarán en todos los ejemplos. La Base URL es https://api.wuxianzhiapi.com/v1, el nombre del modelo es fijo uncensored y el método de autenticación es el encabezado de la petición Authorization: Bearer <clave>. Solo hay dos endpoints: POST /v1/chat/completions para conversaciones y GET /v1/models para verificar la disponibilidad del modelo. El formato de peticiones y respuestas es idéntico al de las Chat Completions de OpenAI, por lo que el SDK oficial openai solo requiere cambiar base_url y la clave, sin modificar el código de negocio.

La clave se muestra inmediatamente tras el registro en /get-api-key/ con solo correo y contraseña. Las cuentas nuevas tienen un crédito de prueba de $0.50 válido por 7 días, sin necesidad de tarjeta. Todos los ejemplos leen la clave de la variable de entorno WUXIANZHI_API_KEY; no la guardes en repositorios ni en páginas front-end. Límites: ventana de contexto de 100,000 tokens (prompt más salida), cuerpo de petición máximo 8 MB y 300 peticiones por minuto por clave.

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

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

Si devuelve 401, revisa la clave o la variable de entorno. Ver documentación de la API para parámetros completos.

cURL: petición mínima funcional

Independientemente del lenguaje final, te recomendamos probar primero con cURL. Esto aísla los problemas de red, autenticación y formato de petición del código de tu negocio. A continuación hay una petición normal con max_tokens. El JSON de respuesta contiene el cuerpo del mensaje en choices[0].message.content y el consumo de tokens en usage; la facturación se calcula con estos dos valores.

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

No es necesario escapar manualmente el JSON en chino; basta con declarar un JSON compatible con UTF-8 en los encabezados, y la mayoría de las terminales permiten pegarlo directamente. Si depuras en PowerShell de Windows, el manejo de comillas es complicado; guarda el cuerpo de la petición en body.json y envíalo con -d @body.json.

Llamadas completas en Python y Node.js

Para Python usa el paquete oficial openai (v1 o superior). Primero ejecuta pip install openai. Al crear el cliente, pasa base_url y api_key; el resto de las llamadas son idénticas a las de OpenAI. Puedes guardar el siguiente script como chat.py y ejecutarlo.

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 usa el paquete npm de openai (v4+). Ejecuta npm install openai. Usa await a nivel superior; guarda el archivo como chat.mjs o configura package.json con "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);

La estructura es idéntica; solo cambia la sintaxis. Si ya usas OpenAI, cambia la inicialización y el modelo a uncensored. Ver guía de migración.

Cómo leer la salida en streaming (SSE)

Usa streaming para interfaces. Configura stream: true. El servidor envía bloques SSE data: {...} y termina con data: [DONE]. El SDK ya los parsea.

El último bloque de streaming contiene usage y choices vacío. Verifica que no esté vacío antes de acceder a chunk.choices[0] para evitar errores.

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

Para ver datos SSE crudos, usa cURL con -N para desactivar el buffer.

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

Si usas un proxy inverso como Nginx, desactiva el buffer de respuesta para esa ruta; de lo contrario, el frontend verá todo el texto de golpe en lugar de aparecer carácter por carácter.

Llamadas a funciones: herramientas y devolución de resultados

Usa el formato OpenAI: declara funciones con tools. El modelo responde con message.tool_calls. Ejecuta la función y envía el resultado con role: "tool".

tool_choice es "auto" por defecto. Usa {"type": "function", "function": {"name": "get_weather"}} para forzar o "none" para prohibir. Ejemplo completo con tool_calls.

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)

Errores comunes: 1) No incluir tool_calls en el historial. 2) tool_call_id incorrecto. 3) No parsear el JSON de los argumentos con json.loads.

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

Manejo de errores y reintentos: retroceso para 429 y 503

Las respuestas de error son JSON unificados: {"error":{"code":...,"message":...}}. Solo debes reintentar dos casos: 429 (superar el límite de 300 peticiones por minuto) y 503 (upstream_busy, modelo ocupado, prueba de nuevo en unos segundos). También merece la pena reintentar los tiempos de espera de conexión. Los demás errores no se deben reintentar: 400 (petición inválida, p. ej., prompt + max_tokens > 100k), 401 (clave inválida), 402 no_credit (saldo agotado o prueba expirada), 403 content_blocked (contenido bloqueado).

La estrategia de backoff usa crecimiento exponencial con jitter: espera ~1 s en el 1.º intento, ~2 s en el 2.º y ~4 s en el 3.º. Establece un límite y un número máximo de reintentos para evitar que múltiples tareas concurrentes reintenten a la vez y agraven el límite. El SDK oficial incluye max_retries, que reintentará 429 y 5xx; en casos simples, aumenta este valor. Para logs, circuit breaker o tiempos personalizados, implementa tu propio bucle.

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)

En Node.js también puedes aumentar maxRetries; el SDK maneja el retroceso para 429 y 5xx. Para distinguir errores, usa 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;
  }
}

Si se interrumpe una petición en streaming, guarda lo recibido. El reintento regenera desde cero y cobra de nuevo. Para textos largos, usa peticiones segmentadas en lugar de una sola petición grande.

Control de costos y longitud con max_tokens

La facturación es directa: entrada $0.25 / millón de tokens, salida $1.00 / millón de tokens. Saldo prepagado, sin cuota mensual, el saldo no caduca. La salida es 4 veces más cara, así que el ahorro real está en la salida. max_tokens es 2048 por defecto y 32,000 máximo. Si tu caso solo necesita una respuesta breve, establécelo en 200 o 300 para evitar divagaciones y acotar el coste por petición.

Cálculo: con max_tokens a 1,000, el coste máximo de salida es $0.001; a 32,000 es $0.016. Los $0.50 de prueba equivalen a ~500k tokens de salida o 2M de entrada, suficientes para ejecutar los ejemplos varias veces. Recuerda que la suma del prompt y max_tokens no debe superar 100,000, o recibirás 400. Si el prompt es largo, reduce el límite de salida. Para una explicación más detallada de los precios, consulta precios.

Si la respuesta se corta, finish_reason será "length", indicando que max_tokens fue insuficiente. Verifica este campo para decidir si continuas.

Conversaciones multironda: gestión del contexto

La API es sin estado y el servidor no recuerda la petición anterior. Para continuar la conversación, debes reenviar todo el historial de mensajes en orden: system, luego user y assistant alternados. Esto implica que cada ronda añade tokens de entrada, incrementando el coste acumulado; el coste de entrada de cada ronda posterior es mayor que el de la anterior.

El total del contexto está limitado a 100,000 tokens (incluida esta salida), por lo que las conversaciones largas deben recortarse. La forma más sencilla es conservar el mensaje del sistema y las últimas rondas; para un enfoque más complejo, puedes resumir el contenido anterior en una petición y añadirlo al mensaje del sistema. La siguiente clase encapsula la lógica para guardar el historial y recortarlo por cantidad de mensajes, lista para integrar en tu servicio de chat.

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

Recortar por cantidad de mensajes es suficiente, pero impreciso, ya que la longitud de cada mensaje varía mucho. Si necesitas un control estricto, usa usage.prompt_tokens de la respuesta como referencia del uso real: comprime el historial cuando te acerques a los 50,000. Si necesitas convertir la conversación en un producto de acompañamiento a largo plazo, consulta los ejemplos de diseño de contexto en aplicaciones.

Preguntas frecuentes

¿Por qué el último bloque de datos en streaming no tiene contenido?

Es un bloque de estadísticas de uso añadido automáticamente; choices es un array vacío y usage contiene el número de tokens. Al leer, verifica primero si choices está vacío y luego obtén delta; no hace falta pasar parámetros adicionales para activarlo.

¿Debo reintentar tanto 429 como 503? ¿Cuánto esperar?

Ambos merecen un reintento. 429 indica que se superó el límite de 300 peticiones por minuto, y 503 con upstream_busy indica que el modelo está temporalmente ocupado. Se recomienda retroceso exponencial con jitter aleatorio, empezando en 1 segundo, estableciendo un número máximo de intentos y evitando reintentos infinitos.

¿Qué hago si el modelo no devuelve tool_calls durante las llamadas a funciones?

Significa que el modelo considera que no es necesario realizar la llamada; en ese caso, message.content es la respuesta final. Si es obligatorio realizar la llamada, especifica tool_choice para una función concreta y verifica que la descripción de la función y el Schema de parámetros estén bien definidos.

¿Se encarece la conversación a medida que avanza?

Sí. La API es sin estado, por lo que debes reenviar el historial en cada petición; los tokens de entrada se acumulan con las rondas. Puedes conservar solo las últimas rondas o resumir el contenido antiguo, y usar max_tokens para limitar la salida.

Solo necesitas completar el formulario para obtener la clave

Crea una cuenta, copia la clave y modifica la Base URL. La configuración es así de sencilla.

Obtener clave de API