lyyyuna 的小花园

动静中之动, by

RSS

让每种语言都能用上嵌入式向量数据库 -- milvus-lite 的 Go 和 Node.js 封装

发表于 2026-04
milvus-lite 只有 Python 封装,我给它做了 Go 和 Node.js 版本

前言

最近在做一些 AI Agent 相关的开发,需要一个本地的向量数据库做语义搜索。Milvus 是一个不错的选择,而且它有一个轻量版本 milvus-lite,不需要部署 etcd、MinIO 这些依赖,单文件就能跑。

问题是,milvus-lite 只有 Python 封装。如果你用 Go 或者 Node.js 开发 Agent,就没法用了。官方的态度也很明确:Node.js SDK 的维护者直接关掉了 feature request,理由是"C++ core 无法移植到 TypeScript";Go 版本则一直停留在 issue 讨论阶段。

但仔细看 milvus-lite 的架构就会发现,这个说法并不准确。

milvus-lite 到底是什么

milvus-lite 本质上是一个独立的 C++ gRPC 服务进程,Python 那层只做了两件事:

  1. subprocess.Popen 启动这个二进制
  2. 通过标准的 Milvus gRPC 协议通信

也就是说,Python 只是一个启动器。真正干活的是那个 C++ 二进制,它对外暴露的是和 Milvus Standalone 完全一样的 gRPC 接口。

既然如此,任何语言只要能启动一个子进程、连上 gRPC,就能用 milvus-lite。不需要 CGo,不需要 FFI,不需要把 C++ 移植到别的语言。

所以我做了两个封装库:

安装

Go

go get github.com/lyyyuna/milvus-lite-go/v2

二进制通过 go:embed 内嵌在平台子模块里。go get 时 Go module proxy 只会下载你当前平台对应的那个子模块(大约 25-55MB),不会把所有平台的都拉下来。

Node.js

npm install @lyyyuna/milvus-lite @zilliz/milvus2-sdk-node

采用和 esbuild、swc 一样的分发方式:主包是纯 JS,二进制按平台拆成独立的 npm 包放在 optionalDependencies 里。npm 会自动只安装匹配当前系统的那个。

两个版本都不需要运行时下载,安装完就能直接用。

使用

Go

package main

import (
    "context"
    "log"

    milvuslite "github.com/lyyyuna/milvus-lite-go/v2"
    "github.com/milvus-io/milvus-sdk-go/v2/client"
    "github.com/milvus-io/milvus-sdk-go/v2/entity"
)

func main() {
    // 启动 milvus-lite,数据存在本地文件
    server, err := milvuslite.Start("./milvus.db")
    if err != nil {
        log.Fatal(err)
    }
    defer server.Stop()

    // 用官方 SDK 连接,一行不用改
    c, _ := client.NewClient(context.Background(), client.Config{
        Address: server.Addr(),
    })
    defer c.Close()

    // 后面就是正常的 Milvus 操作了
    schema := entity.NewSchema().WithName("demo").
        WithField(entity.NewField().WithName("id").WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true).WithIsAutoID(true)).
        WithField(entity.NewField().WithName("vector").WithDataType(entity.FieldTypeFloatVector).WithDim(128))

    c.CreateCollection(context.Background(), schema, entity.DefaultShardNumber)
}

Node.js

import { start } from "@lyyyuna/milvus-lite";
import { MilvusClient, DataType } from "@zilliz/milvus2-sdk-node";

const server = await start("./milvus.db");

const client = new MilvusClient({ address: server.addr });

await client.createCollection({
    collection_name: "demo",
    fields: [
        { name: "id", data_type: DataType.Int64, is_primary_key: true, autoID: true },
        { name: "vector", data_type: DataType.FloatVector, dim: 128 },
    ],
});

await server.stop();

两个版本的 API 都很简单:Start/start 启动,拿到地址,用各自语言的官方 Milvus SDK 连接。数据操作的代码和连接远程 Milvus 集群完全一样,想迁移到生产环境只需要改一下连接地址。

支持的平台

OS Arch Go npm
macOS arm64 (Apple Silicon)
macOS amd64 (Intel)
Linux amd64
Linux arm64

原理简介

整个方案的核心思路就一句话:不碰 C++ core,只做进程管理和分发

二进制从哪来

milvus-lite 的 Python 包(PyPI 上的 milvus-lite)是一个 wheel 文件,本质上是个 zip 包。里面除了 Python 代码,还打包了编译好的 milvus 二进制和一堆动态库(libknowhere、libglog、libtbb 等)。

我写了一个脚本,从 PyPI 下载各平台的 wheel,解压出 milvus_lite/lib/ 目录,塞到对应的平台包里。

分发方式

Go 用的是子目录多模块方案。一个 repo 里有多个 go.mod,每个平台一个子模块:

platform/
├── darwin-arm64/go.mod    # 独立模块,go:embed 内嵌二进制
├── darwin-amd64/go.mod
├── linux-amd64/go.mod
└── linux-arm64/go.mod

Go module proxy 在分发时会排除有独立 go.mod 的子目录,所以用户 go get 时只下载主模块(几 KB)+ 自己平台的子模块(几十 MB)。运行时通过 build tags 选择对应平台的嵌入数据,解压到临时目录后启动。

npm 用的是 optionalDependencies 方案,和 esbuild 的做法一样。主包声明四个平台包为可选依赖,npm 安装时自动跳过不匹配的平台。

启动流程

  1. 解压(Go)或定位(npm)二进制和动态库到一个目录
  2. 设置 LD_LIBRARY_PATH(Linux)或 DYLD_LIBRARY_PATH(macOS)
  3. exec.Command(Go)或 child_process.spawn(Node.js)启动 milvus 进程
  4. 等待 gRPC 端口就绪
  5. 返回地址,用户用官方 SDK 连接

一个已知的坑

milvus-lite 的 ShowCollections gRPC 响应只填了 collection_names,没填 collection_ids。Python SDK 用的是 collection_names 遍历所以没问题,但 Go SDK 用的是 collection_ids 遍历,导致 client.ListCollections() 返回空列表。Node.js SDK 恰好用的也是 collection_names,所以没这个问题。

Go 版本提供了一个 workaround 函数:

names, _ := milvuslite.ListCollections(ctx, server.Addr())

写在最后

这个项目的代码量并不大,Go 版本几百行,Node.js 版本也差不多。真正花时间的是理解 milvus-lite 的架构、搞清楚各平台的分发机制、处理 Go module 的版本号规则这些事情。

如果你正在用 Go 或 Node.js 开发 AI Agent,需要一个本地嵌入式向量数据库来做语义搜索、RAG 或者知识库,可以试试。

lyyyuna 沪ICP备2025110782号-1