JA ▾
API キーを取得

Wu Xianzhi APIコード例

無検閲 AI API 呼び出し例:Python、Node.js、cURL の完全コード

これは開発者向けの無検閲 AI API 呼び出しマニュアルです。インターフェースは OpenAI Chat Completions と互換性があるため、お持ちの openai SDK の設定を 2 行変更するだけで利用可能です。本ページでは、基本リクエスト、ストリーミング出力、関数呼び出し、エラー再試行、max_tokens によるコスト制御、複数ラウンドのコンテキストウィンドウ維持など、最も頻繁に使用される機能を一括で解説します。すべてのコードはそのままコピーして実行可能で、API キーは環境変数から一元的に読み取ります。

更新日:

ポイント

  1. base_url を https://api.wuxianzhiapi.com/v1,模型名写 uncensored に変更するだけで、公式 openai SDK に他の変更は不要
  2. ストリーミング応答の最後のブロックは使用量統計であり、choices は空です。読み取り時はまず確認してください
  3. 429 と 503 のみに指数関数的なバックオフ再試行を行い、400/401/402/403 の再試行は意味がありません
  4. インターフェースはステートレスなので、複数ラウンドの会話では履歴を自ら再送信し、max_tokens と切り捨てでコストを制御する必要があります

インターフェースの基本情報と環境変数

まず固定パラメータをいくつか覚えてください。後続の全コード例で使います。Base URL は https://api.wuxianzhiapi.com/v1、モデル名は固定で uncensored です。認証はリクエストヘッダー Authorization: Bearer <トークン> を使います。公開されているエンドポイントは 2 つだけで、POST /v1/chat/completions が会話用、GET /v1/models がモデル利用確認用です。リクエストとレスポンスの形式は OpenAI の Chat Completions と一致するため、公式 openai SDK では base_url とトークンのみ変更すれば、ビジネスロジックはほぼ変更不要です。

トークンは /get-api-key/ で登録直後に表示されます。メールアドレスとパスワードだけで登録でき、新規アカウントには $0.50 の無料トライアルクレジットが付与されます(7 日間有効、クレジットカード登録不要)。本文の全コード例では、環境変数 WUXIANZHI_API_KEY からトークンを読み取ります。トークンをコードリポジトリやフロントエンドページに含めないでください。いくつかの制限を事前に明確にします:コンテキストウィンドウは 100,000 トークン(プロンプトと出力の合計)、1 回のリクエストボディは 8 MB 以下、1 つのトークンあたり 1 分あたり 300 リクエストの制限があります。

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

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

このステップで 401 が返された場合は、API キーの誤入力または環境変数の未適用を意味します。まずここを調査し、その後コードを確認してください。完全なパラメータ説明は インターフェースドキュメント を参照してください。

cURL:最小限の実用的なリクエスト

最終的にどの言語を使用するかにかかわらず、まずは cURL で動作確認を行うことをお勧めします。これにより、「ネットワーク、API キー、リクエスト形式」の 3 種類の問題と、自前のビジネスロジックを分離できます。以下は max_tokens を指定した通常のリクエストです。返される JSON の choices[0].message.content が応答本文、usage には今回の入力・出力トークン数が含まれます。課金はこれらの数字に基づいて計算されます。

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

JSON 内の中国語は手動エスケープ不要です。リクエストヘッダーで UTF-8 互換の JSON を宣言すればよく、多くのターミナルではそのまま貼り付けるだけで動作します。Windows の PowerShell でデバッグする際、引用符の処理が面倒な場合は、リクエストボディを body.json として保存し、-d @body.json で送信することをお勧めします。

Python と Node.js の完全な呼び出し

Python では公式 openai パッケージ(v1 以降)を使用します。まず pip install openai を実行してください。クライアント作成時に base_url と api_key を渡すだけで、OpenAI 呼び出し時と全く同じ方法で呼び出せます。以下のスクリプトは 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)

Node.js では openai の npm パッケージ(v4 以降)を使用します。npm install openai を実行してください。以下の書き方ではトップレベルの await を使用しているため、ファイルを chat.mjs として保存するか、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);

2 つのコードは構造が同じで、違いは構文のみです。プロジェクト内に既存の OpenAI 呼び出しがある場合は、通常、クライアント初期化の行のみを変更し、モデル名を uncensored に変更すれば移行できます。他のインターフェースからの一括移行手順は 移行ガイド を参照してください。

ストリーミング出力(SSE)の読み方

長文の生成やチャットインターフェースの構築では、ストリーミング出力を必ず使用してください。そうしないと、ユーザーは全文が生成されるまで最初の文字を見るまで待たなければなりません。stream: true を設定すると、サーバーは SSE(Server-Sent Events)でブロックごとにデータをプッシュします。各ブロックは data: {...} の 1 行で、data: [DONE] で終了します。公式 SDK はすでに解析済みなので、あなたはイテレーションするだけです。

陥りやすい注意点があります:ストリームの最後には、usage を含むデータブロックが自動的に追加されます。このブロックの choices は空の配列です。これを有効にするための追加パラメータは不要ですが、コードで 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);

生の SSE データを確認したい場合は、cURL に -N パラメータを付けて出力バッファリングを無効にしてください。これにより、ブロックが到着するたびに即座に出力され、プロキシやゲートウェイがストリーミング応答をドロップしていないかデバッグする際に役立ちます。

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

Nginx などのリバースプロキシの背後でストリーミング応答を転送する場合は、そのパスに対する応答バッファリングを無効にするのを忘れないでください。そうしないと、フロントエンドでは逐次表示されず、一括で表示されてしまいます。

関数呼び出し:tools とツール結果の回送

関数呼び出しは OpenAI の形式に従います。リクエストで tools を使用して関数名、説明、JSON Schema パラメータを宣言します。モデルが呼び出しを決定すると、応答の message.tool_calls に関数名と JSON 文字列形式のパラメータが含まれます。あなたのコードは実際に関数を実行し、その結果を role: "tool" のメッセージとしてフィードバックし、再度リクエストすることで、モデルが結果に基づいて最終回答を生成します。

tool_choice のデフォルトは "auto" で、モデルが自動判断します。特定の関数を強制呼び出しする場合は {"type": "function", "function": {"name": "get_weather"}} を渡し、"none" を渡すと呼び出しを禁止します。以下の例では、1 回目のリクエストで tool_calls を取得し、ローカル関数を実行し、2 回目のリクエストでツール結果を渡す一連の流れを示しています。

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)

よくある 3 つのミス:1. assistant の tool_calls メッセージを履歴に戻さず、tool メッセージを直接追加するとリクエスト形式が不正になります。2. tool_call_id が一致しない。3. モデルが返すパラメータは文字列なので、まず json.loads する必要があります。解析失敗時のフォールバック処理を忘れず、モデルの出力をコマンドや SQL に直接連結しないでください。Node.js のフローは全く同じで、以下が同等の書き方です。

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

エラー処理と再試行:429、503 のバックオフ方法

エラーレスポンスはすべて統一された JSON 形式です:{"error":{"code":...,"message":...}}。プログラムで実際に再試行すべきは次の 2 種類だけです:429(1 分あたり 300 リクエストの制限超過)と 503(upstream_busy、モデルが一時的にビジー状態なので数秒後に再試行)。ネットワーク層の接続タイムアウトも再試行に値します。それ以外では再試行の価値がありません:400 はリクエスト自体に問題がある場合(例:プロンプトと max_tokens の合計が 100k を超える)、401 はトークン無効、402 no_credit は残高不足または試用期限切れ、403 content_blocked はコンテンツがブロックされた場合です。これらを再試行しても結果は同じです。

バックオフ戦略は指数関数的な増加にランダムジッターを組み合わせます:1 回目は約 1 秒、2 回目は約 2 秒、3 回目は約 4 秒待ちます。上限と最大試行回数を設定し、並行タスクが同時に再試行してレート制限をさらに悪化させないようにします。公式 SDK には max_retries が標準で備わっており、デフォルトで 429 と 5xx に少量の再試行を行います。単純なユースケースではこれを増やすだけで十分です。ログ出力、サーキットブレーカー、カスタム待機時間が必要な場合は、自分でループを書いてください。

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)

Node.js でも maxRetries を直接増やすだけで、SDK がバックオフ戦略に従って 429 と 5xx を処理します。エラーを区別する必要がある場合は、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;
  }
}

また注意点として、ストリーミングリクエストが途中で切断された場合、受信済みのコンテンツは自分で保持する必要があります。再試行すると生成が最初から始まり、課金も再計算されるため、長文生成の場合は一度に大量をリクエストするのではなく、分割してリクエストすることをお勧めします。

max_tokens によるコストと長さの制御

課金ルールはシンプル:入力 $0.25 / 百万トークン、出力 $1.00 / 百万トークン。前払いクレジット、月額なし、期限なし。出力は入力の 4 倍なので、出力側で節約可能。max_tokens はデフォルト 2048、最大 32,000。短い応答なら 200〜300 に設定し、冗長防止と費用上限を同時に管理。

計算してみましょう:1 回のリクエストで max_tokens を 1,000 に設定した場合、最悪ケースでの出力費用は $0.001 です。上限の 32,000 に設定すると、最悪ケースでは $0.016 になります。試用クレジットの $0.50 は約 50 万トークンの出力、または 200 万トークンの入力に相当し、本文の全コード例を何度も実行するのに十分です。プロンプトと max_tokens の合計が 100,000 を超えないように注意してください。超えると 400 エラーが返されるため、入力が長い場合は出力上限を同時に下げる必要があります。より詳細な価格説明は 価格ページ をご覧ください。

もう 1 つの注意点:応答が切り捨てられた場合、レスポンスの finish_reason は "length" になります。これはモデルが自ら完了したのではなく、max_tokens が不足していたことを意味します。長文の生成時にはこのフィールドを確認し、続きの生成が必要かどうかを判断してください。

複数ラウンドの会話:コンテキストウィンドウの自己管理

インターフェース自体はステートレスであり、サーバーは前回のリクエストを記憶しません。モデルに続きの会話をさせるには、履歴メッセージを順番に毎回再送信する必要があります。まず system、その後 user と assistant が交互に続きます。これは、会話ラウンドが増えるごとに入力トークン数が増加し、コストが累積することを意味します。会話が進むにつれて、各ラウンドの入力費用は以前よりも高くなります。

コンテキストの合計量は 100,000 トークン(今回の出力を含む)で制限されるため、長文の会話では必ず切り捨てを行う必要があります。最も簡単な方法は、system メッセージと直近のいくつかのターンを保持することです。少し複雑になりますが、それ以前の会話を 1 回のリクエストで要約し、system メッセージに含める方法もあります。以下のクラスは履歴の保存と件数による切り捨てロジックをカプセル化しており、チャットサービスにそのまま組み込むことができます。

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

メッセージ件数による切り捨ては簡便ですが、各メッセージの長さが大きく異なるため正確性に欠けます。厳密な制御が必要な場合は、レスポンス内のusage.prompt_tokensを実際の使用量の指標として使用してください。50,000に近づいたら、履歴を圧縮します。会話を長期的な対話型製品にする場合は、使用例のコンテキスト設計に関する例を参照してください。

よくある誤解

ストリーミングの最後のデータブロックに内容がないのはなぜですか?

それは自動的に付加される使用量統計ブロックであり、choicesは空の配列、usageにはトークン数が含まれます。読み込み時は、choicesが空かどうかをまず判断し、その後deltaを取得してください。追加パラメータを指定して有効にする必要はありません。

429と503はどちらも再試行すべきですか?どのくらい待てばよいですか?

どちらも再試行に値します。429は1分あたり300回の制限超過を示し、503のupstream_busyはモデルが一時的に混雑していることを意味します。指数バックオフとランダムジッターを組み合わせ、1秒から開始し、最大試行回数を設定し、無限の再試行を避けてください。

関数呼び出し時にモデルがtool_callsを返さない場合はどうすればよいですか?

それはモデルが呼び出し不要と判断したことを意味し、この場合message.contentが最終的な回答となります。必ず呼び出したい場合は、tool_choiceを特定の関数に指定し、関数の説明とパラメータスキーマが明確に記述されているか確認してください。

複数回の会話では、会話が進むにつれてコストは高くなりますか?

なります。APIはステートレスであり、履歴を毎回再送信する必要があります。入力トークンは会話の進行とともに累積します。直近の数回の会話のみを保持するか、以前の会話を要約に圧縮し、max_tokensで出力を制限してください。

フォームに記入するだけでAPIキーを取得できます

アカウントを作成し、APIキーをコピーし、Base URLを変更します。設定はこれだけです。

APIキーを取得