更新日:
中国語アプリの構築:プロンプト、スクリプト制御、UTF-8
中国語機能の提供は、主にモデルの問題ではなくローカライズの問題です。ユーザーに表示する文字セットを決定し、ファイルからネットワーク経由までエンコーディングをクリーンに保ち、空白で区切られないテキストのトークン数を予算として確保します。このガイドでは、チャット補完エンドポイントに対して今日から実行可能なコードとともに、これら3つのレイヤーについて解説します。
接続するもの
サービスは POST /v1/chat/completions と GET /v1/models を https://api.chinesellmapi.com/v1 上で公開しています。認証はベアラーキーであり、唯一のモデルIDは uncensored です。テキストのみAPIでモデルが1つだけなので、選択する対象はありません。ローカライズの作業はプロンプトと、その周囲のコードだけで完結します。
設計前に知っておくべき制限:プロンプトと補完で共有される100,000トークンのコンテキストウィンドウ、デフォルト2,048で上限32,000のmax_tokens、最大8 MBのリクエストボディ、キーあたり1分間300リクエスト。ストリーミング(stream: true)はサーバー送信イベント(SSE)で動作し、OpenAI関数呼び出し形式のツールを受け付けます。
新規アカウントには7日間有効な$0.50の無料トライアルクレジットが付与され、支払い情報の登録は不要です。メールアドレスとパスワードで登録すると、キーが即座に表示されます。トライアルのFAQに詳細なルールが記載されています。
中国語でプロンプトを書き、どの中国語か指定する
多言語混在のプロンプトは、ローカライズ機能におけるドリフトの最も一般的な原因です。指示が英語で、コンテンツが中国語の場合、返信が英語または混合言語になることがあります。信頼できるパターンは、ターゲット言語で書かれたシステムプロンプトを使用し、役割、スクリプト、地域語彙の3点を明記することです。ユーザー提供テキストは別のメッセージに保持し、指示と誤解されないようにします。
以下は簡体中国語の設定例です。システムプロンプトは、モデルが簡体文字のみで回答し、余分な英語を避けるよう指示し、固有名詞を例外としています。
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.chinesellmapi.com/v1",
api_key=os.environ["API_KEY"],
)
SYSTEM_SC = (
"你是一名产品文案助手。始终使用简体中文回答,使用中国大陆常见的用词和标点,"
"不要夹杂繁体字或英文句子,专有名词除外。"
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": SYSTEM_SC},
{"role": "user", "content": "为一款记账应用写三条应用商店的一句话简介。"},
],
max_tokens=300,
temperature=0.7,
)
print(resp.choices[0].message.content)
print(resp.usage)
2つの習慣がすぐに効果をもたらします。第一に、プロンプトを短く具体的にする:役割、出力形式、1〜2つの制約。第二に、言語要件をシステムメッセージに配置し、すべてのユーザーターンで繰り返さないことで、会話履歴による希釈を防ぎます。
簡体字と繁体字の出力制御
スクリプトの選択はモデル設定ではなく、ユーザーロケールに紐づく製品判断です。本土向け製品は簡体文字を期待し、台湾や香港のユーザーは繁体字と異なる語彙(通常はソフトウェア、ネットワーク、データベース用語)を期待します。実用的なアプローチは、ロケールタグをシステムプロンプトにマッピングし、コード内で明示的に選択することです。
- zh-CN、zh-SG:簡体、本土語彙、半角数字、全角中国語句読点。
- zh-TW:繁体、台湾語彙、引用符には角括弧が一般的です。
- zh-HK:繁体で香港の語彙を使用;製品に固定用語がある場合は短い用語集を提供してください。
同じ呼び出しの繁体版は以下のようになります。システムプロンプトのみが変更されます。
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.chinesellmapi.com/v1", api_key=os.environ["API_KEY"])
SYSTEM_TC = (
"你是一名產品文案助手。請一律使用繁體中文回答,採用臺灣常用的詞彙與全形標點,"
"例如「軟體」「網路」「資料庫」,不要混入簡體字。"
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": SYSTEM_TC},
{"role": "user", "content": "請用兩句話說明什麼是雙重驗證。"},
],
max_tokens=200,
)
print(resp.choices[0].message.content)
モデルは、ユーザーメッセージ自体が他の文字セットで記述されている場合、特に他の文字セットの文字を数文字漏れさせることがあります。安価な後処理チェックを追加し、トリガーされた場合はより明確な指示で1回だけ再試行してください。サンプルチェックを超える場合は、OpenCCなどの専用変換ライブラリを介して出力を実行してください。
# A cheap guard: flag replies that contain characters that exist only in Simplified.
# The set below is a small sample, not a full list; use a converter such as OpenCC
# for production-grade checks.
SIMPLIFIED_ONLY = set("这个们说话时间书买卖东车门开关见觉")
def looks_simplified(text: str) -> bool:
return any(ch in SIMPLIFIED_ONLY for ch in text)
reply = "請用繁體中文回覆的範例文字"
print(looks_simplified(reply)) # False
ディスクからネットワーク経由、そして戻りまでのUTF-8
中国語の文字化けバグのほとんどはモデルの問題ではありません。パイプライン内のどこかでデフォルトエンコーディングが原因です:Windowsコンソールがレガシーコードページを使用、エンコーディングなしで開かれたCSV、content typeを書き換えるプロキシなど。すべての境界を明示的にします。
Pythonでは、encoding="utf-8"をopenに渡し、JSONを手動で作成する場合はensure_ascii=Falseの後に.encode("utf-8")を使用します。以下の例はプレーンなrequestsで実行し、呼び出しの生HTTP形状も示しています。
import json
import os
import requests
# 1) Always open files as UTF-8, never rely on the platform default encoding.
with open("notes_zh.txt", "r", encoding="utf-8") as f:
note = f.read()
payload = {
"model": "uncensored",
"messages": [{"role": "user", "content": "请把下面的笔记整理成三个要点:\n" + note}],
"max_tokens": 400,
}
# 2) requests encodes json= as UTF-8 for you. If you build the body by hand,
# keep Chinese readable and make the encoding explicit.
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
r = requests.post(
"https://api.chinesellmapi.com/v1/chat/completions",
headers={
"Authorization": "Bearer " + os.environ["API_KEY"],
"Content-Type": "application/json; charset=utf-8",
},
data=body,
timeout=60,
)
r.raise_for_status()
r.encoding = "utf-8"
print(r.json()["choices"][0]["message"]["content"])
Nodeでは、エンコーディングを指定しない限り readFile は Buffer を返し、中国語を含むテンプレート文字列は、ソースファイル自体がUTF-8で保存されていれば問題ありません。公式SDKはボディをシリアライズしてくれます。
import { readFile } from "node:fs/promises";
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.chinesellmapi.com/v1",
apiKey: process.env.API_KEY,
});
// Pass "utf8" explicitly; without it readFile returns a Buffer, not a string.
const note = await readFile("notes_zh.txt", "utf8");
const res = await client.chat.completions.create({
model: "uncensored",
messages: [{ role: "user", content: `请把下面的笔记整理成三个要点:\n${note}` }],
max_tokens: 400,
});
console.log(res.choices[0].message.content);
console.log(res.usage);
さらに2つの罠があります。バイト長で文字列を切り捨てると文字が半分になるため、常に文字数でスライスしてください。また、ストリーミング時はマルチバイト文字がネットワークチャンク間で分割される可能性があるため、ストリームをUTF-8で増分的にデコードします。SDKはこれを処理しますが、手書きパーサーは処理しないことが多いです。
中国語テキストのトークン予算管理
中国語にはスペースがないため、単語数は予算管理に役立ちません。計画上の仮定として、測定された定数ではなく、中国語文字1文字を約1〜2トークンとして扱い、単一の数値が必要な場合は1.5を使用します。テキストに含まれるラテン語や数字は、1語あたり約1.3トークンに近いです。比率は語彙によって異なるため、唯一の権威ある数値は、すべてのレスポンスに返されるusageオブジェクトです。
小さなヘルパー関数により、送信前にプロンプトの見積もりを容易に行い、実際の使用状況に合わせて比率を調整できます。
import re
CJK = re.compile(r"[㐀-鿿- -〿]")
def rough_tokens(text: str, per_cjk: float = 1.5, per_other_word: float = 1.3) -> int:
"""Planning estimate only. The 1.5 and 1.3 ratios are assumptions, not
measured values; compare against resp.usage and adjust them."""
cjk = len(CJK.findall(text))
others = len(re.findall(r"[A-Za-z0-9_]+", CJK.sub(" ", text)))
return round(cjk * per_cjk + others * per_other_word)
sample = "订单 A-1042 已发货,预计周三送达。"
print(rough_tokens(sample))
計算例(仮定を明記):1文字あたり1.5トークンの2,000文字の記事は、約3,000入力トークンです。400文字の要約は約600出力トークンです。入力トークン100万あたり$0.25、出力トークン100万あたり$1.00の場合、1回の呼び出しの入力は約$0.00075、出力は約$0.0006で、合計約$0.00135です。コンテキストは100,000トークンに制限されているため、同じ仮定では約40,000文字のプロンプトは返信の余地を残します。
長いドキュメントをフィードする必要がある場合は、段落または見出しでチャンク化し、文の途中では分割せず、max_tokens を明示的に設定してください。価格と制限は 価格ページ に記載されています。
マルチターン中国語チャットをウィンドウ内に保持する
チャット機能はリクエストごとに履歴全体を再送信するため、会話が進むごとにコストとコンテキストのサイズが増加します。ウィンドウはプロンプトと補完で共有される100,000トークンであるため、何もしなければ長い会話の最後には400エラーが発生します。エラーに対応するのではなく、早期にトリミングポリシーを決定してください。
シンプルなポリシーは、システムプロンプトを保持し、見積もりプロンプトが予算に収まるまで古いターンを削除し、残りを回答に確保することです。以下のスケッチは、前のセクションの推定器を再利用します。
MAX_PROMPT_TOKENS = 48_000 # leave headroom under the 100,000 window for the reply
def trim_history(messages, estimate):
# messages[0] is the system prompt and is always kept
system, rest = messages[0], messages[1:]
while rest and sum(estimate(m["content"]) for m in [system] + rest) > MAX_PROMPT_TOKENS:
rest.pop(0) # drop the oldest turn first
return [system] + rest
チューターボットやサポートアシスタントなど、初期コンテキストが重要な製品では、削除されたターンを単に破棄するのではなく、短い要約メッセージに置き換えます。モデルに、会話と同じスクリプトで古いターンを数文に圧縮させ、その要約をシステムプロンプトの直後に挿入します。これは時々1回の追加呼び出しコストがかかりますが、ペルソナと事実を安定に保ちます。
また、チャットのためにmax_tokensを適切に制限することも忘れないでください。メッセージングUIでの返信は通常数百トークンも必要なく、よりタイトな制限はレイテンシとコストの予測可能性を高めます。トークンをストリーミングで逐次ストリーミングすると、ユーザーが短いバーストで読む中国語テキストにおいて、知覚速度が向上します。
プレリリースチェックリストと次のステップ
- 各ロケールをシステムプロンプトにマッピングし、マッピングをユニットテストする。
- ファイル読み込み、リクエストボディ、ログでエンコーディングを明示的に設定する。
- 機能ごとに
usageをログに記録し、週次で推定量と比較する。 - 402(
no_credit)、429、503(upstream_busy)を別々に処理してください:チャージ、スローダウン、数秒後にリトライ。 - 403(
content_blocked)をリトライ対象ではなく、そのリクエストの最終回答として扱う。
アプリに翻訳パイプラインが含まれる場合は、翻訳とローカライズガイドを参照してください。 serialized fictionやキャラクターチャットの場合は、ウェブフィクションガイドを参照してください。完全なパラメータリファレンスはドキュメントにあります。
質問と回答
返信を簡体中国語のみにするにはどうすればよいですか?
システムプロンプトで「常に簡体中国語で、中国本土の語彙を使用して回答する」と指定します。システムメッセージにその指示を保持し、出力を厳密にする必要がある場合は後処理チェックを追加してください。
台湾ユーザー向けに繁体中国語をリクエストできますか?
はい。繁体文字で書かれたシステムプロンプトを使用し、コード内でzh-TWロケールから必要な地域語彙を指定してください。
出力に中国語の文字化けが表示されるのはなぜですか?
ほぼ確実にクライアント側のエンコーディングの問題です。ファイルをUTF-8として読み込み、コンテンツタイプの文字セットを設定し、ターミナルやログビューアもUTF-8を使用していることを確認してください。
中国語のテキストにはどのくらいのトークンが必要ですか?
文字あたり約1.5トークンとして概算し、各レスポンスのusageフィールドを確認して見積もりを調整してください。
キーはフォーム 1 つで手に入ります
アカウントを作成し、キーをコピーし、ベースURLを変更します。これだけですべての設定が完了します。