Back to blog
    裝置端語意搜尋:無需伺服器的 AI 搜尋
    semantic searchon-device AIembeddingsmobile AIimplementationsegment:mobile-builder

    裝置端語意搜尋:無需伺服器的 AI 搜尋

    如何建構完全在使用者手機上運行的語意搜尋。本地嵌入、向量相似度和自然語言查詢,無需伺服器或 API 即可搜尋使用者內容。

    EErtas Team·

    當使用者不知道確切的詞彙時,關鍵字搜尋就會失敗。「上週那封關於預算會議的電子郵件」無法匹配主旨為「第三季財務審查」的郵件。語意搜尋理解意義,而非僅僅是關鍵字。

    標準做法是將語意搜尋放在具有向量資料庫的伺服器上。但對於使用者內容在本地的行動應用(筆記、訊息、照片、文件),將該內容發送到伺服器就違背了將其保留在裝置上的初衷。

    裝置端語意搜尋讓一切保持在本地。嵌入模型在手機上運行。向量索引存在本地儲存中。搜尋查詢永遠不會離開裝置。

    語意搜尋的工作原理

    1. 索引: 每段內容使用小型模型轉換為嵌入向量(一組代表其意義的數字)
    2. 儲存: 嵌入向量與內容一起儲存在本地資料庫中
    3. 查詢: 使用者的搜尋查詢使用相同的模型轉換為嵌入向量
    4. 比對: 查詢向量使用餘弦相似度與所有已儲存的向量進行比較
    5. 排序: 結果按相似度分數排序返回

    奧妙在於嵌入。關於同一主題的兩段文字會產生相似的向量,即使它們沒有共同的關鍵字。

    嵌入模型

    裝置端嵌入模型小巧且快速。不同於生成式 LLM(600MB-1.7GB),嵌入模型通常為 20-80MB:

    模型大小維度速度(iPhone 15)
    all-MiniLM-L6-v223MB384500+ 嵌入/秒
    nomic-embed-text-v1.555MB768200+ 嵌入/秒
    bge-small-en-v1.533MB384400+ 嵌入/秒

    以每秒 200-500 個嵌入的速度,索引 1,000 則筆記只需 2-5 秒。查詢嵌入幾乎是即時的(5ms 以內)。

    運行嵌入模型

    你可以透過以下方式運行嵌入模型:

    ONNX Runtime Mobile: 支援 ONNX 格式的嵌入模型。可用於 iOS(透過 Swift)和 Android(透過 Kotlin)。對於行動端嵌入推論來說,這是最成熟的選項。

    // iOS 使用 ONNX Runtime
    let session = try ORTSession(env: env, modelPath: embeddingModelPath)
    let inputTensor = try ORTValue(tensorData: tokenizedInput, shape: shape)
    let outputs = try session.run(withInputs: ["input_ids": inputTensor])
    let embedding = outputs["embeddings"]!.tensorData()

    llama.cpp 嵌入模式: llama.cpp 可以使用嵌入旗標從 GGUF 模型生成嵌入。這讓你可以使用同一個推論引擎進行生成和嵌入。

    向量儲存

    SQLite 搭配自訂擴充

    行動端最簡單的方法:將向量作為 BLOB 儲存在 SQLite 中,並在應用程式碼中計算相似度。

    // Android:儲存嵌入
    fun storeEmbedding(db: SQLiteDatabase, contentId: Long, embedding: FloatArray) {
        val blob = ByteBuffer.allocate(embedding.size * 4)
        embedding.forEach { blob.putFloat(it) }
        db.execSQL(
            "INSERT INTO embeddings (content_id, vector) VALUES (?, ?)",
            arrayOf(contentId, blob.array())
        )
    }
    
    // 按相似度搜尋
    fun search(db: SQLiteDatabase, queryEmbedding: FloatArray, limit: Int): List<SearchResult> {
        val cursor = db.rawQuery("SELECT content_id, vector FROM embeddings", null)
        val results = mutableListOf<SearchResult>()
    
        while (cursor.moveToNext()) {
            val blob = cursor.getBlob(1)
            val stored = FloatArray(blob.size / 4)
            ByteBuffer.wrap(blob).asFloatBuffer().get(stored)
    
            val similarity = cosineSimilarity(queryEmbedding, stored)
            results.add(SearchResult(cursor.getLong(0), similarity))
        }
    
        return results.sortedByDescending { it.similarity }.take(limit)
    }

    這種方法簡單,適用於最多約 10,000 個項目的集合。超過此數,線性掃描會變慢。

    SQLite 搭配向量擴充

    對於更大的集合,使用支援近似最近鄰(ANN)搜尋的 SQLite 向量擴充:

    • sqlite-vss: 使用 Faiss 進行向量搜尋的 SQLite 擴充。支援 iOS 和 Android。
    • sqlite-vec: 專為嵌入式使用設計的輕量級向量搜尋擴充。

    這些擴充在向量上建立索引,使數十萬個項目的搜尋在毫秒以下完成。

    完整流程

    步驟 1:索引內容

    當使用者建立或修改內容(筆記、訊息、文件)時,生成並儲存其嵌入:

    func indexContent(_ content: Content) async {
        let embedding = await embeddingModel.encode(content.text)
        database.storeEmbedding(contentId: content.id, vector: embedding)
    }

    在背景運行索引。使用者不應等待嵌入計算完成。

    步驟 2:搜尋

    當使用者輸入搜尋查詢時:

    func search(query: String) async -> [Content] {
        let queryEmbedding = await embeddingModel.encode(query)
        let results = database.similaritySearch(queryEmbedding, limit: 10)
        return results.map { fetchContent($0.contentId) }
    }

    搜尋按語意相似度排序返回結果。「預算會議筆記」能匹配到「第三季財務審查」,因為嵌入捕捉了語意關係。

    步驟 3:混合搜尋

    結合語意搜尋與關鍵字搜尋以獲得最佳結果:

    1. 運行關鍵字搜尋(SQLite FTS5)進行精確匹配
    2. 運行語意搜尋進行基於意義的匹配
    3. 合併並去除重複結果
    4. 按綜合分數排序(關鍵字匹配優先提升)

    這同時處理了精確查詢(「與 John 的會議」)和模糊查詢(「那封關於專案時程的郵件」)。

    效能預算

    元件儲存RAM速度
    嵌入模型23-55MB推論時 50-100MB200-500 嵌入/秒
    向量索引(10K 項,384d)約 15MB約 15MB每次搜尋 5ms 以內
    向量索引(100K 項,384d)約 150MB約 30MB(使用 ANN 索引)每次搜尋 10ms 以內

    語意搜尋的總額外佔用:40-200MB 儲存、65-130MB 搜尋時 RAM。這只是生成式 LLM 所需的一小部分,即使在受限裝置上也是可行的。

    使用案例

    筆記應用

    按意義搜尋所有筆記。「上週關於產品發布的會議記錄」能找到相關筆記,無論確切用詞為何。

    郵件用戶端

    按主題而非僅按寄件者或主旨找到郵件。「關於合約續約的對話」找出正確的郵件串。

    照片應用

    搭配影像描述(裝置端)實現基於文字的照片搜尋。「海邊的日落」即使沒有手動標記也能找到匹配的照片。

    文件管理器

    按內容和意義搜尋 PDF、文件和檔案。

    搭配裝置端 LLM 使用

    語意搜尋與裝置端生成模型自然搭配。使用搜尋結果作為 LLM 的上下文:

    1. 使用者提出問題
    2. 語意搜尋從使用者資料中檢索相關內容
    3. LLM 使用檢索到的內容作為上下文生成答案

    這就是裝置端 RAG。無需伺服器。整個流程(嵌入、搜尋、生成)都在本地運行。

    對於生成元件,使用像 Ertas 這樣的平台在你的領域資料上微調模型。微調模型搭配本地語意搜尋,創造出強大且完全私密的 AI 助手。

    Ship AI that runs on your users' devices.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Ship AI that runs on your users' devices.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Keep reading