Mis à jour le
Développement d'une application en chinois : prompts, contrôle des scripts et UTF-8
Livrer une fonctionnalité en chinois est principalement un problème de localisation, pas de modélisation. Vous décidez du script que voient les utilisateurs, gardez les encodages propres du fichier au réseau, et budgétisez les tokens pour un texte qui ne se coupe pas aux espaces. Ce guide couvre ces trois couches avec du code que vous pouvez exécuter contre l'endpoint de chat completions dès aujourd'hui.
Ce que vous connectez
Le service expose POST /v1/chat/completions et GET /v1/models sous https://api.chinesellmapi.com/v1. L'authentification se fait par une clé Bearer, et l'unique id de modèle est uncensored. Il s'agit d'une API textuelle avec un seul modèle, il n'y a donc pas de choix à effectuer : votre travail de localisation s'effectue entièrement dans le prompt et dans votre code qui l'entoure.
Limites à connaître avant de concevoir : une fenêtre de contexte de 100 000 tokens partagée entre prompt et complétion, max_tokens par défaut à 2 048 avec un plafond de 32 000, corps de requête jusqu'à 8 Mo, et 300 requêtes par minute par clé. Le streaming (stream: true) fonctionne sur les événements envoyés par le serveur, et les outils au format appel de fonctions OpenAI sont acceptés.
Les nouveaux comptes reçoivent un crédit d'essai de 0,50 $ valable sept jours, sans saisie de carte ; register with an email and password et la clé apparaît immédiatement. La FAQ sur les essais détaille les règles.
Écrivez des prompts en chinois, et précisez lequel
Les prompts mixtes sont la source la plus courante de dérive dans les fonctionnalités localisées. Si les instructions sont en anglais mais le contenu en chinois, les réponses peuvent parfois revenir en anglais ou en mélange. Un modèle fiable est un prompt système écrit dans la langue cible, indiquant trois choses : le rôle, le script et le vocabulaire régional. Conservez le texte fourni par l'utilisateur dans un message séparé pour qu'il ne soit pas confondu avec des instructions.
Voici une configuration pour le chinois simplifié. Notez que le prompt système indique au modèle de répondre uniquement en caractères chinois simplifiés et d'éviter l'anglais qui traîne, les noms propres étant l'exception :
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)
Deux habitudes rapportent rapidement. Premièrement, gardez les prompts courts et concrets : un rôle, un format de sortie, une ou deux contraintes. Deuxièmement, placez l'exigence linguistique dans le message système plutôt que de la répéter à chaque tour utilisateur, afin que l'historique de conversation ne la dilue pas.
Contrôle des sorties Simplifié vs Traditionnel
Le choix du script est une décision produit liée à la locale utilisateur, pas un paramètre du modèle. Les produits orientés continent chinois attendent des caractères simplifiés ; les audiences de Taïwan et de Hong Kong attendent du traditionnel, avec un vocabulaire différent également (les exemples habituels sont les termes logiciels, réseau et base de données). L'approche pratique consiste à mapper votre tag de locale vers un prompt système et à rendre le choix explicite dans le code.
- zh-CN, zh-SG : Chinois simplifié, vocabulaire continental, chiffres demi-cadratin, ponctuation chinoise pleine cadratin.
- zh-TW : Chinois traditionnel, vocabulaire taïwanais, les crochets angulaires sont courants pour les citations.
- zh-HK : Traditionnel avec le vocabulaire de Hong Kong ; fournissez un petit glossaire si votre produit a des termes fixes.
La variante Traditionnelle du même appel ressemble à ceci ; seul le prompt système change :
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)
Les modèles fuient occasionnellement quelques caractères de l'autre script, surtout lorsque le message utilisateur est lui-même dans l'autre script. Ajoutez un contrôle postérieur peu coûteux et réessayez une fois avec une instruction plus ferme si cela se produit. Pour tout ce qui va au-delà d'un contrôle d'échantillon, faites passer la sortie par une bibliothèque de conversion dédiée telle que 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 du disque au réseau et retour
La plupart des bugs de chinois corrompu ne sont pas des problèmes de modèle. Ils proviennent d'un encodage par défaut quelque part dans le pipeline : une console Windows utilisant une page de codes héritée, un fichier CSV ouvert sans encodage, un proxy qui réécrit le type de contenu. Rendez chaque limite explicite.
En Python, passez encoding="utf-8" à open, et si vous construisez du JSON à la main, utilisez ensure_ascii=False suivi de .encode("utf-8"). L'exemple ci-dessous le fait avec requests simple, qui montre également la forme HTTP brute de l'appel :
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"])
En Node, readFile retourne un Buffer sauf si vous fournissez un encodage, et les chaînes de modèle contenant du chinois sont correctes tant que le fichier source est lui-même enregistré en UTF-8. Le SDK officiel sérialise le corps pour vous :
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);
Deux autres pièges. Tronquer les chaînes par longueur d'octets peut couper un caractère en deux, donc découpez toujours par caractères. Et lorsque vous activez le streaming, décodez le flux en UTF-8 de manière incrémentale, car un caractère multi-octets peut être partagé entre plusieurs chunks réseau ; les SDK gèrent cela, mais les analyseurs personnalisés non.
Budgétisation des tokens pour le texte chinois
Le chinois ne comporte pas d'espaces, donc les comptes de mots sont inutiles pour le budget. Comme hypothèse de planification, et non comme constante mesurée, considérez qu'un caractère chinois représente environ 1 à 2 tokens, et utilisez 1,5 lorsque vous avez besoin d'un chiffre unique. Les mots latins et les chiffres intégrés au texte correspondent à environ 1,3 token par mot. Le ratio varie selon le vocabulaire, donc la seule figure autoritaire est l'objet usage retourné avec chaque réponse.
Un petit utilitaire facilite l'estimation d'un prompt avant de l'envoyer, et l'ajustement des ratios contre l'utilisation réelle dans le temps :
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))
Exemple concret, avec les hypothèses énoncées : un article de 2 000 caractères à 1,5 token par caractère fait environ 3 000 tokens d'entrée. Un résumé de 400 caractères fait environ 600 tokens de sortie. À 0,25 $ par million de tokens d'entrée et 1,00 $ par million de tokens de sortie, un appel coûte environ 0,00075 $ pour l'entrée plus 0,0006 $ pour la sortie, soit environ 0,00135 $ au total. Puisque le contexte est limité à 100 000 tokens, la même hypothèse signifie qu'un prompt d'environ 40 000 caractères laisse de la place pour une réponse.
Lorsque vous devez alimenter de longs documents, découpez par paragraphe ou titre, jamais au milieu d'une phrase, et gardez max_tokens explicite. Les prix et limites sont listés sur la page des tarifs.
Maintenir le chat chinois multi-tours dans la fenêtre
Les fonctionnalités de chat renvoie tout l'historique à chaque requête, donc le coût et la fenêtre de contexte augmentent à chaque tour. Comme la fenêtre fait 100 000 tokens partagés entre le prompt et la complétion, une longue conversation finira par déclencher une erreur 400 si vous ne faites rien. Décidez d'une politique de troncature tôt plutôt que de réagir aux erreurs.
Une politique simple est de conserver le prompt système, supprimer les tours les plus anciens jusqu'à ce que le prompt estimé s'adapte à un budget, et réserver le reste pour la réponse. L'esquisse ci-dessous réutilise l'estimateur de la section précédente :
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
Pour les produits où le contexte initial est important, comme un bot de tutorat ou un assistant de support, remplacez les tours supprimés par un message de résumé court au lieu de les jeter complètement. Demandez au modèle de compresser les anciens tours en quelques phrases dans le même script que la conversation, puis insérez ce résumé juste après le prompt système. Cela coûte un appel supplémentaire de temps en temps et maintient la persona et les faits stables.
N'oubliez pas également de limiter max_tokens de manière raisonnable pour le chat. Les réponses dans une interface de messagerie n'ont rarement besoin de plus de quelques centaines de tokens, et une limite plus serrée rend à la fois la latence et le coût plus prévisibles. Le streaming de la réponse token par token améliore la vitesse perçue, en particulier pour le texte chinois où les utilisateurs lisent par courtes bursts.
Liste de contrôle pré-lancement et prochaines étapes
- Mappez chaque locale à un prompt système et testez unitairement le mappage.
- Définissez explicitement les encodages dans les lectures de fichiers, les corps de requête et les logs.
- Loggez
usagepar fonctionnalité et comparez-le à votre estimateur hebdomadairement. - Gérez différemment les 402 (
no_credit), 429 et 503 (upstream_busy) : recharger, ralentir, réessayer après quelques secondes. - Traitez le 403 (
content_blocked) comme une réponse finale pour cette requête, pas un cas de retry.
Si votre application implique des pipelines de traduction, poursuivez avec le guide de traduction et de localisation ; pour la fiction sérialisée et le chat de personnages, consultez le guide de fiction web. La référence complète des paramètres se trouve dans les docs.
Questions et réponses
Comment faire pour que les réponses soient uniquement en chinois simplifié ?
Indiquez-le dans un prompt système, par exemple « réponds toujours en chinois simplifié avec le vocabulaire continental ». Conservez cette instruction dans le message système et ajoutez une vérification post-génération si la rigueur est nécessaire.
Puis-je demander du chinois traditionnel pour les utilisateurs taïwanais ?
Oui. Utilisez un prompt système rédigé en caractères traditionnels qui nomme le vocabulaire régional souhaité, et mappez-le depuis la locale zh-TW dans votre code.
Pourquoi vois-je des caractères chinois corrompus dans ma sortie ?
Presque toujours un problème d'encodage côté client. Lisez les fichiers en UTF-8, définissez le jeu de caractères du type de contenu, et assurez-vous que votre terminal ou votre visualiseur de journaux utilise également l'UTF-8.
Combien de tokens un texte chinois utilise-t-il ?
Prévoyez environ 1,5 token par caractère à titre d'approximation, puis vérifiez le champ d'utilisation dans chaque réponse et ajustez votre estimation.
Votre clé est à un formulaire de vous
Créez un compte, copiez la clé, modifiez l'URL de base. C'est toute la configuration.