十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

hister 语义搜索的零依赖基石:解读 sqlite-vec 本地捆绑包的构建方案

hister 语义搜索的零依赖基石:解读 sqlite-vec 本地捆绑包的构建方案 搜索引擎全文检索后端前端CLI【免费下载链接】histerYour own search engine项目地址https://gitcode.com/GitHub_Trending/hi/hister点击查看免费下载导读hister 是一个自托管的个人搜索引擎Your own search engine它的语义搜索需要向量数据库能力。为了让 sqlite-vec 这一 SQLite 向量扩展能够跨平台、零系统依赖地构建进 Go 二进制项目在server/vectorstore/sqlitevec/目录下维护了一个自包含的本地捆绑包。本文以 server/vectorstore/sqlitevec/README.md 为骨架结合vec.go、update.sh、sqlite.go等源码讲解这一捆绑方案的设计动机、CGO 细节、版本更新流程以及它如何支撑 hister 的 vec0 虚拟表向量检索。为什么需要本地捆绑跨平台构建的痛点sqlite-vec 是 SQLite 的一个 C 语言扩展通过注册vec0虚拟表来提供 KNNK 近邻向量搜索能力。如果像普通扩展那样依赖系统安装的共享库.so/.dylib/.dllhister 的发布和部署就会面临一系列问题用户在 Alpine Linuxmusl libc、macOS、Windows 等环境上需要自行编译或下载对应平台的扩展文件扩展与宿主 SQLite 的版本/编译选项不匹配时可能加载失败Docker 多架构镜像docker-bake.hcl支持多平台构建需要为每个平台准备扩展维护成本极高。README 开门见山地给出了结论WARNING: This is required to be able to build sqlite-vec cross platform without dependencies也就是说这个包的存在不是可选项而是实现跨平台构建的前提。方案本身很简单把 sqlite-vec 的 C 源码sqlite-vec.c和头文件sqlite-vec.h直接拷贝进仓库再补一个sqlite3.h头文件然后通过 CGO 的CGO_CFLAGS指向 mattn/go-sqlite3 的包目录。这样整个构建过程完全自包含不依赖任何系统库。目录结构捆绑包的构成整个捆绑包位于 server/vectorstore/sqlitevec/包含以下文件文件来源作用sqlite-vec.csqlite-vec v0.1.6上游 C 源码vec0 虚拟表、vec_each表值函数及距离函数L2、cosine 等的完整实现sqlite-vec.hsqlite-vec v0.1.6上游头文件声明sqlite3_vec_init入口与版本宏SQLITE_VEC_VERSION v0.1.6sqlite3.hmattn/go-sqlite3 v1.14.42对应 SQLite 3.51.3编译期所需的 SQLite API 声明vec.go本仓库Go 包入口包含 CGO 预处理指令与Auto()注册函数update.sh本仓库从上游自动拉取并更新上述 C 文件的维护脚本其中sqlite3.h是关键的胶水。README 称之为 one-liner sqlite3.h shim实际实现上见 update.sh是把 mattn/go-sqlite3 模块缓存里的sqlite3-binding.h直接拷贝为sqlite3.h——因为 SQLite 的头文件必须与编译进二进制的那份 SQLite 实现来自 mattn/go-sqlite3严格一致否则扩展调用 SQLite API 时可能因 ABI 不匹配而出错。捆绑这份头文件使得构建时无需在系统里额外安装 SQLite 开发包。vec.goCGO 前奏与进程级注册Go 侧的入口是 vec.go它的 CGO 预处理指令是整个捆绑方案的枢纽// #cgo CFLAGS: -DSQLITE_CORE // #cgo CFLAGS: -Du_int8_tuint8_t -Du_int16_tuint16_t -Du_int64_tuint64_t // #cgo linux LDFLAGS: -lm // #include sqlite-vec.h import C逐行拆解-DSQLITE_CORE告诉 sqlite-vec 以内嵌核心模式编译。sqlite-vec.h中据此选择包含sqlite3.h而非加载器模式的sqlite3ext.h即扩展直接静态编译进宿主进程而不是作为运行时动态加载的共享库。-Du_int8_tuint8_t ...musl 兼容这是打包时专门为 Alpine Linux 等 musl libc 环境打的补丁。musl 不提供 POSIX 的u_int*_t别名而 sqlite-vec.c 的源码用它来 typedef C99 的uint*_t类型这些宏让 typedef 在 musl 上变成空操作在 glibc 上也无害类型本就兼容。没有这三行hister 的 Docker 镜像Alpine 基础镜像根本无法编译通过。-lmLinux 下链接数学库vec0 的距离计算需要sqrt等数学函数。包提供的唯一公开 API 是Auto()func Auto() { C.sqlite3_auto_extension((*[0]byte)(C.sqlite3_vec_init)) }它调用 SQLite 的sqlite3_auto_extension把sqlite3_vec_init注册为自动扩展。这意味着进程内此后打开的每一个 SQLite 连接都会自动加载 sqlite-vec调用方无需在每个连接上手动执行SELECT load_extension(...)。这一设计配合了 hister 的数据库访问方式数据库连接可能在不同模块中创建Auto()在初始化时调用一次即可全局生效。真实调用链sqlite.go在 server/vectorstore/sqlite.go 中newSQLite()首先调用sqlitevec.Auto()然后打开主数据库同目录下的vectors.sqlite3向量库并限制为单连接以避免 SQLite 锁竞争func newSQLite(cfg *config.Config) (VectorStore, error) { sqlitevec.Auto() dbPath : cfg.FullPath(cfg.Server.Database) dir : filepath.Dir(dbPath) vecDBPath : filepath.Join(dir, vectors.sqlite3) db, err : sql.Open(sqlite3, vecDBPath) ... db.SetMaxOpenConns(1) ... }随后Init()用SELECT vec_version()校验扩展确实可用并创建基于 vec0 的embeddings虚拟表sqlite.go#L100-L110CREATE VIRTUAL TABLE embeddings USING vec0( user_id INTEGER PARTITION KEY, chunk_key TEXT PRIMARY KEY, embedding FLOAT[dimensions] distance_metriccosine )这里展示了 vec0 的三个特性PARTITION KEY按用户分区KNN 查询可限定分区、TEXT PRIMARY KEYvec0 内部用 int64 rowid 存储、以id text unique模拟文本主键见sqlite-vec.c中VEC0_SHADOW_ROWIDS_CREATE_PK_TEXT的注释、以及distance_metriccosine余弦距离从默认的 L2 迁移而来——sqlite.go的initEmbeddingsTable专门实现了旧 L2 表到 cosine 表的事务性数据迁移。FLOAT[n]的维度n来自配置项semantic_search.dimensions。查询侧sqlite.go#L254-L294使用 vec0 的 KNN 语法SELECT e.chunk_key, e.distance, ... FROM embeddings e LEFT JOIN chunk_meta m ON e.chunk_key m.chunk_key WHERE e.embedding MATCH ? AND e.k ? AND e.user_id ? ORDER BY e.distance其中MATCH传入的是float32ToBlob序列化的小端序vec_f32二进制向量sqlite.go#L310-L318k为返回候选数e.user_id分区条件让 vec0 的vec0BestIndex在 chunk 层面提前过滤分区避免全库扫描。结果中的distance换算为相似度1.0 - distance后再与semantic_search.similarity_threshold比较过滤随后经 vectorstore.go 的候选放大topK * 4与文档级去重每篇文档最多保留 2 个 chunk处理最终进入索引器的语义重排。整套链路完全建立在Auto()注册的捆绑扩展之上。版本维护update.sh 与手工同步的代价捆绑方案的代价在 README 末尾被明确点出The downside is were copying upstream C files and need to update them manually when sqlite-vec releases.即上游发布新版本后必须手动重新拉取 C 文件并同步版本号。为此项目提供了 update.sh支持两种调用方式# 方式一通过 go generate 触发版本号从 vec.go 注释中自动读取 go generate ./server/vectorstore/sqlitevec/ # 方式二显式指定版本 sh update.sh v0.1.6脚本执行流程确定版本优先用命令行参数未传参时从vec.go中以// sqlite-vec source:开头的注释中解析版本号下载 C 源码从 sqlite-vec-go-bindings 仓库对应版本的cgo目录依次下载sqlite-vec.c和sqlite-vec.h依赖curl同步 sqlite3.h通过go list -m -json github.com/mattn/go-sqlite3定位模块缓存目录把其中的sqlite3-binding.h拷贝为sqlite3.h——保证头文件始终与 go.mod 中锁定的 mattn/go-sqlite3 版本当前为 v1.14.42SQLite 3.51.3一致提示收尾提醒更新vec.go中的版本注释。整个更新流程刻意手工化脚本只负责拉文件不自动改代码目的是让版本变更成为一次可审查的显式提交。当前捆绑的版本在vec.go注释与sqlite-vec.h的SQLITE_VEC_VERSION宏中均有记录sqlite-vec v0.1.6。权衡总结何时该采用本地捆绑策略从 this 包的实现可以提炼出一套可复用的取舍逻辑维度本地捆绑本方案系统级依赖共享库构建复杂度高需处理 CGO、musl 兼容低安装即用跨平台可移植性强单二进制自包含弱每平台需准备扩展部署运维无额外步骤需管理扩展生命周期版本更新手动同步上游 C 文件跟随系统包管理器适用场景分发多平台 Docker 镜像、追求零依赖已有系统包管理、单一可控环境hister 选择前者换来的是docker-bake.hcl多平台镜像、Dockerfile 构建、甚至静态部署场景下的统一体验——用户拿到一个二进制就自带完整的向量检索能力这正是Your own search engine自托管理念在工程层面的体现。参考文件索引捆绑包说明server/vectorstore/sqlitevec/README.mdGo 入口与 CGO 指令server/vectorstore/sqlitevec/vec.go上游同步脚本server/vectorstore/sqlitevec/update.sh向量扩展实现server/vectorstore/sqlitevec/sqlite-vec.cSQLite 头文件 shimserver/vectorstore/sqlitevec/sqlite3.h向量库初始化与查询server/vectorstore/sqlite.go向量后端抽象与后端选择server/vectorstore/vectorstore.go赞分享搜索引擎全文检索后端前端CLI【免费下载链接】histerYour own search engine项目地址https://gitcode.com/GitHub_Trending/hi/hister点击查看免费下载相关推荐如何用sqlite-vec构建本地AI知识库向量搜索终极指南如何用sqlite vec构建本地AI知识库向量搜索终极指南 在人工智能快速发展的今天 向量搜索 技术已成为构建智能应用的核心。sqlite vec作为一款向量数据库数据库C语言集成sqlite-vec构建高效向量搜索应用的完整指南C语言集成sqlite vec构建高效向量搜索应用的完整指南 sqlite vec是一个轻量级的向量搜索SQLite扩展专为C语言开发者设计提供高效的向量向量数据库数据库SQLite向量搜索Python实战5步实现本地语义搜索SQLite向量搜索Python实战5步实现本地语义搜索 SQLite向量搜索sqlite vec是一个强大的本地向量搜索扩展让你能够在任何SQLite向量数据库数据库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表