Hầu hết kỹ sư khi lần đầu đưa Gemini 3.8 Flash vào production đều có chung một suy nghĩ: “Giá input rẻ như cho không, chỉ vài chục cent cho một triệu token, cần gì phải tốn công tối ưu?”. Nhưng thực tế thường tát cho chúng ta một gáo nước lạnh vào kỳ thanh toán cuối tháng. Khi ứng dụng của bạn scale lên hàng trăm nghìn hoặc hàng triệu request mỗi ngày, những hạt cát lãng phí token tưởng chừng vô hại sẽ biến thành một ngọn núi tiền đè bẹp ngân sách vận hành.
Trong bài viết này, mình sẽ cùng anh em mổ xẻ những cơ chế ngầm của Gemini 3.8 Flash — từ Context Caching, cạm bẫy Thinking Tokens (Reasoning Overhead), tối ưu Schema Structured Outputs, cho đến chiến lược trích lọc chat history. Đi kèm là các đoạn code thực chiến có thể cắm ngay vào hệ thống backend của bạn.
1. Context Caching: Cứu tinh giảm 80-90% chi phí Input Token
Nếu hệ thống của bạn thường xuyên gửi kèm System Prompt dài, tài liệu tham chiếu (RAG guidelines), hoặc các file OpenAPI spec lớn, Context Caching chính là kỹ thuật bắt buộc phải nắm vững.
Analogy đời thực: Context Caching giống hệt Redis Cache trong kiến trúc backend. Thay vì mỗi lần người dùng gửi một câu hỏi bạn lại bắt LLM “đọc lại từ đầu” một tập tài liệu 50.000 token, Google lưu sẵn trạng thái kích hoạt (KV cache) của văn bản đó trên bộ nhớ máy chủ. Lần gọi sau chỉ việc trỏ vào ID vùng nhớ đệm, bạn tiết kiệm được tới 85-90% chi phí input và giảm độ trễ (TTFT – Time To First Token) rõ rệt.
Gemini hỗ trợ cả Implicit Caching (tự động nhận diện prefix lặp lại khi vượt ngưỡng tối thiểu ~2.048 – 4.096 token) và Explicit Caching (bạn chủ động tạo cache object kèm thời gian sống TTL qua API). Dưới đây là cách tạo một Explicit Cache cho tập tài liệu hệ thống dùng chung:
import os
from google import genai
from google.genai import types
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
# 1. Khởi tạo Context Cache cho tài liệu lớn dùng chung
system_instruction = "Bạn là trợ lý kỹ thuật cấp cao tại công ty Acme Corp..."
company_policies = open("internal_knowledge_base.txt", "r", encoding="utf-8").read()
cache = client.cached_contents.create(
model="gemini-3.8-flash",
config=types.CreateCachedContentConfig(
contents=[company_policies],
system_instruction=system_instruction,
ttl="7200s", # Cache tồn tại trong 2 giờ
display_name="acme_core_knowledge_base"
)
)
print(f"Cache created successfully: {cache.name}")
# 2. Gọi model tận dụng Cache ID vừa tạo
response = client.models.generate_content(
model="gemini-3.8-flash",
contents="Quy trình hoàn tiền cho gói Enterprise được xử lý như thế nào?",
config=types.GenerateContentConfig(
cached_content=cache.name,
temperature=0.2
)
)
# Kiểm tra số lượng token đọc từ cache
usage = response.usage_metadata
print(f"Cached tokens: {usage.cached_content_token_count} / Total prompt: {usage.prompt_token_count}")
Gotcha quan trọng về Caching: Quy tắc “Front-Loading”
Cơ chế caching của mô hình dựa vào Prefix Matching (khớp tiền tố từ trái sang phải). Nếu bạn vô tình nhét một chuỗi động vào đầu prompt — ví dụ như Current Time: 2026-10-21 14:15:00 hoặc User ID: 10482 — toàn bộ chuỗi token phía sau sẽ bị lệch prefix và 100% cache hit sẽ biến thành cache miss. Nguyên tắc vàng: Những gì tĩnh và cố định (System Prompt, Schema, Context) phải luôn nằm ở đầu tiên, thông tin động của người dùng luôn nằm ở cuối cùng.
2. Cái bẫy Thinking Tokens: Khi model “nghĩ” quá nhiều
Trên các dòng mô hình mới như Gemini 2.5 và 3.x Flash, khả năng suy luận (thinking / reasoning process) được tích hợp sẵn để giải quyết các bài toán logic phức tạp. Tuy nhiên, trong môi trường production, đây lại là “hố đen” nuốt ngân sách lớn nhất nếu bạn không kiểm soát.
Cần nhớ: Thinking tokens được tính vào Output Tokens. Mà giá Output Token luôn đắt gấp 3 đến 4 lần Input Token. Nếu một tác vụ phân loại đơn giản chỉ cần trả lời
{"category": "billing"}(tốn 5 token output), nhưng model lại “trầm ngâm” suy nghĩ tới 2.500 reasoning token, bạn đang trả tiền output nhiều gấp 500 lần giá trị thực tế!
Giải pháp cho các tác vụ mang tính nghiệp vụ định sẵn (classification, extraction, format JSON, router agent) là can thiệp vào tham số thinking_budget:
# Tắt hoàn toàn hoặc giới hạn trần Thinking Tokens cho tác vụ nhẹ
config = types.GenerateContentConfig(
temperature=0.1,
thinking_config=types.ThinkingConfig(
thinking_budget=0 # Tắt hẳn suy luận cho task trích xuất thông tin
# Hoặc đặt ngưỡng trần vừa phải: thinking_budget=256
)
)
response = client.models.generate_content(
model="gemini-3.8-flash",
contents="Phân loại email sau vào [Hóa đơn, Khiếu nại, Hỗ trợ kỹ thuật]: 'Tôi không nhận được hóa đơn VAT tháng trước'",
config=config
)
3. Tối ưu Schema Structured Outputs (JSON Mode)
Sử dụng Pydantic hoặc JSON Schema để ép kiểu đầu ra là chuẩn mực trong AI Engineering hiện đại. Nhưng nhiều anh em có thói quen viết các Pydantic class với docstring dài lê thê và tên trường dài bất tận. Toàn bộ schema này được biên dịch thành token và gửi vào context của mỗi lượt gọi API.
❌ Thiết kế Schema lãng phí token:
from pydantic import BaseModel, Field
# Schema này tốn ~250 token cho mỗi lượt gọi chỉ riêng phần metadata mô tả
class UserTicketExtractionVerbose(BaseModel):
customer_full_legal_name: str = Field(description="Họ và tên đầy đủ của khách hàng theo chứng minh thư nhân dân hoặc hộ chiếu")
detailed_problem_summary_description: str = Field(description="Bản tóm tắt chi tiết toàn bộ các vấn đề kỹ thuật mà khách hàng đang gặp phải trong hệ thống")
urgency_level_priority_assessment: str = Field(description="Đánh giá mức độ khẩn cấp theo tiêu chuẩn ISO")
✅ Thiết kế Schema tinh gọn, chuẩn production:
from typing import Literal
from pydantic import BaseModel
# Tinh gọn: Tận dụng tên trường rõ nghĩa và Literal Enum thay vì viết văn xuôi
class UserTicketLean(BaseModel):
name: str
issue: str
priority: Literal["low", "medium", "high", "critical"]
# Tiết kiệm hơn 70% token schema mà model vẫn bắt chính xác 100%
Ngoài ra, hãy luôn thiết lập max_output_tokens tương ứng với dung lượng payload dự kiến. Nếu schema chỉ trả về một object nhỏ, hãy khóa max_output_tokens=300 thay vì để mặc định hàng nghìn token nhằm chặn đứng rủi ro model bị lặp vô tận (infinite looping).
4. Quản lý hội thoại: Đừng ném nguyên cục Chat History vào Prompt
Một lỗi sơ đẳng nhưng cực kỳ phổ biến trong các ứng dụng chatbot: Mỗi khi user gửi tin nhắn mới, backend lại load toàn bộ mảng messages từ database và gửi sang Gemini. Đến lượt thứ 20, mỗi câu chào hỏi đơn giản “Cảm ơn bạn” cũng phải cõng theo 30.000 token của 19 lượt trò chuyện trước đó!
Để tối ưu, bạn cần kết hợp 2 kỹ thuật: Sliding Window và Incremental Summarization.
def build_optimized_chat_context(
system_instruction: str,
rolling_summary: str,
recent_messages: list[dict],
max_recent_turns: int = 4
) -> list[types.Content]:
"""
Chỉ giữ lại k lượt gần nhất (Sliding Window) kết hợp bản tóm tắt quá khứ.
Giữ kích thước context luôn ổn định dưới 2.000 token dù chat cả trăm lượt.
"""
contents = []
# 1. Thêm bản tóm tắt các lượt cũ nếu có
if rolling_summary:
contents.append(types.Content(
role="user",
parts=[types.Part.from_text(f"[Tóm tắt ngữ cảnh cuộc hội thoại trước đó: {rolling_summary}]")]
))
contents.append(types.Content(
role="model",
parts=[types.Part.from_text("Đã ghi nhận ngữ cảnh.")]
))
# 2. Chỉ lấy tối đa max_recent_turns lượt trao đổi gần nhất
windowed = recent_messages[-(max_recent_turns * 2):]
for msg in windowed:
contents.append(types.Content(
role=msg["role"],
parts=[types.Part.from_text(msg["text"])]
))
return contents
5. Chuyển tác vụ offline sang Batch API (-50% chi phí)
Không phải tác vụ nào cũng đòi hỏi phản hồi tức thì trong vòng 500ms. Các công việc như:
- Quét và gán nhãn sản phẩm ban đêm.
- Phân tích cảm xúc hàng loạt đánh giá khách hàng (sentiment analysis).
- Dịch thuật dữ liệu catalog hàng tuần.
- Tóm tắt báo cáo giao dịch cuối ngày.
Google cung cấp Gemini Batch API với mức giảm giá thẳng 50% cho cả Input lẫn Output token. Bạn chỉ việc đẩy danh sách prompt vào một file JSONL, submit job và nhận kết quả sau vài giờ. Đây là cách tiết kiệm tiền nhanh nhất mà không cần phải viết lại một dòng prompt nào.
6. Bảng đối chiếu hiệu quả trước và sau khi tối ưu
| Hạng mục | Trước tối ưu (Ngây thơ) | Sau tối ưu (Chuẩn Production) | Hiệu quả đạt được |
|---|---|---|---|
| System Prompt & Specs | Gửi lặp lại toàn bộ mỗi request | Khai báo Context Caching (TTL) | Giảm ~85% chi phí input |
| Reasoning Overhead | Mặc định Thinking Budget | Đặt thinking_budget=0 cho task nhẹ | Giảm 70-95% output token thừa |
| Chat History | Gửi toàn bộ mảng tin nhắn | Sliding Window (k=4) + Summary | Context giữ trần < 2.000 tokens |
| Schema Output | Mô tả văn xuôi rườm rà | Schema tối giản + Enum Literal | Giảm 60% token định nghĩa schema |
| Xử lý tác vụ nền | Gọi Real-time API liên tục | Gom batch qua Batch API | Giảm 50% flat hóa đơn |
7. Middleware đo lường và cảnh báo Token Real-time
Bạn không thể tối ưu những gì bạn không đo lường được. Hãy luôn bọc client gọi Gemini bằng một middleware chuyên trách để trích xuất usage_metadata và bắn metrics vào hệ thống monitoring (Prometheus / Datadog / OpenTelemetry):
import time
import logging
logger = logging.getLogger("gemini.metrics")
def call_gemini_with_telemetry(client, model: str, contents, config=None):
start_time = time.time()
response = client.models.generate_content(
model=model,
contents=contents,
config=config
)
latency_ms = (time.time() - start_time) * 1000
meta = response.usage_metadata
if meta:
cached = getattr(meta, "cached_content_token_count", 0) or 0
prompt_tokens = meta.prompt_token_count
output_tokens = meta.candidates_token_count
logger.info(
f"Model: {model} | Latency: {latency_ms:.1f}ms | "
f"Prompt: {prompt_tokens} (Cached: {cached}) | "
f"Output: {output_tokens}"
)
# Cảnh báo sớm nếu phát hiện output token bất thường
if output_tokens > 1500:
logger.warning(f"⚠️ Phát hiện spike output tokens: {output_tokens} tokens!")
return response
8. Khi nào KHÔNG NÊN tối ưu quá mức?
Tối ưu là tốt, nhưng tối ưu mù quáng sẽ đánh đổi chất lượng mô hình:
- Không tắt Thinking Budget trên các bài toán phức tạp: Nếu ứng dụng của bạn là review code, phát hiện bug bảo mật, hoặc giải toán logic, việc ép
thinking_budget=0sẽ khiến model hallucinate nghiêm trọng. - Không dùng Context Caching cho prompt quá ngắn: Ngưỡng kích hoạt cache tối thiểu là 2.048 hoặc 4.096 token. Nếu dữ liệu của bạn chỉ vài trăm token, chi phí khởi tạo và duy trì cache TTL thậm chí còn đắt hơn gọi trực tiếp.
- Không nén prompt đến mức mất thông tin: Lược bỏ các từ đệm vô nghĩa là tốt, nhưng nếu cắt gọt cụt ngủn các điều kiện biên (edge cases) trong System Prompt, bạn sẽ phải tốn thêm nhiều lượt gọi lại (retry) để sửa sai.
Tối ưu token trong production không phải là nghệ thuật cắt gọt câu chữ một cách cực đoan — nó là bài toán kiến trúc hệ thống: đặt đúng dữ liệu vào Cache, gán đúng trần suy luận cho từng loại tác vụ, và cắt bỏ những dữ liệu thừa trước khi gửi qua dây mạng.
Tóm lại những gì cần nhớ:
- Áp dụng Context Caching: Luôn đặt System Prompt và tài liệu tĩnh ở đầu (front-loading) để hưởng ưu đãi giảm tới 85-90% chi phí input token.
- Kiểm soát Thinking Tokens: Đặt
thinking_budget=0cho các tác vụ phân loại, trích xuất hoặc routing đơn giản để không bị gánh chi phí output token vô nghĩa. - Tối giản hóa Schema: Sử dụng Typed Enum / Literal thay vì docstring văn xuôi dài dòng trong Pydantic models.
- Sliding Window cho Chat: Chỉ giữ k lượt chat gần nhất kết hợp tóm tắt quá khứ, không bao giờ gửi toàn bộ raw chat history.
- Tận dụng Batch API: Gom các job offline xử lý ban đêm để nhận mức chiết khấu 50% chi phí toàn diện.
Mở ngay dashboard Gemini API của bạn hôm nay: rà soát lại xem có endpoint nào đang để Thinking Budget chạy tự do mà không kiểm soát không. Chỉ cần thêm 1 dòng config thinking_budget: 0 cho các task định tuyến dữ liệu, bạn sẽ thấy hóa đơn API ngày mai hạ nhiệt trông thấy.
Chúc anh em code vui! 🚀
Tags: #Gemini #TokenOptimization #AIEngineering #LLM #SystemDesign #KhoaPhamBK