Tối ưu hóa độ chính xác của RAG với Query Expansion và HyDE: Hướng dẫn thực tế

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Thực tế phũ phàng của RAG cơ bản

Tôi nhớ khi mình xây dựng ứng dụng Retrieval-Augmented Generation (RAG) đầu tiên bằng cách sử dụng một cơ sở dữ liệu vector tiêu chuẩn và tìm kiếm tương đồng đơn giản. Trên lý thuyết, nó trông có vẻ hoàn hảo. Tôi đã nạp vào đó tài liệu nội bộ của công ty, và với những câu hỏi đơn giản như “Chính sách nghỉ lễ là gì?”, nó hoạt động không chê vào đâu được. Tuy nhiên, ngay khi các đồng nghiệp của tôi bắt đầu đặt những câu hỏi phức tạp và sắc thái hơn như “Làm thế nào để khắc phục lỗi trễ (latency) không liên tục trong dịch vụ xác thực?”, hệ thống bắt đầu sụp đổ.

LLM sẽ tự tin khẳng định rằng nó không tìm thấy bất kỳ thông tin liên quan nào, hoặc tệ hơn, nó sẽ tạo ra câu trả lời sai lệch (hallucinate) dựa trên các đoạn mã không liên quan. Nếu bạn đã từng xây dựng một pipeline RAG, có lẽ bạn cũng đã từng vấp phải bức tường này. Bạn có dữ liệu, bạn có một LLM mạnh mẽ, nhưng chiếc cầu nối giữa câu hỏi của người dùng và tài liệu phù hợp đã bị gãy.

Nguyên nhân gốc rễ: Khoảng cách ngữ nghĩa

Vấn đề cơ bản của RAG cơ bản là việc phụ thuộc vào một truy vấn duy nhất của người dùng (thường được diễn đạt chưa tốt) để tìm kiếm các kết quả khớp trong một không gian vector khổng lồ. Đây được gọi là “Khoảng cách ngữ nghĩa” (Semantic Gap). Có ba lý do chính khiến việc tìm kiếm đơn giản thất bại:

  • Sự khác biệt về từ vựng: Người dùng có thể hỏi về sự “chậm chạp”, nhưng tài liệu của bạn lại sử dụng thuật ngữ “độ trễ” (latency) hoặc “điểm nghẽn” (bottleneck). Các vector embedding truyền thống tuy tốt, nhưng không phải lúc nào cũng hoàn hảo trong việc ánh xạ các từ đồng nghĩa này trong không gian cao chiều.
  • Thiếu ngữ cảnh: Truy vấn của người dùng thường ngắn (3-5 từ). Tài liệu thì dài và chi tiết. Việc so sánh một vector ngắn với một vector dài thường dẫn đến điểm số tương đồng thấp.
  • Truy vấn thiếu thông tin cụ thể: Người dùng thường mặc định AI biết họ đang nói về cái gì. Một truy vấn như “Làm thế nào để sửa lỗi đăng nhập?” sẽ trở nên vô dụng nếu cơ sở dữ liệu của bạn chứa nhật ký (log) cho năm hệ thống đăng nhập khác nhau.

So sánh các chiến lược: Query Expansion và HyDE

Để giải quyết những vấn đề này, chúng ta cần tiến xa hơn việc truy xuất đơn giản. Tôi đã thử nghiệm nhiều kỹ thuật, nhưng có hai kỹ thuật nổi bật và hiệu quả nhất để các nhà phát triển triển khai: Multi-Query Expansion và Hypothetical Document Embeddings (HyDE).

1. Multi-Query Expansion

Ý tưởng ở đây rất đơn giản: thay vì tìm kiếm với một truy vấn, hãy sử dụng một LLM để viết lại câu hỏi của người dùng thành 3 hoặc 5 phiên bản khác nhau. Điều này bao quát được các góc nhìn và thuật ngữ khác nhau.

Ưu điểm: Dễ triển khai, nắm bắt các từ đồng nghĩa hiệu quả.
Nhược điểm: Tăng độ trễ (nhiều lần gọi LLM) và có thể kéo theo dữ liệu nhiễu, không liên quan nếu việc mở rộng quá rộng.

2. HyDE (Hypothetical Document Embeddings)

HyDE thực hiện một cách tiếp cận khác. Thay vì viết lại truy vấn, nó yêu cầu LLM viết một câu trả lời “giả định” cho truy vấn đó trước. Sau đó, chúng ta lấy câu trả lời giả định đó, chuyển nó thành một embedding và sử dụng nó để tìm kiếm trong cơ sở dữ liệu.

Ưu điểm: Cực kỳ hiệu quả cho các câu hỏi kỹ thuật phức tạp vì câu trả lời giả định có khả năng chia sẻ cùng cấu trúc và từ vựng kỹ thuật với các tài liệu thực tế.
Nhược điểm: Nếu câu trả lời giả định của LLM hoàn toàn sai (hallucination), nó có thể dẫn việc tìm kiếm đến sai phần của cơ sở dữ liệu.

Cách tiếp cận tốt nhất: Triển khai HyDE với LangChain

Sau khi thử nghiệm cả hai, tôi thấy rằng HyDE thường mang lại sự gia tăng đáng kể nhất về độ chính xác cho tài liệu kỹ thuật. Tôi đã áp dụng phương pháp này trong môi trường production và kết quả đạt được luôn ổn định, đặc biệt là khi xử lý các kiến thức chuyên ngành hẹp vốn không xuất hiện thường xuyên trong dữ liệu đào tạo chung.

Bước 1: Thiết lập môi trường

Bạn sẽ cần Python và một vài thư viện. Tôi khuyên bạn nên sử dụng LangChain vì nó đơn giản hóa việc điều phối các bước này.

pip install langchain openai chromadb tiktoken

Bước 2: Triển khai logic HyDE

Dưới đây là cách bạn có thể thiết lập một chuỗi HyDE. Chúng ta sẽ sử dụng GPT-3.5 hoặc GPT-4 của OpenAI để tạo tài liệu giả định và sau đó sử dụng nó để truy vấn một instance ChromaDB cục bộ.

import os
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser

# Khởi tạo LLM của bạn
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

# 1. Định nghĩa prompt để tạo một tài liệu giả định
hyde_prompt = ChatPromptTemplate.from_template("""
Với tư cách là một kỹ sư phần mềm chuyên gia, hãy viết một đoạn văn kỹ thuật ngắn gọn 
để trả lời câu hỏi sau. Tập trung vào các thuật ngữ kỹ thuật và các giải pháp tiềm năng.

Câu hỏi: {question}

Trả lời:""")

hyde_chain = hyde_prompt | llm | StrOutputParser()

# 2. Ví dụ sử dụng
user_query = "Làm thế nào để xử lý connection pooling trong ứng dụng Python PostgreSQL?"
hypothetical_doc = hyde_chain.invoke({"question": user_query})

print(f"Tài liệu giả định: {hypothetical_doc}")

Bước 3: Tích hợp với Retrieval

Bây giờ, thay vì chuyển user_query vào vector store, chúng ta chuyển hypothetical_doc. Điều này đảm bảo việc tìm kiếm vector đang so sánh các vector “giống tài liệu” với các vector “giống tài liệu” khác.

# Giả định bạn đã có một vectorstore chứa dữ liệu
# vectorstore = Chroma(persist_directory="./db", embedding_function=OpenAIEmbeddings())

# Truy xuất các tài liệu dựa trên câu trả lời giả định
retrieved_docs = vectorstore.similarity_search(hypothetical_doc, k=3)

for doc in retrieved_docs:
    print(f"Nguồn: {doc.metadata['source']}")
    print(f"Đoạn nội dung: {doc.page_content[:100]}...")

Tinh chỉnh chiến lược: Multi-Query như một lưới an toàn

Mặc dù HyDE rất mạnh mẽ, nhưng thỉnh thoảng nó có thể đi chệch hướng nếu LLM không biết các thuật ngữ chuyên môn cụ thể của công ty bạn. Để hệ thống trở nên mạnh mẽ hơn nữa, tôi thường kết hợp Multi-Query Expansion với HyDE. Tôi tạo ra ba biến thể của truy vấn, tạo một tài liệu giả định và tìm kiếm cho cả bốn. Cách tiếp cận “ensemble” (kết hợp) này giúp giảm thiểu khả năng bỏ lỡ ngữ cảnh phù hợp.

Mẹo triển khai thực tế

  • Kiểm soát Temperature: Khi tạo các tài liệu giả định, hãy để temperature của LLM ở mức 0. Bạn muốn một đầu ra kỹ thuật nhất quán, chứ không phải một bài viết sáng tạo.
  • Prompt Engineering: Nói rõ với LLM loại tài liệu mà nó đang viết (ví dụ: “Viết một phần README” hoặc “Viết một câu trả lời theo phong cách StackOverflow”). Điều này giúp căn chỉnh văn bản giả định khớp với định dạng dữ liệu thực tế của bạn.
  • Quản lý chi phí: Hãy nhớ rằng HyDE thêm một lần gọi LLM cho mỗi lần tìm kiếm. Nếu bạn có ngân sách eo hẹp, hãy sử dụng một mô hình rẻ hơn như GPT-3.5-Turbo để tạo tài liệu và dành riêng GPT-4 cho bước tổng hợp cuối cùng.

Tổng kết

RAG tiêu chuẩn thường không đủ cho các nhu cầu thực tế của doanh nghiệp. Bằng cách triển khai các kỹ thuật như Query Expansion và HyDE, bạn sẽ thu hẹp được khoảng cách giữa cách con người đặt câu hỏi và cách tài liệu lưu trữ thông tin.

Hãy bắt đầu với HyDE; nó mang lại hiệu quả lớn nhất so với chi phí bỏ ra xét về mặt cải thiện độ chính xác. Khi đã vận hành ổn định, bạn có thể thử nghiệm với các biến thể đa truy vấn để xử lý các trường hợp ngoại lệ. Việc chuyển từ tìm kiếm cơ bản sang một chiến lược truy xuất tinh vi chính là điểm khác biệt giữa một bản thử nghiệm và một trợ lý AI sẵn sàng cho môi trường thực tế.

Share: