อัปเดต
API แชทสำหรับแปลและปรับภาษาจีน↔อังกฤษ
การแปลสตริง UI และเอกสารด้วยโมเดลแชทง่ายต่อการสาธิตแต่ยากต่อการส่งมอบ ความล้มเหลวเป็นเรื่องธรรมดา: ตัวแปรที่เปลี่ยนชื่อ คำศัพท์ในพจนานุกรมที่แปลได้สามแบบต่างกัน ผลตอบกลับ JSON ที่ห่อหุ้มด้วยข้อความ คู่มือนี้พิจารณาการแปลเป็นสายการผลิตที่มีการตรวจสอบในแต่ละขั้นตอน โดยใช้เอนด์พอยต์การเติมข้อความแชทที่เข้ากันได้กับ OpenAI
คิดเป็นขั้นตอน ไม่ใช่ในพรอมต์เดียว
งานแปลการผลิตมีสี่ขั้นตอน: เตรียมสตริง เรียกโมเดล ตรวจสอบผลลัพธ์ และรวมกลับ ปัญหาคุณภาพส่วนใหญ่เกิดจากการข้ามขั้นตอนการตรวจสอบ วิศวกรโลคัลไลเซชันจะไม่ส่งไฟล์แปลโดยไม่รันโปรแกรมตรวจสอบตัวแปร และวินัยเดียวกันนี้ใช้เมื่อผู้แปลเป็นโมเดล
เอนด์พอยต์คือ https://api.chinesellmapi.com/v1/chat/completions, id โมเดล uncensored, การตรวจสอบสิทธิ์แบบ bearer รองรับทั้งสองทิศทาง อังกฤษเป็นจีนและจีนเป็นอังกฤษ ตัวอย่างด้านล่างใช้สตริงต้นทางภาษาอังกฤษเป็นภาษาจีนแบบง่าย เปลี่ยนทิศทางในพรอมต์ระบบสำหรับการกลับด้าน
โปรดจำไว้ว่าหน้าต่างบริบท 100,000 โทเคนร่วมกัน ค่าเริ่มต้น max_tokens คือ 2,048 (เพิ่มสำหรับเอกสารยาว สูงสุด 32,000) และขีดจำกัดร่างกายคำขอ 8 MB สิ่งเหล่านี้ไม่กระทบกับสตริง UI แต่ทั้งสามสิ่งสำคัญสำหรับเอกสารทั้งหมด
การแทรกพจนานุกรม
ชื่อแบรนด์ คำนามผลิตภัณฑ์ และคำที่อ่อนไหวทางกฎหมายต้องการการแสดงผลที่คงที่ กลไกที่ประหยัดที่สุดคือบล็อกกอซซารีในพรอมต์ระบบ ซึ่งระบุคำต้นทางและเป้าหมายที่ต้องการ กำหนดกฎอย่างชัดเจน: ใช้กอซซารีตามตัวอักษรในการผันรูปของคำต้นทางใด ๆ เก็บกอซซารีให้สั้น เพราะแต่ละบรรทัดจะถูกเรียกเก็บเป็นอินพุตในทุกคำขอ; สิบถึงหลายสิบรายการเป็นเรื่องปกติ ไม่ใช่พันรายการ
import os
from openai import OpenAI
client = OpenAI(base_url="https://api.chinesellmapi.com/v1", api_key=os.environ["API_KEY"])
GLOSSARY = {
"workspace": "工作区",
"pull request": "合并请求",
"seat": "席位",
"billing cycle": "计费周期",
}
def build_system(glossary, target="Simplified Chinese"):
rows = "\n".join(f"- {src} => {dst}" for src, dst in glossary.items())
return (
f"You are a software localization translator. Translate English UI strings into {target}.\n"
"Rules:\n"
"1. Use the glossary below verbatim whenever the source term appears, in any inflection.\n"
"2. Copy placeholders such as {name}, %s, %d and {{count}} exactly, character for character.\n"
"3. Copy HTML tags and attributes exactly; translate only the text between tags.\n"
"4. Output the translation only, with no notes.\n\n"
"Glossary:\n" + rows
)
def translate(text):
r = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": build_system(GLOSSARY)},
{"role": "user", "content": text},
],
temperature=0.2,
max_tokens=400,
)
return r.choices[0].message.content.strip()
print(translate("Invite {name} to the workspace before the next billing cycle."))
หากกอซซารีของคุณมีขนาดใหญ่ ให้กรองต่อคำขอ: รวมเฉพาะรายการที่คำต้นทางปรากฏในแบทช์ การทดสอบสตริงย่อยแบบธรรมดาบนข้อความตัวพิมพ์เล็กก็เพียงพอสำหรับเวอร์ชันแรกและทำให้พรอมต์มีขนาดเล็ก ตั้งอุณหภูมิประมาณ 0.2; ความแปรปรวนเชิงสร้างสรรค์เป็นข้อบกพร่องในข้อความ UI
นอกจากนี้ให้ตัดสินใจคู่มือสไตล์สำหรับภาษาเป้าหมายล่วงหน้า: การทักทายแบบทางการหรือกันเอง การเว้นวรรคระหว่างตัวอักษรจีนและคำหรือตัวเลขภาษาละตินที่ฝังอยู่ และชุดเครื่องหมายวรรคตอนใดที่จะใช้ วางการตัดสินใจเหล่านี้ในพรอมต์ระบบเดียวกันเพื่อให้แบทช์ทุกชุดสืบทอดพวกเขา
การรักษาตัวแปรและมาร์กอัปให้สมบูรณ์
เพลสโฮลเดอร์อาจทำให้โมเดลแปลชื่อตัวแปร, ตัด %s ท้ายบรรทัด หรือเพิ่มช่องว่างในวงเล็บ กฎในพรอมต์ลดข้อผิดพลาดเหล่านี้แต่ไม่สามารถขจัดได้ จึงต้องตรวจสอบในโค้ด แยกเพลสโฮลเดอร์และแท็กจากต้นทางและเป้าหมายด้วย regular expression แล้วเปรียบเทียบเป็นรายการที่เรียงลำดับ ลำดับอาจเปลี่ยนได้ตามภาษา จึงเปรียบเทียบแบบ multisets แทนลำดับ
import re
PLACEHOLDER = re.compile(r"\{\{?\w+\}?\}|%[sd]|</?[a-zA-Z][^>]*>")
def placeholders_ok(source: str, target: str) -> bool:
# Same multiset of placeholders and tags, regardless of order.
return sorted(PLACEHOLDER.findall(source)) == sorted(PLACEHOLDER.findall(target))
src = 'You have <b>{count}</b> unread messages in %s.'
bad = '你在 %s 中有 <b>{数量}</b> 条未读消息。'
good = '你在 %s 中有 <b>{count}</b> 条未读消息。'
print(placeholders_ok(src, bad), placeholders_ok(src, good)) # False True
เมื่อการตรวจสอบล้มเหลว ให้ลองอีกครั้งหนึ่งครั้งโดยมีการจับคู่ที่ล้มเหลวถูกกล่าวถึงในพรอมต์ ตัวอย่างเช่น ข้อความผู้ใช้ที่บอกว่าผลลัพธ์ก่อนหน้าเปลี่ยนตัวแปรและต้องแก้ไข หากยังคงล้มเหลว ให้ทำเครื่องหมายสตริงสำหรับการตรวจสอบโดยมนุษย์มากกว่าการวนซ้ำ ข้อความ rich text ต้องการความระมัดระวังเป็นพิเศษ: เลือกแปลโหนดข้อความและสร้างมาร์กอัปของคุณเอง เนื่องจากนั่นทำให้ความเสียหายของแท็กเป็นไปไม่ได้โดยโครงสร้าง
ผลลัพธ์ที่มีโครงสร้างผ่านคำสั่ง
การส่งสตริงแบบแบทช์ในคำขอเดียวต้องการการตอบกลับที่เครื่องอ่านได้ API รับฟิลด์การตอบแชทมาตรฐาน; การเอาต์พุตที่มีโครงสร้างมาจากการระบุคำสั่งที่ชัดเจนแทนสวิตช์บังคับ schema จึงเขียนสัญญาในพรอมต์, รวม id เพื่อจัดลำดับผลลัพธ์ใหม่ และแยกวิเคราะห์แบบป้องกัน โมเดลอาจห่อ JSON ใน code fence หรือเพิ่มประโยคสุภาพ จึงลบ fence ก่อนแยกวิเคราะห์และจัดการข้อผิดพลาดการแยกวิเคราะห์เป็นเหตุการณ์ที่ลองใหม่ได้
import json
import os
import re
from openai import OpenAI
client = OpenAI(base_url="https://api.chinesellmapi.com/v1", api_key=os.environ["API_KEY"])
SYSTEM = (
"Translate each item from English to Simplified Chinese. "
"Reply with a JSON object only, no code fences, no commentary. "
'Shape: {"items": [{"id": <number>, "zh": "<translation>"}]}. '
"Keep the same ids, keep every placeholder and HTML tag unchanged."
)
def translate_batch(strings):
payload = [{"id": i, "en": s} for i, s in enumerate(strings)]
r = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
],
temperature=0.2,
max_tokens=2000,
)
raw = r.choices[0].message.content.strip()
raw = re.sub(r"^```(?:json)?|```$", "", raw, flags=re.M).strip() # tolerate stray fences
data = json.loads(raw)
by_id = {item["id"]: item["zh"] for item in data["items"]}
return [by_id[i] for i in range(len(strings))]
print(translate_batch(["Save changes", "Delete {count} files?", "Welcome back, <b>{name}</b>"]))
ตรวจสอบเสมอหลังการแยกวิเคราะห์: id ต้องตรงกัน จำนวนต้องตรงกัน และแต่ละรายการต้องผ่านการตรวจสอบเพลสโฮลเดอร์จากส่วนก่อนหน้า สตริงห้าสิบรายการต่อคำขอเป็นแบทช์เริ่มต้นที่เหมาะสมสำหรับข้อความ UI สั้น; เพิ่มเฉพาะเมื่ออัตราการผ่านการตรวจสอบยังคงสูง
การแปลแบทช์ภายใต้ขีดจำกัดอัตรา
คีย์แต่ละตัวจำกัดที่ 300 คำขอต่อนาที งานโลคัลไลเซชันที่มีสตริง 50,000 รายการที่ 20 สตริงต่อคำขอคือ 2,500 คำขอ ซึ่งพอดีภายในสิบนาทีหากคุณจัดจังหวะอย่างสม่ำเสมอ สคริปต์ด้านล่างรวมเซมิโฟร์สำหรับคำขอที่กำลังดำเนินการพร้อมตัวควบคุมแบบล็อก และถอยหลังเมื่อได้รับ 429 และ 503 ต่างจากข้อผิดพลาดการตรวจสอบ ข้อผิดพลาดสองข้อนี้เป็นชั่วคราว: 429 หมายถึงช้าลง และ 503 พร้อม upstream_busy หมายถึงลองใหม่ในไม่กี่วินาที 402 หมายถึงยอดเงินหมด ซึ่งการลองใหม่ไม่สามารถแก้ไขได้
import asyncio
import os
import time
from openai import AsyncOpenAI, APIStatusError
client = AsyncOpenAI(
base_url="https://api.chinesellmapi.com/v1",
api_key=os.environ["API_KEY"],
max_retries=0,
timeout=90,
)
RATE = 240 / 60 # requests per second, below the 300/min cap
gate = asyncio.Lock()
next_slot = 0.0
slots = asyncio.Semaphore(6)
async def pace():
global next_slot
async with gate:
now = time.monotonic()
if next_slot > now:
await asyncio.sleep(next_slot - now)
next_slot = max(now, next_slot) + 1 / RATE
async def call(batch, attempt=0):
async with slots:
await pace()
try:
r = await client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Translate to Simplified Chinese. Keep placeholders unchanged. One line per input line."},
{"role": "user", "content": "\n".join(batch)},
],
max_tokens=1500,
temperature=0.2,
)
return r.choices[0].message.content.splitlines()
except APIStatusError as e:
if e.status_code in (429, 503) and attempt < 4:
await asyncio.sleep(2 ** attempt + 1) # 1s, 3s, 5s, 9s
return await call(batch, attempt + 1)
raise
async def run(all_strings, size=20):
chunks = [all_strings[i:i + size] for i in range(0, len(all_strings), size)]
results = await asyncio.gather(*(call(c) for c in chunks))
return [line for part in results for line in part]
if __name__ == "__main__":
strings = [f"Item {n} was updated by {{user}}" for n in range(60)]
out = asyncio.run(run(strings))
print(len(out), out[0])
เวอร์ชันนี้แยกตามบรรทัดเพื่อความกระชับ ซึ่งใช้ได้สำหรับสตริงแบบบรรทัดเดียว; สำหรับหลายบรรทัด ให้ใช้รูปแบบ JSON จากส่วนก่อนหน้าเพื่อให้การแตกบรรทัดภายในสตริงไม่ถูกเข้าใจผิดว่าเป็นขอบเขตสตริง
ทิศทางย้อนกลับ: ข้อความต้นฉบับภาษาจีน
การแปลภาษาจีนเป็นภาษาอังกฤษมีโหมดล้มเหลวเฉพาะ ภาษาจีนละเว้นการระบุประธานและพหูพจน์ได้ โมเดลจึงต้องคาดเดา; ให้บริบทแก่โมเดลด้วย
วัดขีดจำกัดความยาวอย่างชัดเจน ข้อความ UI ภาษาอังกฤษมักยาวกว่าต้นฉบับภาษาจีน และในทางกลับกันเป็นจริงสำหรับเป้าหมายภาษาจีน ซึ่งมักจะมีอักขระสั้นกว่าแต่กว้างกว่าบนหน้าจอเพราะแต่ละ glyph กว้างเต็มช่อง กำหนดงบประมาณอักขระในพรอมต์เมื่อปุ่มหรือส่วนหัวตารางมีขีดจำกัดแข็ง และตรวจสอบในโค้ดหลังจากได้รับคำตอบ
สำหรับเอกสารต้นฉบับภาษาจีนที่มีชื่อคนและสถานที่ ระบุข้อตกลงการโรมันไนเซชันล่วงหน้า เช่น Hanyu Pinyin โดยไม่มีเครื่องหมายเสียง และเพิ่มชื่อที่พบบ่อยลงในพจนานุกรม หากไม่มีสิ่งนั้น คนคนเดียวกันอาจปรากฏภายใต้การสะกดสองแบบภายในเอกสารเดียว ซึ่งนั่นคือความไม่สอดคล้องกันที่ผู้ตรวจสอบจะสังเกตเห็นเป็นครั้งแรก
การสุ่มตัวอย่างและตรวจสอบก่อนรวม
การตรวจสอบอัตโนมัติจับข้อผิดพลาดโครงสร้าง ไม่ใช่การแปลผิด เพิ่มขั้นตอนมนุษย์แบบเบา: สุ่มเปอร์เซ็นต์คงที่ของแบทช์แต่ละชุด ตัวอย่างเช่น สตริงที่ 20 ทุกครั้ง รวมถึงสตริงทั้งหมดที่ต้องลองซ้ำ และส่งไปยังผู้ตรวจสอบสองภาษา ติดตามอัตราการแก้ไขของผู้ตรวจสอบต่อแบทช์ หากเพิ่มขึ้น ให้กระชับพรอมต์ ลดขนาดแบทช์ หรือเพิ่มรายการพจนานุกรมสำหรับคำที่กำลังแก้ไข
เก็บพรอมต์ เวอร์ชันพจนานุกรมและการตั้งค่าแบทช์ไว้พร้อมกับไฟล์ผลลัพธ์แต่ละไฟล์ เมื่อคำเปลี่ยนในพจนานุกรม คุณสามารถแปลสตริงที่มีคำนั้นใหม่ได้ แทนที่จะรันคอร์ปัสทั้งหมด แฮชเนื้อหาแบบง่ายของสตริงต้นฉบับบวกกับเวอร์ชันพจนานุกรมทำงานเป็นคีย์แคช และหมายความว่าสตริงที่ไม่เปลี่ยนแปลงไม่มีค่าใช้จ่ายในการรันครั้งถัดไป
สุดท้าย เก็บชุดรีเกรสชันของสตริงยาก: ที่มีเพลสโฮลเดอร์ซ้อน, รูปแบบพหูพจน์, HTML ในตัว และคำนามประสมยาว รันชุดนี้ whenever เปลี่ยนพรอมต์ และเปรียบเทียบเอาต์พุตเคียงข้างก่อนปรับใช้การเปลี่ยนแปลง
การประมาณค่าใช้จ่ายสำหรับการรันโลคัลไลเซชัน
สมมติฐานที่ระบุอย่างชัดเจน: สตริงต้นทาง 30,000 อัน, 12 คำภาษาอังกฤษต่อสตริง, แปลแบบแบทช์ละ 20 ใช้โทเคนอินพุตประมาณ 20 ต่อสตริง บวกระบบพรอมต์คงที่ 300 โทเคนต่อคำขอ และโทเคนเอาต์พุตประมาณ 35 ต่อสตริง นั่นคือ 1,500 คำขอ, อินพุตโทเคนประมาณ 1.05 ล้าน และเอาต์พุตโทเคนประมาณ 1.05 ล้าน ที่ $0.25 ต่อล้านโทเคนอินพุตและ $1.00 ต่อล้านโทเคนเอาต์พุต การรันนี้ใช้งบประมาณ $0.26 บวก $1.05 รวมประมาณ $1.31 ถือเป็นการประมาณการลำดับขนาดและแทนที่สมมติฐานด้วยตัวเลขจาก usage logs ของคุณ
เครดิตทดลองใช้คือ $0.50 เป็นเวลาเจ็ดวัน ซึ่งเพียงพอสำหรับการตรวจสอบสายการผลิตบนตัวอย่างของสตริงไม่กี่พันรายการ ดำเนินการต่อด้วย app quickstart สำหรับการควบคุมสคริปต์และการจัดการ UTF-8 หรือดู หน้าราคา สำหรับอัตราปัจจุบัน
ถาม-ตอบ
API สามารถแปลได้ทั้งสองทิศทางหรือไม่?
ใช่ ภาษาจีนเป็นภาษาอังกฤษและภาษาอังกฤษเป็นจีนทำงานผ่านเอนด์พอยต์แชทเดียวกัน; คุณเลือกทิศทางในพรอมต์ระบบ
ฉันจะหยุดโมเดลจากการเปลี่ยนเพลสโฮลเดอร์เช่น {name} ได้อย่างไร?
ระบุกฎในพรอมต์ จากนั้นตรวจสอบในโค้ดโดยเปรียบเทียบรายการตัวแปรระหว่างต้นฉบับและเป้าหมาย ทำซ้ำหรือแจ้งเตือนเมื่อไม่ตรงกัน
มีสวิตช์โหมด JSON หรือไม่?
เอาพุตที่มีโครงสร้างได้รับผ่านคำแนะนำ ระบุรูปร่างที่แน่นอนในพรอมต์ ตัดโค้ด fences ที่หลุดออก จากนั้นแยกวิเคราะห์และตรวจสอบผลลัพธ์
ฉันสามารถรันแบทช์ขนาดใหญ่ได้เร็วแค่ไหน?
คีย์แต่ละตัวอนุญาต 300 คำขอต่อนาที จัดจังหวะคำขออย่างสม่ำเสมอและถอยหลังเมื่อได้รับ 429 หรือ 503
คีย์ของคุณอยู่ห่างแค่แบบฟอร์มเดียว
สร้างบัญชี คัดลอกคีย์ เปลี่ยน URL ฐาน แค่นี้ก็พร้อมใช้งาน