Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 6 — Query rewriting và hybrid search cho agent

Đã đọc 0/9 mục · còn khoảng 3 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

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.
Bài 7: ghép tất cả vào một agent RAG hoàn chỉnh biết tự quyết định khi nào tra cứu.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi