Cách Xây Dựng Hệ Thống RAG trong 10 Dòng Python mà Không Cần Frameworks

Mở khóa sức mạnh của RAG (Retrieval Augmented Generation) với hướng dẫn mã Python 10 dòng này. Khám phá thu thập thông tin, xây dựng hệ thống tài liệu của riêng bạn và khai thác LLM để có phản hồi mạnh mẽ - tất cả mà không cần dựa vào các khuôn khổ phức tạp. Tối ưu hóa nội dung của bạn để tối ưu hóa SEO và tương tác.

14 tháng 2, 2025

party-gif

Khám phá cách xây dựng một hệ thống tạo ra nội dung được tăng cường bằng truy xuất (RAG) mạnh mẽ từ đầu chỉ trong 10 dòng mã Python, mà không cần dựa vào bất kỳ khuôn khổ bên ngoài nào. Cách tiếp cận súc tích nhưng toàn diện này sẽ cung cấp cho bạn một sự hiểu biết sâu sắc về các thành phần cốt lõi, giúp bạn tạo ra các giải pháp AI mạnh mẽ và có thể tùy chỉnh cho các ứng dụng dựa trên tài liệu của bạn.

Hiểu khái niệm về Retrieval Augmented Generation (RAG)

Retrieval Augmented Generation (RAG) là một kỹ thuật được sử dụng để truy xuất thông tin từ các tài liệu của riêng bạn. Nó bao gồm một quy trình hai bước:

  1. Tạo Cơ Sở Dữ Liệu: Các tài liệu được chia thành các tiểu tài liệu nhỏ hơn, và các embedding được tính toán cho mỗi phần. Các embedding này và các phần gốc được lưu trữ, thường trong một vector store.

  2. Truy Xuất và Tạo Ra: Khi có một truy vấn mới của người dùng, một embedding được tính toán cho truy vấn. Các phần liên quan nhất được truy xuất bằng cách so sánh embedding của truy vấn với các embedding của các phần đã lưu trữ. Các phần đã truy xuất sau đó được thêm vào truy vấn gốc và được đưa vào một mô hình ngôn ngữ (LLM) để tạo ra phản hồi cuối cùng.

Cấu hình này được gọi là một "đường ống tạo ra được tăng cường bằng truy xuất", vì việc tạo ra của LLM được tăng cường bằng thông tin liên quan được truy xuất từ cơ sở dữ liệu.

Mặc dù có các framework như Langchain và LlamaIndex giúp đơn giản hóa việc triển khai các đường ống RAG, các thành phần cốt lõi có thể được xây dựng chỉ bằng Python, một mô hình embedding và một LLM. Cách tiếp cận này cung cấp một sự hiểu biết tốt hơn về các khái niệm và thành phần cơ bản và cho phép linh hoạt hơn trong việc tùy chỉnh đường ống.

Học cách chia tài liệu thành các đoạn

Khi xây dựng một hệ thống tạo ra được tăng cường bằng truy xuất (RAG), một trong những bước quan trọng là chia các tài liệu đầu vào thành các phần nhỏ hơn và dễ quản lý hơn. Trong ví dụ này, chúng tôi đang chia tài liệu đầu vào (một bài viết Wikipedia) thành các đoạn.

Lý do đằng sau cách tiếp cận này là các đoạn thường đại diện cho các đơn vị thông tin logic có thể được truy xuất và sử dụng hiệu quả để tăng cường truy vấn của người dùng. Bằng cách chia tài liệu theo cách này, chúng tôi có thể xác định được các phần liên quan nhất để bao gồm trong phản hồi cuối cùng.

Mã cho bước này như sau:

# Chia tài liệu thành các đoạn
chunks = [paragraph.strip() for paragraph in text.split('\n') if paragraph.strip()]

Tại đây, chúng tôi sử dụng một cách tiếp cận đơn giản là chia văn bản đầu vào theo các ký tự newline để trích xuất các đoạn riêng lẻ. Sau đó, chúng tôi loại bỏ bất kỳ khoảng trắng dẫn đầu hoặc kết thúc nào từ mỗi đoạn để đảm bảo một biểu diễn sạch sẽ.

Danh sách chunks kết quả chứa các đoạn riêng lẻ, sau đó có thể được embedding và lưu trữ trong vector store. Điều này cho phép chúng tôi truy xuất hiệu quả các đoạn liên quan nhất dựa trên truy vấn của người dùng và bao gồm chúng trong phản hồi cuối cùng được tạo ra bởi mô hình ngôn ngữ.

Nhúng tài liệu và truy vấn người dùng bằng cách sử dụng Sentence Transformers

Để embedding các phần tài liệu và truy vấn của người dùng, chúng tôi sẽ sử dụng thư viện Sentence Transformers. Thư viện này cung cấp các mô hình được pre-trained có thể tạo ra các embedding chất lượng cao cho văn bản.

Đầu tiên, chúng tôi tải mô hình embedding:

from sentence_transformers import SentenceTransformer
embedding_model = SentenceTransformer('all-mpnet-base-v2')

Tiếp theo, chúng tôi tính toán các embedding cho các phần tài liệu:

chunk_embeddings = embedding_model.encode(chunks, normalize_embeddings=True)

Tại đây, chunks là một danh sách các phần văn bản từ tài liệu. Tùy chọn normalize_embeddings=True đảm bảo rằng các embedding được chuẩn hóa, điều này quan trọng cho việc tính toán độ tương tự sau này.

Để lấy embedding cho truy vấn của người dùng, chúng tôi chỉ cần chạy:

query_embedding = embedding_model.encode([user_query], normalize_embeddings=True)[0]

Bây giờ, chúng tôi có các embedding cho các phần tài liệu và truy vấn của người dùng, những thứ này chúng ta có thể sử dụng cho bước truy xuất.

Truy xuất các đoạn liên quan dựa trên độ tương đồng cosin

Để truy xuất các phần liên quan nhất dựa trên truy vấn của người dùng, trước tiên chúng ta cần tính toán độ tương tự cosine giữa embedding của truy vấn và các embedding của các phần tài liệu. Đây là cách chúng ta có thể làm:

  1. Tính toán embedding của truy vấn người dùng bằng cùng mô hình embedding như các phần tài liệu.
  2. Tính tích vô hướng giữa embedding của truy vấn và mỗi embedding của các phần tài liệu. Điều này cho chúng ta các điểm số độ tương tự cosine.
  3. Sắp xếp các phần tài liệu dựa trên các điểm số độ tương tự cosine và chọn K phần liên quan nhất.
  4. Truy xuất nội dung văn bản của các phần đã chọn để sử dụng làm ngữ cảnh cho mô hình ngôn ngữ.

Các bước chính là:

  1. Tính toán embedding của truy vấn
  2. Tính toán các điểm số độ tương tự cosine
  3. Sắp xếp và chọn K phần hàng đầu
  4. Truy xuất nội dung văn bản của các phần

Cách tiếp cận đơn giản này cho phép chúng tôi truy xuất hiệu quả thông tin liên quan nhất từ bộ sưu tập tài liệu để tăng cường truy vấn của người dùng trước khi chuyển nó đến mô hình ngôn ngữ để tạo ra.

Tăng cường truy vấn người dùng bằng các đoạn đã truy xuất và tạo phản hồi bằng cách sử dụng OpenAI GPT-4

Để tạo ra một phản hồi sử dụng các phần đã truy xuất và truy vấn của người dùng, trước tiên chúng tôi tạo một lời nhắc bao gồm các phần liên quan và truy vấn của người dùng. Sau đó, chúng tôi sử dụng mô hình OpenAI GPT-4 để tạo ra một phản hồi dựa trên lời nhắc này.

Đây là mã:

# Lấy truy vấn của người dùng
user_query = "What is the capital of France?"

# Truy xuất các phần liên quan nhất dựa trên truy vấn của người dùng
top_chunk_ids = [6, 8, 5]
top_chunks = [chunks[i] for i in top_chunk_ids]

# Tạo lời nhắc
prompt = "Use the following context to answer the question at the end. If you don't know the answer, say that you don't know and do not try to make up an answer.\n\nContext:\n"
for chunk in top_chunks:
    prompt += chunk + "\n\n"
prompt += f"\nQuestion: {user_query}"

# Tạo ra phản hồi sử dụng OpenAI GPT-4
response = openai_client.chat.create(
    model="gpt-4",
    messages=[
        {"role": "user", "content": prompt}
    ],
    max_tokens=1024,
    n=1,
    stop=None,
    temperature=0.7,
).choices[0].message.content

print(response)

Trong mã này, trước tiên chúng tôi truy xuất các phần liên quan nhất dựa trên truy vấn của người dùng bằng cách tìm 3 phần hàng đầu có điểm số tương tự cao nhất. Sau đó, chúng tôi tạo một lời nhắc bao gồm các phần này và truy vấn của người dùng. Cuối cùng, chúng tôi sử dụng mô hình OpenAI GPT-4 để tạo ra một phản hồi dựa trên lời nhắc này.

Phản hồi được tạo ra sẽ là một câu trả lời ngắn gọn và liên quan đến truy vấn của người dùng, tận dụng thông tin từ các phần đã truy xuất.

Kết luận

Trong hướng dẫn này, chúng tôi đã học cách xây dựng một hệ thống trò chuyện hoàn chỉnh với một hệ thống truy xuất tài liệu chỉ bằng 10 dòng mã Python. Chúng tôi đã bao phủ các thành phần chính của một đường ống Retrieval Augmented Generation (RAG), bao gồm tạo cơ sở dữ liệu, chia tài liệu thành các phần, embedding, truy xuất và tạo ra sử dụng một mô hình ngôn ngữ lớn.

Các điểm chính là:

  1. Bạn có thể triển khai một đường ống RAG cơ bản mà không cần các framework phức tạp như Langchain hoặc LlamaIndex. Python thuần và một vài thư viện là đủ.
  2. Chia tài liệu của bạn dựa trên cấu trúc (ví dụ: các đoạn) là một chiến lược đơn giản nhưng hiệu quả cho hầu hết các trường hợp sử dụng.
  3. Embedding các phần tài liệu và truy vấn của người dùng, sau đó tính toán các điểm số tương tự, cho phép bạn truy xuất thông tin liên quan nhất để tăng cường truy vấn của người dùng.
  4. Tích hợp các phần đã truy xuất với truy vấn của người dùng và đưa vào một mô hình ngôn ngữ lớn cho phép tạo ra một phản hồi liên quan và có thông tin.

Mặc dù ví dụ này cung cấp một nền tảng vững chắc, vẫn còn nhiều cơ hội để xây dựng các hệ thống RAG mạnh mẽ và nâng cao hơn. Các framework như Langchain và LlamaIndex có thể hữu ích khi tích hợp với các vector store và mô hình ngôn ngữ khác nhau. Tuy nhiên, bắt đầu với một triển khai Python thuần túy có thể giúp bạn hiểu rõ hơn về các khái niệm và thành phần cốt lõi của một đường ống RAG.

Nếu bạn quan tâm đến việc khám phá các kỹ thuật RAG nâng cao hơn, tôi khuyên bạn nên kiểm tra khóa học của tôi "RAG Beyond Basics", nó đi sâu hơn vào việc xây dựng các hệ thống RAG phức tạp và sẵn sàng cho sản xuất.

Câu hỏi thường gặp