Quick Start: Chạy RedisVL trong 5 phút
Mỗi lần gọi LLM tốn 2–3 giây và một lượng token nhất định. Nhân lên với hàng nghìn request mỗi ngày, con số đó không còn nhỏ. RedisVL là thư viện Python biến Redis thành vector store và semantic cache — và điểm mấu chốt là: hai câu hỏi khác từ ngữ nhưng cùng nghĩa sẽ trả về kết quả cache thay vì gọi LLM lại.
Cài đặt và chạy Redis Stack (cần module RediSearch):
pip install redisvl
# Khởi động Redis Stack qua Docker
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
Test semantic cache ngay:
from redisvl.extensions.llmcache import SemanticCache
cache = SemanticCache(
name="llm_cache",
redis_url="redis://localhost:6379",
distance_threshold=0.15 # cosine distance, thấp hơn = tương đồng hơn
)
# Lần đầu: cache miss, lưu kết quả
response = cache.check(prompt="Python là gì?")
if not response:
answer = "Python là ngôn ngữ lập trình bậc cao, dễ đọc, đa năng..."
cache.store(prompt="Python là gì?", response=answer)
print("Cache miss — đã lưu")
else:
print(f"Cache hit: {response[0]['response']}")
# Lần sau hỏi "Cho mình biết về Python" → vẫn cache hit
# vì vector embedding gần giống "Python là gì?"
Chỉ vậy thôi. Lần sau khi user hỏi “Cho mình biết về Python” hay “Python là ngôn ngữ gì?” — dù câu hỏi khác từ ngữ — Redis trả về kết quả cũ trong vài millisecond thay vì gọi API tốn tiền.
Vấn đề mà RedisVL giải quyết
Tại sao exact-match cache không đủ?
Cache thông thường dùng hash của câu hỏi làm key. “Python là gì?” và “Python là ngôn ngữ gì?” là hai key hoàn toàn khác nhau dù câu trả lời giống hệt. Kết quả: cache hit rate rất thấp, có khi dưới 5% trong thực tế.
Semantic caching dùng vector embedding thay vì hash. “Python là gì?” và “Python là ngôn ngữ gì?” có embedding rất gần nhau — đủ để cùng hit một cache entry với threshold 0.15. Hit rate thực tế có thể đạt 35–45%, cao hơn hẳn so với exact-match.
Kiểm tra Redis có hỗ trợ vector search không
redis-cli MODULE LIST | grep search
# Phải thấy "search" trong output
# Nếu không có → cần dùng Redis Stack hoặc Redis Cloud với Search enabled
Giải thích chi tiết: Vector Search Index tùy chỉnh
Xây dựng knowledge base cho RAG
RedisVL không chỉ dừng ở caching — bạn còn có thể build vector search index trực tiếp trên Redis, phù hợp cho RAG hay knowledge base search. Mình đã dùng để index tài liệu nội bộ công ty (~3.000 entries), latency truy vấn ổn định dưới 15ms trên dataset đó:
from redisvl.index import SearchIndex
from redisvl.schema import IndexSchema
import numpy as np
from openai import OpenAI
client = OpenAI()
# Định nghĩa schema
schema = IndexSchema.from_dict({
"index": {
"name": "docs_index",
"prefix": "doc:",
"storage_type": "hash"
},
"fields": [
{"name": "content", "type": "text"},
{"name": "category", "type": "tag"},
{"name": "embedding", "type": "vector", "attrs": {
"dims": 1536,
"distance_metric": "cosine",
"algorithm": "hnsw",
"datatype": "float32"
}}
]
})
index = SearchIndex(schema, redis_url="redis://localhost:6379")
index.create(overwrite=True)
Load dữ liệu và truy vấn
from redisvl.query import VectorQuery
def get_embedding(text):
res = client.embeddings.create(input=text, model="text-embedding-3-small")
return res.data[0].embedding
docs = [
{"content": "Docker là nền tảng container hóa ứng dụng", "category": "devops"},
{"content": "Kubernetes orchestrates container deployments", "category": "devops"},
{"content": "Redis là in-memory database tốc độ cao", "category": "database"},
]
data = []
for i, doc in enumerate(docs):
emb = get_embedding(doc["content"])
data.append({
"content": doc["content"],
"category": doc["category"],
"embedding": np.array(emb, dtype=np.float32).tobytes()
})
index.load(data)
# Tìm kiếm semantic
query_emb = get_embedding("Container orchestration tool")
results = index.query(
VectorQuery(
vector=query_emb,
vector_field_name="embedding",
return_fields=["content", "category"],
num_results=3
)
)
for r in results:
print(f"Score: {r['vector_distance']:.3f} | {r['content']}")
Nâng cao: Metadata filter, TTL và Async
Cache riêng theo ngữ cảnh user
cache = SemanticCache(
name="app_cache",
redis_url="redis://localhost:6379",
distance_threshold=0.15,
ttl=3600 # Cache hết hạn sau 1 giờ
)
# Store kèm metadata
cache.store(
prompt="Giải thích về neural network",
response="Neural network là mô hình học máy...",
metadata={"language": "vi", "user_tier": "premium"}
)
# Check với filter — chỉ hit cache cùng ngôn ngữ
results = cache.check(
prompt="Neural network là gì?",
filter_expression="@language:{vi}"
)
if results:
print(f"Cache hit: {results[0]['response']}")
Async cho production traffic cao
from redisvl.extensions.llmcache import AsyncSemanticCache
async def handle_request(prompt: str, llm_func):
cache = AsyncSemanticCache(
name="async_cache",
redis_url="redis://localhost:6379",
distance_threshold=0.15
)
cached = await cache.acheck(prompt=prompt)
if cached:
return cached[0]["response"]
response = await llm_func(prompt)
await cache.astore(prompt=prompt, response=response)
return response
Tips thực tế
Chọn distance_threshold phù hợp
Sau khoảng 1 tuần chạy production với 10.000+ requests, đây là những gì mình quan sát được ở từng dải threshold:
- 0.05–0.10: Rất strict. Cache hit rate thấp (~15%) nhưng response chính xác cao.
- 0.10–0.20: Cân bằng tốt. Hit rate ~35–45%, đủ chính xác cho hầu hết use case.
- 0.20+: Aggressive. Hit rate cao nhưng có thể trả về kết quả “gần đúng” thay vì chính xác.
Bắt đầu với 0.15, monitor 1–2 tuần rồi điều chỉnh.
Dùng embedding model hỗ trợ tiếng Việt
RedisVL mặc định dùng text-embedding-ada-002. Muốn chạy local không tốn tiền và hỗ trợ tiếng Việt tốt hơn:
from redisvl.utils.vectorize import HFTextVectorizer
vectorizer = HFTextVectorizer(
model="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
# Hỗ trợ 50+ ngôn ngữ kể cả tiếng Việt, chạy local hoàn toàn
)
cache = SemanticCache(
name="vi_cache",
vectorizer=vectorizer,
redis_url="redis://localhost:6379",
distance_threshold=0.15
)
Tích hợp với LangChain
Stack đang dùng LangChain? Chỉ cần thêm vài dòng config là mọi LLM call tự động đi qua semantic cache:
from langchain_community.cache import RedisSemanticCache
from langchain_openai import OpenAIEmbeddings
import langchain
langchain.llm_cache = RedisSemanticCache(
redis_url="redis://localhost:6379",
embedding=OpenAIEmbeddings(),
score_threshold=0.2
)
# Từ đây mọi LLM call trong LangChain tự động dùng semantic cache
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("Redis là gì?") # Lần 2 sẽ cache hit
Tránh cache poisoning
Cache poisoning là rủi ro ít người để ý. LLM hallucinate ở lần đầu → response sai được cache → mọi câu hỏi tương tự sau đó đều nhận kết quả sai. Cách phòng tránh:
- Đặt TTL hợp lý (3600–86400 giây tùy loại data)
- Không cache câu hỏi về dữ liệu real-time (giá cổ phiếu, thời tiết)
- Dùng metadata để tag nguồn gốc, dễ invalidate theo batch khi cần
- Monitor và có cơ chế clear cache khi phát hiện lỗi hệ thống:
# Xem stats
stats = cache.index.info()
print(f"Total cached entries: {stats['num_docs']}")
# Clear toàn bộ cache khi rollout model mới
cache.clear()
Đã có Redis trong stack? Thêm RedisVL không cần deploy hạ tầng mới. Cache hit dưới 10ms so với 1–3 giây gọi LLM — với hit rate 35–45% ở threshold 0.15, chi phí token thực tế giảm đáng kể sau vài tuần đầu. Đáng thử trước khi đổi sang vector DB riêng.

