LLMOps 6 — Observability & tối ưu chi phí LLM
LLMOps 6 — Observability & tối ưu chi phí LLM
Một trợ lý LLM chạy production giống một hộp đen nhiều tầng: câu hỏi đi vào, sau một khoảng lặng vài giây, câu trả lời đi ra. Khi nó trả lời chậm, sai, hoặc hóa đơn cuối tháng gấp ba dự toán, câu hỏi đầu tiên luôn là "chuyện gì xảy ra bên trong?" — và nếu bạn không có công cụ để nhìn vào, câu trả lời sẽ là "không biết". Bài này nói về hai mặt của cùng một đồng xu vận hành LLM: nhìn thấy (observability) và trả ít tiền hơn (cost optimization) — hai thứ luôn đi cùng nhau, vì bạn không thể tối ưu cái mình không đo được.
Bài này nối tiếp đánh giá & chống ảo giác (các tín hiệu chất lượng online mà ta sẽ giám sát) và mở rộng nền tảng observability chung ở observability tổng quan sang những chỗ đặc thù của LLM.
Observability LLM khác app thường ở đâu
Observability truyền thống (xem tổng quan) đứng trên ba trụ logs, metrics, traces cho các service tất định: cùng input thường cho cùng output, một request qua vài service, latency tính bằng mili-giây. Ứng dụng LLM phá vỡ mọi giả định đó, nên cần bổ sung một lớp riêng:
- Không tất định. Cùng một prompt có thể cho hai câu trả lời khác nhau. Bạn không thể assert "output = X"; phải đo phân phối chất lượng và theo dõi xu hướng.
- Một "request" là một pipeline nhiều bước. Một câu hỏi thường kích hoạt: viết lại truy vấn → retrieval (kéo tài liệu) → dựng prompt → gọi LLM → có thể gọi tool → có thể gọi LLM lần nữa → hậu xử lý/guardrail → output. Lỗi có thể nằm ở bất kỳ bước nào, và phần lớn thời gian trôi qua ở đây chứ không phải "một cú gọi hàm".
- Chi phí là biến số hàng đầu, tính theo token. App thường tốn CPU/RAM cố định; app LLM trả tiền theo token mỗi request, dao động lớn theo độ dài prompt và câu trả lời. Chi phí là một metric vận hành ngang hàng với latency, không phải chuyện của kế toán.
- Chất lượng phải đo bằng ngữ nghĩa. "200 OK" không có nghĩa câu trả lời đúng. Model có thể trả về HTTP 200 kèm một điều khoản bịa. Ta cần tín hiệu chất lượng riêng (feedback, tỷ lệ guardrail chặn, ảo giác) chồng lên metric hạ tầng.
- Input/output là văn bản tự nhiên chứa PII. Prompt và response thường có tên khách, số tài khoản, số dư. Log chúng để debug thì cực kỳ hữu ích, nhưng lưu thô là vi phạm quyền riêng tư — một ràng buộc mà app thường ít gặp ở mức này.
Tracing: xương sống của observability LLM
Trace là bản ghi đầy đủ của một request từ đầu đến cuối, chia thành các span — mỗi span là một bước có thời gian bắt đầu/kết thúc và metadata riêng. Đây là công cụ debug quan trọng nhất cho pipeline nhiều bước: khi một câu trả lời tệ, bạn mở trace và thấy ngay bước nào hỏng — retrieval kéo về tài liệu sai, hay prompt đúng mà LLM vẫn bịa.
Một trace điển hình cho một truy vấn RAG có tool:
Với trace này bạn trả lời được những câu mà chỉ nhìn log rời rạc không bao giờ trả lời được: phần lớn 3,5s trôi ở đâu? (hai cú gọi LLM), retrieval có kéo đúng chunk không? (mở span xem nội dung 5 chunk), token phình ở bước nào? (llm#2 nuốt cả tài liệu retrieval).
Cần ghi gì trong mỗi span
| Span | Metadata cần log |
|---|---|
| Retrieval | truy vấn, top-k chunk kéo về (id + điểm similarity), độ trễ |
| Prompt build | template version, độ dài prompt cuối (token), có few-shot nào |
| LLM call | model + version, token in/out, TTFT, tổng latency, temperature, finish_reason, chi phí |
| Tool call | tên tool, tham số (che PII), kết quả, độ trễ, lỗi nếu có |
| Guardrail | loại kiểm, verdict (pass/block), lý do |
| Trace gốc | session/user id (ẩn danh hoá), tổng token, tổng chi phí, tổng latency, feedback |
Log prompt/response — nhưng che PII trước khi lưu
Log đầy đủ prompt và response là vàng để debug: khi model trả lời sai, bạn cần thấy chính xác nó đã nhận prompt gì. Nhưng prompt của trợ lý ngân hàng chứa đầy PII (personally identifiable information — thông tin định danh cá nhân): tên, số CCCD, số tài khoản, số dư. Nguyên tắc bắt buộc:
- Che (redact/mask) trước khi ghi, không phải sau. PII đi qua một bước masking (regex + NER nhận diện thực thể) ngay trước khi vào hệ log — không bao giờ để bản thô chạm ổ đĩa. Ví dụ
123456789012→****9012, tên khách →[NAME]. - Phân quyền truy cập log theo vai trò; log LLM chứa nội dung nhạy cảm phải được kiểm soát như dữ liệu nghiệp vụ (xem privacy & compliance và access control).
- Đặt thời hạn lưu (retention) hợp lý và tuân thủ quy định; đừng giữ nội dung prompt vô thời hạn.
- Với môi trường siêu nhạy cảm, có thể chỉ log hash/độ dài/metadata thay vì nội dung, đánh đổi khả năng debug lấy an toàn.
Đo chất lượng online
Trace và metric hạ tầng không cho biết câu trả lời có đúng không — cần các tín hiệu chất lượng chồng lên (nối tiếp bài 5):
- Feedback trực tiếp: thumbs up/down, gắn vào trace để soi ngược các phiên bị chê.
- Tỷ lệ guardrail chặn: bao nhiêu % câu trả lời bị guardrail bám nguồn chặn — tăng đột biến là dấu hiệu model hoặc retrieval xuống cấp.
- Tỷ lệ ảo giác / escalation: ước lượng qua chấm mẫu định kỳ + tỷ lệ phiên phải chuyển sang nhân viên thật; là tín hiệu chất lượng sát thực tế nhất.
Các metric then chốt
Đây là bảng chỉ số một dashboard vận hành LLM tối thiểu phải có:
| Nhóm | Metric | Ý nghĩa |
|---|---|---|
| Token | token in / token out mỗi request | Cấu phần trực tiếp của chi phí; theo dõi phân phối, không chỉ trung bình |
| Chi phí | chi phí/request, tổng chi phí/ngày, chi phí/người dùng | Kiểm soát ngân sách; bắt request "phình" bất thường |
| Độ trễ | TTFT (time-to-first-token), latency tổng, p50/p95/p99 | TTFT quyết định cảm nhận "nhanh"; đuôi p95/p99 quan trọng hơn trung bình |
| Throughput | request/giây, token/giây | Năng lực phục vụ, lập kế hoạch dung lượng |
| Độ tin cậy | tỷ lệ lỗi, timeout, refusal rate (từ chối) | Lỗi API, quá tải; refusal cao bất thường = prompt/guardrail lệch |
| Cache | cache hit rate (prompt cache, semantic cache) | Đo hiệu quả tối ưu; hit cao → chi phí & latency giảm |
Vài lưu ý dễ bỏ sót:
- TTFT ≠ latency tổng. Time-to-first-token là thời gian tới token đầu tiên hiện ra; với UI streaming, đây mới là thứ người dùng cảm nhận là "phản hồi nhanh", dù toàn bộ câu trả lời còn chạy tiếp vài giây.
- Nhìn phân vị, không nhìn trung bình. Latency trung bình 1,5s che giấu việc p99 là 12s — và chính những request đuôi đó làm người dùng bực. Luôn theo dõi p95/p99.
- Token out đắt hơn token in (thường 3–5 lần theo bảng giá), nên câu trả lời dài phạt chi phí nặng hơn prompt dài.
Công cụ
Không nên tự dựng lại từ đầu. Các nền tảng phổ biến:
- LangSmith, Langfuse, Phoenix (Arize): chuyên cho LLM — tự động dựng trace theo span, gắn feedback, chạy eval, xem token/chi phí/latency. Langfuse và Phoenix là mã nguồn mở (self-host được — quan trọng với ngân hàng không muốn gửi prompt ra ngoài).
- OpenTelemetry GenAI: chuẩn semantic convention đang chuẩn hoá cho trace/metric LLM, cho phép đưa dữ liệu LLM vào chung hệ observability doanh nghiệp (xem obs-01) thay vì công cụ tách rời.
Tối ưu chi phí LLM
Chi phí LLM = token × đơn giá, tính riêng cho input và output. Mọi kỹ thuật tối ưu đều xoay quanh: giảm token, tái dùng kết quả, hoặc dùng model rẻ hơn cho việc dễ.
1. Giảm token
Đòn bẩy trực tiếp nhất, và thường bị bỏ quên:
- Prompt gọn. Cắt system prompt lê thê, bỏ hướng dẫn lặp. Prompt 2000 token nhân với hàng triệu request là tiền thật.
- Ít few-shot thừa. Nhiều ví dụ mẫu (few-shot) tốn token mỗi lần gọi. Kiểm chứng bằng eval xem có thật cần 8 ví dụ hay 2 là đủ.
- Nén ngữ cảnh (context compression). Với RAG, đừng nhồi cả tài liệu; tóm tắt/lọc chunk chỉ giữ phần liên quan trước khi đưa vào prompt.
- Giới hạn output (
max_tokens). Câu trả lời dài lan man vừa tốn tiền (token out đắt) vừa chậm; đặt trần và yêu cầu trả lời súc tích.
2. Prompt caching
Prompt caching cache phần đầu prompt lặp lại (system prompt dài, tài liệu tham chiếu cố định, few-shot) ở phía nhà cung cấp model. Lần gọi sau, phần đã cache không phải xử lý lại từ đầu — giảm mạnh cả chi phí lẫn độ trễ cho phần input lặp. Cực hữu ích khi bạn có một system prompt/tài liệu chính sách lớn dùng chung cho mọi câu hỏi.
3. Semantic caching
Khác với prompt cache (cache phần input), semantic caching cache câu trả lời cho các câu hỏi tương tự về ngữ nghĩa. Câu hỏi mới được embed và so (cosine) với các câu đã hỏi; nếu đủ gần một câu cũ, trả thẳng câu trả lời đã lưu, không gọi LLM. Với trợ lý nội bộ nơi nhiều người hỏi cùng vài chục câu FAQ ("phí thường niên thẻ Gold?"), hit rate cao và tiết kiệm lớn. Đánh đổi: ngưỡng similarity quá lỏng → trả nhầm câu trả lời cho câu hỏi khác; cần TTL để tránh trả thông tin đã lỗi thời.
4. Model routing (cascade)
Không phải request nào cũng cần model to nhất. Model routing phân loại độ khó rồi định tuyến: việc dễ (phân loại ý định, trả FAQ, trích trường) → model nhỏ/rẻ; việc khó (suy luận nhiều bước, tổng hợp) → model lớn. Kiểu cascade: thử model rẻ trước, nếu độ tự tin thấp hoặc kết quả không đạt kiểm thì mới nâng lên model đắt.
5. Model nhỏ / distilled / self-host cho khối lượng lớn
Với tác vụ khối lượng lớn nhưng đơn giản (phân loại, gán nhãn, trích xuất), một model nhỏ, một model distilled (chưng cất từ model lớn), hoặc self-host một open model có thể rẻ hơn nhiều lần so với gọi API model hàng đầu — với chất lượng "đủ dùng" cho việc đó. Xem thêm khía cạnh triển khai ở LLM production.
6. Batching & streaming
- Batching: gom nhiều request xử lý theo lô (đặc biệt khi self-host, tận dụng GPU) hoặc dùng batch API giá rẻ cho tác vụ không cần real-time (ví dụ chấm điểm hàng loạt qua đêm).
- Streaming: không giảm chi phí nhưng cải thiện cảm nhận độ trễ — người dùng thấy chữ hiện dần ngay sau TTFT, thay vì chờ trọn câu trả lời trong im lặng. Đây là công cụ trải nghiệm, không phải công cụ tiết kiệm.
Đánh đổi chất lượng – chi phí – độ trễ
Ba thứ này là một tam giác, kéo góc này thường phải nhả góc kia:
| Hành động | Chi phí | Độ trễ | Chất lượng |
|---|---|---|---|
| Dùng model nhỏ hơn | ↓ | ↓ | có thể ↓ |
| Prompt/semantic cache hit | ↓↓ | ↓↓ | rủi ro trả câu cũ |
| Cắt token / nén ngữ cảnh | ↓ | ↓ | rủi ro thiếu thông tin |
| Cascade (thử rẻ trước) | ↓ trung bình | ↑ ở case phải nâng | giữ được |
Giới hạn max_tokens chặt | ↓ | ↓ | rủi ro cụt câu trả lời |
Nguyên tắc: mọi tối ưu chi phí phải chạy qua eval suite của bài 5 trước khi lên production. Rẻ hơn mà bịa quy định thì không phải tiết kiệm, mà là rủi ro tuân thủ.
Đặt ngân sách & cảnh báo
Cuối cùng, phải có hàng rào tài chính: đặt ngân sách theo ngày/tháng, theo dõi chi phí real-time trên dashboard, và bật cảnh báo khi vượt ngưỡng hoặc tăng đột biến (một vòng lặp agent lỗi có thể đốt tiền rất nhanh). Đặt giới hạn tốc độ (rate limit) theo người dùng để một cá nhân không kéo sập ngân sách chung. Đây là FinOps áp cho LLM.
Ví dụ trace & config (minh hoạ)
Dưới đây là minh hoạ một bản ghi trace (JSON) — không phải SQL, không phải câu lệnh chạy được:
{
"trace_id": "3f9c...",
"user": "emp_hash_9a2",
"total_latency_ms": 3480,
"ttft_ms": 380,
"total_tokens": {"in": 2300, "out": 240},
"cost_usd": 0.012,
"cache": {"prompt_cache_hit": true, "semantic_cache_hit": false},
"spans": [
{"name": "retrieval", "ms": 140, "chunks": 5},
{"name": "llm_call_1", "model": "small", "ms": 1200, "tok_in": 900, "tok_out": 60},
{"name": "tool_fee_lookup", "ms": 95},
{"name": "llm_call_2", "model": "large", "ms": 1400, "tok_in": 1400, "tok_out": 180},
{"name": "guardrail_grounding", "verdict": "pass", "ms": 220}
],
"feedback": null
}
Và minh hoạ một config routing/cache (giả định, không phải API thật của nhà cung cấp cụ thể):
# minh hoạ cấu hình định tuyến & cache — KHÔNG phải config chạy được
cache:
prompt_cache: true # cache system prompt + tài liệu chính sách cố định
semantic_cache:
enabled: true
similarity_threshold: 0.92 # đủ gần mới trả câu cũ
ttl_hours: 24
routing:
classifier: small-model
rules:
- if: intent in [faq, classify, extract]
use: small-model
- if: intent in [reasoning, summarize_long]
use: large-model
cascade:
escalate_if_confidence_below: 0.7
budget:
daily_usd: 50
alert_at_pct: 80
per_user_rate_limit_per_min: 20
Use case thực tế
Bối cảnh: Trợ lý nội bộ NCB phục vụ ~400 cán bộ chi nhánh hỏi về sản phẩm, biểu phí, quy trình. Sau một tháng chạy: chi phí ~52 USD/ngày, p95 latency 6,8s, và nhiều phàn nàn "chậm". Không ai biết tiền và thời gian đi đâu.
Cách làm:
- Bật tracing (Langfuse self-host) cho mọi request, log prompt/response đã che PII (số tài khoản →
****, tên →[NAME]), gắn thumbs feedback vào trace. - Đọc dashboard sau 1 tuần và phát hiện: (a) system prompt 1800 token gửi lại mỗi lần; (b) 38% câu hỏi trùng ~30 câu FAQ; (c) mọi câu — kể cả "phí thẻ Gold?" — đều gọi model lớn nhất; (d) p95 cao vì một số phiên gọi LLM ba lần.
- Áp bốn tối ưu rồi đo lại (mỗi thay đổi chạy qua golden set 120 câu ở bài 5 để canh chất lượng):
| Thay đổi | Kết quả |
|---|---|
| Prompt caching phần system + chính sách | token in −40%, TTFT −0,3s |
| Semantic cache cho FAQ (ngưỡng 0,92) | ~35% request không gọi LLM |
| Model routing: FAQ/phân loại → model nhỏ | chi phí model −45% ở nhóm dễ |
Giới hạn max_tokens + prompt gọn | token out −25%, latency ↓ |
- Kết quả tổng: chi phí từ 52 → ~19 USD/ngày (−63%), p95 latency 6,8 → 3,1s, faithfulness giữ nguyên 0,91 (nhờ chạy eval trước khi bật từng thay đổi).
- Hàng rào: đặt ngân sách 30 USD/ngày, cảnh báo ở 80%, rate limit 20 req/phút/người — tuần sau một script test lỗi tạo vòng lặp bị chặn ở rate limit thay vì đốt hết ngân sách.
Không có trace, cả năm biện pháp trên chỉ là phỏng đoán; nhờ nhìn thấy từng span, mỗi đồng tiết kiệm đều có bằng chứng.
Ghi nhớ
- Observability LLM khác app thường: không tất định, một request là pipeline nhiều bước, chi phí tính theo token là metric hàng đầu, và "200 OK" không đảm bảo câu trả lời đúng.
- Tracing là xương sống: mỗi request một trace, mỗi bước (retrieval → prompt → LLM call → tool → guardrail → output) một span — để tìm đúng chỗ hỏng và biết thời gian/token trôi đi đâu.
- Log prompt/response rất quý để debug nhưng phải che PII TRƯỚC khi lưu, phân quyền truy cập và đặt retention — ràng buộc bắt buộc trong ngân hàng.
- Đo chất lượng online: feedback, tỷ lệ guardrail chặn, tỷ lệ ảo giác/escalation (nối bài 5).
- Metric then chốt: token in/out, chi phí/request & tổng, TTFT & latency tổng (p95/p99), throughput, tỷ lệ lỗi/timeout/refusal, cache hit. Nhìn phân vị chứ không nhìn trung bình.
- Công cụ: LangSmith, Langfuse, Phoenix, OpenTelemetry GenAI (Langfuse/Phoenix self-host được — hợp ngân hàng).
- Tối ưu chi phí (token × đơn giá): giảm token (prompt gọn, ít few-shot, nén ngữ cảnh, giới hạn output), prompt caching (cache input lặp), semantic caching (cache câu trả lời cho câu tương tự), model routing/cascade (model rẻ cho việc dễ), model nhỏ/distilled/self-host cho khối lượng lớn, batching, streaming (chỉ cải thiện cảm nhận độ trễ, không giảm chi phí).
- Luôn cân tam giác chất lượng–chi phí–độ trễ và chạy mọi tối ưu qua eval suite trước khi lên production; đặt ngân sách + cảnh báo + rate limit làm hàng rào tài chính.
Bài viết liên quan
Phân biệt AI/ML/DL, các kiểu học máy, quy trình ML end-to-end và thuật ngữ nền tảng.
Chất lượng dữ liệu, xử lý thiếu/ngoại lai, mã hoá, chuẩn hoá, tạo đặc trưng và tránh data leakage.
Mô hình ngôn ngữ lớn hoạt động ra sao, token, context window, tham số sinh, và kỹ thuật viết prompt.
Retrieval-Augmented Generation, vector database, tool calling, agent và MCP để xây ứng dụng LLM thực tế.