AI 博客

LanceDB、Chroma、sqlite-vec 与 FAISS:本地智能体知识库的四种形状

在挑本地向量存储之前,先注意一件事:Claude Code、Cursor 与 Codex 都把自己的删掉了——领先的编程智能体用 grep 检索,而不是嵌入。如果你的语料仍然需要索引,那么这四者并非互相竞争的产品,而是四种不同的架构:一个不带存储的搜索库、一个 SQLite 扩展、一个带预写日志的嵌入式引擎,以及一种落在磁盘上的列式格式。

作者 智能体 AI 维基 52 分钟读完

关于 2026 年本地向量存储,最有教益的一个事实是:生产环境中调用量最大的那批检索系统,把自己的向量库删掉了——Claude Code 早期版本带过一个本地向量数据库,后来把它移除,改用 grep 检索。如果你的语料仍然需要索引——散文类语料确实需要——那么这四者并非四个互相竞争的产品,而是四种不同的架构;而选错形状,代价比选错牌子大得多。

一览

四者都在你自己的硬件上、在你自己的进程内运行。相似之处到此为止:一个是完全不带存储的库,一个是文件格式,一个是你多半本来就在用的数据库的扩展,还有一个是带预写日志的引擎。

项目起始年份它究竟是什么数据存在哪里
FAISS 2017 一个相似度搜索库——C++ 加 Python 绑定,支持 CPU 与 GPU。 哪儿都不存。存储由你自己选择与管理。
Chroma 2022 一个嵌入式向量数据库,自 1.0 起内核为 Rust。 由引擎自己管理的一个持久化目录。
LanceDB 2023 架在 Lance 列式格式之上的一个检索引擎。 本地磁盘或对象存储上的 Lance 文件。
sqlite-vec 2024 一个纯 C 的 SQLite 扩展,新增 vec0 虚拟表。 就在你已经有的那个 SQLite 文件里。

许可协议在这里不构成区分点——FAISS 是 MIT,另外三个是 Apache 2.0,四者都能真正自托管且不回传数据。人气值得看一眼,但也仅此而已,因为它同时在度量三件不同的事:FAISS 积累了八年的研究者用户,而 sqlite-vec 才两岁,还没到 1.0。

GitHub stars — FAISS, Chroma, LanceDB, sqlite-vec Horizontal bar chart of GitHub stars in thousands, snapshot 26 July 2026: FAISS leads at 40.6k, Chroma 28.9k, LanceDB 11.0k, sqlite-vec 7.9k. FAISS is a search library rather than a database, and sqlite-vec is the youngest project of the four. GitHub stars (thousands, 26 Jul 2026) 0 10k 20k 30k 40k 50k stars FAISS library, since 2017 40.6k Chroma embedded engine, 2022 28.9k LanceDB columnar format, 2023 11.0k sqlite-vec SQLite extension, 2024 7.9k
它衡量的是年龄与受众,而非质量——FAISS 自 2017 年起就是参考级 ANN 库,而 sqlite-vec 只存在了两年。
Local vector store capability matrix Heatmap comparing LanceDB, Chroma, sqlite-vec and FAISS across five axes: built-in storage, ANN index, metadata filtering, hybrid or full-text search, and handling corpora larger than RAM. Strength runs from light neutral (weak) through soft accent (medium) to solid accent (strong). What each store gives you out of the box Storage built in ANN index Metadata filtering Hybrid / full-text Larger than RAM LanceDB Lance format IVF-PQ SQL predicates FTS + rerank Disk-native Chroma Persistent dir HNSW where filters Substring only Segments sqlite-vec SQLite file Brute force Typed columns Join FTS5 Paged scan FAISS None Flat/IVF/HNSW/PQ ID masks only None On-disk IVF Weak Medium Strong
请把"弱"的格子读成设计决策而非缺口:FAISS 是有意不做存储,sqlite-vec 是有意不做 ANN 索引,好让自己维持为一个零依赖的小 C 文件。

比选型更靠前的那个问题

在比较存储之前,值得知道的是:资源最充足的那批智能体团队看过这条流水线,然后掉头走开了。Anthropic 的 Claude Code 早期版本背后是一个本地向量数据库,后来把整套东西——嵌入模型、索引、分块启发式——统统换成了实时 ripgrep 调用。Cursor、Codex 与 Cline 在代码检索上也都倚重 grep 形态、可脚本化的检索。其理由是结构性的:当你在找 PaymentRetryPolicy 时,精确匹配就是正确;建在正被编辑的文件之上的索引,在写入与重建索引之间是陈旧的;而一次可以被收窄成另一次搜索的搜索,是一个计划,而不是一发子弹。

迄今最好的一次测量来自普华永道 2026 年 5 月的一篇 arXiv 论文《Is Grep All You Need? How Agent Harnesses Reshape Agentic Search》。在 116 道 LongMemEval 题目与若干"外壳/模型"组合上,当结果被内联注入上下文时,词法搜索一致胜过向量检索——某个 Claude Opus 配置下为 93.1% 对 83.6%,差距最小的一组是 76.7% 对 75.0%。但当同样的结果被写进文件让智能体自己去读、而不是拼接进提示词时,受测配置中有一半发生了排序反转。

值得带走的正是这次反转。检索质量并不只是检索器自己的属性;外壳与结果投递方式对数字的影响,与算法本身一样大。所以本文其余部分诚实的定位很窄:当语料庞大、以散文为主、且按语义而非按字符串来查询时,你需要索引——支持工单历史、合同、研究资料、政策文件;而当语料是代码形态且就在本地磁盘上时,你需要的是 grep。多数真实系统最后两者兼备。

FAISS — 深入

FAISS architecture FAISS is a search library loaded into your own process: it holds an index of vectors and returns integer IDs with distances. Persistence, metadata, filtering and the join back to documents are all code you write yourself, outside the library. Your process — everything here is your code FAISS — a search library C++ with Python bindings · CPU or GPU · no server, no file format Index structures Flat (exact) · IVF · HNSW · PQ (compression) choose the recall × latency × memory point yourself Search returns (id, distance) integer IDs only — the library never saw your text filtering is an ID bitmask you build and pass in Everything a database would have given you Persistence serialize the index to disk, reload on start, decide what happens on crash Metadata store a separate table mapping id → text, source, tenant, timestamp — kept in sync by you Collections & lifecycle adds, deletes, rebuilds, versioning — no concept of a “collection” exists in the library The trade You get the fastest and most tunable ANN implementation available, including GPU search over billions of vectors. You also get to write, test and operate the three boxes on the right — which is most of what a vector database is.
FAISS 返回整数 ID 与距离。让这些 ID 变得有用的一切,都是你要写的代码。

它如何存储

它不存储。这是关于 FAISS 最需要理解的一点,也是一半涉及它的对比文章分类错误的原因。没有文件格式、没有集合概念、没有元数据、没有持久化层——你在内存里建一个索引,而序列化它、重新加载它、以及决定崩溃之后怎么办,都是你的问题。这个库从没见过你的文本;它见到的是浮点数组,交回来的是整数。

它如何搜索

好得出奇,而且掌控力胜过本文其他任何一个。Flat 提供精确的暴力搜索;IVF 把空间划分成簇并只探查其中几个;HNSW 构建一张可导航的图;PQ 把向量压缩成编码,好让几十亿个也装得进内存。你可以把它们组合起来——IVF4096,PQ64 就是一个真实的索引描述——并亲手调节"召回—延迟—内存"这个三角。GPU 搜索是原生的,本文其他任何数据库都不具备。

它让什么变难

过滤,而那恰恰是生产环境真正的查询形状。"取 top-k,条件是 tenant_id = 7lang = 'en'"并不是 FAISS 建模的东西;逃生阀是你从一张独立元数据表构造出来的 ID 位掩码,这意味着每个候选都要多一次间接寻址,以及两个绝不能互相矛盾的存储之间的同步问题。删除同样得手工来。如果你对"元数据放在哪儿?"的回答是"在 Postgres 里,我做个 join",那你刚刚描述的就是一个向量数据库的大部分,你该问问自己是不是本来就想要一个。

sqlite-vec — 深入

sqlite-vec architecture sqlite-vec is a C extension that adds vec0 virtual tables to SQLite. Vectors, typed metadata columns, partition keys and auxiliary payload columns live inside one ordinary SQLite file, queried with SQL alongside FTS5 full-text tables and your existing application rows. One ordinary SQLite file pure-C extension, zero dependencies · runs wherever SQLite runs, including WASM vec0 virtual table CREATE VIRTUAL TABLE chunks USING vec0(...) embedding float, int8, bit metadata cols filterable, ≤16 partition keys shard by tenant +aux columns payload only Brute-force KNN every vector scanned, exact recall partition keys cut the scan set fine to ~10⁵ · slow far beyond Your existing tables documents, users, permissions + FTS5 for BM25 keyword search joined in the same statement One SQL query, one transaction SELECT … WHERE tenant = ? AND embedding MATCH ? ORDER BY distance LIMIT k Agent / app No server the database is a file you can copy or ship Any language Python, Node, Go, Rust, Ruby, browser WASM Backups are cp whatever you already do with a SQLite file Still pre-1.0 expect breaking changes
整个知识库就是一个文件——可以复制、可以随应用一起分发、也可以在浏览器里打开。

它如何存储

以行的形式,存在你已经有的那个 SQLite 文件里。CREATE VIRTUAL TABLE … USING vec0(…) 给你一张表,里面放着 float、int8 或二值向量,旁边可以有最多十六个可用于过滤的类型化元数据列、最多四个对索引做物理分片的分区键,以及承载负载但不建索引的辅助列。它是纯 C、零依赖的,所以能跑在 Linux、macOS、Windows、树莓派上,也能通过 WASM 跑在浏览器里——是本文唯一能随客户端应用一起分发的选项。

它如何搜索

靠扫描。默认是对分区内每一个向量做暴力 KNN,这听上去像缺陷,但多半是特性:召回是精确的、没有索引要建也没有参数要调,而且刚插入的行立即可被搜到,没有压实延迟。分区键是那个扩展杠杆——如果每次查询都限定在一个租户内,你扫的是那个租户的向量,而不是整份语料。官方建议是让每个分区键取值下有数百个向量,而不是寥寥几个,因为过度分片的代价大于收益。

它让什么变难

增长。线性扫描在数十万向量以内没问题,到一千万就很痛苦——那些把它排在 ANN 索引后面两三个数量级的基准,量的正是这一点,而且量得公道。这个项目也仍然明确处于 1.0 之前,预期会有破坏性变更;对一个由你掌控的文件来说这是可接受的风险,对一个不易迁移的 schema 来说则不是。作为交换,你免费拿到了本文最好的混合检索故事:FTS5 本来就在 SQLite 里,所以 BM25 加向量再加一次与权限表的联接,只是一条语句。

Chroma — 深入

Chroma architecture Chroma runs in-process against a persistent directory. Writes append to an immutable write-ahead log; background compaction materializes the log into HNSW segments; reads query the segments. Since version 1.0 the core is Rust, with native bindings for Python, JavaScript, Ruby and Swift. Your code one import, one path collection.add(...) collection.query(...) Python · JS · Ruby · Swift Rust core (since 1.0) — lock-free, multithreaded log-structured: writes go to a log, reads go to segments, compaction bridges them 1 · Write-ahead log immutable, append-only writes acknowledge here ingest never blocks 2 · Compaction background, asynchronous log becomes segments so there is a brief lag 3 · HNSW segments graph index, in memory queries read here SPANN is distributed-only Query: vector search + metadata where-filters + substring document match no BM25 ranking — if you want true hybrid retrieval you add a keyword index yourself filters are applied against the segment metadata, not a separate store A persistent directory on your disk no server process in embedded mode the same API also talks to a self-hosted server or Chroma Cloud The trade Shortest distance from nothing to working retrieval. The memory-resident HNSW is also the ceiling: comfortable to roughly a million vectors, then memory pressure decides for you.
写与读在设计上就是解耦的——这既是摄取快的原因,也是刚写入的行短时间内还搜不到的原因。

它如何存储

存在一个由它自己管理的持久化目录里,采用日志结构设计——1.0 的 Rust 重写让这一点变得明确。写入追加进一个不可变的预写日志并立即确认;一个后台压实进程把日志物化成段;读取查询这些段。这次重写宣称在写入与查询两侧都比此前的 Python 实现提升 3–5 倍,并带来了 JavaScript、Ruby 与 Swift 的原生绑定,以及面向浏览器的 WASM 构建。

它如何搜索

用 HNSW——一张常驻内存的可导航小世界图,这是有充分理由的标准选择:当索引装得下时,它在低延迟下有出色的召回。元数据 where 过滤在同一条查询路径上针对段元数据施加,而不是走一个独立存储;文档文本上还支持子串匹配。它没有 BM25 排序,所以在 Chroma 技术栈里说"混合检索",意味着关键词索引是你自己带来的。另外请注意:SPANN 这个磁盘友好的索引只存在于 Chroma 的分布式形态中,本文讨论的单机嵌入式模式里没有它。

它让什么变难

越过内存去扩展,以及察觉自己已经越过了。HNSW 常驻内存,所以单机上的实际天花板大约在一百万向量,再往上延迟就开始显出压力;越过之后的答案是分布式 Chroma——那是一个要运维的服务,而不是一个目录。另一处锋利的边缘是压实延迟:你刚写下的一行是持久的,但未必已经被索引,这一点在 notebook 里看不出来,在一个"写完立刻搜"的智能体循环里则令人困惑。

LanceDB — 深入

LanceDB architecture LanceDB stores tables in the Lance columnar format on local disk or object storage, queried in-process over memory-mapped files. IVF-PQ vector indexes, full-text and scalar indexes sit beside the data, every write creates a new version, and Arrow-compatible tools read the same files with no copy. In-process engine Rust core, no server Python · TypeScript · Rust · Java vector search + full-text search + SQL filters = hybrid query with reranking, one call Lance columnar format — on disk or object storage memory-mapped, read column-by-column; the corpus does not have to fit in RAM Data columns text, vectors, images, audio, arbitrary blobs Indexes beside data IVF-PQ vector index + full-text + scalar Versioned every write is a new version you can read back Filters and vectors resolve against the same files no second metadata store to keep in sync — and no sync job to get wrong Zero-copy to the data stack Apache Arrow · Pandas · Polars · DuckDB read the same files directly so evaluating your retrieval set is a dataframe query, not an export The trade Scales past RAM on one machine and keeps data, metadata and index in one place — at the cost of a smaller community than the server databases, and IVF-PQ's approximation, which loses ground under tight metadata filters.
数据、元数据与索引是同一批文件——所以没有第二个存储要保持同步,也没有同步作业会出错。

它如何存储

以 Lance 列式格式,构建在 Apache Arrow 之上,存放在本地磁盘或对象存储上。这正是区分它的那次架构下注:引擎从内存映射文件里按列读取,而不是把索引攥在内存里,所以"语料大于内存"是常规情况而非故障情况。每次写入都产生一个可回读的新版本,这让"我在重新分块之前,检索是什么样子?"成为一个可回答的问题。列可以承载文本、向量、图像、音频或任意二进制块,这也是该项目围绕多模态数据做宣传的原因。

它如何搜索

默认用 IVF-PQ——倒排文件分区加乘积量化,一种压缩向量、能在普通硬件上扩展到极大集合的近似索引。向量索引旁边还有全文索引与标量索引,而查询 API 把向量检索、全文检索、SQL 式过滤与重排序组合成一次混合调用。这是四者中开箱即用最完整的检索方案,而它比裸 QPS 更要紧:在多数技术栈里,混合检索加重排序才是最大的那根质量杠杆。

它让什么变难

两件事,都很实在。IVF-PQ 是近似且量化的,所以召回是一个待调参数而非既定事实;独立基准显示,在收紧的元数据过滤下它的吞吐会下滑——那种困住多数分区型 ANN 索引的后过滤行为。另外,它的社区比 Chroma 或 Qdrant 小:教程更少、被回答过的问题更少,而且多进程并发访问有已记录的限制。买下一种磁盘原生格式,也就买下了它的生态。

横向对比

这个数据库有多少要你自己写

How much of the knowledge base you write yourself Four-column comparison of the code you must supply around each store: FAISS leaves you persistence, metadata and lifecycle; sqlite-vec gives you SQL storage but you own the scan budget; Chroma supplies storage, index and filters but no keyword ranking; LanceDB supplies storage, indexes, full-text and hybrid retrieval in one call. How much of the knowledge base you still have to write FAISS Persistence, metadata, collections, deletes — all yours. You get an index, not a database. In exchange: the most tunable ANN there is, and GPU search. sqlite-vec Storage, metadata and joins come free with SQLite; BM25 is one FTS5 table away. You own the scan cost: no ANN index, so growth is your problem. Chroma Storage, HNSW index and metadata filters are supplied. Almost nothing to assemble. You add the keyword half yourself — there is no BM25 ranking. LanceDB Storage, vector index, full-text index and reranking arrive as one hybrid query. You own the format choice and a smaller community of answers.
它们缺的东西在种类上不同:FAISS 缺的是数据库,Chroma 缺的是关键词那一半,sqlite-vec 缺的是索引。

四者构成一道干净的梯度。FAISS 交给你一个索引,把持久化、元数据、集合管理与删除完全留给你——大约四百行胶水代码,而且从此永远归你维护。sqlite-vec 从 SQLite 那里免费继承了存储、事务、联接与 BM25,于是周边代码几乎消失,但作为交换把扫描预算交到了你手上。Chroma 以最短的上手坡道供给存储、索引与过滤,然后悄悄把混合检索的关键词那一半留作习题。LanceDB 用一次调用供给得最多——向量、全文、过滤、重排序——代价是要你接受一种存储格式,以及一个更小的支持社区。

各自在哪里到头

Where each store stops being the right answer Four-column comparison of scale limits on one machine: sqlite-vec's brute-force scan fades past a few hundred thousand vectors; Chroma's memory-resident HNSW is comfortable to about a million; FAISS scales to billions but only as an index you operate; LanceDB reads from disk so corpus size is bounded by storage rather than RAM. Comfortable working range on a single machine sqlite-vec ~100k Brute-force scan is exact and simple, then linear cost catches up. Partition keys extend this when queries are per-tenant. Chroma ~1M HNSW lives in memory, so RAM sets the limit and degrades past it. Beyond that the answer is distributed Chroma, which is a service. FAISS billions IVF-PQ compression and GPU search go as far as any library goes. The ceiling is operational, not algorithmic: you are running it all yourself. LanceDB disk-bound Reads from mapped files, so the corpus may exceed RAM by a wide margin. The cost is approximation: IVF-PQ recall dips under tight metadata filters.
其中三道天花板关乎内存,只有 LanceDB 那道关乎磁盘。

这里的规模上限由"索引住在哪里"决定,而不是由代码有多快决定。sqlite-vec 的暴力扫描是线性的,所以它的天花板来得最早——数十万以内从容,而当分区键让每次查询只触及一个租户的切片时还能再往外推。Chroma 的天花板是内存:HNSW 常驻内存,机器一旦吃紧就退化,在典型硬件上大约落在一百万向量。FAISS 靠量化与 GPU 搜索在技术上走得最远,但它的天花板是运维性而非算法性的——几十亿向量意味着你现在在运营一套系统。LanceDB 是唯一一个上限取决于存储而非内存的,这正是那次列式下注的全部意义,代价则是近似召回。

新鲜度,以及它在智能体循环里的后果

四者在写入之后的那几秒里表现不同,而智能体会以批处理流水线不会有的方式察觉到这一点。sqlite-vec 没有索引要更新,所以事务一提交,行就可被搜到——四者中最强的新鲜度保证,而当一个智能体写下一条笔记随即去搜它时,这是一项被低估的优势。Chroma 会立即向它的日志确认写入,但要等压实之后才可被搜到,于是"写完就读"的循环可能落空。FAISS 需要你显式地把向量加进索引并定期重建,所以新鲜度就是你代码实现出来的样子。LanceDB 每次写入都产生版本并做增量建索引,但新加入的行在索引追上之前是靠暴力搜索被检索的——那是一种优雅降级,而不是落空。

如何选

按"数据必须放在哪里"和"数据有多少"来选——而不是按某个没跑过你那些过滤条件的基准给出的 QPS 图表。

如果这描述的是你因为
你的语料是本地磁盘上的代码、配置或一棵文档树一个都不选globgrepread 交给智能体。精确匹配、零索引维护、永不陈旧。
你的应用本来就带一个 SQLite 文件,或跑在设备端、浏览器里sqlite-vec知识库变成你本来就在备份的那个文件里的行,还免费附带 FTS5 混合检索与权限表联接。
你想今天下午就有能用的检索,之后再做决定Chroma从零到一个能用索引的最短路径,也是多数框架教程里的默认项,所以示例与你的代码对得上。
语料大于内存、或是多模态、或你想直接拿到混合检索加重排序而不用自己拼LanceDB磁盘原生的列式存储,向量、全文与标量索引都架在同一批文件上,一次查询搞定。
搜索本身就是那个难题——GPU、几十亿向量、自定义索引调参FAISS没有别的东西能给你这么多"召回—延迟—内存"三角上的掌控力。请为你将围着它写出来的那个数据库留预算。
你本来就在运维 Postgrespgvector本地知识库并不必然意味着一个新依赖。向量与业务数据同行、一次事务、一套备份方案。

有一条告诫值得直说:无论你选哪个,限制答案质量的很少是存储本身,而是解析、分块与重排序。一个选了"错的"存储却加上本地交叉编码器重排序的团队,会胜过一个选了"对的"存储却只做纯稠密 top-k 检索的团队。

常见问题

如果智能体可以直接 grep,我还需要向量数据库吗?

对代码、配置或日志来说不需要——领先的编程智能体把自己的删掉了,效果反而更好。当语料以散文为主、按语义来查询时你才需要索引,因为用户的措辞永远不会与文档的措辞重合。多数生产系统两者并存,并在它们之间做路由。

这四个里哪个最快?

这个问题的欠定程度足以让基准变得有误导性。FAISS 在裸 ANN 吞吐上取胜,在 GPU 上尤其如此。sqlite-vec 在大集合上会慢两到三个数量级,因为暴力扫描是线性的;而在小集合上它恰恰正确,因为没有索引开销。LanceDB 在大规模、驻留磁盘的语料上很快,在收紧的元数据过滤下会下滑。Chroma 在索引还装得进内存之前一直很快。请拿你自己的过滤条件与语料规模去测,否则就别测。

我能先用一个、以后再换吗?

能,而且比听上去便宜——但前提是你保留分块并重新嵌入。除非嵌入模型完全相同,否则向量在不同存储之间没有可迁移的意义,而元数据 schema 总归要重塑。请把解析、分块之后的语料以一种持久格式作为事实来源保存下来,并把索引当作可重建的派生数据。仅这一条纪律,就让每一次存储选型都可逆。

sqlite-vec 的暴力搜索是致命伤吗?

只在超过你那个规模阈值之后才是。五万个分块时,一次全扫是毫秒级的,还免调参地给出精确召回;一千万时就没法用了。当查询天然限定在一个租户或一个项目内时,分区键能把这条线大幅外推。请核对你一年后实际会有多少向量,而不是基准里有多少。

那 pgvector、Qdrant 或 Milvus Lite 呢?

都很合理;而对任何已经在跑 Postgres 的团队来说,pgvector 是诚实的默认选项——服务端市场请看我们的 pgvector、Pinecone、Weaviate 与 Qdrant 对比。本文谈的是嵌入式的情形:没有服务进程、数据即文件、检索与智能体在同一个进程内。

该给它们配哪个嵌入模型?

几乎在所有情况下都用本地的——开源权重的嵌入模型在公开榜单上是直接领先的。Qwen3-Embedding 位居多语言 MTEB 榜首,高于主流托管接口;BGE-M3 是 MIT 许可的多语言主力,一次前向即产出稠密、稀疏与多向量输出;EmbeddingGemma 或 nomic-embed-text 占用远不到 1 GB。请把版本钉死:更换嵌入模型会让你存下的每一个向量作废。

延伸阅读

本站相关:

项目来源: