The Vietnamese Token Tax: Measuring and Cutting LLM Costs for Non-English Text
Tuần này trên Hacker News có bài thảo luận khá sôi nổi: generative AI tốn nhiều năng lượng hơn khi xử lý văn bản không phải tiếng Anh. Với dev Việt, chuyện này ảnh hưởng thẳng đến công việc hằng ngày. Cùng một ý, viết bằ
Tuần này trên Hacker News có bài thảo luận khá sôi nổi: generative AI tốn nhiều năng lượng hơn khi xử lý văn bản không phải tiếng Anh. Với dev Việt, chuyện này ảnh hưởng thẳng đến công việc hằng ngày. Cùng một ý, viết bằng tiếng Việt có thể tốn gấp 1.5–3 lần số token so với tiếng Anh, tùy tokenizer. Số token nhiều hơn kéo theo hóa đơn API cao hơn, latency lâu hơn, context window bị lấp đầy nhanh hơn và tốn thêm điện ở data center. Mình đã gặp chuyện này khi làm chatbot CSKH cho một khách hàng: tính chi phí theo prompt mẫu bằng tiếng Anh, chạy production bằng tiếng Việt thì bill gần gấp đôi dự kiến. Bài này chia sẻ cách đo, các bẫy thường gặp và cách giảm “thuế token” tiếng Việt.
Tại sao tiếng Việt lại “đắt” hơn?
Phần lớn LLM hiện nay dùng byte-level BPE (Byte Pair Encoding). Tokenizer học cách gộp các chuỗi byte hay xuất hiện thành một token, và dữ liệu dùng để train tokenizer chủ yếu là tiếng Anh. Kết quả là database thường chỉ tốn 1 token, còn người hay triển khai có thể bị cắt thành nhiều mảnh.
Còn một lý do nữa là UTF-8. Ký tự a chiếm 1 byte, nhưng ư, ờ, ế chiếm 2–3 byte. Nếu tokenizer chưa học được cách gộp những chuỗi byte này thì mỗi ký tự có dấu có thể tốn nhiều token.
flowchart LR
A[Văn bản tiếng Việt] --> B[Unicode normalize]
B --> C[UTF-8 bytes]
C --> D[BPE merges]
D --> E[Token IDs]
E --> F[Chi phí API]
E --> G[Latency]
E --> H[Context window]
Các tokenizer mới đã cải thiện đáng kể. Ví dụ o200k_base có vocabulary khoảng 200K token, gấp đôi cl100k_base, và tiếng Việt được hưởng lợi khá rõ. Dù vậy, khoảng cách với tiếng Anh vẫn còn, và với nhiều model open-source thì khoảng cách này còn lớn hơn.
Đo trước khi tối ưu
Đừng tin bất kỳ con số nào trên mạng, kể cả con số trong bài này. Hãy đo trên chính dữ liệu của bạn. Với các model dùng tokenizer kiểu OpenAI, tiktoken là cách nhanh nhất:
# pip install "tiktoken>=0.8"
import tiktoken
samples = {
'en': 'The payment service failed to connect to the database after deployment.',
'vi': 'Dịch vụ thanh toán không kết nối được với cơ sở dữ liệu sau khi triển khai.',
}
for name in ['cl100k_base', 'o200k_base']:
enc = tiktoken.get_encoding(name)
en = len(enc.encode(samples['en']))
vi = len(enc.encode(samples['vi']))
print(f'{name:12} en={en:3} vi={vi:3} ratio={vi/en:.2f}x')
Đừng chỉ đo vài câu mẫu. Hãy lấy khoảng 500–1000 message thật từ log production (nhớ ẩn danh hóa trước), chạy script và tính tỷ lệ trung bình. Con số đó mới dùng được để dự toán chi phí.
Với model local, đặc biệt khi hệ sinh thái ggml/llama.cpp ngày càng ổn định (ggml vừa lên v1.0.0), bạn có thể dùng tool llama-tokenize đi kèm llama.cpp để kiểm tra tokenizer của từng file GGUF:
# So sánh tokenizer của 2 model trước khi chọn model để self-host
for m in models/qwen-7b-q4_k_m.gguf models/llama-8b-q4_k_m.gguf; do
echo "== $m"
./llama-tokenize -m "$m" \
-p "Dịch vụ thanh toán không kết nối được với cơ sở dữ liệu" \
--show-count --log-disable | tail -n 1
done
Khi so sánh hai model có chất lượng tương đương, model có tokenizer xử lý tiếng Việt hiệu quả hơn sẽ cho throughput thực tế (số ký tự/giây) cao hơn, dù chỉ số tokens/s trên benchmark giống nhau.
Cái bẫy ít ai để ý: Unicode NFC và NFD
Đây là lỗi mình thấy nhiều nhất trong các dự án Việt. Tiếng Việt có hai cách biểu diễn trong Unicode:
-
NFC (dựng sẵn):
ếlà 1 code point. -
NFD (tổ hợp):
ế=e+ dấu mũ + dấu sắc, tức là 3 code point.
Text ở dạng NFD thường đến từ tên file trên macOS, dữ liệu copy từ một số tài liệu Word cũ, hoặc từ bộ gõ được cấu hình kiểu Unicode tổ hợp. Mắt thường không thấy khác biệt, nhưng tokenizer thì thấy:
import unicodedata
import tiktoken
enc = tiktoken.get_encoding('o200k_base')
s = 'Tiếng Việt có dấu thanh và dấu mũ, người dùng hay gõ sai.'
for form in ['NFC', 'NFD']:
t = unicodedata.normalize(form, s)
print(f'{form}: {len(t):3} chars, {len(enc.encode(t)):3} tokens')
# Kiểm tra nhanh một dataset có bao nhiêu dòng chưa ở dạng NFC
def count_non_nfc(lines):
return sum(1 for l in lines if not unicodedata.is_normalized('NFC', l))
Text NFD gần như luôn tốn nhiều token hơn. Ngoài ra nó còn làm hỏng exact match khi search, làm embedding lệch đi trong hệ thống RAG và khiến prompt cache bị miss. Vì vậy hãy luôn normalize về NFC ngay ở bước ingest dữ liệu, trước khi lưu DB, tạo embedding hoặc gửi cho LLM.
Chiến lược giảm chi phí trong production
Đây là pipeline mình đang dùng cho các service gọi LLM:
flowchart TD
A[User input] --> B[Normalize NFC + gộp whitespace]
B --> C[Đếm token]
C --> D{Vượt budget?}
D -- Không --> F[Gọi LLM]
D -- Có --> E[Cắt bớt hoặc chia chunk]
E --> F
F --> G[Log token usage theo ngôn ngữ]
Phía Node.js có thể dùng js-tiktoken để làm guard trước khi gọi API:
// npm i js-tiktoken
import { Tiktoken } from 'js-tiktoken/lite';
import o200k_base from 'js-tiktoken/ranks/o200k_base';
const enc = new Tiktoken(o200k_base);
export function tokenGuard(text, maxTokens = 2000) {
const clean = text.normalize('NFC').replace(/\s+/g, ' ').trim();
const tokens = enc.encode(clean);
if (tokens.length <= maxTokens) {
return { text: clean, tokens: tokens.length, truncated: false };
}
// decode có thể cắt giữa một ký tự nhiều byte -> bỏ ký tự lỗi ở cuối
const cut = enc.decode(tokens.slice(0, maxTokens)).replace(/\uFFFD+$/, '');
return { text: cut, tokens: maxTokens, truncated: true };
}
Một số kinh nghiệm thực tế:
-
Viết system prompt bằng tiếng Anh, giữ output bằng tiếng Việt. System prompt được gửi lại ở mọi request, nên tiết kiệm 30–40% token ở đây có lợi rất lớn. Chỉ cần thêm một dòng
Always respond in Vietnameselà model hiện đại làm theo khá tốt. - Không dịch input sang tiếng Anh rồi dịch kết quả ngược lại. Cách này nghe thì hợp lý, nhưng bạn tốn thêm hai lần gọi model, latency tăng và mất sắc thái. Hầu như không bao giờ có lời.
- Đặt các phần cố định lên đầu prompt để tận dụng prompt caching của provider. Phần tiếng Việt dài như tài liệu hay FAQ càng nên được cache.
-
Log token usage theo ngôn ngữ. Thêm field
langvào log để biết chính xác tiếng Việt đang chiếm bao nhiêu phần trăm chi phí. - Đưa hiệu quả tokenizer vào tiêu chí chọn model. Khi so giá theo mỗi 1M token, hãy quy đổi sang giá theo mỗi 1M ký tự tiếng Việt. Có khi model rẻ hơn trên bảng giá lại đắt hơn khi chạy thực tế.
Kết luận
“Thuế token” với tiếng Việt là có thật, nhưng hoàn toàn đo được và kiểm soát được. Checklist để bạn áp dụng ngay tuần này:
-
Đo: chạy script
tiktokenhoặcllama-tokenizetrên 500+ message thật để có tỷ lệ token Việt/Anh của riêng bạn. - Normalize: ép mọi text về NFC ở bước ingest. Đây là cách tối ưu rẻ nhất và đồng thời sửa luôn bug search/RAG.
- Guard: đếm token trước khi gọi API, đặt budget cho từng endpoint.
- Prompt: system prompt bằng tiếng Anh, phần cố định để đầu, bật prompt caching.
- Chọn model: so giá theo ký tự tiếng Việt, không phải theo token.
Tiết kiệm token thì tiết kiệm được tiền, giảm latency và bớt tốn điện. Cả ba đều đáng để bạn bỏ ra một buổi chiều đo đạc.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.