CreateCollection 传 nil 作为 embedding 函数时默认走 OpenAI,需设置 OPENAI_API_KEY 环境变量;项目处于 beta 阶段,v1.0.0 前可能有破坏性变更;README 称更新和删除文档功能尚未实现
chromem-go
philippgilleMPL-2.0活跃维护Go 语言的可嵌入向量数据库,接口类似 Chroma,零第三方依赖,内存存储并可选持久化,方便在 Go 应用里直接做向量检索和 RAG。
Embeddable vector database for Go with Chroma-like interface and zero third-party dependencies. In-memory with optional persistence.
go get github.com/philippgille/chromem-go@latestREADME 未说明显存或 GPU 要求;基准测试在 Intel i5-1135G7 CPU 上完成,纯 CPU 即可运行
可嵌入 Go 程序的向量数据库,无需单独部署服务即可实现 RAG 和语义检索
Go 语言的可嵌入向量数据库,接口类似 Chroma,零第三方依赖,内存存储并可选持久化,方便在 Go 应用里直接做向量检索和 RAG。
核心能力
- 零第三方依赖,纯 Go 实现,可直接嵌入应用
- 提供类 Chroma 的 Collection/Add/Query 接口
- 支持多线程并发添加与查询文档
- 内置多种 embedding 提供方,如 OpenAI 和 Ollama
- 内存存储,可选持久化
容易踩的坑
- v0.7.0v0.7.0 (2024-09-01)最新正式版2024-09-01 · 2024-09-01 17:30 UTC
Highlights in this release are the possibility to export/import the DB to/from object storage like S3, a way to run a negative query and either filter or subtract the results from the regular query results, and the license change from AGPL to MPL. But many other additions, improvements and important fixes made it into this release as well, all without breaking changes:
Added
- Added
DB.ExportToWriter()to allow users to pass anyio.Writerimplementation for the DB export, not just a file. This allows for example to export the DB to AWS S3 or compatible services (like Ceph, MinIO etc.). (PR #71) - Added
DB.ImportFromReader()to allow users to pass anyio.ReadSeekerimplementation for the DB import, not just a file. This allows for example to import the DB from AWS S3 or compatible services (like Ceph, MinIO etc.). (PR #72) - Added example code for S3 export/import with the ⬆️ new methods (PR #73)
- Added Azure OpenAI compatibility (PR #74 by @iwilltry42)
- Added lint job in GitHub Action (PR #82 by @erikdubbelboer)
- Added the feature to run a negative query and either filter or subtract them from the regular query results (PR #80 by @erikdubbelboer)
- This PR also added the new
DB.QueryWithOptionsmethod and related options structs and constants for future extensibility without breaking the parameter list of the query method!
- This PR also added the new
- Added option to only import/export selected collections to/from a DB (PR #88 by @iwilltry42)
- Added Google / GCP Vertex AI embedding function (PR #91 by @iwilltry42 and #93)
- Added new embedding model constants for Jina and Mixedbread (PR #94)
- Added
Collection.GetByID()to get a document for a known ID (PR #97, for issue #95)
Improved
- Changed license from AGPL to MPL (PR #87)
- Updated
golangci-lintin CI to its latest version (PR #99) - Added Go 1.23 to the CI build matrix (PR #98)
- Improved Godoc (PR #100)
Fixed
- The
Collection.QueryEmbedding()call assumed/expected the query embedding from the parameter to be normalized already, but it wasn't documented and it's also inconvenient for users who use an embedding model/API that doesn't return normalized embeddings. Now we check whether the embedding is normalized and if it's not then we normalize it. (PR #77)- (Currently
chromem-goonly does cosine similarity, and document embeddings are already being normalized, so the query embedding has to be normalized as well. In the future we might offer other distance functions or allow to inject your own and make the normalization optional)
- (Currently
- Fixed test panic on unexpected pass (PR #78)
- Fixed out of range panic on query (PR #79)
- Fixed all
golangci-lintwarnings (PR #82 by [@erikdubbelboer](https
- Added
- v0.6.0v0.6.0 (2024-04-25)2024-04-25 · 2024-04-25 21:11 UTC
查看更新说明
Highlights in this release are an extended interface, experimental WebAssembly bindings, and the option to use a custom Ollama URL. But also the fact that three people contributed to this release! Thank you so much! 🙇♂️
Added
- Added
Collection.Delete()to delete documents from a collection (PR #63 by @iwilltry42) - Added an experimental WebAssembly binding (package
wasm) and example (PR #69)
Improved
- Use prefixes for
nomic-embed-textmodel in RAG-Wikipedia-Ollama example (PR #49, #65)- Thanks @rinor for pointing out the bug!
- Made Ollama URL configurable (PR #64 by @erikdubbelboer)
- Added building of code examples to CI (PR #66)
- Improved RAG template (PR #67)
Breaking changes
NewEmbeddingFuncOllamanow requires a second parameter for the base URL. But it can be empty to use the default which was also used in the past.
New Contributors
- @rinor made their first contribution by suggesting a fix in https://github.com/philippgille/chromem-go/pull/49
- @iwilltry42 made their first contribution in https://github.com/philippgille/chromem-go/pull/63
- @erikdubbelboer made their first contribution in https://github.com/philippgille/chromem-go/pull/64
Full Changelog: https://github.com/philippgille/chromem-go/compare/v0.5.0...v0.6.0
- Added
- v0.5.0v0.5.0 (2024-03-23)2024-03-23 · 2024-03-23 11:52 UTC
查看更新说明
Highlights in this release are query performance improvements (5x faster, 98% fewer memory allocations), export/import of the entire DB to/from a single file with optional gzip-compression and AES-GCM encryption, optional gzip-compression for the regular persistence, a new code example for semantic search across 5,000 arXiv papers, and an embedding func for Cohere.
Added
- Added arXiv semantic search example (PR #45)
- Added basic query benchmark (PR #46)
- Added unit test for collection query errors (PR #51)
- Added
Collection.QueryEmbedding()method for when you already have the embedding of your query (PR #52) - Added export and import of the entire DB to/from a single file, with optional gzip-compression and AES-GCM encryption (PR #58)
- Added optional gzip-compression to the regular persistence (i.e. the DB from
NewPersistentDB()which writes a file for each added collection and document) (PR #59) - Added minimal example (PR #60, #62)
- Added embedding func for Cohere (PR #61)
Improved
- Changed the example link target to directory instead of
main.gofile (PR #43) - Improved query performance (5x faster, 98% fewer memory allocations) (PR #47, #53, #54)
-
goos: linux goarch: amd64 pkg: github.com/philippgille/chromem-go cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz │ before │ after │ │ sec/op │ sec/op vs base │ Collection_Query_NoContent_100-8 413.69µ ± 4% 90.79µ ± 2% -78.05% (p=0.002 n=6) Collection_Query_NoContent_1000-8 2759.4µ ± 0% 518.8µ ± 1% -81.20% (p=0.002 n=6) Collection_Query_NoContent_5000-8 12.980m ± 1% 2.144m ± 1% -83.49% (p=0.002 n=6) Collection_Query_NoContent_25000-8 66.559m ± 1% 9.947m ± 2% -85.06% (p=0.002 n=6) Collection_Query_NoContent_100000-8 282.41m ± 3% 39.75m ± 1% -85.92% (p=0.002 n=6) Collection_Query_100-8 416.75µ ± 2% 90.99µ ± 1% -78.17% (p=0.002 n=6) Collection_Query_1000-8 2792.8µ ± 23% 595.2µ ± 13% -78.69% (p=0.002 n=6) Collection_Query_5000-8 15.643m ± 1% 2.556m ± 1% -83.66% (p=0.002 n=6) Collection_Query_25000-8 78.29m ± 1% 11.66m ± 1% -85.11% (p=0.002 n=6) Collection_Query_100000-8 338.54m ± 5% 39.70m ± 12% -88.27% (p=0.002 n=6) geomean 12.97m 2.192m -83.10% │ before │ after │ │ B/op │ B/op vs base │ Collection_Query_NoContent_100-8 1211.007Ki ± 0% 5.030Ki ± 0% -99.58% (p=0.002 n=6) Collection_Query_NoContent_1000-8 12082.16Ki ± 0% 13.24Ki ± 0% -99.89% (p=0.002 n=6) Collection_Query_NoContent_5000-8 60394.23Ki ± 0% 45.99Ki ± 0% -99.92% (p=0.002 n=6) Collection_Query_NoContent_25000-8 301962.1Ki ± 0% 206.7Ki ± 0% -99.93% (p=0.002
-
- v0.4.0v0.4.0 (2024-03-06)2024-03-06 · 2024-03-06 22:46 UTC
查看更新说明
Highlights in this release are optional persistence, an extended interface, support for creating embeddings with Ollama, the exporting of the
Documentstruct, and more Go-idiomatic methods to add documents to collections.Added
- Extended the interface:
DB.ListCollections()(PR #12)DB.GetCollection()(PR #13 + #19)DB.DeleteCollection()(PR #14)DB.Reset()(PR #15)DB.GetOrCreateCollection()(PR #22)Collection.Count()(PR #27)Documentstruct,NewDocument()function,Collection.AddDocument()andCollection.AddDocuments()methods (PR #34)- More Go-idiomatic alternatives to
Collection.Add()
- More Go-idiomatic alternatives to
- Added various unit tests (PR #20, #39)
- Added optional persistence! Via multiple PRs:
- Added support for creating embeddings with Ollama (PR #32)
- Added example documentation (PR #42)
Improved
- Improved example (PR #11, #28, #33)
- Stop exporting
Collection.Metadata(PR #16)- Goal: Prevent direct modifications which could cause data races in case of the user doing a modification while
chromem-gofor example ranges over it during aCollection.Query()call.
- Goal: Prevent direct modifications which could cause data races in case of the user doing a modification while
- Copy metadata in constructors (PR #17)
- Goal: Prevent direct modifications which could cause data races in case of the user doing a modification while
chromem-gofor example ranges over it.
- Goal: Prevent direct modifications which could cause data races in case of the user doing a modification while
- Improved CI (PR #18)
- Add Go 1.22 to test matrix, update used GitHub Action from v4 to v5, use race detector during tests
- Reorganize code internally (PR #21)
- Switched to newer recommended check for file related
ErrNotExisterrors (PR #29) - Added more validations in several existing methods (PR #30)
- Internal variable renamed (PR #37)
- Fail unit tests immediately (PR #40)
Fixed
- Fixed metadatas validation in
Collection.AddConcurrently()(PR #35) - Fixed Godoc of
Collection.Query()method (PR #36) - Fixed length of result slice (PR #38)
- Fixed filter test (PR #41)
Breaking changes
- Because functions can't be (de-)serialized,
GetCollectionrequires a new parameter of typeEmbeddingFunc, in order to se
- Extended the interface:
- v0.3.0v0.3.0 (2024-02-10)2024-02-10 · 2024-02-10 18:19 UTC
查看更新说明
Added
- Added support for more OpenAI embedding models (PR #6)
- Added support for more embedding creators/providers: (PR #10)
Improved
- Improve concurrency when adding documents to collection (PR #2)
- Rename
ClienttoDBto better indicate that the database is embedded and there's no client-server separation (PR #3) - Change OpenAPI embedding model from "text-embedding-ada-002" to "text-embedding-3-small" (PR #4)
- Allow custom base URL for OpenAI, enabling the use of Azure OpenAI, LiteLLM, ollama etc. (PR #7)
- Renamed
EmbeddingFuncconstructors to follow best practice (PR #9)
Fixed
- Don't allow
nResultsarg < 0 (PR #5)
Breaking changes
- Several function names and signatures were changed in this release. This can happen as long as the version is at
v0.x.y.
用户点评
登录用户的真实使用记录,可按版本和硬件筛选还没有人点评。第一个说出真实体验的人最有价值。
chromem-go
Embeddable vector database for Go with Chroma-like interface and zero third-party dependencies. In-memory with optional persistence.
Because chromem-go is embeddable it enables you to add retrieval augmented generation (RAG) and similar embeddings-based features into your Go app without having to run a separate database. Like when using SQLite instead of PostgreSQL/MySQL/etc.
It's not a library to connect to Chroma and also not a reimplementation of it in Go. It's a database on its own.
The focus is not scale (millions of documents) or number of features, but simplicity and performance for the most common use cases. On a mid-range 2020 Intel laptop CPU you can query 1,000 documents in 0.3 ms and 100,000 documents in 40 ms, with very few and small memory allocations. See Benchmarks for details.
⚠️ The project is in beta, under heavy construction, and may introduce breaking changes in releases before
v1.0.0. All changes are documented in theCHANGELOG.
Contents
- Use cases
- Interface
- Features + Roadmap
- Installation
- Usage
- Benchmarks
- Development
- Motivation
- Related projects
Use cases
With a vector database you can do various things:
- Retrieval augmented generation (RAG), question answering (Q&A)
- Text and code search
- Recommendation systems
- Classification
- Clustering
Let's look at the RAG use case in more detail:
RAG
The knowledge of large language models (LLMs) - even the ones with 30 billion, 70 billion parameters and more - is limited. They don't know anything about what happened after their training ended, they don't know anything about data they were not trained with (like your company's intranet, Jira / bug tracker, wiki or other kinds of knowledge bases), and even the data they do know they often can't reproduce it exactly, but start to hallucinate instead.
Fine-tuning an LLM can help a bit, but it's more meant to improve the LLMs reasoning about specific topics, or reproduce the style of written text or code. Fine-tuning does not add knowledge 1:1 into the model. Details are lost or mixed up. And knowledge cutoff (about anything that happened after the fine-tuning) isn't solved either.
=> A vector database can act as the up-to-date, precise knowledge for LLMs:
- You store relevant documents that you want the LLM to know in the database.
- The database stores the embeddings alongside the documents, which you can either provide or can be created by specific "embedding models" like OpenAI's
text-embedding-3-small.chromem-gocan do this for you and supports multiple embedding providers and models out-of-the-box.
- Later, when you want to talk to the LLM, you first send the question to the vector DB to find similar/related content. This is called "nearest neighbor search".
- In the question to the LLM, you provide this content alongside your question.
- The LLM can take this up-to-date precise content into account when answering.
Check out the example code to see it in action!
Interface
Our original inspiration was the Chroma interface, whose core API is the following (taken from their README):
import chromadb
# setup Chroma in-memory, for easy prototyping. Can add persistence easily!
client = chromadb.Client()
# Create collection. get_collection, get_or_create_collection, delete_collection also available!
collection = client.create_collection("all-my-documents")
# Add docs to the collection. Can also update and delete. Row-based API coming soon!
collection.add(
documents=["This is document1", "This is document2"], # we handle tokenization, embedding, and indexing automatically. You can skip that and add your own embeddings as well
metadatas=[{"source": "notion"}, {"source": "google-docs"}], # filter on these!
ids=["doc1", "doc2"], # unique for each doc
)
# Query/search 2 most similar results. You can also .get by id
results = collection.query(
query_texts=["This is a query document"],
n_results=2,
# where={"metadata_field": "is_equal_to_this"}, # optional filter
# where_document={"$contains":"search_string"} # optional filter
)
Our Go library exposes the same interface:
package main
import "github.com/philippgille/chromem-go"
func main() {
// Set up chromem-go in-memory, for easy prototyping. Can add persistence easily!
// We call it DB instead of client because there's no client-server separation. The DB is embedded.
db := chromem.NewDB()
// Create collection. GetCollection, GetOrCreateCollection, DeleteCollection also available!
collection, _ := db.CreateCollection("all-my-documents", nil, nil)
// Add docs to the collection. Update and delete will be added in the future.
// Can be multi-threaded with AddConcurrently()!
// We're showing the Chroma-like method here, but more Go-idiomatic methods are also available!
_ = collection.Add(ctx,
[]string{"doc1", "doc2"}, // unique ID for each doc
nil, // We handle embedding automatically. You can skip that and add your own embeddings as well.
[]map[string]string{{"source": "notion"}, {"source": "google-docs"}}, // Filter on these!
[]string{"This is document1", "This is document2"},
)
// Query/search 2 most similar results. You can also get by ID.
results, _ := collection.Query(ctx,
"This is a query document",
2,
map[string]string{"metadata_field": "is_equal_to_this"}, // optional filter
map[string]string{"$contains": "search_string"}, // optional filter
)
}
Initially chromem-go started with just the four core methods, but we added more over time. We intentionally don't want to cover 100% of Chroma's API surface though.
We're providing some alternative methods that are more Go-idiomatic instead.
For the full interface see the Godoc: https://pkg.go.dev/github.com/philippgille/chromem-go
Features
- Zero dependencies on third party libraries
- Embeddable (like SQLite, i.e. no client-server model, no separate DB to maintain)
- Multithreaded processing (when adding and querying documents), making use of Go's native concurrency features
- Experimental WebAssembly binding
- Embedding creators:
- Hosted:
- OpenAI (default)
- Azure OpenAI
- GCP Vertex AI
- Cohere
- Mistral
- Jina
- mixedbread.ai
- Local:
- Bring your own (implement
chromem.EmbeddingFunc) - You can also pass existing embeddings when adding documents to a collection, instead of letting
chromem-gocreate them
- Hosted:
- Similarity search:
- Exhaustive nearest neighbor search using cosine similarity (sometimes also called exact search or brute-force search or FLAT index)
- Filters:
- Document filters:
$contains,$not_contains - Metadata filters: Exact matches
- Document filters:
- Storage:
- In-memory
- Optional immediate persistence (writes one file for each added collection and document, encoded as gob, optionally gzip-compressed)
- Backups: Export and import of the entire DB to/from a single file (encoded as gob, optionally gzip-compressed and AES-GCM encrypted)
- Includes methods for generic
io.Writer/io.Readerso you can plug S3 buckets and other blob storage, see examples/s3-export-import for example code
- Includes methods for generic
- Data types:
- Documents (text)
Roadmap
- Performance:
- Use SIMD for dot product calculation on supported CPUs (draft PR: #48)
- Add roaring bitmaps to speed up full text filtering
- Embedding creators:
- Add an
EmbeddingFuncthat downloads and shells out to llamafile
- Add an
- Similarity search:
- Approximate nearest neighbor search with index (ANN)
- Hierarchical Navigable Small World (HNSW)
- Inverted file flat (IVFFlat)
- Approximate nearest neighbor search with index (ANN)
- Filters:
- Operators (
$and,$oretc.)
- Operators (
- Storage:
- JSON as second encoding format
- Write-ahead log (WAL) as second file format
- Optional remote storage (S3, PostgreSQL, ...)
- Data types:
- Images
- Videos
Installation
go get github.com/philippgille/chromem-go@latest
Usage
See the Godoc for a reference: https://pkg.go.dev/github.com/philippgille/chromem-go
For full, working examples, using the vector database for retrieval augmented generation (RAG) and semantic search and using either OpenAI or locally running the embeddings model and LLM (in Ollama), see the example code.
Quickstart
This is taken from the "minimal" example:
package main
import (
"context"
"fmt"
"runtime"
"github.com/philippgille/chromem-go"
)
func main() {
ctx := context.Background()
db := chromem.NewDB()
// Passing nil as embedding function leads to OpenAI being used and requires
// "OPENAI_API_KEY" env var to be set. Other providers are supported as well.
// For example pass `chromem.NewEmbeddingFuncOllama(...)` to use Ollama.
c, err := db.CreateCollection("knowledge-base", nil, nil)
if err != nil {
panic(err)
}
err = c.AddDocuments(ctx, []chromem.Document{
{
ID: "1",
Content: "The sky is blue because of Rayleigh scattering.",
},
{
ID: "2",
Content: "Leaves are green because chlorophyll absorbs red and blue light.",
},
}, runtime.NumCPU())
if err != nil {
panic(err)
}
res, err := c.Query(ctx, "Why is the sky blue?", 1, nil, nil)
if err != nil {
panic(err)
}
fmt.Printf("ID: %v\nSimilarity: %v\nContent: %v\n", res[0].ID, res[0].Similarity, res[0].Content)
}
Output:
ID: 1
Similarity: 0.6833369
Content: The sky is blue because of Rayleigh scattering.
Benchmarks
Benchmarked on 2024-03-17 with:
- Computer: Framework Laptop 13 (first generation, 2021)
- CPU: 11th Gen Intel Core i5-1135G7 (2020)
- Memory: 32 GB
- OS: Fedora Linux 39
- Kernel: 6.7
$ go test -benchmem -run=^$ -bench .
goos: linux
goarch: amd64
pkg: github.com/philippgille/chromem-go
cpu: 11th Gen Intel(R) Core(TM) i5-1135G7 @ 2.40GHz
BenchmarkCollection_Query_NoContent_100-8 13164 90276 ns/op 5176 B/op 95 allocs/op
BenchmarkCollection_Query_NoContent_1000-8 2142 520261 ns/op 13558 B/op 141 allocs/op
BenchmarkCollection_Query_NoContent_5000-8 561 2150354 ns/op 47096 B/op 173 allocs/op
BenchmarkCollection_Query_NoContent_25000-8 120 9890177 ns/op 211783 B/op 208 allocs/op
BenchmarkCollection_Query_NoContent_100000-8 30 39574238 ns/op 810370 B/op 232 allocs/op
BenchmarkCollection_Query_100-8 13225 91058 ns/op 5177 B/op 95 allocs/op
BenchmarkCollection_Query_1000-8 2226 519693 ns/op 13552 B/op 140 allocs/op
BenchmarkCollection_Query_5000-8 550 2128121 ns/op 47108 B/op 173 allocs/op
BenchmarkCollection_Query_25000-8 100 10063260 ns/op 211705 B/op 205 allocs/op
BenchmarkCollection_Query_100000-8 30 39404005 ns/op 810295 B/op 229 allocs/op
PASS
ok github.com/philippgille/chromem-go 28.402s
Development
- Build:
go build ./... - Test:
go test -v -race -count 1 ./... - Benchmark:
go test -benchmem -run=^$ -bench .(add> bench.outor similar to write to a file)- With profiling:
go test -benchmem -run ^$ -cpuprofile cpu.out -bench .- (profiles:
-cpuprofile,-memprofile,-blockprofile,-mutexprofile)
- (profiles:
- Compare benchmarks:
- Install
benchstat:go install golang.org/x/perf/cmd/benchstat@latest - Compare two benchmark results:
benchstat before.out after.out
- Install
Motivation
In December 2023, when I wanted to play around with retrieval augmented generation (RAG) in a Go program, I looked for a vector database that could be embedded in the Go program, just like you would embed SQLite in order to not require any separate DB setup and maintenance. I was surprised when I didn't find any, given the abundance of embedded key-value stores in the Go ecosystem.
At the time most of the popular vector databases like Pinecone, Qdrant, Milvus, Chroma, Weaviate and others were not embeddable at all or only in Python or JavaScript/TypeScript.
Then I found @eliben's blog post and example code which showed that with very little Go code you could create a very basic PoC of a vector database.
That's when I decided to build my own vector database, embeddable in Go, inspired by the ChromaDB interface. ChromaDB stood out for being embeddable (in Python), and by showing its core API in 4 commands on their README and on the landing page of their website.
Related projects
- Shoutout to @eliben whose blog post and example code inspired me to start this project!
- Chroma: Looking at Pinecone, Qdrant, Milvus, Weaviate and others, Chroma stood out by showing its core API in 4 commands on their README and on the landing page of their website. It was also putting the most emphasis on its embeddability (in Python).
- The big, full-fledged client-server-based vector databases for maximum scale and performance:
- Some non-specialized SQL, NoSQL and Key-Value databases added support for storing vectors and (some of them) querying based on similarity:
- pgvector extension for PostgreSQL: Client-server model
- Redis (1, 2): Client-server model
- sqlite-vss extension for SQLite: Embedded, but the Go bindings require CGO. There's a CGO-free Go library for SQLite, but then it's without the vector search extension.
- DuckDB has a function to calculate cosine similarity (1): Embedded, but the Go bindings use CGO
- MongoDB's cloud platform offers a vector search product (1): Client-server model
- Some libraries for vector similarity search:
- Some orchestration libraries, inspired by the Python library LangChain, but with no or only rudimentary embedded vector DB: