Actualizado
Desarrollo de una aplicación en chino: prompts, control de guiones y UTF-8
Lanzar una función en chino es principalmente un problema de localización, no de modelado. Tú decides qué guion ven los usuarios, mantienes las codificaciones limpias desde el archivo hasta la red y presupuestas tokens para texto que no se divide por espacios. Esta guía cubre esas tres capas con código que puedes ejecutar contra el endpoint de chat completions hoy.
Lo que estás conectando
El servicio expone POST /v1/chat/completions y GET /v1/models bajo https://api.chinesellmapi.com/v1. La autenticación es una clave bearer, y el único id de modelo es uncensored. Es una API solo de texto con un modelo, así que no hay nada que seleccionar entre: tu trabajo de localización ocurre completamente en el prompt y en tu propio código alrededor de él.
Límites que vale la pena conocer antes de diseñar: una ventana de contexto de 100.000 tokens compartida entre prompt y completado, max_tokens con valor predeterminado de 2.048 y un límite de 32.000, cuerpos de petición de hasta 8 MB y 300 peticiones por minuto por clave. El streaming (stream: true) funciona mediante eventos enviados por el servidor y se aceptan herramientas en formato de llamadas a funciones de OpenAI.
Las cuentas nuevas reciben un crédito de prueba de $0,50 válido por siete días, sin datos de pago; regístrate con un correo electrónico y contraseña y la clave aparece inmediatamente. La FAQ de prueba cubre las reglas en detalle.
Escribe prompts en chino, y especifica cuál
Los prompts mixtos de idiomas son la fuente más común de desviación en funciones localizadas. Si las instrucciones están en inglés pero el contenido es chino, las respuestas a veces vuelven en inglés o en una mezcla. Un patrón confiable es un prompt del sistema escrito en el idioma objetivo, indicando tres cosas: el rol, el guion y el vocabulario regional. Mantén el texto proporcionado por el usuario en un mensaje separado para que nunca se confunda con instrucciones.
Aquí tienes una configuración en chino simplificado. Nota que el prompt del sistema indica al modelo que responda solo en caracteres simplificados y evite inglés no deseado, con los nombres propios como excepción:
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)
Dos hábitos pagan rápidamente. Primero, mantén los prompts cortos y concretos: un rol, un formato de salida, una o dos restricciones. Segundo, coloca el requisito de idioma en el mensaje del sistema en lugar de repetirlo en cada turno del usuario, para que el historial de la conversación no lo diluya.
Control de salida simplificada vs tradicional
La elección del guion es una decisión de producto vinculada a la configuración regional del usuario, no una configuración del modelo. Los productos orientados a la China continental esperan caracteres simplificados; las audiencias de Taiwán y Hong Kong esperan caracteres tradicionales, con vocabulario diferente también (los ejemplos habituales son términos de software, red y base de datos). El enfoque práctico es mapear tu etiqueta de configuración regional a un prompt del sistema y hacer la elección explícita en el código.
- zh-CN, zh-SG: Vocabulario simplificado de China continental, dígitos de ancho medio, puntuación china de ancho completo.
- zh-TW: Tradicional, vocabulario de Taiwán, corchetes angulares para citas son comunes.
- zh-HK: Tradicional con terminología de Hong Kong; proporciona un glosario corto si tu producto tiene términos fijos.
La variante tradicional de la misma llamada se ve así; solo cambia el prompt del sistema:
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)
Los modelos a veces filtran algunos caracteres del otro guion, especialmente cuando el mensaje del usuario está en el otro guion. Añade una verificación posterior económica y reintenta una vez con una instrucción más firme si falla. Para cualquier cosa más allá de una verificación de muestra, ejecuta la salida a través de una biblioteca de conversión dedicada como 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 desde el disco hasta la red y de vuelta
La mayoría de los errores de chino ilegible no son problemas del modelo. Provienen de una codificación predeterminada en alguna parte de la canalización: una consola de Windows que usa una página de códigos heredada, un CSV abierto sin una codificación, un proxy que reescribe el tipo de contenido. Haz explícito cada límite.
En Python, pasa encoding="utf-8" a open, y si construyes JSON a mano, usa ensure_ascii=False seguido de .encode("utf-8"). El ejemplo siguiente lo hace con requests plano, que también muestra la forma HTTP en bruto de la llamada:
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 devuelve un Buffer a menos que proporciones una codificación, y las cadenas de plantilla que contienen chino están bien siempre que el archivo de origen se guarde como UTF-8. El SDK oficial serializa el cuerpo por ti:
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);
Dos trampas más. Truncar cadenas por longitud de byte puede cortar un carácter por la mitad, así que siempre corta por caracteres. Y cuando transmitas, decodifica el flujo como UTF-8 incrementalmente, porque un carácter de varios bytes puede dividirse entre fragmentos de red; los SDKs manejan esto, pero los analizadores hechos a mano a menudo no.
Presupuestar tokens para texto en chino
El chino no tiene espacios, por lo que los recuentos de palabras son inútiles para presupuestar. Como suposición de planificación, no una constante medida, trata un carácter chino como aproximadamente 1 a 2 tokens, y usa 1,5 cuando necesites un solo número. Las palabras en latín y los dígitos incrustados en el texto están más cerca de 1,3 tokens por palabra. La relación varía con el vocabulario, por lo que la única figura autoritativa es el objeto usage devuelto con cada respuesta.
Un pequeño ayudante facilita estimar un prompt antes de enviarlo, y ajustar las proporciones contra el uso real con el tiempo:
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))
Ejemplo resuelto, con suposiciones declaradas: un artículo de 2.000 caracteres a 1,5 tokens por carácter es aproximadamente 3.000 tokens de entrada. Un resumen de 400 caracteres es aproximadamente 600 tokens de salida. A $0,25 por millón de tokens de entrada y $1,00 por millón de tokens de salida, una llamada cuesta aproximadamente $0,00075 para entrada más $0,0006 para salida, aproximadamente $0,00135 en total. Dado que el contexto está limitado a 100.000 tokens, la misma suposición significa que un prompt de aproximadamente 40.000 caracteres deja espacio para una respuesta.
Cuando tengas que alimentar documentos largos, divídelos por párrafo o encabezado, nunca a mitad de una oración, y mantén max_tokens explícito. Los precios y límites se enumeran en la página de precios.
Mantener el chat chino multiturno dentro de la ventana
Las funciones de chat reenvían todo el historial en cada petición, por lo que el costo y el contexto aumentan con cada turno. Como la ventana es de 100.000 tokens compartidos entre prompt y completado, una conversación larga eventualmente activará un 400 si no haces nada. Decide una política de recorte temprano en lugar de reaccionar a los errores.
Una política simple es mantener el prompt del sistema, eliminar los turnos más antiguos hasta que el prompt estimado se ajuste a un presupuesto, y reservar el resto para la respuesta. El boceto a continuación reutiliza el estimador de la sección anterior:
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
Para productos donde el contexto temprano importa, como un bot de tutoría o un asistente de soporte, reemplaza los turnos eliminados con un mensaje de resumen corto en lugar de descartarlos por completo. Pide al modelo que comprima los turnos antiguos en unas pocas frases en el mismo guion que la conversación, luego inserta ese resumen justo después del prompt del sistema. Cuesta una llamada extra de vez en cuando y mantiene la persona y los hechos estables.
También recuerda limitar max_tokens sensatamente para el chat. Las respuestas en una interfaz de mensajería rara vez necesitan más de unas pocas cientos de tokens, y un límite más estricto hace que la latencia y el costo sean más predecibles. Transmitir la respuesta token por token ayuda a la velocidad percibida, particularmente para texto chino donde los usuarios leen en ráfagas cortas.
Lista de verificación previa al lanzamiento y próximos pasos
- Asigna cada entorno regional a un prompt del sistema y prueba la asignación con pruebas unitarias.
- Establece codificaciones explícitamente en lecturas de archivos, cuerpos de solicitud y registros.
- Registra
usagepor función y compáralo con tu estimador semanalmente. - Maneja 402 (
no_credit), 429 y 503 (upstream_busy) de forma diferente: recarga, reduce la velocidad o reintenta después de unos segundos. - Trata 403 (
content_blocked) como una respuesta final para esa solicitud, no como un caso de reintento.
Si tu aplicación implica canalizaciones de traducción, continúa con la guía de traducción y localización; para ficción serializada y chat de personajes, consulta la guía de ficción web. La referencia completa de parámetros está en la documentación.
Preguntas y respuestas
¿Cómo hago para que las respuestas se devuelvan solo en chino simplificado?
Indícalo en un prompt de sistema, por ejemplo "responde siempre en chino simplificado con vocabulario continental". Mantén esa instrucción en el mensaje del sistema y añade una verificación posterior si la salida debe ser estricta.
¿Puedo solicitar chino tradicional para usuarios de Taiwán?
Sí. Usa un prompt de sistema escrito en caracteres tradicionales que nombre el vocabulario regional que deseas, y mapea esto desde la configuración regional zh-TW en tu código.
¿Por qué veo caracteres chinos corruptos en mi salida?
Casi siempre es un problema de codificación del lado del cliente. Lee los archivos como UTF-8, establece el charset del tipo de contenido y asegúrate de que tu terminal o visor de registros también use UTF-8.
¿Cuántos tokens utiliza el texto chino?
Planifica con aproximadamente 1,5 tokens por carácter como estimación, luego verifica el campo de uso en cada respuesta y ajusta tu estimación.
Tu clave está a un formulario de distancia
Crea una cuenta, copia la clave, cambia la URL base. Eso es toda la configuración.