Design Patterns for LLM Microservices — Mục tiêu: Service contracts, retries, fallbacks, observability
Trợ lý AI của Hải
Bài viết dưới đây được anh Hải định hướng, mình giúp anh ấy trình bày chi tiết để anh em dễ đọc. Anh em cứ đọc cho vui, thấy đúng thì like, thấy sai thì comment — anh Hải sẽ rep (hoặc không).
Mở đầu — Một ngày làm việc của anh Hải
Sáng nay, anh Hải vừa fix xong cái bug “LLM trả về JSON không valid” làm team QA mất 3 tiếng sáng.
Trưa về, anh nghe đứa em nó kể: “API model nó 504 hoài, mà không biết lỗi ở đâu luôn anh ơi.”
Tối lên họp, Product Manager hỏi: “Tại sao user lại nhận được câu ‘Xin lỗi, tôi không hiểu’ dù model vẫn chạy?”
Anh Hải thở dài.
Vấn đề không phải là “model có chạy không”, mà là cách bạn đóng gói, vận hành và quan sát (observe) model đó trong hệ thống microservices.
Hôm nay, anh Hải chia sẻ góc nhìn của một “Hải Architect” về các Design Patterns then chốt khi xây dựng LLM Microservices: Service contracts, retries, fallbacks, observability.
1. Service Contracts — “Nói trước, làm sau”
Tại sao cần Service Contract?
LLM không phải là một service truyền thống. Nó không trả về HTTP 200 OK là xong. Nó có thể:
- Trả về text thay vì JSON (dù bạn yêu cầu JSON)
- Timeout sau 30s
- Bị rate-limit
- Trả về nội dung độc hại (toxic)
Vì vậy, Service Contract ở đây không chỉ là schema request/response, mà là thỏa thuận vận hành giữa các microservices.
1.1 Contract mẫu cho LLM Service
Dưới đây là một contract mẫu (dùng OpenAPI 3.1) cho LLM Inference Service:
openapi: 3.1.0
info:
title: LLM Inference API
version: 1.0.0
description: Contract for LLM microservice (GPT/Claude/Llama)
servers:
- url: https://llm.internal.company.com/v1
paths:
/chat/completions:
post:
summary: Chat completion with structured output
requestBody:
required: true
content:
application/json:
schema:
type: object
required: ["model", "messages", "response_format"]
properties:
model:
type: string
enum: ["gpt-4o-mini", "claude-3.5-sonnet", "llama-3.2-70b"]
messages:
type: array
items:
$ref: '#/components/schemas/Message'
response_format:
type: object
properties:
type:
type: string
enum: ["json_object", "text"]
schema:
description: JSON Schema for structured output
type: object
temperature:
type: number
minimum: 0
maximum: 1
default: 0.2
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/LLMResponse'
'429':
description: Rate limit exceeded
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
description: LLM service error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
components:
schemas:
Message:
type: object
required: ["role", "content"]
properties:
role:
type: string
enum: ["system", "user", "assistant"]
content:
type: string
LLMResponse:
type: object
required: ["id", "choices", "usage"]
properties:
id:
type: string
choices:
type: array
items:
type: object
properties:
message:
$ref: '#/components/schemas/Message'
finish_reason:
type: string
enum: ["stop", "length", "content_filter", "tool_calls"]
usage:
type: object
properties:
prompt_tokens: { type: integer }
completion_tokens: { type: integer }
total_tokens: { type: integer }
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
type:
type: string
1.2 Best Practices cho Contract
⚠️ Cảnh báo: Không bao giờ tin tưởng hoàn toàn vào
finish_reason = "stop"để xác định model đã trả về JSON valid.
- Luôn validate response trước khi dùng — dù contract có ghi
response_format: json_object - Định nghĩa rõ timeout — không để client timeout mặc định (thường 60s), hãy set timeout theo SLA (Ví dụ: 15s cho GPT-4o-mini, 25s cho Claude 3.5)
- Rate limit header — trả về
x-ratelimit-remaining,x-ratelimit-reset
2. Retries — “Thất bại là mẹ thành công, nhưng đừng retry mãi”
2.1 Khi nào nên retry?
LLM service có thể fail do:
- 502/503/504 — Transient error, có thể retry
- 429 — Rate limit, cần exponential backoff
- 400 — Bad request, không retry
- 401/403 — Auth error, không retry
- Content filter — Model từ chối trả lời, không retry
2.2 Chiến lược Retry thông minh
# Python 3.11 + httpx + tenacity
import httpx
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
class LLMApiClient:
def __init__(self):
self.client = httpx.AsyncClient(timeout=20.0)
@retry(
retry=retry_if_exception_type((httpx.NetworkError, httpx.TimeoutException)) |
retry_if_exception_type(httpx.HTTPStatusError, lambda e: e.response.status_code in [502, 503, 504]),
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, max=10), # 1s -> 2s -> 4s
reraise=True
)
async def chat_completion(self, payload: dict):
try:
response = await self.client.post(
"https://llm.internal.company.com/v1/chat/completions",
json=payload,
headers={"Authorization": f"Bearer {self.api_key}"}
)
response.raise_for_status()
return response.json()
except httpx.HTTPStatusError as e:
if e.response.status_code == 429:
# Extract rate limit headers
retry_after = e.response.headers.get("Retry-After")
if retry_after:
await asyncio.sleep(int(retry_after))
# Re-raise to trigger retry
raise e
elif e.response.status_code in [400, 401, 403]:
# Do not retry
raise e
else:
# Re-raise to trigger retry
raise e
2.3 Circuit Breaker — “Cắt điện khi có cháy”
Khi LLM service fail quá nhiều, đừng cố retry — hãy “mở mạch” (open circuit) để tránh sập hệ thống.
from circuit_breaker import CircuitBreaker
llm_circuit = CircuitBreaker(
failure_threshold=5,
recovery_timeout=60, # 60s sau mới thử lại
expected_exception=httpx.HTTPStatusError
)
@llm_circuit
async def safe_llm_call(payload: dict):
return await self.chat_completion(payload)
🛡️ Best Practice: Circuit Breaker giúp bạn tránh “thảm họa domino” khi một service fail kéo theo cả hệ thống sập theo.
3. Fallbacks — “Kế hoạch B là kế hoạch sống sót”
3.1 Fallback Strategy
Khi LLM chính fail, bạn có thể:
- Switch model — từ GPT-4o sang GPT-4o-mini (rẻ hơn, nhanh hơn)
- Rule-based fallback — dùng template response cố định
- Cache fallback — trả về response đã cache sẵn
- Async fallback — queue job, trả về “đang xử lý, sẽ có kết quả sau”
3.2 Ví dụ: Fallback Chain
class LLMService:
async def get_answer(self, question: str):
# Try primary model
try:
return await self.call_model("gpt-4o", question)
except Exception:
pass
# Fallback to cheaper model
try:
return await self.call_model("gpt-4o-mini", question)
except Exception:
pass
# Fallback to rule-based
if "giá" in question.lower():
return {"answer": "Vui lòng liên hệ hotline để được báo giá chi tiết."}
# Fallback to cache
cached = await self.cache.get(f"faq:{question[:50]}")
if cached:
return cached
# Final fallback
return {"answer": "Xin lỗi, tôi cần thêm thời gian để xử lý câu hỏi của bạn."}
⚡ Hiệu năng: Fallback chain giúp bạn giảm downtime từ 5% xuống còn 0.2% trong điều kiện model chính bị sập.
4. Observability — “Nhìn thấu mọi ngóc ngách”
4.1 3 trụ cột Observability
- Logs — Ghi lại mọi request/response (lưu ý: không log prompt/response nhạy cảm)
- Metrics — Đo lường latency, error rate, token usage
- Traces — Theo dõi request đi qua các service
4.2 Metrics then chốt cho LLM Service
# Prometheus + OpenTelemetry
from prometheus_client import Counter, Histogram, Gauge
import time
# Metrics
llm_requests_total = Counter('llm_requests_total', 'Total LLM requests', ['model', 'status'])
llm_request_duration = Histogram('llm_request_duration_seconds', 'LLM request duration')
llm_tokens_used = Counter('llm_tokens_used_total', 'Total tokens used', ['model', 'type']) # prompt/completion
llm_fallbacks_total = Counter('llm_fallbacks_total', 'Fallback count', ['strategy'])
# Instrumentation
@observe
async def instrumented_llm_call(model: str, payload: dict):
start_time = time.time()
try:
result = await self.chat_completion(payload)
llm_requests_total.labels(model=model, status="success").inc()
llm_tokens_used.labels(model=model, type="prompt").inc(result["usage"]["prompt_tokens"])
llm_tokens_used.labels(model=model, type="completion").inc(result["usage"]["completion_tokens"])
return result
except Exception as e:
llm_requests_total.labels(model=model, status="error").inc()
raise e
finally:
duration = time.time() - start_time
llm_request_duration.observe(duration)
4.3 Dashboard mẫu (Grafana)
- P99 Latency < 15s cho GPT-4o-mini
- Error Rate < 1%
- Token Usage — Theo dõi chi phí thực tế
- Fallback Rate — Nếu fallback > 5%, cần xem lại primary model
🐛 Bug kinh điển: Team anh Hải từng bị sập do không monitor token usage. Một con bot hỏi vòng lặp vô hạn, làm token usage tăng 10x trong 10 phút, hóa đơn tăng 5000$.
5. Bảng so sánh các giải pháp
| Giải pháp | Độ khó | Hiệu năng | Cộng đồng | Learning Curve |
|---|---|---|---|---|
| Retry + Exponential Backoff | Dễ | Cao (giảm fail 60%) | Rất lớn | Thấp |
| Circuit Breaker | Trung bình | Rất cao (chống domino) | Lớn | Trung bình |
| Fallback Chain | Dễ | Trung bình (tăng availability) | Lớn | Thấp |
| OpenTelemetry + Prometheus | Khó | Rất cao (debug nhanh) | Rất lớn | Cao |
6. Lỗi thường gặp & Cách xử lý
6.1 Lỗi JSON không valid
import json
import jsonschema
def validate_llm_json(response_text: str, schema: dict):
try:
data = json.loads(response_text)
jsonschema.validate(data, schema)
return data
except (json.JSONDecodeError, jsonschema.ValidationError):
# Log invalid response for analysis
logger.warning(f"Invalid JSON from LLM: {response_text[:200]}")
return None
6.2 Deadlock khi gọi đồng thời nhiều model
🛡️ Best Practice: Dùng connection pool giới hạn, không để mỗi request tạo 1 connection mới.
# httpx.AsyncClient với connection pool
client = httpx.AsyncClient(
limits=httpx.Limits(max_connections=100, max_keepalive_connections=20)
)
7. Kết luận — 3 Key Takeaways
- Service Contract không chỉ là schema — nó là bản “thỏa thuận vận hành” giữa các microservices, giúp bạn kiểm soát được risk.
- Retries + Circuit Breaker + Fallbacks là bộ ba không thể tách rời khi làm việc với LLM — giúp hệ thống sống sót khi model fail.
- Observability là yếu tố then chốt — không có monitoring, bạn sẽ không biết khi nào hệ thống đang “chảy máu” token hay user đang gặp lỗi.
Thảo luận
Anh em đã từng gặp trường hợp model trả về JSON không valid bao giờ chưa? Các bạn xử lý thế nào? Dùng regex parse hay có cách nào “đẹp” hơn?
Chốt nhẹ một chút
Nếu anh em đang cần tích hợp AI nhanh vào app mà lười build từ đầu, thử ngó qua con Serimi App xem, mình thấy API bên đó khá ổn cho việc scale.
Còn anh em nào làm Content hay SEO mà muốn tự động hóa quy trình thì tham khảo bộ công cụ bên noidungso.io.vn nhé, đỡ tốn cơm gạo thuê nhân sự part-time.
Nội dung được Hải định hướng, trợ lý AI giúp mình viết chi tiết.








