minirag

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2026 License: MIT Imports: 10 Imported by: 0

README

MiniRAG — 轻量移动端 RAG 框架

MiniRAG 是 GoRAG 的移动端版本,通过 gomobile 编译为 iOS/Android 原生 framework。

设计原则: ML 推理(向量化)由平台侧负责,MiniRAG 只做向量存储与检索。

  • iOS: Core ML 计算嵌入向量,MiniRAG 存储/检索
  • Android: ML Kit / TFLite 计算嵌入向量,MiniRAG 存储/检索

目录


前置要求

通用
  • Go 1.21+
  • gomobile
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
iOS
  • Xcode 15+
  • macOS (编译时)
Android
  • Android SDK (API 21+)
  • Android NDK r26+ (构建时需要)
    # Homebrew
    brew install android-ndk
    export ANDROID_NDK_HOME="/usr/local/share/android-ndk"
    
  • Android Studio (开发时)

构建

# iOS Framework
make minirag-ios
# 或手动:gomobile bind -target=ios -o MiniRAG.xcframework ./minirag

# Android AAR
make minirag-android
# 或手动:ANDROID_HOME=~/Library/Android/sdk ANDROID_NDK_HOME=/usr/local/share/android-ndk \
#   gomobile bind -target=android -androidapi 21 -o MiniRAG.aar ./minirag

# 全部
make minirag-all

# 清理
make minirag-clean
产物
平台 产物 大小
iOS MiniRAG.xcframework/ ~55MB
Android MiniRAG.aar ~20MB

iOS: 在 Xcode 中使用

导入 Framework
  1. 目标 Target → General → Frameworks, Libraries, and Embedded Content
  2. 点击 +Add OtherAdd Files...
  3. 选择 MiniRAG.xcframework
  4. Embed 模式设为 Embed & Sign
实现 Embedder
import CoreML
import MiniRAG

final class CoreMLEmbedder: MiniragEmbedderProtocol {
    private let model: MLModel

    init(model: MLModel) {
        self.model = model
    }

    func embedText(_ text: String) throws -> Data {
        // 调用 Core ML 模型,返回 float32 小端字节序
        let input = try MLMultiArray(shape: [1, 512], dataType: .float32)
        // ... 填充文本特征 ...

        let output = try model.prediction(from: YourInput(text: text))
        let embedding = output.featureValue(for: "embedding")!.multiArrayValue!
        return Data(bytes: embedding.dataPointer, count: embedding.count * 4)
    }
}
Swift 原生封装

创建一个封装类,隐藏 JSON 序列化细节:

import MiniRAG

struct Chunk: Decodable {
    let id: String
    let content: String
}

struct Hit: Decodable {
    let id: String
    let content: String
    let score: Double
}

final class MiniRAG {
    private let impl: MiniragNewRAG

    init(dataDir: String, dimension: Int, embedder: MiniragEmbedderProtocol) throws {
        impl = try MiniragNewRAG(dataDir: dataDir, dimension: dimension, embedder: embedder)
    }

    func addIndex(_ content: String) throws -> [Chunk] {
        let data = try impl.addIndex(content)
        return try JSONDecoder().decode([Chunk].self, from: data)
    }

    func search(_ query: String, topK: Int = 10) throws -> [Hit] {
        let data = try impl.search(query, topK: topK)
        return try JSONDecoder().decode([Hit].self, from: data)
    }

    func delete(_ id: String) throws { try impl.delete(id) }
    func close() throws { try impl.close() }
}
使用
let docDir = NSHomeDirectory() + "/Documents/minirag"
let rag = try MiniRAG(dataDir: docDir, dimension: 512, embedder: CoreMLEmbedder(model: mlModel))

let chunks = try rag.addIndex("原神是一款开放世界冒险游戏")
let results = try rag.search("开放世界", topK: 5)

try rag.delete(chunks[0].id)
rag.close()

Android: 在 Android Studio 中使用

导入 AAR
  1. MiniRAG.aar 放入 app/libs/
  2. app/build.gradle.kts 添加:
android {
    // ...
}

dependencies {
    implementation(fileTree("libs") { include("*.aar") })
}
  1. Sync Project with Gradle Files
Kotlin 封装

Gomobile 生成的 Java API 使用 byte[] 传递向量数据。建议做一个 Kotlin 封装层:

package com.example.minirag

import minirag.NewRAG
import minirag.Embedder
import org.json.JSONArray
import org.json.JSONObject

// — 原生类型 —
data class Chunk(val id: String, val content: String)
data class Hit(val id: String, val content: String, val score: Double)

// — Embedder 接口(由 ML Kit / TFLite 实现)—
interface MiniRAGEmbedder {
    fun embedText(text: String): ByteArray  // float32 LE bytes
}

// — Kotlin 封装 —
class MiniRAG(
    dataDir: String,
    dimension: Int,
    embedder: MiniRAGEmbedder
) : AutoCloseable {

    private val impl: NewRAG

    init {
        val goEmbedder = object : Embedder() {
            override fun embedText(text: String): ByteArray {
                return embedder.embedText(text)
            }
        }
        impl = NewRAG(dataDir, dimension, goEmbedder)
    }

    fun addIndex(content: String): List<Chunk> {
        val json = impl.addIndex(content) ?: return emptyList()
        val arr = JSONArray(String(json))
        return (0 until arr.length()).map { i ->
            val obj = arr.getJSONObject(i)
            Chunk(obj.getString("id"), obj.getString("content"))
        }
    }

    fun search(query: String, topK: Int = 10): List<Hit> {
        val json = impl.search(query, topK) ?: return emptyList()
        val arr = JSONArray(String(json))
        return (0 until arr.length()).map { i ->
            val obj = arr.getJSONObject(i)
            Hit(obj.getString("id"), obj.getString("content"), obj.getDouble("score"))
        }
    }

    fun delete(id: String) { impl.delete(id) }
    override fun close() { impl.close() }
}
ML Kit Embedder 示例
package com.example.minirag

import android.content.Context
import com.google.mlkit.nl.embedding.TextEmbedder
import com.google.mlkit.nl.embedding.TextEmbedderOptions
import java.nio.ByteBuffer
import java.nio.ByteOrder

class MLKitEmbedder(context: Context) : MiniRAGEmbedder {

    private val embedder: TextEmbedder

    init {
        val options = TextEmbedderOptions.Builder()
            .build()
        embedder = TextEmbedder.getClient(options)
    }

    override fun embedText(text: String): ByteArray {
        val result = embedder.embedText(text)
            .addOnSuccessListener { /* ok */ }
            // ML Kit 的同步调用方式视版本而定

        // 假设 result.embedding 是 FloatArray
        val floats = result?.embedding ?: FloatArray(512)
        val buf = ByteBuffer.allocate(floats.size * 4).order(ByteOrder.LITTLE_ENDIAN)
        floats.forEach { buf.putFloat(it) }
        return buf.array()
    }
}
使用
class MainActivity : AppCompatActivity() {
    private lateinit var rag: MiniRAG

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val dataDir = filesDir.absolutePath + "/minirag"
        val embedder = MLKitEmbedder(this)
        rag = MiniRAG(dataDir, 512, embedder)

        // 索引
        lifecycleScope.launch(Dispatchers.IO) {
            val chunks = rag.addIndex("原神是一款开放世界冒险游戏")
            Log.d("MiniRAG", "indexed: $chunks")
        }
    }

    override fun onDestroy() {
        rag.close()
        super.onDestroy()
    }
}

API 参考

Go → ObjC/Java 映射
Go 函数 ObjC (iOS) Java (Android)
New(dataDir, dim, emb) +newWithDataDir:dimension:embedder:error: NewRAG(dataDir, dim, embedder)
AddIndex(content) -addIndex:error:NSData* addIndex(String)byte[]
Search(query, topK) -search:topK:error:NSData* search(String, int)byte[]
Delete(id) -delete:error:BOOL delete(String)void
Close() -close:error:BOOL close()void
JSON 格式

AddIndex 返回:

[{"id":"a1b2c3d4e5f6","content":"原神是一款开放世界冒险游戏"}]

Search 返回:

[{"id":"a1b2c3d4e5f6","content":"原神是一款开放世界冒险游戏","score":0.956}]

向量数据格式

所有跨语言边界的向量以 byte[] / Data 传递,格式为 float32 小端字节序

文本 → ML 模型 → [0.012, -0.034, 0.156, ...] (512个float32)
               → byte[0..3]  第一个 float32 的 LE 编码
               → byte[4..7]  第二个 float32 的 LE 编码
               → ...
               → 总计 512 × 4 = 2048 字节

常见问题

macOS: gomobile: command not found
export PATH=$HOME/go/bin:$PATH
# 或永久添加
echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.zshrc
iOS: No such module MiniRAG

检查 Build Phases → Link Binary With Libraries 是否已添加 MiniRAG.xcframework。如已添加,检查 Framework Search Paths

Android: NDK 未安装
# 方法一:通过 sdkmanager
sdkmanager --install "ndk;26.1.10909125"

# 方法二:通过 Homebrew
brew install android-ndk

# 方法三:Android Studio → SDK Manager → SDK Tools → NDK
Android: UnsatisfiedLinkError

确保 MiniRAG.aar 包含了对应架构的 .so(gomobile 默认包含 arm64-v8a,如需 armeabi-v7a 需加参数 -target=android/arm,android/arm64)。

Documentation

Overview

Package minirag 轻量移动端 RAG 索引器 可通过 gomobile bind 编译为 iOS/Android 原生 framework。

iOS: gomobile bind -target=ios -o MiniRAG.xcframework ./minirag Android: gomobile bind -target=android -o MiniRAG.aar ./minirag

Embedder 由平台侧注入(iOS: Core ML, Android: ML Kit), ML 推理在平台侧完成,NewRAG 只负责向量存储与检索。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Embedder

type Embedder interface {
	EmbedText(text string) ([]byte, error)
}

Embedder 嵌入向量计算器 — 由平台层实现。 EmbedText 接收文本,返回 float32 小端字节序的向量(4 字节/维)。

type NewRAG

type NewRAG struct {
	// contains filtered or unexported fields
}

NewRAG 轻量移动端 RAG 索引器

func New

func New(dataDir string, dimension int, emb Embedder) (*NewRAG, error)

New 创建 NewRAG 实例。 dataDir: 持久化目录(iOS 传 NSDocumentDirectory) dimension: 向量维度(与 ML 模型输出一致) emb: 平台侧 Embedder 实现

func (*NewRAG) AddIndex

func (r *NewRAG) AddIndex(content string) ([]byte, error)

AddIndex 索引文本:分块 → 嵌入 → 存储。 返回 JSON: [{"id":"..","content":".."},...]

func (*NewRAG) Close

func (r *NewRAG) Close() error

Close 关闭索引器

func (*NewRAG) Delete

func (r *NewRAG) Delete(id string) error

Delete 删除指定记录

func (*NewRAG) Search

func (r *NewRAG) Search(query string, topK int) ([]byte, error)

Search 搜索文本:嵌入 → 向量检索。 返回 JSON: [{"id":"..","content":"..","score":0.95},...]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL