client-vector-search
一个客户端向量搜索库,可以进行嵌入、搜索和缓存。适用于浏览器和服务器端。
它的性能优于 OpenAI 的 text-embedding-ada-002,速度远快于 Pinecone 和其他 VectorDBs。
我是 searchbase.app 的创始人,我们的产品和客户都需要这个库。我们将在生产环境中使用这个库。您可以放心,它将得到维护和改进。
- 默认使用 transformers 进行文档嵌入:gte-small (~30mb)。
- 计算嵌入之间的余弦相似度。
- 在客户端创建索引并进行搜索。
- 支持浏览器缓存的向量缓存。
很多改进即将到来!
路线图
我们的目标是构建一个超级简单、快速的向量搜索,它可以处理几百到几千个向量。每个用户约1k个向量覆盖 99% 的使用场景。
我们最初会保持非常简单,响应时间在 100ms 以下。
待办事项
- 添加在 node 和浏览器环境下工作的 HNSW 索引,不依赖 hnsw binder 库
- 为库添加一个合适的测试套件和 CI/CD
- 简单的健康测试
- 模拟 @xenova/transformers 进行 jest 测试,目前它不兼容
- 性能测试,召回率,内存使用,CPU 使用等
- 简单的健康测试
安装
npm i client-vector-search
快速开始
该库提供了嵌入和向量搜索的即插即用解决方案。设计目标是易用、高效和多功能。以下是快速入门指南:
import { getEmbedding, EmbeddingIndex } from 'client-vector-search';
// getEmbedding 是一个异步函数,因此需要使用 'await' 或 '.then()' 获取结果
const embedding = await getEmbedding("Apple"); // 返回嵌入作为 number[] 类型
// 每个对象应有一个 'embedding' 属性,类型为 number[]
const initialObjects = [
{ id: 1, name: "Apple", embedding: embedding },
{ id: 2, name: "Banana", embedding: await getEmbedding("Banana") },
{ id: 3, name: "Cheddar", embedding: await getEmbedding("Cheddar")},
{ id: 4, name: "Space", embedding: await getEmbedding("Space")},
{ id: 5, name: "database", embedding: await getEmbedding("database")},
];
const index = new EmbeddingIndex(initialObjects); // 创建索引
// 查询应为 number[] 类型的嵌入
const queryEmbedding = await getEmbedding('Fruit'); // 查询嵌入
const results = await index.search(queryEmbedding, { topK: 5 }); // 返回最相似的对象
// 指定存储类型
await index.saveIndex('indexedDB');
const results = await index.search([1, 2, 3], {
topK: 5,
useStorage: 'indexedDB',
// storageOptions: { // 仅当你覆盖默认值时使用
// indexedDBName: 'clientVectorDB',
// indexedDBObjectStoreName: 'ClientEmbeddingStore',
// },
});
console.log(results);
await index.deleteIndexedDB(); // 如果你覆盖了默认值,指定数据库名称
疑难解答
NextJS
要在 NextJS 项目中使用它,需要更新 next.config.js
文件,包括以下内容:
module.exports = {
// 覆盖默认的 webpack 配置
webpack: (config) => {
// 参见 https://webpack.js.org/configuration/resolve/#resolvealias
config.resolve.alias = {
...config.resolve.alias,
sharp$: false,
"onnxruntime-node$": false,
};
return config;
},
};
页面加载后模型加载
可以在使用前初始化模型,以确保模型加载完毕,提高用户体验。
import { initializeModel } from "client-vector-search"
...
useEffect(() => {
try {
initializeModel();
} catch (e) {
console.log(e);
}
}, []);
使用指南
本指南提供了该库的主要功能的逐步讲解。涵盖了从为字符串生成嵌入,到在索引上进行操作(如添加、更新和删除对象)的所有内容。还包括如何将索引保存到数据库以及在其内执行搜索操作的说明。
在我们完成参考文档之前,您可以在本指南中找到所有方法及其使用方式。每个步骤都配有代码片段,说明该方法的使用方式。请务必跟随步骤,在您的环境中尝试这些示例,以更好地理解其工作原理。
让我们开始吧!
步骤 1: 为字符串生成嵌入
使用 getEmbedding
方法生成给定字符串的嵌入。
const embedding = await getEmbedding("Apple"); // 返回嵌入作为 number[] 类型
注意:
getEmbedding
是异步的,请确保使用await
。
步骤 2: 计算余弦相似度
计算两个嵌入之间的余弦相似度。
const similarity = cosineSimilarity(embedding1, embedding2, 6);
注意: 两个嵌入的长度应相同。
步骤 3: 创建索引
使用初始对象数组创建索引。每个对象必须有一个 'embedding' 属性。
const initialObjects = [...];
const index = new EmbeddingIndex(initialObjects);
步骤 4: 添加到索引
向索引中添加一个对象。
const objectToAdd = { id: 6, name: 'Cat', embedding: await getEmbedding('Cat') };
index.add(objectToAdd);
步骤 5: 更新索引
更新索引中的现有对象。
const vectorToUpdate = { id: 6, name: 'Dog', embedding: await getEmbedding('Dog') };
index.update({ id: 6 }, vectorToUpdate);
步骤 6: 从索引中删除
从索引中删除一个对象。
index.remove({ id: 6 });
步骤 7: 从索引中检索
从索引中检索一个对象。
const vector = index.get({ id: 1 });
步骤 8: 搜索索引
使用查询嵌入搜索索引。
const queryEmbedding = await getEmbedding('Fruit');
const results = await index.search(queryEmbedding, { topK: 5 });
步骤 9: 打印索引
将整个索引打印到控制台。
index.printIndex();
步骤 10: 保存索引到 IndexedDB(浏览器)
将索引保存到持久的 IndexedDB 数据库中。
await index.saveIndex("indexedDB", { DBName: "clientVectorDB", objectStoreName:"ClientEmbeddingStore"})
重要: 在 IndexedDB 中搜索
在 IndexedDB 中执行搜索操作。
const results = await index.search(queryEmbedding, {
topK: 5,
useStorage: "indexedDB",
storageOptions: { // 仅在希望覆盖默认选项时使用,默认选项如下
indexedDBName: 'clientVectorDB',
indexedDBObjectStoreName: 'ClientEmbeddingStore'
}
});
删除数据库
删除整个数据库。
await IndexedDbManager.deleteIndexedDB("clientVectorDB");
删除对象存储
从数据库中删除对象存储。
await IndexedDbManager.deleteIndexedDBObjectStore("clientVectorDB", "ClientEmbeddingStore");
检索所有对象
从特定的对象存储中检索所有对象。
const allObjects = await IndexedDbManager.getAllObjectsFromIndexedDB("clientVectorDB", "ClientEmbeddingStore");