Hướng dẫn sử dụng LightRAG với Python: Xây dựng Graph RAG tối ưu chi phí và tốc độ

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

Quick start: Dựng Graph RAG với LightRAG trong 5 phút

Nếu từng thử nghiệm GraphRAG của Microsoft, bạn sẽ thấy rào cản lớn nhất nằm ở chi phí API và tốc độ. Quá trình index 100 trang PDF có thể ngốn hàng chục USD tiền token và mất cả tiếng đồng hồ. LightRAG giải quyết điểm nghẽn này nhờ cơ chế trích xuất thực thể tinh gọn cùng kiến trúc truy xuất hai tầng (dual-level retrieval).

Cài đặt thư viện qua pip:

pip install lightrag-hku openai

Tạo file quickstart.py để index văn bản và test truy vấn:

import os
from lightrag import LightRAG, QueryParam
from lightrag.llm import gpt_4o_mini_complete, openai_embedding

# Thiết lập thư mục lưu trữ đồ thị tri thức
WORKING_DIR = "./lightrag_storage"
os.makedirs(WORKING_DIR, exist_ok=True)

rag = LightRAG(
    working_dir=WORKING_DIR,
    llm_model_func=gpt_4o_mini_complete,
    embedding_func=openai_embedding,
)

# Dữ liệu mẫu cần index
sample_text = """
Nguyễn Du sinh năm 1765 tại Thăng Long, là đại thi hào văn học Việt Nam.
Ông là tác giả của tác phẩm Truyện Kiều (Đoạn trường tân thanh).
Truyện Kiều được viết bằng chữ Nôm theo thể thơ lục bát gồm 3254 câu thơ.
"""

# 1. Chèn dữ liệu (Insert & Graph Indexing)
rag.insert(sample_text)

# 2. Truy vấn dữ liệu với chế độ kết hợp (Hybrid)
query = "Nguyễn Du đã sáng tác tác phẩm gì và nó có đặc điểm gì nổi bật?"
response = rag.query(query, param=QueryParam(mode="hybrid"))
print(response)

Chạy script:

export OPENAI_API_KEY="sk-proj-..."
python quickstart.py

Ngay sau khi chạy xong, LightRAG tự động bóc tách các thực thể như Nguyễn Du, Truyện Kiều, chữ Nôm cùng các mối quan hệ liên kết. Toàn bộ đồ thị được lưu cục bộ trong thư mục ./lightrag_storage.

Tại sao LightRAG xử lý câu hỏi tổng quan tốt hơn Naive RAG?

Hạn chế cố hữu của Vector Search đơn thuần

Naive RAG chia nhỏ văn bản thành từng chunk cố định khoảng 500 đến 1000 tokens. Cách làm này hoạt động tốt với câu hỏi tra cứu vị trí cụ thể. Tuy nhiên, khi gặp câu hỏi dạng: “Tổng hợp toàn bộ rủi ro kiến trúc được nhắc rải rác trong 50 file tài liệu?”, Vector Search sẽ bó tay. Câu trả lời nằm phân tán ở nhiều file khác nhau, không trọn vẹn trong một chunk nào.

Cơ chế Dual-Level Retrieval trong LightRAG

LightRAG kết hợp đồ thị tri thức với hai tầng truy xuất linh hoạt:

  • Low-level retrieval (Tầng chi tiết): Truy vết các node thực thể và cạnh lân cận. Phù hợp cho câu hỏi tìm kiếm thông số, mã lỗi hoặc dữ kiện cụ thể.
  • High-level retrieval (Tầng tổng quan): Gom nhóm các cụm chủ đề liên quan trên toàn đồ thị. Hỗ trợ tổng hợp bức tranh toàn cảnh và phân tích xu hướng.

Thư viện cung cấp 4 chế độ query phục vụ từng nhu cầu:

  1. naive: Tìm kiếm vector thuần theo chunk văn bản.
  2. local: Tập trung khai thác các thực thể lân cận.
  3. global: Quét qua các mối quan hệ tổng quan ở tầng cao.
  4. hybrid: Phối hợp cả local lẫn global để câu trả lời vừa sâu sát vừa bao quát.

Nâng cao: Chạy Local LLM với Ollama và tùy biến Storage

Để bảo vệ dữ liệu nội bộ và cắt giảm chi phí API, bạn có thể chuyển sang dùng mô hình mã nguồn mở thông qua Ollama.

import asyncio
from lightrag import LightRAG, QueryParam
from lightrag.llm import ollama_model_complete, ollama_embedding
from lightrag.utils import EmbeddingFunc

WORKING_DIR = "./local_rag_storage"

async def main():
    rag = LightRAG(
        working_dir=WORKING_DIR,
        llm_model_func=ollama_model_complete,
        llm_model_name="qwen2.5:7b",
        llm_model_kwargs={"host": "http://localhost:11434"},
        embedding_func=EmbeddingFunc(
            embedding_dim=768,
            max_token_size=8192,
            func=lambda texts: ollama_embedding(
                texts, 
                embed_model="nomic-embed-text", 
                host="http://localhost:11434"
            )
        )
    )

    # Index tài liệu dài bất đồng bộ
    with open("system_spec.txt", "r", encoding="utf-8") as f:
        await rag.ainsert(f.read())

    # Truy vấn chế độ global
    res = await rag.aquery(
        "Tóm tắt kiến trúc mạng và các điểm nghẽn tiềm ẩn?",
        param=QueryParam(mode="global")
    )
    print(res)

if __name__ == "__main__":
    asyncio.run(main())

LightRAG hỗ trợ sẵn cả hai interface synchronous và asynchronous (ainsert, aquery). Nhờ đó, việc tích hợp vào các backend asynchronous như FastAPI hay Sanic trở nên rất thuận tiện.

Kinh nghiệm thực chiến khi đưa LightRAG lên Production

Khi áp dụng LightRAG vào hệ thống tra cứu nghiệp vụ thực tế, bạn nên lưu ý các điểm sau để tối ưu chi phí và độ ổn định:

1. Chọn model trích xuất Entity phù hợp

Giai đoạn index tiêu tốn nhiều token nhất vì LLM phải phân tích từng đoạn văn để trích xuất entity và relationship. Hãy ưu tiên các model nhỏ có khả năng output JSON chuẩn như gpt-4o-mini hoặc gemini-1.5-flash. Cách này giúp bạn giảm tới 80-90% chi phí API so với model lớn mà chất lượng đồ thị vẫn đảm bảo.

2. Tận dụng Incremental Update

LightRAG cho phép cập nhật đồ thị theo dạng vi mô. Bạn không cần dựng lại toàn bộ graph từ đầu mỗi khi có tài liệu mới:

# Chỉ cần nạp thêm văn bản mới, LightRAG sẽ tự ghép nối vào đồ thị hiện có
rag.insert("Tài liệu bổ sung về chính sách bảo mật v2.0...")

Hệ thống tự động liên kết các thực thể mới vào mạng lưới sẵn có. Toàn bộ tiến trình tra cứu của người dùng vẫn diễn ra bình thường, không bị gián đoạn.

3. Phân luồng Query Mode theo Intent

Tránh lạm dụng hybrid cho mọi request vì chế độ này gọi LLM hai lần để tổng hợp context. Hãy phân loại ý định người dùng trước khi gọi query:

  • Tra cứu định nghĩa, mã lỗi, thông số kỹ thuật: Chọn local để phản hồi nhanh và tiết kiệm token.
  • So sánh kiến trúc, đánh giá rủi ro, phân tích xu hướng: Chọn global.
  • Câu hỏi mơ hồ hoặc đòi hỏi phân tích đa chiều: Chọn hybrid.

4. Backup và phân tách Graph Storage

Theo mặc định, LightRAG lưu cấu trúc đồ thị vào file graph_chunk_entity_relation.graphml kèm các file JSON key-value trong thư mục working_dir. Khi đóng gói chạy trong Docker container, hãy mount thư mục này ra Persistent Volume ngoài. Điều này đảm bảo dữ liệu đồ thị không bị mất khi container khởi động lại hoặc redeploy.

Share: