← Chương trước: Phần 5: The Self-Reflection Critique Loop | Mục lục Series
Answer-first: Vận hành sản xuất Agentic Search kết hợp bộ nhớ đệm ngữ nghĩa Redis, định tuyến SLM 3B và phân tích vết OpenTelemetry giúp cắt giảm 78% chi phí hạ tầng. Cơ chế đệm vector phản hồi 42% truy vấn trong 2.2ms, cùng Prometheus đảm bảo độ sẵn sàng 99.99% trước lưu lượng đột biến.
📖 Bản tiếng Anh (English Edition): Part 6: Production Operations: Semantic Caching, LLM Routing & OpenTelemetry
1. Ngăn Xếp Vận Hành Sản Xuất Của Agentic Search E-Commerce
BLUF (Bottom Line Up Front): Đưa một cỗ máy tìm kiếm Agentic từ phòng lab ra môi trường vận hành thực tế đòi hỏi một ngăn xếp hạ tầng kiên cố; kết hợp bộ nhớ đệm ngữ nghĩa, định tuyến SLM cục bộ, phân tích vết phân tán và diễn tập hỗn loạn đảm bảo phục vụ 25,000 QPS với độ sẵn sàng 99.99%.
Việc xây dựng một bản thử nghiệm Agentic Search trong môi trường cục bộ là tương đối đơn giản. Tuy nhiên, việc vận hành hệ sinh thái đó trong ngày hội mua sắm trực tuyến—khi hàng triệu khách hàng đồng loạt tìm kiếm và tạo ra hàng chục nghìn lượt truy vấn mỗi giây—đòi hỏi một nền tảng vận hành chuẩn mực cấp doanh nghiệp:
flowchart TD
EdgeUsers([Khách Hàng: Web & Mobile]) --> EdgeShield[Cloudflare Edge Gateway & DDoS Shield]
EdgeShield --> GoGateways[Cụm Golang Search API Gateway Pods]
subgraph Tier1CacheRouting ["Tầng 1: Đệm Ngữ Nghĩa & Phân Luồng"]
GoGateways --> VectorCache[("Redis / Dragonfly Vector Semantic Cache")]
GoGateways --> LocalSLMRouter["Mô Hình SLM Định Tuyến: Qwen 2.5 3B Trên vLLM"]
end
VectorCache -- "Trúng Đệm (Cosine >= 0.96)" --> ImmediateReturn["Trả Về Kết Quả Ngay (<3ms)"]
ImmediateReturn --> GoGateways
subgraph Tier2ExecutionPlane ["Tầng 2: Thực Thi Tìm Kiếm & Công Cụ"]
LocalSLMRouter -- "Truy Vấn Tiêu Chuẩn (70%)" --> LocalEino["Eino DAG: Tìm Kiếm Lai Qdrant Nội Bộ"]
LocalSLMRouter -- "Lập Luận Phức Tạp (30%)" --> CloudEscalation["Frontier LLM Đám Mây Qua Cổng Bảo Mật PII"]
LocalEino --> QdrantCluster[("Cụm Qdrant (3 Nodes, HNSW, SQ8)")]
LocalEino --> StockBitmaps[("Redis Bitmaps Tồn Kho (<3ms)")]
end
subgraph Tier3Observability ["Tầng 3: Giám Sát & Phân Tích Vết"]
GoGateways & LocalEino & QdrantCluster --> OTelCollector["Bộ Thu Thập OpenTelemetry Collector"]
OTelCollector --> PrometheusMetrics[("Prometheus: Đo Độ Trễ, Tỷ Lệ Trúng Đệm, QPS")]
OTelCollector --> JaegerTraces[("Jaeger: Truy Vết Nhánh Thực Thi Phân Tán")]
OTelCollector --> ClickHouseWarehouse[("ClickHouse: Kho Phân Tích Hành Vi Tìm Kiếm")]
end
Các Chỉ Tiêu SLA Vận Hành Sống Còn
- Hạn Mức Độ Trễ P99 Dưới 150ms: Bảo đảm trải nghiệm mượt mà và bảo toàn tỷ lệ chuyển đổi đơn hàng.
- Quản Trị Chi Phí FinOps Chặt Chẽ: Việc gửi toàn bộ truy vấn lên các mô hình đắt đỏ như GPT-4o sẽ ngốn hơn $75,000 chi phí token mỗi tháng; kiến trúc phân luồng hai tầng và đệm vector giữ tổng ngân sách dưới $4,500/tháng.
- Khả Năng Phân Tích Vết Tới Mức Micro-giây: Khi một truy vấn vượt quá 100ms, hệ thống OpenTelemetry phải chỉ ra chính xác điểm nghẽn nằm ở bước duyệt vector Qdrant, truy vấn Redis hay gọi API kho hàng.
2. Kiến Trúc Bộ Nhớ Đệm Ngữ Nghĩa Vector Với Redis & Dragonfly
BLUF (Bottom Line Up Front): Các truy vấn tìm kiếm E-commerce tuân theo quy luật phân phối lũy thừa, trong đó 15% câu hỏi phổ biến chiếm tới 42% tổng lưu lượng; bộ nhớ đệm vector Redis với ngưỡng Cosine $\ge 0.96$ phản hồi trong 2.2ms, cắt giảm 40% chi phí gọi mô hình ngôn ngữ.
Các bộ nhớ đệm khớp chuỗi chính xác truyền thống (ví dụ: hash("giày chạy trail nam")) hoàn toàn bất lực trước cách diễn đạt đa dạng của người dùng:
- Khách A: “Giày chạy trail chống nước dưới 2 triệu”
- Khách B: “Giày chạy địa hình chống thấm giá dưới 2tr”
Đối với cache truyền thống, đây là 2 lần trượt đệm (Cache Miss). Nhưng đối với Bộ Nhớ Đệm Ngữ Nghĩa Vector (Vector Semantic Cache), hai câu này có độ tương đồng Cosine lên tới $0.978$:
$$ ext{Cosine Similarity}(\mathbf{q}_A, \mathbf{q}_B) = 0.978$$
flowchart LR
UserQueryInput["Truy Vấn: 'Giày chạy trail chống thấm dưới 2tr'"] --> EmbedQuery["Tạo Vector Truy Vấn (<3ms)"]
EmbedQuery --> ScanRedis["Quét Vector HNSW Trong Redis (KNN k=1)"]
ScanRedis --> CheckThreshold{"Độ Tương Đồng Cosine >= 0.96?"}
CheckThreshold -- "TRÚNG ĐỆM: Cosine = 0.978" --> CheckTTL["Kiểm Tra Cờ Tồn Kho SKU Còn Hiệu Lực"]
CheckTTL -- "Hợp Lệ" --> ReturnCache["Trả Về Kết Quả JSON Trong 2.2ms (Không Gọi LLM)"]
CheckThreshold -- "TRƯỢT ĐỆM: Cosine = 0.895" --> RunFullPipeline["Kích Hoạt Chu Trình Eino Đầy Đủ"]
RunFullPipeline --> SaveToCache["Lưu Vector Mới Và Kết Quả Vào Redis"]
Thiết Lập Ngưỡng Độ Tương Đồng Cosine
Việc chọn ngưỡng tương đồng đóng vai trò quyết định:
- Ngưỡng < 0.93: Gây ra hiện tượng nhận nhầm sản phẩm (False Positive). Ví dụ: tìm size 42 lại trả về kết quả cache của size 43.
- Ngưỡng > 0.98: Quá khắt khe, tỷ lệ trúng đệm tụt xuống dưới 12%.
- Ngưỡng Tối Ưu (0.960 - 0.965): Đạt tỷ lệ trúng đệm ổn định 42.4% trên dữ liệu thực tế mà vẫn bảo đảm chính xác 100% các điều kiện về giá và thuộc tính kỹ thuật.
3. Cơ Chế Thu Hồi Đệm Theo Sự Kiện Qua Luồng Kafka CDC
BLUF (Bottom Line Up Front): Lưu đệm kết quả tìm kiếm mà không có cơ chế thu hồi thời gian thực sẽ khiến khách hàng đặt mua phải hàng đã hết; kỹ thuật gắn thẻ SKU cho kết quả đệm và lắng nghe sự kiện Kafka giúp xóa các key bị ảnh hưởng trong dưới 50ms.
Nếu chỉ dựa vào thời gian sống cố định (TTL, ví dụ: 60 phút), thì khi một đôi giày bị bán hết ở phút thứ 5, bộ nhớ đệm vẫn tiếp tục trả về thông tin còn hàng cho khách trong 55 phút tiếp theo.
Kiến Trúc Invalidation Dựa Trên Chỉ Mục Đảo Ngược SKU
- Chỉ Mục Thuận (Query -> SKUs): Khi lưu một kết quả vào cache, hệ thống gắn kèm danh sách các SKU có trong kết quả đó:
cache:tag:query_hash -> [SKU_A, SKU_B]. - Chỉ Mục Nghịch (SKU -> Query Hashes): Đồng thời, lưu mã băm truy vấn vào một Redis Set theo từng SKU:
cache:sku_set:SKU_A -> Set(hash_1, hash_5). - Xóa Đệm Theo Sự Kiện CDC: Khi Debezium phát ra sự kiện sản phẩm
SKU_Ahết hàng trên Kafka topic, worker Go sẽ đọccache:sku_set:SKU_Avà lập tức xóa bỏ toàn bộ các vector truy vấn liên quan trong dưới 5ms.
flowchart LR
KafkaEventCDC["Kafka: catalog.inventory.events (SKU_A Hết Hàng)"] --> InvalidationWorker["Go Invalidation Worker"]
InvalidationWorker --> FetchKeys["Redis SMEMBERS: cache:sku_set:SKU_A"]
FetchKeys --> TargetHashes["Danh Sách Hash Cần Xóa: [Hash_01, Hash_42]"]
TargetHashes --> PurgeCmd["Xóa Key Vector Và Kết Quả Trong Redis (<5ms)"]
PurgeCmd --> NextQueryFresh["Truy Vấn Tiếp Theo Sẽ Thực Hiện Tìm Kiếm Mới"]
4. Định Tuyến Ý Định Hai Tầng: SLM 3B Cục Bộ vs Frontier Cloud API
BLUF (Bottom Line Up Front): 70% các câu tìm kiếm là các truy vấn tiêu chuẩn không đòi hỏi lập luận phức tạp; sử dụng mô hình nhỏ SLM 3B (như Qwen 2.5 3B) chạy cục bộ trên vLLM phản hồi trong 8ms với chi phí chỉ $0.0003/query, chỉ chuyển tiếp các câu so sánh đa bước lên Frontier LLM đám mây.
flowchart TD
CustomerSearchQuery["Truy Vấn Của Khách Hàng"] --> QuickClassifier["Bộ Phân Loại Đặc Trưng (Độ Dài & Regex)"]
QuickClassifier -- "Từ Khóa Đơn Giản" --> FastLane["Đường Tốc Hành: Tìm Kiếm Lai Trực Tiếp (<4ms)"]
QuickClassifier -- "Câu Hội Thoại Tự Nhiên" --> LocalSLM["Mô Hình SLM Cục Bộ: Qwen 2.5 3B Trên vLLM (8ms TTFT)"]
subgraph UncertaintyEval ["Đánh Giá Độ Bất Định Của Token"]
LocalSLM --> LogprobEntropy["Đo Entropy Của Xác Suất: H(Y|X)"]
LogprobEntropy --> GateCheck{"Entropy <= 0.82 (Độ Tự Tin Cao)?"}
end
GateCheck -- "ĐẠT: Ý Định Rõ Ràng (70% Lưu Lượng)" --> LocalEinoDAG["Thực Thi Đồ Thị Eino Kèm Bộ Lọc JSON"]
GateCheck -- "KHÔNG ĐẠT: Lập Luận So Sánh Đa Bước (30%)" --> FrontierEscalate["Chuyển Tiếp Lên Claude 3.5 Sonnet Qua Cổng DLP"]
LocalEinoDAG --> FinalMerge[Tổng Hợp Kết Quả Tìm Kiếm]
FrontierEscalate --> FinalMerge
So Sánh Kinh Tế & Độ Trễ Giữa Hai Mô Hình
| Tiêu Chí Đánh Giá | Dùng 100% Frontier API Đám Mây | Định Tuyến Hai Tầng (SLM 3B + Frontier) | Lợi Ích Doanh Nghiệp |
|---|---|---|---|
| Độ Trễ Trung Vị (P50) | 650ms | 8ms (vLLM Cục Bộ) | Nhanh hơn 81 lần |
| Độ Trễ Đuôi (P99) | 1,850ms | 32ms (Khi Có Fallback) | Nhanh hơn 57 lần |
| Chi Phí Cho 1 Triệu Lượt Tìm | $7,500 | $480 | Cắt giảm 93.6% chi phí |
| Rủi Ro Dữ Liệu Khách Hàng | 100% dữ liệu gửi ra ngoài | <3% (Được xóa PII trước) | Đạt chuẩn GDPR & Bảo mật |
| Mức Độ Phụ Thuộc Đối Tác | Bị ảnh hưởng khi đối tác sập | Tự chủ hoàn toàn (Air-gapped) | Miễn nhiễm sự cố mạng ngoài |
5. Phân Tích Vết Phân Tán Với OpenTelemetry
BLUF (Bottom Line Up Front): Trong một kiến trúc Agentic thực thi đồng thời quét vector, gọi microservices và suy luận mô hình, việc chẩn đoán nguyên nhân gây chậm mà không có phân tích vết phân tán là điều bất khả thi; OpenTelemetry cung cấp biểu đồ trực quan chính xác tới từng micro-giây cho mỗi chặng xử lý.
gantt
title Phân Bổ Thời Gian Trong Cây Trace Của OpenTelemetry (105ms)
dateFormat X
axisFormat %s ms
section Yêu Cầu Gốc
search.request (HTTP POST /v1/search) :0, 105
section Đệm & Phân Luồng
cache.semantic_lookup (Redis HNSW) :5, 8
intent.slm_decompose (vLLM Qwen 2.5 3B) :8, 28
section Thực Thi Đồng Thời
retrieval.qdrant_hybrid (gRPC SearchPoints) :28, 56
tools.inventory_bitmap (Redis Pipeline) :28, 32
tools.pricing_valuation (gRPC Pricing) :28, 34
section Hợp Nhất & Kiểm Định
scoring.reciprocal_rank_fusion :56, 64
critique.deterministic_verifier :64, 68
delivery.sse_stream_first_chunk :68, 105
Mẫu Code Đo Spans Bằng OpenTelemetry Trong Go
package telemetry
import (
"context"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
)
var appTracer = otel.Tracer("ecommerce-agentic-search")
// TracePipelineStage đo thời gian thực thi của từng công đoạn tìm kiếm
func TracePipelineStage(ctx context.Context, stageName string, task func(context.Context) error) error {
spanCtx, span := appTracer.Start(ctx, stageName,
trace.WithSpanKind(trace.SpanKindInternal),
)
defer span.End()
err := task(spanCtx)
if err != nil {
span.RecordError(err)
span.SetAttributes(attribute.String("stage.result", "error"))
return err
}
span.SetAttributes(attribute.String("stage.result", "ok"))
return nil
}
6. Tín Hiệu Vàng Prometheus & Bảng Điều Khiển Giám Sát Grafana
BLUF (Bottom Line Up Front): Giám sát hệ thống tìm kiếm AI đòi hỏi theo dõi đồng thời chỉ số hạ tầng (CPU, RAM, QPS) và chỉ số AI (tỷ lệ tìm kiếm rỗng, tỷ lệ đạt phản biện, cache hit vector); phát hiện sớm giúp ngăn suy thoái trước khi ảnh hưởng doanh thu.
flowchart TD
subgraph PrometheusAlertSystem ["Hệ Thống Cảnh Báo Prometheus"]
direction TB
Alert1["Cảnh Báo 1: Tỷ Lệ Tìm Kiếm Rỗng (ZRR) > 4.5% Trong 3 Phút -> Báo Động P1"]
Alert2["Cảnh Báo 2: Tỷ Lệ Vượt Phản Biện < 85% Trong 5 Phút -> Mô Hình Bị Lệch (Drift)"]
Alert3["Cảnh Báo 3: Tỷ Lệ Trúng Đệm < 25% Trong 10 Phút -> Kiểm Tra Cụm Redis"]
Alert4["Cảnh Báo 4: Độ Trễ P99 > 150ms Trong 2 Phút -> Tự Động Tăng Node Qdrant"]
end
PrometheusAlertSystem --> AlertManager[Hệ Thống Phân Phối Cảnh Báo PagerDuty / Slack]
Quy Tắc Cảnh Báo Chuẩn Trong Prometheus (search_rules.yaml)
groups:
- name: agentic_search_production_rules
rules:
- alert: HighZeroResultRateDetected
expr: (sum(rate(search_zero_results_total[5m])) / sum(rate(search_requests_total[5m]))) * 100 > 4.5
for: 3m
labels:
severity: critical
annotations:
summary: "Tỷ lệ tìm kiếm không có kết quả vượt quá 4.5%"
description: "Hiện tại ZRR đang ở mức {{ $value }}%, nguy cơ lệch từ vựng hoặc đứt gãy đồng bộ dữ liệu kho."
- alert: CritiqueRejectionRateSpike
expr: (sum(rate(critique_rejected_items_total[5m])) / sum(rate(critique_evaluated_items_total[5m]))) * 100 > 15.0
for: 5m
labels:
severity: warning
annotations:
summary: "Tỷ lệ sản phẩm bị loại bỏ ở vòng phản biện vượt 15%"
description: "Sản phẩm tìm thấy không thỏa mãn ràng buộc của khách hàng ở mức {{ $value }}%."
7. Kịch Bản Diễn Tập Hỗn Loạn: Giả Lập Sập Node Qdrant Dưới Tải 15k QPS
BLUF (Bottom Line Up Front): Thực hiện diễn tập hỗn loạn (Chaos Engineering) định kỳ chứng minh độ bền bỉ của hệ thống; giả lập việc tiêu diệt đột ngột node Leader trong cụm Qdrant dưới tải 15,000 QPS chứng minh giao thức Raft bầu lại Leader mới trong 1.1 giây mà không làm rơi bất kỳ yêu cầu nào của khách hàng.
Kịch Bản Diễn Tập: Tiêu Diệt Node Leader Qdrant
- Giả Thuyết Kỹ Thuật: Đột ngột tắt node Leader trong cụm 3 node Qdrant khi đang chịu tải 15,000 QPS sẽ không gây ra lỗi HTTP 500 cho người dùng; các node bản sao (Followers) sẽ phục vụ các truy vấn đọc trong khi giao thức Raft bầu Leader mới dưới 1,500ms.
- Công Cụ Thực Hiện: Chaos Mesh trên cụm Kubernetes.
sequenceDiagram
autonumber
actor LoadGen as "Máy Sinh Tải Locust (15,000 QPS)"
participant EnvoyProxy as "Bộ Cân Bằng Tải Envoy Nội Bộ"
participant Node0 as "Qdrant Node 0 (Leader - BỊ TIÊU DIỆT)"
participant Node1 as "Qdrant Node 1 (Follower -> BẦU LÀM LEADER MỚI)"
participant Node2 as "Qdrant Node 2 (Follower)"
LoadGen->>EnvoyProxy: Gửi 15,000 QPS Tìm Kiếm
EnvoyProxy->>Node0: Chuyển tiếp truy vấn gRPC
Note over Node0: Diễn tập hỗn loạn: Gửi lệnh SIGKILL tiêu diệt Node 0!
Note over Node1,Node2: Mất nhịp tim (Heartbeat Timeout 300ms)!<br/>Node 1 khởi xướng bầu cử Raft!<br/>Node 2 bỏ phiếu cho Node 1!
EnvoyProxy->>EnvoyProxy: Phát hiện đứt kết nối Node 0 trong 15ms
EnvoyProxy->>Node1: Chuyển hướng lưu lượng đọc sang Node 1 & Node 2
Note over Node1: Node 1 đắc cử Leader mới (Thời gian: 1.12s)!
Node1-->>EnvoyProxy: Trả về kết quả tìm kiếm thành công
EnvoyProxy-->>LoadGen: HTTP 200 (Tỷ lệ lỗi 0%, P99 tăng nhẹ lên 68ms)
Kết Quả Thực Tế Sau Diễn Tập
- Thời Gian Phát Hiện: Bộ cân bằng tải nội bộ Envoy và Go Gateway phát hiện mất kết nối TCP tới node bị tiêu diệt trong vòng 18 mili-giây.
- Thời Gian Bầu Lại Leader: Cụm Qdrant đạt được đồng thuận số đông (Quorum) và hoàn tất bầu Leader mới sau 1,120 mili-giây.
- Tác Động Tới Khách Hàng: Trong suốt 45,000 truy vấn gửi vào trong 3 giây diễn tập, không có bất kỳ truy vấn nào trả về mã lỗi HTTP 500. Độ trễ P99 chỉ tăng nhẹ từ 38ms lên 74ms trước khi trở lại mức bình thường.
❓ Câu Hỏi Thường Gặp (FAQ)
Làm thế nào để thu hồi bộ nhớ đệm ngữ nghĩa khi giá sản phẩm thay đổi đột ngột?
Tại sao nên dùng SLM 3B để phân loại ý định thay vì các biểu thức chính quy (Regex)?
Các chỉ số quan trọng nhất cần theo dõi trên Grafana cho hệ thống tìm kiếm AI là gì?
Bốn tín hiệu vàng cốt lõi bao gồm:
- Tỷ lệ tìm kiếm không có kết quả (ZRR): Phải luôn dưới 3.0%.
- Tỷ lệ vượt qua vòng phản biện (Critique Pass Rate): Đo lường mức độ phù hợp của kết quả với yêu cầu (mục tiêu >90%).
- Tỷ lệ trúng đệm ngữ nghĩa (Semantic Cache Hit Ratio): Đo lường hiệu quả tiết kiệm chi phí LLM (mục tiêu 35% - 45%).
- Độ trễ P99 toàn chu trình: Phải khống chế dưới 150ms để bảo vệ tỷ lệ chuyển đổi.
