Bijgewerkt
Een app in het Chinees bouwen: prompts, scriptbesturing en UTF-8
Een functie in het Chinees uitbrengen is vooral een lokaaliseringsprobleem, geen modelprobleem. Je bepaalt welk script gebruikers zien, houdt coderingen schoon van bestand tot netwerk en budgetteert tokens voor tekst die niet op spaties splitst. Deze gids behandelt deze drie lagen met code die je vandaag nog kunt draaien tegen het chat-completions endpoint.
Wat je aan het verbinden bent
De service exposeert POST /v1/chat/completions en GET /v1/models onder https://api.chinesellmapi.com/v1. Authenticatie is een bearer sleutel, en het enige model id is uncensored. Het is een text-only API met één model, dus er is niets om tussen te selecteren: je lokaalisatiewerk gebeurt volledig in de prompt en in je eigen code eromheen.
Limieten die je moet kennen voordat je iets ontwerpt: een 100.000-token contextvenster gedeeld door prompt en completion, max_tokens standaard ingesteld op 2.048 met een plafond van 32.000, request bodies tot 8 MB, en 300 requests per minuut per sleutel. Streaming (stream: true) werkt via server-sent events, en tools in OpenAI function-calling formaat worden geaccepteerd.
Nieuwe accounts krijgen een $0,50 trial credit geldig voor zeven dagen, zonder betalingsgegevens; registreer met een e-mail en wachtwoord en de sleutel verschijnt direct. De trial FAQ behandelt de regels in detail.
Schrijf prompts in het Chinees, en geef aan welk Chinees
Gemengd-talige prompts zijn de meest voorkomende bron van drift in gelokaliseerde features. Als de instructies in het Engels zijn maar de inhoud Chinees, komen antwoorden soms terug in het Engels of in een mengsel. Een betrouwbaar patroon is een systeem-prompt geschreven in de doeltaal, met drie dingen: de rol, het script en de regionale vocabulaire. Houd door de gebruiker aangeleverde tekst in een apart bericht zodat deze niet verward wordt met instructies.
Hier is een setup voor vereenvoudigd Chinees. Merk op dat de systeem-prompt het model vertelt om alleen in vereenvoudigde karakters te antwoorden en zwevend Engels te vermijden, met eigennamen als uitzondering:
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)
Twee gewoontes betalen zich snel uit. Ten eerste, houd prompts kort en concreet: een rol, een uitvoerformaat, één of twee beperkingen. Ten tweede, zet de taaleis in de systeemboodschap in plaats van het in elke user turn te herhalen, zodat de conversatiegeschiedenis het niet verduidt.
Vereenvoudigd vs traditioneel uitvoer controleren
Scriptkeuze is een productbesluit gekoppeld aan de user locale, geen modelinstelling. Producten gericht op het vasteland verwachten vereenvoudigde karakters; publiek in Taiwan en Hongkong verwacht traditioneel, met ook andere vocabulaire (de gebruikelijke voorbeelden zijn software-, netwerk- en databasetermen). De praktische aanpak is om je locale tag te koppelen aan een systeem-prompt en de keuze expliciet in code te maken.
- zh-CN, zh-SG: Vereenvoudigd, vasteland vocabulaire, halfbrede cijfers, volbrede Chinese leestekens.
- zh-TW: Traditioneel, Taiwan vocabulaire, hoekhaakjes voor citaten zijn gebruikelijk.
- zh-HK: Traditioneel met Hongkong-termen; lever een korte glossary als je product vaste termen heeft.
De traditionele variant van dezelfde call ziet er als volgt uit; alleen de systeem-prompt verandert:
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)
Modellen lekken af en toe een paar karakters van het andere script, vooral als de user message zelf in het andere script is. Voeg een goedkope post-check toe en probeer het opnieuw met een strengere instructie als het misgaat. Voor alles meer dan een sample check, laat de uitvoer via een gespecialiseerde converter library zoals OpenCC lopen:
# 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 van schijf naar netwerk en terug
De meeste garbled-Chinese bugs zijn geen modelproblemen. Ze komen door een standaard codering ergens in de pipeline: een Windows-console die een legacy code page gebruikt, een CSV geopend zonder encoding, een proxy die het content type herschrijft. Maak elke grens expliciet.
In Python, geef encoding="utf-8" aan open, en als je hand-build JSON, gebruik ensure_ascii=False gevolgd door .encode("utf-8"). Het onderstaande voorbeeld doet dit met plain requests, wat ook de ruwe HTTP-vorm van de call toont:
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"])
In Node, readFile retourneert een Buffer tenzij je een encoding opgeeft, en template strings met Chinese tekst zijn prima zolang het bronbestand zelf als UTF-8 is opgeslagen. De officiële SDK serialiseert de body voor je:
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);
Twee meer valkuilen. Strings afkappen op byte-lengte kan een karakter in tweeën snijden, dus slice altijd op karakters. En als je streamt, decodeer de stream dan incrementeel als UTF-8, omdat een multi-byte karakter over netwerk chunks verdeeld kan zijn; de SDKs lossen dit op, maar hand-rolled parsers doen dat vaak niet.
Tokens budgetteren voor Chinese tekst
Chinees heeft geen spaties, dus woordtellingen zijn nutteloos voor budgettering. Als een planning-aanname, geen gemeten constante, behandel één Chinees teken als ongeveer 1 tot 2 tokens, en gebruik 1,5 als je één getal nodig hebt. Latijnse woorden en cijfers ingebed in de tekst liggen dichter bij 1,3 tokens per woord. De ratio varieert met vocabulaire, dus de enige gezaghebbende figuur is het usage object dat bij elke response wordt teruggegeven.
Een kleine helper maakt het makkelijk om een prompt te schatten voordat je hem verstuurt, en om de verhoudingen af te stemmen op echt gebruik in de loop van de tijd:
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))
Uitgewerkt voorbeeld, met gestelde aannames: een artikel van 2.000 karakters bij 1,5 tokens per karakter is ongeveer 3.000 input tokens. Een samenvatting van 400 karakters is ongeveer 600 output tokens. Bij $0,25 per miljoen input tokens en $1,00 per miljoen output tokens kost één call ongeveer $0,00075 voor input plus $0,0006 voor output, in totaal ongeveer $0,00135. Omdat context beperkt is tot 100.000 tokens, betekent dezelfde aanname dat een prompt van ongeveer 40.000 karakters ruimte laat voor een reply.
Als je lange documenten moet voeden, chunk dan per paragraaf of kop, nooit midden in een zin, en houd max_tokens expliciet. Prijzen en limieten staan op de pricing page.
Multi-turn Chinese chat binnen het contextvenster houden
Chat-functies sturen de hele history opnieuw bij elk request, dus kosten en context groeien met elke beurt. Omdat het venster 100.000 tokens is, gedeeld tussen prompt en completion, zal een lang gesprek uiteindelijk een 400 triggeren als je niets doet. Beslis vroeg over een trimming-beleid in plaats van te reageren op fouten.
Een eenvoudig beleid is om de systeem-prompt te behouden, de oudste turns te verwijderen totdat de geschatte prompt past in een budget, en de rest te reserveren voor het antwoord. De schets hieronder hergebruikt de estimator uit het vorige deel:
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
Voor producten waar vroege context belangrijk is, zoals een tutorbot of support assistant, vervang verwijderde turns door een korte samenvattingsboodschap in plaats van ze volledig te verwijderen. Vraag het model om de oude turns samen te persen tot een paar zinnen in hetzelfde script als de conversatie, en voeg die samenvatting direct na de systeem-prompt in. Het kost één extra call af en toe en houdt het persona en de feiten stabiel.
Vergeet ook niet om max_tokens verstandig af te stellen voor chat. Replies in een messaging UI hebben zelden meer dan een paar honderd tokens nodig, en een strakkere cap maakt zowel latency als kosten voorspelbaarder. Streaming de reply token by token helpt de waargenomen snelheid, vooral voor Chinese tekst waar gebruikers in korte bursts lezen.
Pre-launch checklist en volgende stappen
- Koppel elke locale aan een systeem-prompt en unit-test de koppeling.
- Stel coderingen expliciet in bij bestand-leesacties, request bodies en logs.
- Log
usageper feature en vergelijk het wekelijks met je estimator. - Behandel 402 (
no_credit), 429 en 503 (upstream_busy) anders: top up, vertraag, probeer het na een paar seconden opnieuw. - Behandel 403 (
content_blocked) als een definitief antwoord voor dat request, geen retry-geval.
Als je app vertaalpipelines bevat, ga dan verder met de vertaal- en lokalisatiegids; voor geserialiseerde fictie en personagechat, zie de gids voor webfictie. De volledige parameterreferentie staat in de documentatie.
Vragen en antwoorden
Hoe zorg ik dat replies alleen in vereenvoudigd Chinees terugkomen?
Geef dit op in een systeem-prompt, bijvoorbeeld "antwoord altijd in vereenvoudigd Chinees met woordenschat uit het vasteland". Houd deze instructie in het systeembericht en voeg een post-check toe als de output strikt moet zijn.
Kan ik traditioneel Chinees aanvragen voor Taiwan-gebruikers?
Ja. Gebruik een systeem-prompt geschreven in traditionele karakters die de gewenste regionale woordenschat benoemt, en koppel dit aan de zh-TW-locale in je code.
Waarom zie ik onleesbare Chinese karakters in mijn output?
Bijna altijd een coderingsprobleem aan de clientkant. Lees bestanden als UTF-8, stel de content type charset in en zorg dat je terminal of logviewer ook UTF-8 gebruikt.
Hoeveel tokens gebruikt Chinese tekst?
Plan met ongeveer 1,5 tokens per karakter als benadering, controleer dan het usage veld in elke response en pas je schatting aan.
Je sleutel is nog maar één formulier verwijderd
Maak een account aan, kopieer de sleutel, pas de base URL aan. Dat is de hele setup.