Hướng dẫn về AI Edge RAG cho Android

SDK RAG AI Edge cung cấp các thành phần cơ bản để tạo một quy trình Tạo tăng cường truy xuất (RAG) bằng LLM Inference API. Một quy trình RAG cung cấp cho LLM quyền truy cập vào dữ liệu do người dùng cung cấp, có thể bao gồm thông tin đã cập nhật, thông tin nhạy cảm hoặc thông tin dành riêng cho miền. Với khả năng truy xuất thông tin bổ sung từ RAG, các LLM có thể tạo ra những câu trả lời chính xác hơn và phù hợp với ngữ cảnh cho các trường hợp sử dụng cụ thể.

Hướng dẫn này sẽ hướng dẫn bạn cách triển khai cơ bản một ứng dụng mẫu bằng LLM Inference API với AI Edge RAG SDK. Hướng dẫn này tập trung vào việc xây dựng một quy trình RAG. Để biết thêm thông tin về cách sử dụng LLM Inference API, hãy xem hướng dẫn về LLM Inference cho Android.

Bạn có thể tìm thấy ứng dụng mẫu hoàn chỉnh trên GitHub. Để bắt đầu, hãy tạo ứng dụng, đọc dữ liệu do người dùng cung cấp (sample_context.txt) và đặt câu hỏi cho LLM liên quan đến thông tin trong tệp văn bản.

Chạy ứng dụng ví dụ

Hướng dẫn này đề cập đến một ví dụ về ứng dụng tạo văn bản cơ bản có RAG cho Android. Bạn có thể sử dụng ứng dụng mẫu này làm cơ sở cho ứng dụng Android của riêng mình hoặc tham khảo ứng dụng này khi sửa đổi một ứng dụng hiện có.

Ứng dụng này được tối ưu hoá cho các thiết bị cao cấp hơn như Pixel 8, Pixel 9, S23 và S24. Kết nối thiết bị Android với máy trạm và đảm bảo bạn có phiên bản Android Studio hiện tại. Để biết thêm thông tin, hãy xem hướng dẫn thiết lập Android.

Tải mã ứng dụng xuống

Các hướng dẫn sau đây cho biết cách tạo bản sao cục bộ của mã ví dụ bằng công cụ dòng lệnh git.

Sao chép kho lưu trữ git bằng lệnh sau:

git clone https://github.com/google-ai-edge/ai-edge-apis

Sau khi tạo một phiên bản cục bộ của mã ví dụ, bạn có thể nhập dự án vào Android Studio và chạy ứng dụng.

Tải một mô hình xuống

Ứng dụng mẫu được định cấu hình để sử dụng Gemma-3 1B. Gemma-3 1B là một phần của nhóm Gemma gồm các mô hình nguồn mở, nhẹ và hiện đại được xây dựng dựa trên cùng một nghiên cứu và công nghệ được dùng để tạo ra các mô hình Gemini. Mô hình này chứa 1 tỷ tham số và trọng số mở.

Tải Gemma-3 1B xuống

Sau khi tải Gemma-3 1B xuống từ Hugging Face, hãy chuyển mô hình này sang thiết bị của bạn:

cd ~/Downloads
tar -xvzf gemma3-1b-it-int4.tar.gz
$ adb shell rm -r /data/local/tmp/llm/ # Remove any previously loaded models
$ adb shell mkdir -p /data/local/tmp/llm/
$ adb push output_path /data/local/tmp/llm/model_version.task

Bạn cũng có thể sử dụng các mô hình khác với ứng dụng mẫu, nhưng có thể cần thêm các bước định cấu hình.

Thiết lập một trình nhúng

Trình nhúng lấy các đoạn văn bản từ dữ liệu do người dùng cung cấp và chuyển chúng thành các biểu diễn bằng số được vectơ hoá để nắm bắt ý nghĩa ngữ nghĩa của dữ liệu đó. LLM tham chiếu các mục nhúng này để xác định các vectơ có liên quan và kết hợp các khối ngữ nghĩa phù hợp nhất trong đầu ra được tạo.

Ứng dụng mẫu được thiết kế để hoạt động với 2 trình nhúng, đó là trình nhúng Gemini và trình nhúng Gecko.

Thiết lập bằng trình nhúng Gecko

Theo mặc định, ứng dụng mẫu được định cấu hình để sử dụng trình nhúng Gecko (GeckoEmbeddingModel) và chạy mô hình hoàn toàn trên thiết bị.

Tải Gecko 110m-en xuống

Trình nhúng Gecko có sẵn dưới dạng các mô hình dấu phẩy động và mô hình được lượng tử hoá, với nhiều phiên bản cho các độ dài chuỗi khác nhau. Để biết thêm thông tin, hãy xem thẻ mô hình Gecko.

Bạn có thể tìm thấy thông số kỹ thuật của mô hình trong tên tệp mô hình. Ví dụ:

  • Gecko_256_f32.tflite: Mô hình số thực hỗ trợ các chuỗi có tối đa 256 mã thông báo.
  • Gecko_1024_quant.tflite: Mô hình được lượng tử hoá hỗ trợ các chuỗi có tối đa 1024 mã thông báo.

Độ dài chuỗi là kích thước đoạn tối đa mà mô hình có thể nhúng. Ví dụ: nếu mô hình Gecko_256_f32.tflite được truyền một đoạn vượt quá độ dài chuỗi, thì mô hình sẽ nhúng 256 mã thông báo đầu tiên và cắt phần còn lại của đoạn.

Đẩy mô hình mã hoá (sentencepiece.model) và trình nhúng Gecko vào thiết bị của bạn:

adb push sentencepiece.model /data/local/tmp/sentencepiece.model
adb push Gecko_256_f32.tflite /data/local/tmp/gecko.tflite

Mô hình nhúng tương thích với cả CPU và GPU. Theo mặc định, ứng dụng mẫu được định cấu hình để trích xuất các mục nhúng bằng mô hình Gecko trên GPU.

companion object {
  ...
  private const val USE_GPU_FOR_EMBEDDINGS = true
}

Thiết lập bằng Gemini Embedder

Gemini Embedder (GeminiEmbedder) tạo các vectơ nhúng bằng Gemini Cloud API. Bạn cần có khoá Google Gemini API để chạy ứng dụng này. Bạn có thể lấy khoá này trên trang thiết lập Google Gemini API.

Lấy khoá Gemini API trong Google AI Studio

Thêm khoá Gemini API và đặt COMPUTE_EMBEDDINGS_LOCALLY thành false trong RagPipeline.kt:

companion object {
  ...
  private const val COMPUTE_EMBEDDINGS_LOCALLY = false
  private const val GEMINI_API_KEY = "<API_KEY>"
}

Cách hoạt động

Phần này cung cấp thông tin chi tiết hơn về các thành phần trong quy trình RAG của ứng dụng. Bạn có thể xem hầu hết mã tại RagPipeline.kt.

Phần phụ thuộc

RAG SDK sử dụng thư viện com.google.ai.edge.localagents:localagents-rag. Thêm phần phụ thuộc này vào tệp build.gradle của ứng dụng Android:

dependencies {
    ...
    implementation("com.google.ai.edge.localagents:localagents-rag:0.1.0")
    implementation("com.google.mediapipe:tasks-genai:0.10.22")
}

Dữ liệu do người dùng cung cấp

Dữ liệu do người dùng cung cấp trong ứng dụng là một tệp văn bản có tên là sample_context.txt, được lưu trữ trong thư mục assets. Ứng dụng này lấy các đoạn của tệp văn bản, tạo các mục nhúng của những đoạn đó và tham chiếu đến các mục nhúng khi tạo văn bản đầu ra.

Bạn có thể tìm thấy đoạn mã sau trong MainActivity.kt:

class MainActivity : ComponentActivity() {
  lateinit var chatViewModel: ChatViewModel
...
    chatViewModel.memorizeChunks("sample_context.txt")
...
}

Phân đoạn

Để đơn giản, tệp sample_context.txt bao gồm các thẻ <chunk_splitter> mà ứng dụng mẫu dùng để tạo các khối. Sau đó, các vectơ nhúng sẽ được tạo cho từng đoạn. Trong các ứng dụng phát hành công khai, kích thước của các khối là một yếu tố quan trọng cần cân nhắc. Khi một đoạn quá lớn, vectơ sẽ không có đủ độ đặc hiệu để hữu ích; và khi quá nhỏ, vectơ sẽ không có đủ ngữ cảnh.

Ứng dụng mẫu xử lý việc phân đoạn thông qua hàm memorizeChunks trong RagPipeline.kt.

Nhúng

Ứng dụng này cung cấp 2 cách nhúng văn bản:

  • Gecko embedder: Trích xuất văn bản nhúng cục bộ (trên thiết bị) bằng mô hình Gecko.
  • Gemini Embedder: Trích xuất văn bản dựa trên đám mây bằng Generative Language Cloud API.

Ứng dụng mẫu sẽ chọn trình nhúng dựa trên việc người dùng có ý định tính toán các mục nhúng cục bộ hay thông qua Google Cloud. Bạn có thể tìm thấy đoạn mã sau trong RagPipeline.kt:

private val embedder: Embedder<String> = if (COMPUTE_EMBEDDINGS_LOCALLY) {
  GeckoEmbeddingModel(
    GECKO_MODEL_PATH,
    Optional.of(TOKENIZER_MODEL_PATH),
    USE_GPU_FOR_EMBEDDINGS,
    )
  } else {
    GeminiEmbedder(
      GEMINI_EMBEDDING_MODEL,
      GEMINI_API_KEY
      )
  }

Cơ sở dữ liệu

Ứng dụng mẫu sử dụng SQLite (SqliteVectorStore) để lưu trữ các mục nhúng văn bản. Bạn cũng có thể sử dụng cơ sở dữ liệu DefaultVectorStore để lưu trữ vectơ không cố định.

Bạn có thể tìm thấy đoạn mã sau trong RagPipeline.kt:

private val config = ChainConfig.create(
    mediaPipeLanguageModel, PromptBuilder(QA_PROMPT_TEMPLATE1),
    DefaultSemanticTextMemory(
        SqliteVectorStore(768), embedder
    )
)

Ứng dụng mẫu đặt phương diện nhúng thành 768, tức là chiều dài của mỗi vectơ trong cơ sở dữ liệu vectơ.

Chuỗi

RAG SDK cung cấp các chuỗi, kết hợp nhiều thành phần RAG thành một quy trình duy nhất. Bạn có thể dùng chuỗi để điều phối các mô hình truy xuất và truy vấn. API này dựa trên giao diện Chain.

Ứng dụng mẫu này sử dụng chuỗi Truy xuất và suy luận. Bạn có thể tìm thấy đoạn mã sau trong RagPipeline.kt:

private val retrievalAndInferenceChain = RetrievalAndInferenceChain(config)

Chuỗi này được gọi khi mô hình tạo ra các câu trả lời:

suspend fun generateResponse(
    prompt: String,
    callback: AsyncProgressListener<LanguageModelResponse>?
): String =
    coroutineScope {
        val retrievalRequest =
            RetrievalRequest.create(
                prompt,
                RetrievalConfig.create(2, 0.0f, TaskType.QUESTION_ANSWERING)
            )
        retrievalAndInferenceChain.invoke(retrievalRequest, callback).await().text
    }