EzyRAG là lớp truy hồi tri thức dành cho hệ sinh thái EzyPlatform. Hệ thống tiếp nhận nội dung từ website, chuyển nội dung thành vector để tìm kiếm theo ngữ nghĩa, rồi cung cấp các đoạn liên quan cho EzyAI hoặc thành phần AI khác.
EzyRAG tập trung vào phần Retrieval của kiến trúc Retrieval-Augmented Generation. Việc gọi mô hình ngôn ngữ và sinh câu trả lời cuối cùng thuộc trách nhiệm của EzyAI.

Kiến trúc tổng thể

Mã nguồn EzyRAG được chia thành bốn module chính:
  • SDK lõi: định nghĩa mô hình dữ liệu, abstraction và pipeline RAG.
  • Admin plugin: cấu hình hệ thống và quản trị dữ liệu.
  • Web plugin: cung cấp các thành phần RAG trong web runtime.
  • Socket plugin: kết nối RAG với EzyAI và luồng chat thời gian thực.
flowchart TB
    subgraph EzyPlatform["EzyPlatform"]
        ADMIN["Admin plugin<br/>Cấu hình và quản trị"]
        WEB["Web plugin<br/>Web runtime"]
        SOCKET["Socket plugin<br/>Chat thời gian thực"]
        AI["EzyAI<br/>Sinh câu trả lời"]
    end

    subgraph CORE["EzyRAG SDK"]
        PIPELINE["Pipeline RAG"]
        MODELS["Mô hình dữ liệu"]
        EXTENSIONS["Các điểm mở rộng"]
    end

    EMBEDDING["Dịch vụ embedding"]
    VECTOR["Cơ sở dữ liệu vector"]
    APP_DB["Cơ sở dữ liệu ứng dụng"]
    CONTENT["Bài viết, sản phẩm,<br/>media và văn bản"]

    ADMIN --> CORE
    WEB --> CORE
    SOCKET --> CORE

    CONTENT --> PIPELINE
    PIPELINE --> EMBEDDING
    PIPELINE --> VECTOR
    PIPELINE --> APP_DB

    WEB --> AI
    SOCKET --> AI
    CORE --> AI

SDK lõi

SDK chứa các hợp đồng và triển khai dùng chung cho toàn bộ hệ thống. Đây là nơi điều phối hai luồng chính:
  • Lập chỉ mục dữ liệu.
  • Tìm kiếm và xây dựng tri thức.
Các plugin admin, web và socket cung cấp dependency theo runtime nhưng cùng sử dụng pipeline trong SDK. Nhờ vậy, cách chunk dữ liệu và tìm kiếm được giữ nhất quán giữa các môi trường.
flowchart LR
    CLIENT["Bộ điều phối RAG"]

    CLIENT --> LOADER["Data loader"]
    CLIENT --> CLEANER["Text cleaner"]
    CLIENT --> CHUNKER["Data chunker"]
    CLIENT --> EMBEDDING["Embedding service"]
    CLIENT --> VECTOR["Vector database service"]
    CLIENT --> RETRIEVER["Data retriever"]
    CLIENT --> BUILDER["Knowledge data builder"]
    CLIENT --> SETTINGS["Setting service"]

Bộ nạp dữ liệu

Data loader chuyển từng loại nguồn dữ liệu thành đầu vào văn bản thống nhất cho pipeline.
EzyRAG hiện hỗ trợ:
NguồnNội dung được lập chỉ mụcMetadata đi kèm
Văn bản trực tiếpNội dung do người quản trị nhậpMetadata của yêu cầu
Bài viếtTiêu đề và nội dungTiêu đề, slug, tóm tắt
Sản phẩmTên, mã, mô tả và nội dung đa ngôn ngữTên, slug, mã sản phẩm, giá, tiền tệ
MediaThuộc tính mô tả và nội dung tài liệuTiêu đề, URL tài nguyên
Một nguồn có thể sinh nhiều đầu vào. Ví dụ, sản phẩm đa ngôn ngữ có thể tạo một đầu vào cho nội dung mặc định và các đầu vào tiếp theo cho từng bản dịch.
flowchart LR
    SOURCE["Nguồn dữ liệu"] --> SELECT{"Loại nguồn"}

    SELECT -->|Văn bản| TEXT["Text loader"]
    SELECT -->|Bài viết| POST["Post loader"]
    SELECT -->|Sản phẩm| PRODUCT["Product loader"]
    SELECT -->|Media| MEDIA["Media loader"]

    TEXT --> INPUT["RAG input"]
    POST --> INPUT
    PRODUCT --> INPUT
    MEDIA --> INPUT

Bộ đọc tài liệu

Khi nguồn là media, hệ thống chọn bộ đọc theo MIME type hoặc phần mở rộng của tệp.
Các định dạng được hỗ trợ gồm:
  • PDF.
  • Microsoft Word .docx.
  • Microsoft Excel .xlsx.
  • Microsoft PowerPoint .pptx.
  • Văn bản thuần và Markdown.
Bộ đọc tài liệu hoạt động như một lớp con của media loader:
flowchart TD
    MEDIA["Media"] --> INFO["Đọc tiêu đề và mô tả"]
    MEDIA --> FILE{"Tệp có bộ đọc phù hợp?"}

    FILE -->|PDF| PDF["PDF reader"]
    FILE -->|DOCX| WORD["Word reader"]
    FILE -->|XLSX| EXCEL["Excel reader"]
    FILE -->|PPTX| PPT["PowerPoint reader"]
    FILE -->|Text hoặc Markdown| TEXT["Text reader"]
    FILE -->|Không| SKIP["Chỉ dùng thông tin media"]

    PDF --> BLOCKS["Các khối văn bản"]
    WORD --> BLOCKS
    EXCEL --> BLOCKS
    PPT --> BLOCKS
    TEXT --> BLOCKS

    INFO --> INPUTS["Danh sách RAG input"]
    BLOCKS --> INPUTS
    SKIP --> INPUTS

Bộ làm sạch văn bản

Text cleaner chuẩn hóa dữ liệu trước khi chia đoạn:
  • Chuẩn hóa Unicode theo NFC.
  • Chuyển kiểu xuống dòng về một định dạng.
  • Loại bỏ ký tự điều khiển không cần thiết.
  • Thu gọn khoảng trắng và tab.
  • Loại bỏ khoảng trắng quanh dấu xuống dòng.
  • Giới hạn số dòng trống liên tiếp.
  • Cắt khoảng trắng ở đầu và cuối nội dung.
Bước này giúp cùng một nội dung không tạo ra embedding khác nhau chỉ vì khác biệt về định dạng.

Bộ chia đoạn

EzyRAG sử dụng chiến lược chia đoạn phân cấp. Nội dung được ưu tiên chia theo ranh giới tự nhiên trước khi phải cắt cứng.
flowchart TD
    RAW["Văn bản đã làm sạch"] --> HTML["Loại bỏ HTML<br/>Giữ ranh giới khối"]
    HTML --> CHECK{"Độ dài có nằm<br/>trong giới hạn?"}

    CHECK -->|Có| SINGLE["Tạo một chunk"]
    CHECK -->|Không| PARA["Chia và ghép theo đoạn văn"]

    PARA --> LONG_PARA{"Đoạn văn quá dài?"}
    LONG_PARA -->|Không| CHUNKS["Danh sách chunk"]
    LONG_PARA -->|Có| SENTENCE["Chia và ghép theo câu"]

    SENTENCE --> LONG_SENTENCE{"Câu vẫn quá dài?"}
    LONG_SENTENCE -->|Không| CHUNKS
    LONG_SENTENCE -->|Có| HARD["Cắt theo số ký tự"]
    HARD --> CHUNKS
Giới hạn độ dài chunk, dấu phân cách đoạn và biểu thức xác định ranh giới câu đều có thể cấu hình.
Thuật toán hiện chia theo số ký tự, chưa phải theo số token của model embedding hoặc mô hình ngôn ngữ.

Dịch vụ embedding

Embedding service chuyển văn bản thành vector số.
Triển khai mặc định sử dụng OpenAI và cho phép cấu hình model embedding. Kích thước vector được lấy từ collection đích, giúp embedding và cấu trúc collection thống nhất với nhau.
Embedding service nằm sau một interface và manager, vì vậy có thể bổ sung nhà cung cấp khác mà không phải thay đổi pipeline chính.
flowchart LR
    TEXT["Nội dung hoặc câu hỏi"] --> SERVICE["Embedding service"]
    CONFIG["Model và cấu hình"] --> SERVICE
    SIZE["Kích thước collection"] --> SERVICE
    SERVICE --> VECTOR["Vector embedding"]

Cơ sở dữ liệu vector

EzyRAG hiện có hai adapter lưu trữ vector:
  • EzyVector.
  • Qdrant.
Vector database service cung cấp các thao tác chung:
  • Tạo collection nếu chưa tồn tại.
  • Upsert các vector point.
  • Xóa vector point.
  • Tìm các vector gần nhất.
  • Nhận diện tên dịch vụ đang sử dụng.
Mỗi vector point dùng định danh của chunk làm ID. Payload chỉ chứa thông tin tối thiểu như loại nguồn, ID nguồn và vị trí chunk. Nội dung đầy đủ được lưu tại cơ sở dữ liệu ứng dụng.
flowchart LR
    CHUNK["Data chunk"] --> EMB["Embedding"]
    EMB --> POINT["Vector point"]

    POINT --> VECTOR_DB["Kho vector<br/>Vector và payload tối thiểu"]
    CHUNK --> APP_DB["Cơ sở dữ liệu ứng dụng<br/>Nội dung, hash và metadata"]

    VECTOR_DB -. "chunk ID" .-> APP_DB

Collection

Collection mô tả không gian vector mà dữ liệu được lưu vào. Một collection gắn với:
  • Tên collection.
  • Dịch vụ vector database.
  • Kích thước vector.
  • Trạng thái hoạt động.
  • Thông tin tạo và cập nhật.
Hệ thống hỗ trợ collection mặc định cho từng dịch vụ vector. Khi truy vấn không chỉ định rõ collection, EzyRAG sử dụng collection mặc định trong cấu hình.

Kho chunk và metadata

EzyRAG lưu dữ liệu chunk trong cơ sở dữ liệu ứng dụng, tách biệt với kho vector.
Một chunk chứa:
  • Loại nguồn dữ liệu.
  • ID nguồn.
  • ID collection.
  • Vị trí chunk trong nguồn.
  • Nội dung.
  • Hash nội dung.
  • Tên dịch vụ embedding.
  • Vector embedding.
Metadata được lưu riêng theo chunk, có thể bao gồm:
  • Tiêu đề.
  • Slug.
  • Tóm tắt.
  • URL tài nguyên.
  • Mã sản phẩm.
  • Giá.
  • Đơn vị tiền tệ.
Cách tách này cho phép kho vector tập trung vào tìm kiếm tương đồng, còn cơ sở dữ liệu ứng dụng giữ nội dung nghiệp vụ đầy đủ.

Pipeline lập chỉ mục

Khi lập chỉ mục một nguồn dữ liệu, bộ điều phối thực hiện các bước sau:
sequenceDiagram
    participant A as Admin hoặc ứng dụng
    participant R as Bộ điều phối EzyRAG
    participant L as Data loader
    participant C as Cleaner và chunker
    participant D as Cơ sở dữ liệu
    participant E as Embedding service
    participant V as Kho vector

    A->>R: Lập chỉ mục nguồn vào collection
    R->>L: Nạp dữ liệu theo loại nguồn
    L-->>R: Một hoặc nhiều đầu vào

    loop Mỗi đầu vào
        R->>C: Làm sạch và chia đoạn
        C-->>R: Danh sách chunk

        loop Mỗi chunk
            R->>D: Tìm chunk cũ theo nguồn và vị trí
            D-->>R: Hash và embedding hiện có
            R->>D: Lưu nội dung cùng metadata

            alt Nội dung mới hoặc đã thay đổi
                R->>E: Tạo embedding
                E-->>R: Vector
                R->>D: Lưu embedding
            else Nội dung không đổi
                R->>R: Tái sử dụng embedding
            end

            R->>V: Upsert vector point
        end
    end

    R->>D: Xóa các chunk cũ nằm ngoài phiên bản mới
Mỗi chunk được tính hash từ nội dung. Với các nguồn có định danh ổn định, hash được dùng để tránh gọi lại embedding service khi nội dung không thay đổi.
Đối với văn bản nhập trực tiếp, pipeline hiện tạo chunk mới thay vì tìm lại embedding theo nguồn và vị trí.
Một giới hạn đáng chú ý: sau khi lập chỉ mục lại với số chunk ít hơn, dữ liệu chunk dư được xóa khỏi cơ sở dữ liệu ứng dụng. Luồng lập chỉ mục không thể hiện thao tác xóa đồng thời các vector point dư khỏi vector database; việc đồng bộ phần này cần được kiểm soát ở lớp vận hành.

Bộ xử lý truy vấn

Trước khi tạo embedding, câu truy vấn đi qua query processor. Triển khai mặc định chuẩn hóa khoảng trắng trong câu hỏi.
Query processor cũng được quản lý qua manager, cho phép bổ sung các bước như:
  • Chuẩn hóa theo ngôn ngữ.
  • Viết lại truy vấn.
  • Loại bỏ từ không cần thiết.
  • Mở rộng từ đồng nghĩa.
Những khả năng trên là điểm mở rộng; chúng không đồng nghĩa tất cả đã được triển khai trong phiên bản hiện tại.

Pipeline tìm kiếm

Luồng tìm kiếm chuyển câu hỏi thành vector, tìm các chunk gần nhất rồi tái dựng tài liệu đầy đủ.
sequenceDiagram
    participant U as Người dùng
    participant AI as EzyAI
    participant R as EzyRAG
    participant Q as Query processor
    participant E as Embedding service
    participant V as Kho vector
    participant D as Cơ sở dữ liệu
    participant B as Knowledge builder

    U->>AI: Gửi câu hỏi
    AI->>R: Tìm tri thức
    R->>Q: Chuẩn hóa câu hỏi
    Q-->>R: Truy vấn đã xử lý
    R->>E: Tạo embedding truy vấn
    E-->>R: Vector truy vấn
    R->>V: Tìm kiếm trong collection
    V-->>R: Danh sách chunk ID
    R->>D: Lấy chunk và metadata
    D-->>R: Các tài liệu liên quan
    R->>B: Chuyển thành KnowledgeData
    B-->>R: Ngữ cảnh cho AI
    R-->>AI: Danh sách tri thức
    AI-->>U: Sinh câu trả lời
Nếu dịch vụ vector hoặc collection không được truyền trong tham số, search strategy sử dụng cấu hình mặc định. Nếu không tìm thấy cấu hình hoặc collection, kết quả trả về là danh sách rỗng.

Bộ truy hồi dữ liệu

Vector database chỉ trả về kết quả tìm kiếm chứa định danh chunk. Data retriever chịu trách nhiệm:
  1. Thu thập ID của các chunk tìm được.
  2. Đọc nội dung chunk từ cơ sở dữ liệu ứng dụng.
  3. Đọc metadata tương ứng.
  4. Chuyển chúng thành tài liệu RAG hoàn chỉnh.
Thiết kế này tránh lưu toàn bộ nội dung nghiệp vụ trong payload của vector database.

Bộ dựng dữ liệu tri thức

Knowledge data builder chuyển tài liệu RAG thành cấu trúc mà EzyAI hiểu được.
Kết quả có thể chứa:
  • ID và loại nguồn.
  • Tiêu đề.
  • Nội dung chunk.
  • Slug.
  • Tóm tắt.
  • URL tài nguyên.
  • Mã sản phẩm.
  • Giá và đơn vị tiền tệ.
Ngoài tìm kiếm vector, nguồn tri thức còn hỗ trợ lấy nội dung trực tiếp theo loại nguồn và ID hoặc mã. Khi một nguồn có nhiều chunk, chúng được ghép theo thứ tự để tái tạo nội dung đầy đủ.

Hệ thống manager và khả năng mở rộng

Mỗi giai đoạn quan trọng đều có một manager dùng để tìm implementation theo tên hoặc loại dữ liệu:
flowchart TB
    CONFIG["Cấu hình"] --> MANAGERS["Các manager"]

    MANAGERS --> L["Data loader"]
    MANAGERS --> C["Text cleaner"]
    MANAGERS --> H["Data chunker"]
    MANAGERS --> E["Embedding service"]
    MANAGERS --> Q["Query processor"]
    MANAGERS --> V["Vector database service"]
    MANAGERS --> R["Data retriever"]
    MANAGERS --> K["Knowledge data builder"]

    CUSTOM["Implementation mở rộng"] -. "Đăng ký qua dependency container" .-> MANAGERS
Khi có nhiều implementation xử lý cùng một loại dữ liệu, độ ưu tiên quyết định implementation được chọn. Kiến trúc này cho phép mở rộng từng bước của pipeline độc lập.

Admin plugin

Admin plugin cung cấp giao diện và API quản trị cho:
  • Cấu hình chunker, retriever và knowledge builder.
  • Cấu hình OpenAI embedding.
  • Cấu hình EzyVector và Qdrant.
  • Quản lý collection.
  • Chọn collection mặc định.
  • Lập chỉ mục bài viết, sản phẩm, media hoặc văn bản.
  • Xem danh sách chunk.
  • Tìm kiếm thử trên collection.
  • Kích hoạt hoặc vô hiệu hóa collection.
flowchart LR
    ADMIN["Quản trị viên"] --> UI["Giao diện quản trị"]
    UI --> API["API quản trị"]
    API --> SETTINGS["Cấu hình"]
    API --> COLLECTIONS["Quản lý collection"]
    API --> INDEXING["Lập chỉ mục dữ liệu"]
    API --> SEARCH["Kiểm tra tìm kiếm"]
Các API quản trị được bảo vệ bởi cơ chế xác thực của EzyPlatform. Giá trị nhạy cảm như API key được truy cập qua dịch vụ cấu hình dành cho mật khẩu, không được nhúng trực tiếp trong mã nguồn.

Web plugin

Web plugin cung cấp phiên bản runtime của các thành phần SDK:
  • Repository và service truy cập dữ liệu.
  • Data loader cho bài viết, sản phẩm, media và văn bản.
  • Bộ đọc tài liệu.
  • Cleaner, chunker và query processor.
  • Embedding service.
  • Adapter EzyVector và Qdrant.
  • Data retriever và knowledge builder.
  • Knowledge data source cho EzyAI.
Web plugin chủ yếu đóng vai trò kết nối SDK với dependency container và tài nguyên của web runtime.

Socket plugin

Socket plugin tích hợp nguồn tri thức RAG vào luồng socket và chat thời gian thực.
flowchart LR
    CLIENT["Ứng dụng chat"] --> SOCKET["Socket runtime"]
    SOCKET --> AI["EzyAI"]
    AI --> SOURCE["Nguồn tri thức EzyRAG"]
    SOURCE --> SEARCH["Tìm kiếm vector"]
    SEARCH --> CONTEXT["Các đoạn liên quan"]
    CONTEXT --> AI
    AI --> SOCKET
    SOCKET --> CLIENT
Socket plugin không tạo ra một giao thức RAG độc lập cho client. Nó đăng ký search strategy và knowledge data source để hạ tầng EzyAI gọi nội bộ.

Ranh giới trách nhiệm

flowchart LR
    CONTENT["Nội dung website"] --> RAG["EzyRAG<br/>Chuẩn bị và truy hồi tri thức"]
    QUESTION["Câu hỏi"] --> RAG
    RAG --> CONTEXT["Ngữ cảnh liên quan"]
    CONTEXT --> AI["EzyAI<br/>Điều phối LLM và sinh câu trả lời"]
    QUESTION --> AI
    AI --> ANSWER["Câu trả lời"]
EzyRAG chịu trách nhiệm:
  • Nạp và chuẩn hóa dữ liệu.
  • Chia dữ liệu thành chunk.
  • Tạo và lưu embedding.
  • Quản lý collection.
  • Tìm kiếm theo vector.
  • Khôi phục nội dung và metadata.
  • Cung cấp tri thức cho EzyAI.
EzyRAG không chịu trách nhiệm:
  • Huấn luyện mô hình ngôn ngữ.
  • Sinh câu trả lời cuối cùng.
  • Quản lý hội thoại.
  • Tự động đánh giá độ chính xác của câu trả lời.
  • Đảm bảo mọi vector cũ được dọn khỏi vector database trong chính luồng tái lập chỉ mục.