为指定的 Datasette 表维护一个 FAISS 索引
有关此项目的背景信息,请参阅语义搜索答案:使用 GPT3 + OpenAI 嵌入对文档进行问答。
在 Datasette 所在的同一环境中安装此插件。
datasette install datasette-faiss此插件在启动时为指定的表创建内存中的 FAISS 索引,使用 IndexFlatL2 FAISS 索引类型。
如果表在服务器启动后被修改,索引尚不会反映这些更改。
要建立索引的表必须包含 id 和 embedding 列。embedding 列必须是一个 blob,其中包含已使用以下 Python 函数编码的浮点数数组形式的嵌入向量
def encode(vector):
    return struct.pack("f" * len(vector), *vector)您可以像这样从该包导入该函数
from datasette_faiss import encode您可以通过将此内容添加到 metadata.json 来指定哪些表应为其创建索引
{
    "plugins": {
        "datasette-faiss": {
            "tables": [
                ["blog", "embeddings"]
            ]
        }
    }
}每个表都是一个数组,列出了数据库名称和表名称。
如果您使用 metadata.yml,配置应如下所示
plugins:
  datasette-faiss:
    tables:
    - ["blog", "embeddings"]此插件在 Datasette 中提供了四个新的 SQL 函数
返回在指定的数据库和表中找到的、距离给定 embedding 最近的 k 个邻居。例如
select faiss_search('blog', 'embeddings', (select embedding from embeddings where id = 3), 5)这将返回一个 JSON 数组,包含 blog 数据库中 embeddings 表中距离指定嵌入向量最近的五个记录的 ID。返回的值如下所示
["1", "1249", "1011", "5", "10"]您可以使用 SQLite 的 json_each() 函数将其转换为类似表的序列,以便进行连接(JOIN)操作。
以下是一个执行此操作的示例查询
with related as (
  select value from json_each(
    faiss_search(
      'blog',
      'embeddings',
      (select embedding from embeddings limit 1),
      5
    )
  )
)
select * from blog_entry, related
where id = value接受与上述函数相同的参数,但返回值是一个 JSON 对数组,每对包含一个 ID 和一个分数 - 示例如下
[
    ["1", 0.0],
    ["1249", 0.21042244136333466],
    ["1011", 0.29391372203826904],
    ["5", 0.29505783319473267],
    ["10", 0.31554925441741943]
]给定一个浮点数的 JSON 数组,返回可用于其他函数的二进制嵌入向量 blob
select faiss_encode('[2.4, 4.1, 1.8]')
-- Returns a 12 byte blob
select hex(faiss_encode('[2.4, 4.1, 1.8]'))
-- Returns 9A991940333383406666E63F与 faiss_encode() 功能相反。
select faiss_decode(X'9A991940333383406666E63F')返回结果
[2.4000000953674316, 4.099999904632568, 1.7999999523162842]请注意,浮点运算的结果在往返转换时可能与预期值不完全一致。
此聚合函数可用于查找表中每个唯一的 id 值对应的、距离 compare_embedding 最近的 k 个邻居。例如
select faiss_agg(
    id, embedding, (select embedding from embeddings where id = 3), 5
) from embeddings与 faiss_search() 函数不同,此函数不依赖于插件首次运行时创建的每表索引。相反,每次运行聚合函数时都会构建一个索引。
这意味着它只适用于较小的数据集 - 一旦超过 10,000 个左右,此函数的性能可能会变得非常昂贵,难以接受。
该函数返回一个 JSON 数组,包含距离得分最近的 k 行的 ID,如下所示
[1324, 344, 5562, 553, 2534]您可以使用 json_each() 函数将其转换为类似表的序列,以便进行连接(JOIN)操作。
这类似于 faiss_agg() 聚合函数,但它返回一个对列表,每对包含一个 ID 和相应的分数 - 如果 k 是 2,看起来像这样
[[2412, 0.25], [1245, 24.25]]尝试一个 fais_agg_with_scores() 示例查询.
要在本地设置此插件,首先检出代码。然后创建一个新的虚拟环境
cd datasette-faiss
python3 -m venv venv
source venv/bin/activate现在安装依赖和测试依赖
pip install -e '.[test]'运行测试
pytest