Trong thế giới công nghệ hiện nay, chatbot AI đang mọc lên như nấm sau mưa. Từ những trợ lý ảo đơn giản đến các hệ thống phức tạp có khả năng trò chuyện như người thật, chúng ta đang chứng kiến một cuộc cách mạng trong cách tương tác với máy móc. Tuy nhiên, một thách thức lớn mà hầu hết các mô hình ngôn ngữ lớn (LLM) như GPT-4o gặp phải là chúng chỉ biết những gì đã được dạy trong kho dữ liệu khổng lồ tại một thời điểm nhất định. Làm thế nào để chúng có thể trả lời các câu hỏi về dữ liệu riêng tư của doanh nghiệp bạn, những thông tin mới nhất, hay một bộ tài liệu chuyên ngành mà chúng chưa từng thấy? Câu trả lời nằm ở một kỹ thuật mang tên RAG – Retrieval-Augmented Generation.
Mục lục
- I. NỀN TẢNG LÝ THUYẾT CỦA RAG CHATBOT: KIẾN TRÚC 3 BƯỚC CỐT LÕI
- II. CHUẨN BỊ “BỘ NÃO” CHO CHATBOT: CẤU HÌNH SUPABASE LÀM VECTOR DATABASE
- III. “NẠP” DỮ LIỆU VÀO TRÍ NHỚ: XÂY DỰNG WORKFLOW INDEXING VỚI N8N
- IV. “DẠY” CHATBOT CÁCH “GIAO TIẾP”: XÂY DỰNG WORKFLOW CONVERSATIONAL AGENT
- V. THỬ NGHIỆM VÀ NHỮNG GIỚI HẠN CỦA BASIC RAG (NAÏVE RAG)
RAG không phải là một khái niệm quá xa vời. Về cơ bản, đây là phương pháp tăng cường cho bộ não của LLM bằng cách cho phép nó truy cập và đọc một kho kiến thức bên ngoài (knowledge base) mà bạn cung cấp. Thay vì trả lời dựa trên trí nhớ chung chung, chatbot sẽ tìm kiếm thông tin liên quan nhất từ kho dữ liệu của bạn trước, sau đó sử dụng thông tin đó để tạo ra câu trả lời chính xác và phù hợp với ngữ cảnh. Trong bài viết này, Toàn sẽ hướng dẫn các bạn một cách chi tiết và thẳng thắn nhất, từng bước xây dựng một RAG chatbot cơ bản (hay còn gọi là Naïve RAG) từ con số không. Chúng ta sẽ sử dụng bộ công cụ cực kỳ mạnh mẽ và linh hoạt: n8n để tự động hóa quy trình, Supabase làm cơ sở dữ liệu vector, và tất nhiên là một mô hình LLM từ OpenAI. Đây là một hành trình thực tế, đi từ lý thuyết khô khan đến một sản phẩm chạy được, giúp bạn nắm vững nền tảng của một trong những công nghệ AI ứng dụng hấp dẫn nhất hiện nay.
Xem thêm về các công cụ tự động hóa tại: Make vs n8n vs Google Apps Script: Nên chọn “vũ khí” tự động hóa nào cho nhu cầu của bạn?
I. NỀN TẢNG LÝ THUYẾT CỦA RAG CHATBOT: KIẾN TRÚC 3 BƯỚC CỐT LÕI
Trước khi bắt tay vào code hay kéo-thả bất cứ thứ gì, điều quan trọng nhất là phải hiểu được kiến trúc nền tảng. Một hệ thống RAG, dù đơn giản hay phức tạp, luôn vận hành dựa trên ba quy trình chính. Việc hiểu rõ ba bước này giống như việc có một tấm bản đồ vững chắc, giúp bạn không bị lạc lối trong quá trình triển khai.
1. Indexing – Lập chỉ mục: Biến dữ liệu thô thành kho tri thức cho chatbot
Đây là giai đoạn chuẩn bị, nơi chúng ta dạy cho hệ thống về kho kiến thức của mình. Giả sử bạn có một file PDF 100 trang về quy trình của công ty. Máy tính không thể “đọc” toàn bộ file này một lượt. Thay vào đó, quy trình Indexing sẽ:
- Chunking (Chia nhỏ): Tài liệu gốc sẽ được băm nhỏ thành các đoạn văn bản ngắn hơn (gọi là chunks). Việc này giúp hệ thống dễ dàng xác định các phần thông tin cụ thể và liên quan thay vì phải xử lý cả một tài liệu lớn.
- Embedding (Vector hóa): Mỗi chunk văn bản sau đó được chuyển đổi thành một chuỗi các con số, gọi là vector. Quá trình này được thực hiện bởi một mô hình AI chuyên biệt (Embedding Model). Vector này đại diện cho ý nghĩa ngữ nghĩa của đoạn văn bản. Các đoạn văn có ý nghĩa tương tự sẽ có vector gần giống nhau trong không gian toán học.
- Storing (Lưu trữ): Tất cả các cặp chunk và vector tương ứng của nó sẽ được lưu trữ trong một loại cơ sở dữ liệu đặc biệt gọi là Cơ sở dữ liệu Vector (Vector Database).
2. Retrieval – Truy xuất: Tìm kiếm thông tin phù hợp nhất
Khi người dùng đặt một câu hỏi (query), giai đoạn Truy xuất bắt đầu. Đây là lúc hệ thống thể hiện sự “thông minh” của mình:
- Câu hỏi của người dùng cũng được chuyển đổi thành một vector bằng chính mô hình embedding đã sử dụng ở bước Indexing.
- Hệ thống thực hiện một phép tìm kiếm tương đồng (similarity search) trong Vector Database. Nó so sánh vector của câu hỏi với tất cả các vector của các chunks đã lưu và tìm ra những chunks có vector gần nhất (tương đồng nhất về mặt ngữ nghĩa) với câu hỏi.
- Những chunks liên quan nhất này sẽ được lấy ra. Đây chính là bối cảnh (context) mà chúng ta sẽ cung cấp cho LLM.
3. Generation (Tạo sinh): Tổng hợp và trả lời
Đây là bước cuối cùng. Thay vì chỉ đưa câu hỏi của người dùng cho LLM một cách trần trụi, chúng ta sẽ gửi một prompt (câu lệnh) được tăng cường. Prompt này bao gồm:
- Câu hỏi gốc của người dùng.
- Các chunks thông tin liên quan đã được truy xuất ở bước 2.
LLM (ví dụ: GPT-4o) sẽ nhận được yêu cầu kiểu như: “Dựa vào thông tin sau đây: [chèn các chunk vào đây], hãy trả lời câu hỏi này: [chèn câu hỏi của người dùng vào đây]”. Nhờ có bối cảnh cụ thể, LLM có thể tạo ra một câu trả lời chính xác, chi tiết và bám sát vào kho kiến thức của bạn, giảm thiểu đáng kể hiện tượng ảo giác (hallucination – là hiện tượng AI tạo ra thông tin sai lệch hoặc không có trong dữ liệu nguồn) hay bịa đặt thông tin.
II. CHUẨN BỊ “BỘ NÃO” CHO CHATBOT: CẤU HÌNH SUPABASE LÀM VECTOR DATABASE
Lý thuyết đã xong, giờ là lúc bắt tay vào thực hành. Bộ não nơi chúng ta lưu trữ tri thức vector hóa chính là Supabase. Toàn chọn Supabase vì nó là một nền tảng Backend-as-a-Service (BaaS) mã nguồn mở, cung cấp một bộ công cụ hoàn chỉnh bao gồm cơ sở dữ liệu PostgreSQL, xác thực, lưu trữ file, và quan trọng nhất đối với chúng ta: hỗ trợ vector thông qua extension (phần mở rộng) `pg_vector`. Gói miễn phí của Supabase cũng rất hào phóng, đủ sức cho các dự án vừa và nhỏ.
1. Khởi tạo project và kích hoạt extension vector
Bước đầu tiên là tạo một tài khoản và một project mới trên Supabase. Sau khi project được khởi tạo, việc quan trọng nhất bạn cần làm là kích hoạt extension `pg_vector`. Extension này mở rộng khả năng của PostgreSQL, cho phép nó hiểu, lưu trữ và truy vấn dữ liệu dạng vector. Nếu không có bước này, cơ sở dữ liệu của bạn sẽ chỉ là một cơ sở dữ liệu quan hệ thông thường và không thể thực hiện tìm kiếm ngữ nghĩa.
2. Tạo bảng “documents”: ngôi nhà cho tri thức mà bạn cung cấp
Tiếp theo, chúng ta cần một nơi để chứa dữ liệu. Chúng ta sẽ tạo một bảng (table) tên là `documents`. Bạn có thể tạo bằng giao diện đồ họa hoặc chạy một đoạn script SQL (kịch bản SQL). LangChain, một framework (bộ khung phát triển) phổ biến, cung cấp sẵn script để làm việc này, giúp tiết kiệm rất nhiều thời gian. Bảng này thường có các cột chính:
- id: Một mã định danh duy nhất cho mỗi chunk.
- content: Nội dung văn bản gốc của chunk.
- metadata: Dữ liệu bổ sung về chunk (ví dụ: nó đến từ file nào, trang bao nhiêu). Metadata cực kỳ hữu ích cho các hệ thống RAG nâng cao.
- embedding: Cột quan trọng nhất, lưu trữ vector ngữ nghĩa của chunk. Kiểu dữ liệu của cột này sẽ là `vector`.
3. Tạo Hàm “match_documents”: Công cụ tìm kiếm ngữ nghĩa
Để thực hiện bước Retrieval, chúng ta cần một cơ chế tìm kiếm. Thay vì viết logic tìm kiếm mỗi lần, chúng ta có thể tạo một hàm (function) trong cơ sở dữ liệu tên là `match_documents`. Hàm này nhận đầu vào là một vector của câu hỏi (query_embedding) và một ngưỡng tương đồng (match_threshold), sau đó nó sẽ tính toán khoảng cách cosine giữa vector câu hỏi và tất cả các vector trong bảng `documents`, trả về những chunk có độ tương đồng cao nhất. Đây chính là trái tim của cơ chế semantic search (tìm kiếm theo ngữ nghĩa).
III. “NẠP” DỮ LIỆU VÀO TRÍ NHỚ: XÂY DỰNG WORKFLOW INDEXING VỚI N8N
Bây giờ, chúng ta sẽ dùng n8n – một công cụ tự động hóa quy trình làm việc mã nguồn mở – để xây dựng hệ thần kinh kết nối các bộ phận lại với nhau. Workflow (luồng công việc) đầu tiên chúng ta cần là quy trình Indexing, tự động hóa toàn bộ việc nạp dữ liệu vào Supabase.
1. BƯỚC 1: Tải dữ liệu nguồn
Workflow bắt đầu bằng một node (nút) kích hoạt (trigger), có thể là thủ công hoặc theo lịch. Tiếp theo, ta dùng một node như Google Drive để lấy tài liệu nguồn. Trong ví dụ của mình, Toàn sử dụng một file Google Docs chứa thông tin về một cửa hàng giả tưởng. n8n sẽ tải file này về và chuyển đổi nó thành dữ liệu văn bản thuần túy.
2. BƯỚC 2: Chia nhỏ và Vector hóa (Chunking & Embedding)
Đây là bước phức tạp nhất và được đảm nhiệm bởi một node duy nhất trong n8n: Supabase Vector Store (với action là “Add Documents”). Bên trong node này, chúng ta cần cấu hình vài thứ:
- Text Splitter: Chúng ta chọn phương thức chia nhỏ văn bản. Một lựa chọn phổ biến là `RecursiveCharacterTextSplitter`. Nó sẽ cố gắng chia văn bản tại các điểm ngắt tự nhiên (như xuống dòng, dấu chấm) để giữ ngữ nghĩa của các chunk tốt hơn.
- Chunk Size & Chunk Overlap: `Chunk Size` là độ dài tối đa của mỗi chunk (ví dụ: 1000 ký tự). `Chunk Overlap` là số ký tự được lặp lại giữa hai chunk liên tiếp (ví dụ: 100 ký tự). Overlap giúp đảm bảo rằng các câu hoàn chỉnh không bị cắt đứt đột ngột ở ranh giới giữa hai chunk, giữ cho ngữ cảnh không bị mất mát.
- Embedding Model: Chúng ta cần chỉ định mô hình để vector hóa các chunk. Ở đây, Toàn kết nối đến API (Giao diện lập trình ứng dụng) của OpenAI và sử dụng một trong các mô hình embedding của họ (ví dụ: `text-embedding-3-small`).
3. BƯỚC 3: Lưu Trữ vào Supabase
Sau khi cấu hình, node Supabase Vector Store sẽ tự động thực hiện toàn bộ quy trình: lấy văn bản, chia nhỏ theo các quy tắc đã định, gửi từng chunk đến API của OpenAI để nhận về vector, và cuối cùng, chèn các cặp (content, embedding) vào bảng `documents` trong project Supabase của bạn. Sau khi chạy workflow này, bạn có thể vào Supabase và sẽ thấy bảng `documents` của mình đã được lấp đầy dữ liệu, sẵn sàng cho việc truy vấn.
IV. “DẠY” CHATBOT CÁCH “GIAO TIẾP”: XÂY DỰNG WORKFLOW CONVERSATIONAL AGENT
Khi kho tri thức đã sẵn sàng, chúng ta sẽ xây dựng workflow thứ hai, cũng là workflow chính: con chatbot tương tác với người dùng. Workflow này được khởi tạo bằng một `Chat Trigger` và có trái tim là node `AI Agent` của n8n.
1. Chat Model (mô hình ngôn ngữ)
Đây là bộ não xử lý ngôn ngữ, chịu trách nhiệm tạo ra câu trả lời cuối cùng. Chúng ta sẽ kết nối nó với một mô hình mạnh mẽ như Gemini 2.5 Flash của Google hoặc GPT-4o của OpenAI. Đây là thành phần sẽ tổng hợp thông tin và trò chuyện một cách tự nhiên.
Xem thêm về các mô hình AI mạnh nhất hiện nay tại: [So sánh] Mô hình AI nào mạnh nhất hiện nay?
2. Tool (công cụ)
Đây là phần ma thuật của RAG. Một AI Agent trần trụi chỉ có thể nói chuyện dựa trên những gì nó biết. Chúng ta cần trang bị cho nó công cụ. Trong trường hợp này, công cụ chính là Supabase Vector Store (với action là “Retrieve”). Khi cấu hình công cụ này, chúng ta cho agent biết rằng: “Khi cần tìm thông tin về một chủ đề nào đó, ngươi có thể sử dụng công cụ này để tìm kiếm trong bảng `documents` của Supabase”. Agent sẽ tự động biết cách sử dụng tool này: nó nhận câu hỏi của người dùng, dùng tool để tìm các chunk liên quan, sau đó mới tiến hành bước tạo sinh câu trả lời.
3. Memory (Bộ nhớ): giúp chatbot không bị “não cá vàng”
Một cuộc trò chuyện có ý nghĩa cần có tính liên tục. Nếu bạn hỏi một câu, chatbot trả lời, rồi bạn hỏi câu tiếp theo liên quan đến câu trước mà chatbot không nhớ gì cả, thì trải nghiệm sẽ rất tồi tệ. Đây là lúc “Memory” phát huy tác dụng. Chúng ta sẽ cấu hình bộ nhớ cho agent, sử dụng PostgreSQL Chat Memory. Nó sẽ kết nối đến chính cơ sở dữ liệu Supabase của chúng ta và tạo một bảng mới (ví dụ: `n8n_chat_history`) để lưu lại lịch sử cuộc trò chuyện. Nhờ vậy, ở mỗi lượt nói, agent không chỉ biết câu hỏi hiện tại mà còn biết cả ngữ cảnh của những câu trao đổi trước đó, giúp nó trả lời một cách mạch lạc và thông minh hơn.
V. THỬ NGHIỆM VÀ NHỮNG GIỚI HẠN CỦA BASIC RAG (NAÏVE RAG)
Sau khi hoàn tất hai workflow, chúng ta đã có một RAG chatbot hoàn chỉnh. Giờ là lúc kiểm tra và nhìn nhận thực tế về khả năng của nó.
1. Khi mọi thứ hoạt động hoàn hảo
Khi Toàn thử nghiệm với một câu hỏi đơn giản và trực tiếp như “Cửa hàng của bạn ở đâu?”, chatbot hoạt động hoàn hảo. Nó truy xuất đúng chunk văn bản chứa thông tin về địa chỉ và đưa ra câu trả lời ngắn gọn, chính xác. Điều này cho thấy với các câu hỏi rõ ràng và thông tin nằm gọn trong một vài chunk, mô hình basic RAG hoạt động rất hiệu quả.
2. Vấp ngã với câu hỏi phức tạp: bài toán về “chức năng chính”
Tuy nhiên, khi Toàn hỏi một câu tưởng chừng đơn giản: “What are the key features of this product?” (chức năng chính của sản phẩm này là gì), vấn đề bắt đầu xuất hiện. Ý của Toàn là hỏi về tính năng chính của sản phẩm. Nhưng trong tài liệu gốc, từ “features” xuất hiện ở nhiều nơi với các ngữ cảnh khác nhau, kết quả là chatbot ngây ngô của chúng ta đã truy xuất tất cả các chunk có chứa từ “features” và liệt kê một danh sách dài các tiêu đề không liên quan, thay vì tập trung vào chức năng chính của sản phẩm. Nó không hiểu được ý định (intent) thực sự đằng sau câu hỏi.
3. Tại sao Basic RAG chưa đủ tốt?
Ví dụ trên đã phơi bày những giới hạn cố hữu của một hệ thống RAG cơ bản:
- Chất lượng truy xuất là tối quan trọng: Nếu bước Retrieval lấy về những chunk sai hoặc không đầy đủ, thì dù LLM có thông minh đến đâu, câu trả lời cuối cùng cũng sẽ sai. “Rác vào, rác ra” (Garbage in, garbage out).
- Thiếu khả năng suy luận phức tạp: Basic RAG không thực sự “suy nghĩ”. Nó chỉ tìm kiếm sự tương đồng về mặt văn bản. Nó không thể tổng hợp thông tin từ nhiều nguồn khác nhau hoặc suy luận để trả lời các câu hỏi gián tiếp, phức tạp.
- Thách thức với dữ liệu nhiễu: Khi kho kiến thức của bạn lớn, phức tạp và chứa nhiều thông tin nhiễu, khả năng chatbot truy xuất nhầm thông tin sẽ tăng lên đáng kể.
Kết luận
Qua bài viết dài này, Toàn hy vọng đã cung cấp cho các bạn một cái nhìn toàn cảnh và thực tế về việc xây dựng một RAG chatbot. Chúng ta đã cùng nhau đi từ việc tìm hiểu kiến trúc 3 bước cốt lõi, tự tay cấu hình backend trên Supabase, cho đến việc xây dựng các workflow tự động hóa trên n8n. Quan trọng hơn, chúng ta đã thấy được cả sức mạnh và những giới hạn của một hệ thống RAG cơ bản. Nó là một điểm khởi đầu tuyệt vời, nhưng chắc chắn không phải là đích đến cuối cùng.
Thế giới của RAG vẫn đang phát triển từng ngày với các kỹ thuật nâng cao (Advanced RAG) như Re-ranking (sắp xếp lại), Query Transformation (biến đổi truy vấn), HyDE… để giải quyết chính những vấn đề mà chúng ta đã thảo luận. Đây là một lĩnh vực cực kỳ thú vị và Toàn sẽ tiếp tục chia sẻ sâu hơn về các chủ đề này trong những bài viết tới. Hãy chia sẻ bài viết này nếu bạn thấy hữu ích và đừng quên đăng ký nhận tin từ website để không bỏ lỡ những kiến thức chuyên sâu về AI, Tự động hóa và Tối ưu Vận hành nhé.