Công nghệ Tạo sinh Tăng cường Truy xuất (Retrieval-Augmented Generation – RAG) đã trở thành một mẫu kiến trúc nền tảng để xây dựng các hệ thống AI có khả năng trả lời câu hỏi dựa trên dữ liệu nội bộ. Theo phương pháp truyền thống, RAG dựa vào các kỹ thuật nhúng vector (vector embeddings) để truy xuất các đoạn văn bản liên quan, sau đó chuyển các đoạn này cho mô hình ngôn ngữ (LLM) để tạo ra câu trả lời. Tuy nhiên, khi các hệ thống ngày càng mở rộng quy mô và các trường hợp sử dụng trở nên phức tạp hơn, một mô hình mới đang nổi lên: RAG Không Vector (Vectorless RAG), còn được biết đến là truy xuất dựa trên lý luận (reasoning-based retrieval). Thay vì phụ thuộc vào điểm nhúng và tìm kiếm độ tương đồng, Vectorless RAG điều hướng thông tin giống như cách con người làm — bằng cách đi theo cấu trúc, lập luận từng bước và tự động xác định nơi cần tìm kiếm tiếp theo. Bài viết này sẽ khám phá:
* Vectorless RAG là gì.
* Cách nó khác biệt so với RAG truyền thống.
* Những ưu điểm và hạn chế của nó.
* Khi nào nên (và không nên) sử dụng nó.
### RAG Truyền Thống: Cơ Sở Nền Tảng
RAG truyền thống hoạt động qua ba bước chính:
1. **Chia đoạn (Chunking):** Tách các tài liệu dài thành các đoạn nhỏ.
2. **Nhúng (Embedding):** Chuyển mỗi đoạn thành một vector số học.
3. **Truy xuất (Retrieval):** Sử dụng tìm kiếm độ tương đồng (ví dụ: cosine similarity) để tìm các đoạn liên quan.
Sau đó, các đoạn Top-k liên quan sẽ được gửi tới LLM, và mô hình này sẽ tạo ra câu trả lời.
**Luồng hoạt động điển hình:**
Truy vấn → Nhúng → Cơ sở dữ liệu Vector → Các đoạn Top-k → LLM → Câu trả lời.
Bản thân RAG truyền thống cũng không ngừng phát triển mà có nhiều biến thể khác nhau để khắc phục các hạn chế:
1. **RAG Tái xếp hạng (Re-ranking RAG):** Cải thiện độ chính xác của bước truy xuất bằng cách thêm một giai đoạn thứ hai, sử dụng LLM để sắp xếp lại các tài liệu đã được tìm thấy ban đầu. Thay vì tin vào điểm tương đồng thô, hệ thống sẽ tự hỏi: “Kết quả nào thực sự liên quan nhất đến truy vấn?”
2. **RAG Lai (Hybrid RAG):** Kết hợp nhiều chiến lược truy xuất, điển hình là tìm kiếm vector dung lượng cao (dense vector search) với các phương pháp dựa trên từ khóa như BM25. Điều này khắc phục được điểm yếu của embeddings là dễ bỏ sót các lần khớp chính xác (ví dụ: ID, tên riêng, hoặc thuật ngữ hiếm), trong khi tìm kiếm từ khóa thuần túy lại thiếu hiểu biết về ngữ nghĩa.
3. **RAG Dựa trên Tác tử (Agentic RAG):** Đưa khả năng suy luận lặp lại vào quy trình làm việc. Thay vì chỉ truy xuất một lần, hệ thống có thể:
* Phân tách truy vấn lớn thành các câu hỏi phụ.
* Thực hiện nhiều bước truy xuất qua các bước khác nhau.
* Quyết định động thông tin cần lấy tiếp theo.
Điều này bắt đầu xóa nhòa ranh giới giữa việc truy xuất và suy luận, giúp hệ thống linh hoạt hơn nhưng cũng phức tạp hơn.
### Hạn Chế Của RAG Truyền Thống: Cái Nhìn Sâu Hơn
Mặc dù RAG truyền thống rất hiệu quả đối với nhiều ứng dụng, đặc biệt là khi truy xuất nội dung có sự tương đồng về mặt ngữ nghĩa trên quy mô lớn, các hạn chế của nó thường bị hiểu sai. Vấn đề cốt lõi không phải là hệ thống “hỏng”, mà là cách thức truy xuất được thực hiện và yếu tố nào được tối ưu hóa.
1. **Truy xuất Bề mặt (Shallow Retrieval) – Hạn chế cốt lõi:** Hạn chế cơ bản nhất của RAG truyền thống là việc truy xuất chỉ dựa trên sự tương đồng ngữ nghĩa, chưa phải là sự liên quan theo yêu cầu tác vụ hay khả năng suy luận. Tìm kiếm vector chỉ trả lời câu hỏi: “Văn bản nào trông tương tự truy vấn?” Nhưng nhiều truy vấn trong thực tế đòi hỏi:
* Khả năng hiểu nhân quả.
* Suy luận đa bước.
* Tổng hợp thông tin từ nhiều phần khác nhau.
Kết quả là, hệ thống có thể truy xuất các đoạn văn bản cùng chủ đề nhưng lại không thực sự hữu ích để trả lời câu hỏi. Đây là hạn chế nội tại của việc truy xuất dựa trên embeddings và không thể được giải quyết hoàn toàn chỉ bằng việc chia đoạn hay lập chỉ mục tốt hơn.
2. **Phân mảnh Ngữ cảnh (Context Fragmentation) – Có thể khắc phục:** Các tài liệu thường được chia thành các đoạn nhỏ trước khi nhúng. Điều này có thể dẫn đến tình trạng:
* Ngữ cảnh quan trọng bị chia cắt qua nhiều đoạn.
* Các đoạn truy xuất thiếu thông tin xung quanh đầy đủ.
* Mối quan hệ giữa các phần bị mất.
Tuy nhiên, đây chủ yếu là vấn đề kỹ thuật chứ không phải là hạn chế nền tảng. Các kỹ thuật như:
* Chia đoạn chồng lấp (overlapping chunks).
* Cửa sổ trượt (sliding windows).
* Tái xếp hạng (re-ranking).
* Truy xuất đa bước (multi-hop retrieval).
có thể giảm thiểu đáng kể tình trạng phân mảnh.
3. **Mất Cấu trúc (Loss of Structure) – Phụ thuộc triển khai:** Trong các triển khai đơn giản, tài liệu bị làm phẳng thành các đoạn, làm mất cấu trúc gốc (các chương, phần, tiểu mục). Tuy nhiên, các hệ thống RAG hiện đại thường giữ lại cấu trúc bằng cách sử dụng:
* Siêu dữ liệu (metadata) (ví dụ: tiêu đề phần, hệ thống phân cấp).
* Chia đoạn phân cấp (hierarchical chunking).
* Chiến lược truy xuất cha-con (parent-child retrieval).
Khi được triển khai đúng cách, RAG truyền thống vẫn có thể giữ lại phần lớn cấu trúc tài liệu. Do đó, đây không phải là hạn chế nội tại mà là hệ quả của các quy trình xử lý thô sơ.
4. **Chi phí Xử lý Tiền kỳ (Preprocessing Overhead) – Sự đánh đổi kiến trúc:** RAG truyền thống yêu cầu:
* Tạo embedding.
* Lưu trữ trong cơ sở dữ liệu vector.
* Lập chỉ mục và duy trì.
Mặc dù điều này làm tăng chi phí ban đầu và độ phức tạp của hệ thống, nhưng nó cho phép:
* Truy xuất nhanh.
* Truy vấn độ trễ thấp.
* Hiệu suất quy mô lớn.
Đây nên được hiểu là sự đánh đổi về phân bổ chi phí: **chi phí ban đầu cao hơn, nhưng chi phí trên mỗi truy vấn thấp hơn**, chứ không phải là một hạn chế thực sự.
**Bài học quan trọng:**
Trong tất cả các vấn đề được nêu ra, hạn chế căn bản duy nhất là: **RAG truyền thống truy xuất dựa trên sự tương đồng, chứ không phải dựa trên khả năng suy luận.** Mọi thứ khác – cấu trúc, phân mảnh, và chi phí – đều có thể được giải quyết bằng thiết kế hệ thống tốt hơn. Sự khác biệt này rất quan trọng khi so sánh RAG truyền thống với các phương pháp mới như Vectorless RAG (truy xuất dựa trên suy luận), vốn nhằm chuyển việc truy xuất từ một vấn đề về tương đồng thành một quy trình ra quyết định.
### Vectorless RAG: Ý Tưởng Cốt Lõi
Nếu hạn chế chính của RAG truyền thống là thiếu khả năng suy luận trong việc truy xuất, thì câu hỏi tự nhiên đặt ra là: *Điều gì sẽ xảy ra nếu bản thân việc truy xuất có thể suy luận?*
Để hiểu điều này, hãy
**Ngữ cảnh Hóa Cấu Trúc (Vectorless RAG)**
Khi đã xác định các phần liên quan, hệ thống sẽ thực hiện các bước sau:
1. Truy xuất toàn bộ nội dung của các phần (node) đó.
2. Tùy chọn bao gồm các phần con để đảm bảo tính đầy đủ và toàn diện.
3. Kết hợp chúng thành một ngữ cảnh (context) có cấu trúc rõ ràng.
Điều này đảm bảo rằng quá trình truy xuất diễn ra ở cấp độ phần nội dung (section level), thay vì chỉ tìm kiếm các đoạn văn bản ngẫu nhiên (arbitrary chunks).
**Bước 4: Tạo Câu Trả Lời**
Cuối cùng, ngữ cảnh đã được truy xuất sẽ được truyền đến Mô hình Ngôn ngữ Lớn (LLM) để tạo ra câu trả lời. Mô hình sẽ được hướng dẫn:
* Chỉ sử dụng ngữ cảnh được cung cấp.
* Tổng hợp thông tin từ nhiều phần khác nhau.
* Tùy chọn trích dẫn nguồn.
Bước này tương tự như cách hoạt động của RAG truyền thống, nhưng khác biệt cốt lõi nằm ở phương pháp chọn lọc ngữ cảnh.
**So Sánh Giữa Vector RAG và Vectorless RAG (Góc nhìn thực tiễn)**
Để minh họa sự khác biệt, hãy xem xét câu hỏi mẫu: *“Bigtable xử lý tính nhất quán (consistency) giữa các bản sao (replicas) như thế nào?”*
* **RAG Truyền thống (Traditional RAG):** Truy xuất các đoạn văn bản/khối văn bản (chunks) dựa trên độ tương đồng của các từ khóa như “consistency” hoặc “replication”. Phương pháp này có thể trả về các đoạn văn bản chỉ liên quan một phần và yêu cầu mô hình phải tự lọc bỏ thông tin nhiễu (noise) trong quá trình tạo câu trả lời.
* **Vectorless RAG:** Trước tiên, hệ thống xác định toàn bộ phần nội dung liên quan (ví dụ: “Tính nhất quán và Đồng bộ hóa”). Sau đó, nó truy xuất toàn bộ phần đó. Điều này cung cấp một ngữ cảnh mạch lạc, tập trung và toàn diện hơn nhiều.
**Ưu Điểm và Hạn Chế Khi Sử Dụng**
Vectorless RAG có thể đặc biệt hữu ích trong các trường hợp sau:
* Các tài liệu có cấu trúc rõ ràng (có tiêu đề, chương, mục).
* Câu hỏi phức tạp đòi hỏi người dùng phải “đi qua” nhiều mục khác nhau trong tài liệu.
* Thông tin cần được suy luận từ nhiều tiểu mục (subsections) có liên quan.
Tuy nhiên, nó không phải là giải pháp tối ưu cho mọi tình huống. Vẫn tồn tại những sự đánh đổi (trade-offs):
* **Độ trễ cao hơn (Higher latency):** Vì mỗi truy vấn có thể yêu cầu nhiều lệnh gọi đến LLM.
* **Chi phí cao hơn:** So với việc chỉ thực hiện tra cứu vector đơn thuần.
* **Phụ thuộc vào chất lượng cấu trúc:** Nếu tài liệu có cấu trúc yếu kém hoặc mơ hồ, hiệu quả của phương pháp này sẽ giảm mạnh.
* **Ít phù hợp:** Với các tập dữ liệu dung lượng lớn, không có cấu trúc rõ ràng (large, unstructured corpora).
**Tổng Kết Về Chi Phí và Hiệu Suất**
Vectorless RAG không thay thế RAG truyền thống; nó chỉ dịch chuyển chiến lược truy xuất.
* **RAG truyền thống:** Hiệu suất cao, dựa trên sự tương đồng từ khóa, dễ mở rộng.
* **Vectorless RAG:** Có cấu trúc, dựa trên khả năng suy luận/tư duy logic, chọn lọc cao hơn.
Việc lựa chọn giữa hai phương pháp này phụ thuộc vào tính chất của vấn đề:
* **Tìm kiếm quy mô lớn (Search at scale):** Áp dụng Vector RAG.
* **Suy luận trên tài liệu có cấu trúc (Reasoning over structured documents):** Áp dụng Vectorless RAG.
**Triển Khai Hệ Thống (Implementation)**
Quá trình triển khai một hệ thống Vectorless RAG cơ bản bao gồm hai giai đoạn chính:
**1. Bước 1: Tạo Cây Cấu Trúc (Tree Generation)**
Đây là bước đầu tiên và quan trọng nhất: chuyển đổi văn bản thô (PDF) thành một biểu diễn cấu trúc, có thể điều hướng được. Thay vì coi tài liệu là một khối văn bản phẳng, chúng ta xây dựng một cây phân cấp (hierarchical tree) thể hiện tổ chức logic của nó.
* **Cơ chế hoạt động:** Chúng ta sử dụng các thư viện xử lý tài liệu (ví dụ: `pymupdf4llm`) để trích xuất nội dung dưới dạng Markdown, giữ nguyên các tiêu đề và cấu trúc.
* **Ý tưởng cốt lõi:** Xây dựng một cây mà:
* Mỗi nút (node) đại diện cho một phần (chương, tiểu mục).
* Các nút cha con thể hiện mối quan hệ cấu trúc của tài liệu.
* Mỗi nút được gán phạm vi trang và nội dung tương ứng.
* **Chiến lược xử lý:**
* Trích xuất Markdown có bảo toàn bố cục và tiêu đề.
* Phân tích các tiêu đề Markdown thành các cấp độ, sử dụng phương pháp dựa trên ngăn xếp (stack-based approach) để hình thành cấu trúc cây.
* Xác định ranh giới trang chính xác để đảm bảo mỗi nút được ánh xạ chính xác tới tài liệu gốc.
* **Kết quả:** Một `DocumentTree` — một biểu diễn có cấu trúc của PDF, cho phép hệ thống duyệt qua, truy vấn và thực hiện suy luận trên các phần nội dung một cách có hệ thống.
Đây là một mô tả về cơ chế hoạt động xử lý tài liệu, chứ không phải là một đoạn mã cần dịch. Tôi sẽ viết lại với giọng văn kỹ thuật, rõ ràng để giải thích chức năng của toàn bộ hệ thống.
***
### Cơ chế Xử lý và Phân tích Độ sâu Tài liệu (Document Parser System)
Hệ thống này được thiết kế để phân tích các tệp PDF và xây dựng một cấu trúc cây tài liệu (Document Tree) phân cấp, giữ nguyên thứ tự và mối quan hệ logic giữa các phần nội dung.
**1. Hàm Phân tích PDF Chính (parse_pdf):**
Đây là bước khởi tạo quy trình, với chiến lược ba bước:
* **Trích xuất Nội dung Tổng thể:** Sử dụng công cụ chuyên dụng (PyMuPDF4LLM) để trích xuất toàn bộ nội dung PDF thành định dạng Markdown. Điều này cung cấp bức tranh tổng thể về văn bản và cấu trúc tiêu đề.
* **Trích xuất Đoạn Nội dung Trang (Page Chunks):** Trích xuất nội dung theo từng khối (chunk) của từng trang để đảm bảo khả năng định vị trang chính xác khi cần thiết.
* **Xây dựng Cây và Hoàn thiện:** Triển khai hai bước liên tiếp:
* Gọi hàm xây dựng cây từ Markdown để tạo khung sườn (headings).
* Điều chỉnh ranh giới trang (Refinement) và phân bổ nội dung chi tiết.
**2. Xây dựng Cây từ Markdown (_build_tree_from_markdown):**
Hàm này là lõi xử lý cấu trúc. Nó đọc các dòng Markdown tuần tự, sử dụng một bộ ghi nhớ trạng thái (stack) để theo dõi cấp độ hiện tại của văn bản.
* **Xử lý Tiêu đề:** Khi phát hiện một tiêu đề (bắt đầu bằng các ký tự `#`), hệ thống sẽ thực hiện các bước sau:
* Hoàn tất và gắn nội dung đã thu thập được cho nút cha hiện tại.
* Xác định **Cấp độ** (level) và **Loại hình** (Type) của tiêu đề (ví dụ: I., A., 1., hay tiêu đề phụ).
* Tạo nút mới và gắn nó vào nút cha thích hợp trong bộ ghi nhớ trạng thái.
* Cập nhật phạm vi trang dự kiến cho nút cha và nút mới.
* **Xử lý Nội dung:** Các dòng không phải tiêu đề được thu thập và lưu tạm thời.
* **Cơ chế Hoàn tất:** Sau khi đọc hết tài liệu, tất cả nội dung còn tồn đọng sẽ được gắn vào nút cuối cùng.
**3. Các Cơ chế Hỗ trợ Nâng cao:**
* **Phân loại Tiêu đề (_classify_heading):** Xác định tính chất của tiêu đề dựa trên các mẫu biểu (patterns) đã định nghĩa:
* **Numbered:** Số thứ tự liên tục (ví dụ: 1., 2., 3.).
* **Roman:** Số La Mã (ví dụ: I., II., III.).
* **Letter:** Chữ cái (ví dụ: A., B., C.).
* **Unnumbered:** Tiêu đề không có định dạng đánh số rõ ràng.
* **Ước tính Trang (_estimate_page_number):** Cung cấp một phỏng đoán sơ bộ về số trang dựa trên vị trí dòng văn bản.
* **Tinh chỉnh Ranh giới Trang (_refine_page_boundaries):** Đây là bước quan trọng nhất để đảm bảo độ chính xác. Hệ thống lấy phần nội dung đã thu thập từ các nút mới và tìm kiếm đoạn văn bản đó trong kho dữ liệu “Page Chunks” để xác định chính xác số trang thực tế mà nội dung đó tồn tại.
* **Phân phối Nội dung (_distribute_content_to_leaves):** Đảm bảo rằng nội dung chính chỉ được lưu trữ ở các nút lá (leaf nodes). Đối với các nút cấu trúc (non-leaf nodes), nội dung được tóm tắt hoặc bỏ qua để giữ tính gọn gàng của cấu trúc cây.
Với những đoạn mã trên, mục đích là tạo ra một cây tài liệu (DocumentTree) có cấu trúc và nội dung được tối ưu hóa, chuẩn bị cho bước truy xuất thông tin nâng cao (Agentic Retrieval).
—
### Công đoạn Chuẩn Bị Cấu Trúc Tài Liệu (Tree Building)
Trước khi thực hiện truy vấn, hệ thống phải đảm bảo rằng cây tài liệu (DocumentTree) được xây dựng chính xác:
1. **Tinh chỉnh Nội dung Node:** Các hàm như `refine_node` và `_distribute_content_to_leaves` được sử dụng để hoàn thiện cấu trúc cây.
* **Tối ưu hóa Phạm vi Trang (Page Range):** Đối với bất kỳ node cha nào, phạm vi trang của node đó (`node.page_start` và `node.page_end`) phải được cập nhật để bao trọn toàn bộ phạm vi của tất cả các node con.
* **Tóm tắt Nội dung:** Nếu một node là một tiêu đề phần (có con), nội dung chi tiết dài dòng của nó sẽ bị cắt bớt và thay thế bằng một bản tóm tắt ngắn gọn phía trên node con. Điều này giúp node đó hoạt động hiệu quả như một mục lục hoặc tiêu đề lớn.
### Cơ Chế Truy Xuất Thông Tin Nâng Cao (Agentic Retrieval)
Sau khi có một cây tài liệu được cấu trúc rõ ràng, bước tiếp theo là truy xuất thông tin. Thay vì thực hiện một lần tìm kiếm chung (one-shot lookup), hệ thống hoạt động bằng cách *điều hướng cây* một cách từng bước, cho phép mô hình ngôn ngữ lớn (LLM) tự quyết định bước đi tiếp theo.
**Ý tưởng Cốt lõi:**
Quá trình truy vấn được xem là một chuỗi các quyết định trên cấu trúc cây:
1. **Bắt đầu từ Gốc (Root):** Hệ thống bắt đầu bằng việc đánh giá mức độ liên quan của truy vấn so với node gốc.
2. **Đánh giá tại Từng Node:** Tại mỗi node, mô hình sẽ đánh giá mối liên hệ giữa truy vấn và thông tin của node đó.
3. **Ra Quyết định:**
* **Dừng lại (Stop):** Nếu độ tin cậy hoặc khả năng tìm kiếm tại nhánh này thấp, hệ thống sẽ dừng và trích xuất nội dung của node hiện tại.
* **Đi sâu (Descend):** Nếu thấy các node con nào có khả năng liên quan cao hơn, hệ thống sẽ đi sâu hơn vào nhóm tiểu mục đó, lặp lại quy trình.
4. **Tiếp tục lặp lại:** Quá trình này tiếp diễn cho đến khi gặp điều kiện dừng (ví dụ: đạt độ sâu tối đa, hết node con, hoặc độ tin cậy quá thấp).
**Các Thành phần Chính (The Pipeline):**
* **Trạng thái (State):** Nơi lưu trữ tất cả các thông tin và bằng chứng có thể liên quan đến câu hỏi.
* **Khả năng Tự phản ánh (Self-Correction):** Hệ thống có cơ chế liên tục tự đánh giá xem liệu thông tin hiện tại có giải đáp được câu hỏi hay không và tự điều chỉnh chiến lược tìm kiếm.
### Triển khai Kỹ thuật (Code Implementation Snippet Concept)
Triển khai sử dụng một mô hình Graph/State Machine để mô phỏng quá trình truy vấn:
“`python
class SearchEngine:
def __init__(self, root_node):
self.history = []
self.current_state = root_node
self.knowledge_graph = root_node.children # Represents the hierarchy
def traverse_and_query(self, query):
# Step 1: Analyze the query against the current node content
relevance_score = self._calculate_relevance(query, self.current_state)
# Step 2: Decision making (Should we go deeper or collect findings?)
if relevance_score < 0.5 and self.knowledge_graph:
# If low relevance, try traversing deeper (explore)
best_child = self._select_optimal_child(query, self.knowledge_graph)
self.current_state = best_child
self.history.append(f”Traversing to: {best_child.name}”)
else:
# If high relevance or no children left, collect findings (conclude)
findings = self._extract_findings(query, self.current_state)
self.history.append(f”Findings collected at: {self.current_state.name}”)
return findings
# Helper methods (simplified for concept)
def _calculate_relevance(self, query, node):
# Logic to compare query keywords with node content
return 0.8
def _select_optimal_child
Đoạn mã này định nghĩa và xây dựng một hệ thống xử lý thông tin phức tạp, mô phỏng quy trình một Trí tuệ Nhân tạo (AI) nghiên cứu và đọc hiểu tài liệu (thường là tài liệu nghiên cứu hoặc sách) để trả lời một câu hỏi cụ thể. Hệ thống này được xây dựng dựa trên khung LangGraph, cho phép thiết lập một luồng làm việc (workflow) có tính định hướng, tự quyết định bước đi tiếp theo.
**Tóm tắt chức năng chính:**
Đây là một hệ thống tự động hóa quy trình “Tìm kiếm – Khai thác – Trả lời” (Search – Retrieve – Answer – RAG).
**1. Các Thành Phần Xử Lý Trạng Thái (Nodes):**
Các hàm `_make_…` đại diện cho các bước hành động mà hệ thống có thể thực hiện:
* **`analyze_node` (Phân tích Nút):** Đây là bước quyết định cốt lõi. Tại nút này, hệ thống sử dụng mô hình Ngôn ngữ Lớn (LLM) để đánh giá mức độ liên quan của nút hiện tại và các nút con đối với truy vấn ban đầu.
* **Kết quả:** Nó trả về ba thông tin quan trọng:
* `confidence`: Mức độ tin cậy của việc tìm thấy câu trả lời.
* `should_descend`: Quyết định xem có nên đi vào các nút con sâu hơn hay không.
* `target_child_id`: ID của nút con được cho là liên quan nhất.
* **Chức năng Log:** Ngoài ra, nó ghi nhật ký chi tiết về quyết định (log) và hiệu suất (thời gian xử lý).
* **`_make_descend` (Đi sâu):** Nếu bước phân tích quyết định rằng nút con có liên quan hơn, bước này sẽ kích hoạt hành động di chuyển xuống nút con đó để tiếp tục quá trình tìm kiếm.
* **`_make_retrieve` (Khai thác Nội dung):** Sau khi không còn nút con nào để đi sâu hoặc đạt đến giới hạn độ sâu, hệ thống sẽ thực hiện bước này. Nó thu thập toàn bộ nội dung văn bản từ nút hiện tại, coi đây là nguồn thông tin thô (chunks) để chuyển sang bước trả lời.
* **`_make_generate` (Tạo câu trả lời):** Đây là bước tổng hợp cuối cùng. Nó tập hợp tất cả các đoạn văn bản đã thu thập được (`retrieved_content`) và gửi chúng cùng với câu hỏi ban đầu tới LLM để yêu cầu tạo ra một câu trả lời cuối cùng, đảm bảo mọi tuyên bố đều phải được trích dẫn nguồn (tên phần và phạm vi trang).
**2. Cơ Chế Định Tuyến và Luồng Bài (Routing and Flow):**
* **`_route`:** Hàm này đóng vai trò là người định tuyến, quyết định bước tiếp theo dựa trên trạng thái (state) hiện tại:
* Nếu `confidence` quá thấp, luồng dừng lại (`”end”`).
* Nếu đã đạt đến **giới hạn độ sâu** (`MAX_DEPTH`), luồng sẽ chuyển sang bước thu thập nội dung (`”retrieve”`).
* Nếu bước phân tích cho thấy cần đi sâu xuống nút con (`should_descend`), luồng sẽ chuyển sang bước `descend`.
* Nếu không thể đi sâu và không dừng lại, luồng sẽ chuyển sang bước `retrieve`.
* **`_build_graph`:** Hàm này kết nối tất cả các thành phần trên thành một **Đồ thị Trạng thái (State Graph)** hoàn chỉnh.
* **Luồng hoạt động:**
1. Luôn bắt đầu bằng `analyze` (Phân tích).
2. Từ `analyze`, nó chuyển đến `descend` (nếu có) hoặc `retrieve`.
3. Từ `descend`, nó quay lại `analyze` để đánh giá nút mới.
4. Cuối cùng, sau khi `retrieve`, hệ thống luôn đi đến `generate` (Tạo câu trả lời).
5. Từ `generate`, luồng kết thúc (`END`).
**Tóm lại:**
Mã này mô tả một quy trình xử lý tài liệu cao cấp, hoạt động theo cơ chế phản hồi và tự điều chỉnh: Nó đọc tài liệu theo cấu trúc cây, liên tục tự hỏi bản thân (thông qua LLM) xem thông tin nên được thu thập từ nút nào tiếp theo, thu thập đủ nguồn, và cuối cùng tổng hợp thành một câu trả lời có trích dẫn nguồn chính xác.
## Hệ thống RAG Truy Vấn Tài liệu Có Cấu Trúc Cây (Vectorless RAG)
**Mô tả Tổng thể:**
Đây là một hệ thống *Truy xuất Tăng cường (RAG)* tiên tiến, được xây dựng trên khuôn khổ LangGraph. Thay vì sử dụng cơ sở dữ liệu vector truyền thống, hệ thống này tổ chức tài liệu dưới dạng một cây thư mục (Document Tree) để cho phép tác tử (Agent) điều hướng và truy vấn thông tin có cấu trúc, mô phỏng quá trình đọc tài liệu thực tế.
**Luồng Hoạt Động (Workflow):**
1. **Bước 1: Thiết lập và Tải Tài liệu:**
* Tự động tải tài liệu PDF nguồn (ví dụ: tài liệu Bigtable).
* **Xây dựng Cây Tài liệu:** Sử dụng thư viện chuyên dụng (như PyMuPDF4LLM) để phân tích PDF và tạo thành một cấu trúc cây logic (`DocumentTree`). Quá trình này chỉ được thực hiện một lần và kết quả được lưu vào bộ nhớ đệm JSON.
2. **Bước 2: Lập Đồ Thị Truy Vấn (LangGraph):**
* Xây dựng một đồ thị trạng thái (State Graph) với luồng hoạt động được xác định như sau:
* **Điểm Khởi Đầu (Entry Point):** `analyze` (Phân tích).
* **Các Nút (Nodes):** `analyze` (Phân tích yêu cầu), `descend` (Di chuyển xuống nhánh con), `retrieve` (Truy xuất thông tin), `generate` (Tạo câu trả lời).
* **Các Cạnh Mặc Định (Edges):**
* `analyze` có một cạnh điều kiện (conditional edge) đến các trạng thái: `descend`, `retrieve`, hoặc `end` (kết thúc).
* `descend` dẫn ngược lại đến `analyze` (để tiếp tục điều hướng).
* `retrieve` dẫn đến `generate`.
* `generate` dẫn đến trạng thái `END`.
3. **Bước 3: Quá trình Truy vấn (Hàm `retrieve`):**
* **Đầu vào:** Nhận câu hỏi (`query`), cây tài liệu (`tree`), và mô hình ngôn ngữ (LLM Client).
* **Thực thi Đồ thị:** Hệ thống chạy đồ thị qua hàm `invoke()` với các trạng thái đầu vào (bao gồm các thông tin theo dõi như `path_taken`, `retrieved_content`, `reasoning`, v.v.).
* **Logic Chính:** Bộ tác tử tuần tự đi qua các bước:
* **`analyze`:** Đánh giá câu hỏi và quyết định phương thức tiếp cận tốt nhất (có cần điều hướng sâu xuống các nhánh con không, hay chỉ cần tìm kiếm văn bản).
* **`descend`:** Nếu cần, tác tử sẽ điều hướng xuống một nút (nhánh) con cụ thể trong cây tài liệu.
* **`retrieve`:** Tác tử truy xuất các đoạn văn bản liên quan từ cây dựa trên ngữ cảnh.
* **`generate`:** Sử dụng thông tin ngữ cảnh thu được để tạo ra câu trả lời cuối cùng.
* **Kết quả:** Trả về câu trả lời chi tiết cùng với bằng chứng (ngữ cảnh) được trích xuất, tuân theo quy trình tuần tự: **Cấu trúc → Triển khai → Tổng hợp.**
**Tóm tắt Quy trình Hoạt động:**
Sau khi thiết lập cây tài liệu, hệ thống sẽ **thực thi** quá trình trích xuất và tổng hợp bằng cách đi qua các bước sau: nhận diện vấn đề, tìm kiếm các phần liên quan trong cấu trúc cây, và cuối cùng tạo ra câu trả lời mạch lạc, đảm bảo tính minh bạch về nguồn thông tin.
Khi chạy lệnh `((.venv)) python main.py` trong terminal, hệ thống sẽ thực hiện quy trình Trích xuất Thông tin Tăng cường (RAG) mà không cần lập chỉ mục trang (vectorless RAG) trên tệp PDF tài liệu “bigtable-osdi06.pdf”.
**Các bước thực hiện và kết quả:**
1. **Tiền xử lý tài liệu:** Hệ thống bắt đầu bằng việc trích xuất văn bản từ PDF và xây dựng một cấu trúc cây tài liệu (document tree). Cấu trúc này được tạo ra bằng thư viện `pymupdf4llm` và cấu trúc dữ liệu mẫu bao gồm thông tin chi tiết về từng phần (node), bao gồm tiêu đề, phạm vi trang (page\_start – page\_end), và nội dung liên quan.
2. **Tạo Đầu ra:** Sau khi xử lý, hệ thống lưu lại cấu trúc cây này dưới dạng tệp JSON (`results/document_tree.json`) và tạo sơ đồ trực quan luồng công việc (workflow diagram) trong thư mục `results/workflow.png`.
3. **Quy trình Hỏi – Đáp:** Hệ thống tiến hành thực hiện truy vấn mẫu đầu tiên: “Bigtable là gì và nó giải quyết vấn đề gì?”.
* **Bước 1 (Thiết lập ngữ cảnh):** Hệ thống xác định rằng điểm xuất phát là toàn bộ tài liệu (“bigtable-osdi06.pdf”), chứa các chương và mục lớn.
* **Bước 2 (Gọi Mô hình Ngôn ngữ – LLM Call #1):** Với độ tin cậy 85%, mô hình dự đoán rằng thông tin cần tìm nằm ở nhánh con có tiêu đề **”Bigtable: A Distributed Storage System for Structured Data”**. Điều này cho thấy hệ thống đã thành công trong việc xác định vị trí logic nhất để bắt đầu tìm kiếm câu trả lời.
*(Sự phân tích và trích xuất câu trả lời chi tiết sẽ tiếp tục dựa trên việc đi sâu vào node đã được xác định này.)*
Bigtable là một hệ thống lưu trữ phân tán được thiết kế để quản lý dữ liệu có cấu trúc tại Google. Hệ thống này giải quyết yêu cầu về một nền tảng có khả năng mở rộng ổn định tới cấp petabyte dữ liệu và hàng nghìn máy chủ, đồng thời đảm bảo tính khả dụng, hiệu suất cao và khả năng ứng dụng rộng rãi. Bigtable không chỉ giới hạn ở các ứng dụng cụ thể mà còn được hơn sáu mươi sản phẩm của Google sử dụng cho nhiều khối lượng công việc khác nhau, từ xử lý hàng loạt (batch-processing) đến các tác vụ phục vụ dữ liệu nhạy cảm độ trễ cao cho người dùng cuối.
Về mô hình dữ liệu, Bigtable được thiết kế đơn giản hóa, không yêu cầu tuân thủ một mô hình dữ liệu quan hệ (relational data model) đầy đủ. Thay vào đó, nó cho phép người dùng kiểm soát linh hoạt bố cục và định dạng dữ liệu. Dữ liệu được lập chỉ mục bằng các chuỗi ký tự tùy ý cho tên hàng (row) và tên cột (column). Bigtable xử lý dữ liệu dưới dạng chuỗi ký tự không được diễn giải, cho phép các ứng dụng của người dùng tự tuần tự hóa (serialize) cả dữ liệu cấu trúc và bán cấu trúc. Người dùng còn có khả năng tác động đến tính cục bộ dữ liệu (data locality) thông qua lựa chọn lược đồ và kiểm soát việc liệu dữ liệu sẽ được phục vụ từ bộ nhớ hay từ đĩa.
Về mặt kiến trúc, việc xây dựng một hệ thống truy xuất không dựa trên vector (Vectorless RAG) đòi hỏi nhiều cân nhắc thiết thực. Chất lượng cấu trúc tài liệu là yếu tố quyết định: việc phân tích cú pháp (parsing) tài liệu thành cây cấu trúc càng sạch (như tiêu đề rõ ràng) thì khả năng điều hướng càng tốt; ngược lại, với các PDF kém chất lượng, quyết định duyệt qua sẽ yếu kém.
Trong thực tế, việc đầu tư vào khả năng phân tích layout và tiêu đề là rất quan trọng. Độ chi tiết của các nút (node) là một sự đánh đổi lớn giữa độ chính xác và hiệu suất: các nút quá chung chung sẽ dẫn đến câu trả lời thiếu chính xác, trong khi các nút quá nhỏ (chunk) sẽ gây ra quá trình duyệt qua sâu, tiêu tốn nhiều cuộc gọi LLM. Giải pháp tối ưu là hệ thống phân cấp cân bằng (ví dụ: Chương → Tiểu mục → Đoạn).
Về mặt kỹ thuật, mỗi lần thực hiện bước duyệt qua đều làm tăng độ trễ tổng thể, do đó cần thiết lập giới hạn về độ sâu tối đa và tinh chỉnh ngưỡng tin cậy để tránh thăm dò không cần thiết. Hơn nữa, chất lượng của các câu lệnh chỉ dẫn (prompt design) là nền tảng; chỉ dẫn càng rõ ràng, quyết định của hệ thống càng chính xác.
Cuối cùng, việc ghi nhật ký (logging) là không thể thiếu vì nó cung cấp khả năng hiển thị tuyệt đối, cho phép người vận hành biết chính xác hệ thống đã đi qua đâu và tại sao, từ đó điều chỉnh hành vi hệ thống. Phương pháp tiếp cận này hoạt động tốt nhất với các tài liệu có cấu trúc rõ ràng (như báo cáo, luận văn) và kém hiệu quả khi xử lý nội dung vô cấu trúc (như nhật ký máy chủ, cuộc trò chuyện). Tóm lại, Vectorless RAG thay đổi góc nhìn truy xuất từ một hành động tìm kiếm chung chung thành một quy trình được dẫn dắt và có kiểm soát, và nó có thể được coi là một giải pháp kiến trúc bổ sung hoặc song hành với RAG truyền thống, không cần phải lựa chọn giữa hai phương pháp này.
Tham khảo: medium.com

Bài viết liên quan: