Câu hỏi của người dùng thường ngắn, mơ hồ, hoặc phụ thuộc hội thoại trước. Câu hỏi "thế còn trường hợp nghỉ ốm thì sao?" không thể embed thẳng để retrieval tốt. Bài này dạy các kỹ thuật giúp agent biến câu hỏi thô thành truy vấn tốt và kết hợp nhiều phương pháp tìm kiếm.
Query rewriting (viết lại truy vấn)
Agent dùng LLM để viết lại câu hỏi trước khi retrieval:
- Giải tham chiếu hội thoại: "thế còn nghỉ ốm?" → "chính sách quy đổi phép năm khi nghỉ ốm".
- Bổ sung từ khóa: thêm thuật ngữ chính thức mà tài liệu dùng.
- Tách câu hỏi phức thành nhiều truy vấn con.
def rewrite(history, question):
prompt = f"""Viết lại câu hỏi thành truy vấn tìm kiếm độc lập, rõ ràng.
Lịch sử: {history}
Câu hỏi: {question}
Truy vấn:"""
return llm(prompt).strip()
Multi-query: nhiều góc nhìn
Một câu hỏi có thể diễn đạt nhiều cách. Sinh 3–4 biến thể, retrieval từng cái, gộp kết quả (khử trùng lặp) rồi rerank:
variants = generate_variants(question, n=3)
pool = []
for v in variants:
pool += retrieve(v, k=10)
pool = dedup(pool)
top = rerank(question, pool, top_n=5)
Kỹ thuật này tăng recall đáng kể cho câu hỏi khó.Hybrid search: kết hợp keyword và vector
Embedding giỏi bắt ý nghĩa nhưng dở với từ khóa chính xác: mã sản phẩm "SKU-4471", tên riêng, số điều khoản. Keyword search (BM25) giỏi đúng chỗ đó. Hybrid = kết hợp cả hai.
Vector search → ý nghĩa, câu hỏi diễn giải
BM25 keyword → mã, tên riêng, thuật ngữ chính xác
→ Gộp điểm (RRF) → Rerank
Reciprocal Rank Fusion (RRF) gộp hai bảng xếp hạng đơn giản mà hiệu quả:
def rrf(rank_lists, k=60):
scores = {}
for lst in rank_lists:
for rank, doc_id in enumerate(lst):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank + 1)
return sorted(scores, key=scores.get, reverse=True)
Ví dụ cụ thể
Người dùng hỏi: "điều 4.2 nói gì về quy đổi?"
- Chỉ vector: có thể trả về đoạn về quy đổi nhưng nhầm điều khác vì "4.2" không mang nhiều ngữ nghĩa.
- Hybrid: BM25 khớp chính xác "4.2" + vector khớp "quy đổi" → RRF đẩy đúng đoạn điều 4.2 lên đầu.
Metadata filtering
Dùng metadata để thu hẹp trước khi retrieval: chỉ tìm trong tài liệu phòng ban của người hỏi, chỉ tài liệu còn hiệu lực:
client.search("docs", query_vector=qv,
query_filter={"must": [{"key": "active", "match": {"value": True}}]},
limit=20)
Khung tư duy chọn kỹ thuật
- Câu hỏi phụ thuộc hội thoại? → query rewriting.
- Câu hỏi khó, diễn đạt nhiều cách? → multi-query.
- Có mã/tên riêng/số điều khoản? → hybrid search.
- Cần giới hạn phạm vi (phòng ban, thời gian)? → metadata filter.
Checklist retrieval nâng cao
- [ ] Có bước rewrite giải tham chiếu hội thoại trước retrieval.
- [ ] Bật hybrid nếu tài liệu có nhiều mã/tên riêng.
- [ ] Gộp kết quả bằng RRF rồi mới rerank.
- [ ] Áp metadata filter để loại tài liệu hết hạn/không liên quan.
- [ ] Đo recall@k lại sau mỗi kỹ thuật để xác nhận lợi ích.
Sai lầm thường gặp
- Embed thẳng câu hỏi phụ thuộc hội thoại: "còn cái kia?" retrieval ra rác.
- Bỏ hybrid khi tài liệu đầy mã số: vector một mình luôn trượt "SKU-4471".
- Multi-query nhưng quên dedup: context đầy chunk trùng, lãng phí token.
- Rewrite quá đà: LLM viết lại sai ý người dùng → retrieval đúng câu sai. Luôn giữ câu gốc để đối chiếu.
- Filter quá chặt: lọc mất luôn chunk đúng vì metadata gán sai.