ARTICLE DETAIL

资讯详情

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

Haystack InMemoryDocumentStore 完整 API 指南:内存文档存储的 BM25 检索、向量召回与序列化实战

Haystack InMemoryDocumentStore 完整 API 指南:内存文档存储的 BM25 检索、向量召回与序列化实战 Haystack InMemoryDocumentStore 完整 API 指南内存文档存储的 BM25 检索、向量召回与序列化实战【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack本文以 Haystack 2.23 版本 API 文档中 Document Stores 一章为骨架系统讲解InMemoryDocumentStore的初始化参数、文档读写、元数据过滤、BM25 稀疏检索、嵌入向量召回、异步 API 以及磁盘序列化等全部能力。读完本文你将掌握如何用这一个零依赖的组件搭建 RAG 检索索引、在 Pipeline 中接入InMemoryBM25Retriever与InMemoryEmbeddingRetriever并能根据检索场景调优 BM25 参数与相似度函数。一、模块概览haystack.document_stores.in_memory中的 Document StoreInMemoryDocumentStore是 Haystack 官方提供的最简单的 Document Store 实现Stores data in-memory. Its ephemeral and cannot be saved to disk.数据存于内存生命周期短暂本就不能直接落盘。它不依赖任何外部服务或数据库适合快速实验、单元测试与原型验证官方在 InMemoryDocumentStore 概念文档 中同样明确指出其“great for experimenting with Haystack, however we do not recommend using it for production”。该模块的完整源码位于 haystack/document_stores/in_memory/document_store.py其中除了InMemoryDocumentStore主类外还定义了BM25DocumentStats数据类与若干进程级全局存储。在 Haystack 中Document Store 是文档仓库它统一承担文档的写入、删除、过滤与两类检索原语——BM25 稀疏检索bm25_retrieval与嵌入向量检索embedding_retrieval并在上层由 Retriever 组件封装后接入 PipelineInMemoryBM25Retriever基于关键词的 BM25 检索器InMemoryEmbeddingRetriever对比查询与文档的嵌入向量返回最相关文档。BM25DocumentStatsBM25 统计辅助数据类dataclass class BM25DocumentStats: freq_token: dict[str, int] # 文档内各 token 的频次统计Counter doc_len: int # 文档的 token 总数该数据类用于承载 BM25 评分所需的文档级统计信息freq_token是文档内容的 token 频次表doc_len是文档的 token 长度。二者在写入文档时由_tokenize_bm25计算并维护是 BM25 系列算法计算词频TF与文档长度归一化的基础。二、初始化从零配置到精细调优2.1 构造签名InMemoryDocumentStore.__init__的完整签名如下对应 API 文档中的 InMemoryDocumentStore.init一节def __init__(bm25_tokenization_regex: str r(?u)\b\w\w\b, bm25_algorithm: Literal[BM25Okapi, BM25L, BM25Plus] BM25L, bm25_parameters: dict | None None, embedding_similarity_function: Literal[dot_product, cosine] dot_product, index: str | None None, async_executor: ThreadPoolExecutor | None None, return_embedding: bool True)说明API 文档中记录的bm25_tokenization_regex默认值为r(?u)\b\w\w\b。在仓库当前源码document_store.py中该默认值已演进为r(?u)\b\w\b即允许单个字符 token见test_bm25_retrieval_with_single_char_query相关测试其余参数语义与 API 文档完全一致。本文参数说明以 API 文档为准并标注源码中的最新默认值。2.2 参数详解与调优要点参数默认值含义与建议bm25_tokenization_regexr(?u)\b\w\w\b源码中为r(?u)\b\w\bBM25 检索时用于切分文本 token 的正则表达式。源码中通过re.compile(bm25_tokenization_regex).findall构建 tokenizerdocument_store.py并在_tokenize_bm25中先统一lower()再切分。若文档为中文等无空格语言可改为适合目标语言的分词正则。bm25_algorithmBM25L可选的 BM25 变体BM25Okapi、BM25L、BM25Plus。源码通过_dispatch_bm25将字符串映射到对应的_score_bm25okapi/_score_bm25l/_score_bm25plus评分函数document_store.py传入非法值会抛出ValueError。BM25L 与 BM25Plus 专为长文档设计可缓解 BM25Okapi 对长文档打分偏低的问题。bm25_parametersNone传给 BM25 实现的参数字典例如{k1:1.5, b:0.75, epsilon:0.25}。三个算法读取的参数略有差异BM25Okapi 读取k1、b、epsilonBM25L 读取k1、b、delta默认 0.5BM25Plus 读取k1、b、delta默认 1.0。k1控制词频饱和程度b控制文档长度归一化的强度epsilon/delta用于负 IDF 平滑与打分偏移。API 文档同时指出可参考 rank_bm25 库了解这些参数的理论细节。embedding_similarity_functiondot_product比较文档嵌入的相似度函数二选一dot_product默认或cosine。应根据你所使用的嵌入模型选择若模型训练时使用点积如 OpenAI 的 text-embedding-ada-002 建议 cosine请查阅模型文档确认。源码在_compute_query_embedding_similarity_scores中实现cosine 模式下先对向量做 L2 归一化再点积并专门防护零向量导致的 NaNdocument_store.py。indexNone随机 UUID指定存储文档的索引名。不指定时自动生成随机 UUID多个InMemoryDocumentStore实例使用相同 index即可共享同一份文档数据源码中共享模式下文档存于以 index 为键的进程级全局字典_STORAGES。async_executorNone可选的ThreadPoolExecutor用于异步调用。不提供时源码会自动创建一个单线程 executormax_workers1线程名前缀async-inmemory-docstore-executor-*并在实例销毁时由其回收。return_embeddingTrue是否在检索结果中返回文档的 embedding。默认True置为False可减少返回数据的体积。写入、过滤、检索等多个方法都会尊重该开关。2.3 shared 模式与存储生命周期从源码结构看InMemoryDocumentStore存在两种数据存放方式document_store.pysharedTrue默认文档存放在模块级全局字典_STORAGES中以index为键共享同一 index 的多个实例操作同一份数据数据生命周期与进程相同sharedFalse数据存放在实例自身的_local_storage等局部字典中实例被垃圾回收后数据随之释放适合高频创建如每个请求一个 store的场景以避免进程内内存无限增长。该参数在 API 文档的__init__列表中未展开但在当前源码中已作为正式参数存在to_dict也会将其序列化。三、文档的增删改查与元数据操作3.1 写入write_documents与DuplicatePolicydef write_documents(documents: list[Document], policy: DuplicatePolicy DuplicatePolicy.NONE) - int将文档列表写入 store返回实际写入的文档数量。若传入的不是Document可迭代对象会抛出ValueError。policy用于处理 ID 冲突取自 haystack/document_stores/types/policy.py 中的枚举policy行为DuplicatePolicy.NONEAPI 文档明确若设为NONE实际默认回退为DuplicatePolicy.FAIL即遇到重复 ID 直接报错DuplicatePolicy.FAIL遇到重复 ID 抛出DuplicateDocumentErrorID ... already exists.DuplicatePolicy.SKIP跳过重复文档并记录 warning计入未写入数量DuplicatePolicy.OVERWRITE覆盖旧文档写入时源码会同步增量维护 BM25 统计先delete_documents回滚旧统计再以_tokenize_bm25计算 token、生成BM25DocumentStats、更新_freq_vocab_for_idf词表与_avg_doc_len平均文档长度document_store.py这些统计量是 BM25 检索 IDF/TF 计算的基础。3.2 过滤读取filter_documentsdef filter_documents(filters: dict[str, Any] | None None) - list[Document]返回满足过滤器条件的文档列表filters为None时返回全部文档。过滤器的详细语法规范见DocumentStore.filter_documents()协议文档即 haystack/document_stores/types/protocol.py 中定义的协议。过滤表达式的骨架必须包含field、operator、value或使用operatorconditions组合否则抛出ValueError# 单条件 {field: meta.year, operator: , value: 2020} # 组合条件 {operator: AND, conditions: [ {field: meta.year, operator: , value: 2020}, {field: meta.type, operator: , value: article}, ]}底层由 haystack/utils/filters.py 的document_matches_filter逐文档判定strict_datetime_comparison为False默认时比较 datetime 会把无时区值补齐为有时区再比较。若初始化时return_embeddingFalse过滤结果中的 embedding 会被置空。3.3 删除与统计delete_documents(document_ids: list[str])按 ID 列表删除文档不存在的 ID 静默跳过并同步回滚 BM25 统计词表、平均长度等。count_documents() - int返回 store 中文档总数。从源码结构看该类还实现了delete_all_documents、update_by_filter按过滤条件批量合并元数据并返回更新条数、delete_by_filter、count_documents_by_filter、count_unique_metadata_by_filter、get_metadata_fields_info、get_metadata_field_min_max、get_metadata_field_unique_values等元数据管理方法其中后两者支持字段名带或不带meta.前缀且对唯一值查询支持search_term子串过滤与from_/size分页。API 文档收录的核心方法计数、过滤、写入、删除、两类检索均在此基础上展开。3.4 生命周期管理__del__与shutdownInMemoryDocumentStore若自行创建了 executor会在实例被销毁时__del__自动shutdown它shutdown()则提供显式回收入口仅在 store 拥有 executor 时生效。测试中的 fixture 也遵循用完store.shutdown()的模式见 test/document_stores/test_in_memory.py。四、BM25 稀疏检索bm25_retrieval4.1 签名与参数def bm25_retrieval(query: str, filters: dict[str, Any] | None None, top_k: int 10, scale_score: bool False) - list[Document]使用 BM25 算法返回与查询最相关的文档。参数语义query查询字符串必须为非空否则抛出ValueError(Query should be a non-empty string)测试test_bm25_retrieval_empty_query验证了这一点filters用于缩小搜索空间的过滤字典top_k返回的最相关文档数量默认 10scale_score是否将分数缩放到 0~1 区间默认False。4.2 内部评分机制从源码看bm25_retrieval的执行链路为document_store.py自动附加一个{field: content, operator: !, value: None}条件保证只对含文本内容的文档评分用户提供的 filters 会与该条件以AND组合先经filter_documents缩小候选集再交给初始化时选择的算法函数_score_bm25okapi/_score_bm25l/_score_bm25plus打分按分数降序取前top_k个若scale_scoreTrue用expit(score / BM25_SCALING_FACTOR)BM25_SCALING_FACTOR8源码顶部注释解释了该常数是经验选择分数普遍大于 30 时增大该值可避免全部被映射到接近 1把无界分数压到 0~1未缩放时BM25Okapi允许返回有意义的负分negatives_are_valid逻辑而其余算法会把score 0的结果剔除。4.3 调优示例from haystack.document_stores.in_memory import InMemoryDocumentStore store InMemoryDocumentStore( bm25_algorithmBM25L, bm25_parameters{k1: 1.5, b: 0.75, delta: 0.5}, ) results store.bm25_retrieval(queryHow to build a RAG pipeline, top_k5, scale_scoreTrue) for doc in results: print(doc.id, doc.score, doc.content[:50])test_bm25_retrieval_with_scale_score等测试test/document_stores/test_in_memory.py验证了缩放开关与BM25Okapi负分行为scale_scoreFalse时 Okapi 保留负分结果scale_scoreTrue时分数归一化到 0~1。五、向量召回embedding_retrieval5.1 签名与参数def embedding_retrieval(query_embedding: list[float], filters: dict[str, Any] | None None, top_k: int 10, scale_score: bool False, return_embedding: bool | None False) - list[Document]使用向量相似度度量返回与查询嵌入最相似的文档query_embedding查询的嵌入向量必须是非空 float 列表否则抛出ValueErrorfilters缩小搜索空间的条件top_k返回数量默认 10scale_score是否缩放分数默认Falsereturn_embedding是否在结果中携带 embedding。若传None则回退使用初始化时的return_embedding值方法签名默认值为False但None语义才是使用组件初始化设置见 API 文档说明。5.2 相似度计算与分数缩放核心逻辑位于_compute_query_embedding_similarity_scoresdocument_store.pydot_product默认直接计算查询嵌入与文档嵌入矩阵的点积cosine先对查询与文档向量做 L2 归一化再点积且用np.where(norm 0, 1.0, norm)防护零向量除零导致的 NaN对应测试test_embedding_retrieval_with_zero_vector_does_not_produce_nan维度不一致时会抛出DocumentStoreError提示所有文档必须由同一模型嵌入、查询与文档嵌入维度必须一致缩放策略因相似度函数而异dot_product用expit(score / DOT_PRODUCT_SCALING_FACTOR)DOT_PRODUCT_SCALING_FACTOR100cosine用(score 1) / 2映射到 0~1。store InMemoryDocumentStore(embedding_similarity_functioncosine) # 假设 query_embedding 来自与文档相同的嵌入模型 results store.embedding_retrieval( query_embedding[0.1, 0.2, 0.3, 0.4], filters{field: meta.category, operator: , value: tech}, top_k3, scale_scoreTrue, )检索前还需注意没有 embedding 的文档会被跳过不足时打 warning 并提示使用DocumentEmbedder生成嵌入全部文档都无 embedding 时返回空列表源码中的logger.warning分支。六、异步 API与 async Pipeline 无缝协作InMemoryDocumentStore为全部核心操作提供了_async后缀的协程版本签名与同步版一一对应count_documents_async() - intfilter_documents_async(filtersNone) - list[Document]write_documents_async(documents, policyDuplicatePolicy.NONE) - intNONE同样回退为FAILdelete_documents_async(document_ids) - Nonebm25_retrieval_async(query, filtersNone, top_k10, scale_scoreFalse) - list[Document]embedding_retrieval_async(query_embedding, filtersNone, top_k10, scale_scoreFalse, return_embeddingFalse) - list[Document]实现机制上异步方法通过asyncio.get_running_loop().run_in_executor(self.executor, ...)把阻塞操作投递到ThreadPoolExecutor中执行document_store.py从而在不阻塞事件循环的前提下复用同步实现。异步测试如test_bm25_retrieval_async、test_embedding_retrieval_async位于 test/document_stores/test_in_memory.py。七、序列化与磁盘持久化7.1to_dict/from_dictPipeline 序列化基石def to_dict() - dict[str, Any] # 序列化为字典 classmethod def from_dict(cls, data: dict[str, Any]) - InMemoryDocumentStore # 从字典反序列化to_dict使用default_to_dict导出初始化参数含bm25_tokenization_regex、bm25_algorithm、bm25_parameters、embedding_similarity_function、index、shared、return_embedding、strict_datetime_comparison结果带有完整的type: haystack.document_stores.in_memory.document_store.InMemoryDocumentStore类型标识from_dict经default_from_dict还原实例。测试test_to_dicttest/document_stores/test_in_memory.py验证了这一格式。这两个方法让 Document Store 可以被嵌入 YAML/JSON 描述的 Pipeline 中实现整体保存与恢复。7.2save_to_disk/load_from_disk手动快照def save_to_disk(path: str) - None classmethod def load_from_disk(cls, path: str) - InMemoryDocumentStore虽然 store 本身ephemeral and cannot be saved to disk但你可以通过这两个方法手动持久化与恢复数据save_to_disk(path)把to_dict()结果与全部文档doc.to_dict(flattenFalse)一并写入指定的 JSON 文件load_from_disk(path)读取 JSON先反序列化配置再以DuplicatePolicy.OVERWRITE策略批量写回文档文件不存在时抛出FileNotFoundError读取异常时抛出DocumentStoreError。store.save_to_disk(my_index.json) restored InMemoryDocumentStore.load_from_disk(my_index.json) print(restored.count_documents())八、在 Pipeline 中使用最小 RAG 检索示例结合 InMemoryDocumentStore 概念文档 与 Retriever 组件一个完整的检索链路如下from haystack import Document, Pipeline from haystack.components.retrievers.in_memory import InMemoryBM25Retriever from haystack.document_stores.in_memory import InMemoryDocumentStore document_store InMemoryDocumentStore(bm25_algorithmBM25L) document_store.write_documents([ Document(contentHaystack is an open-source AI orchestration framework., meta{topic: haystack}), Document(contentInMemoryDocumentStore stores documents in memory for fast experimentation., meta{topic: docstore}), ]) pipe Pipeline() pipe.add_component(retriever, InMemoryBM25Retriever(document_storedocument_store)) result pipe.run({retriever: {query: What stores documents in memory?, top_k: 3}}) for doc in result[retriever][documents]: print(doc.content, doc.score)若需向量召回可用InMemoryEmbeddingRetriever配合TextEmbedder/DocumentEmbedder先为文档写入 embedding再按查询嵌入检索——此时应保证查询与文档由同一嵌入模型生成并将embedding_similarity_function与模型文档建议的相似度函数对齐。九、小结与适用边界InMemoryDocumentStore以零外部依赖的方式完整覆盖了 RAG 检索所需的核心能力基于正则与 BM25L/Okapi/Plus 的可调稀疏检索、基于点积/余弦的向量召回、细粒度的元数据过滤、覆盖全 API 的异步版本以及字典/JSON 序列化。从源码haystack/document_stores/in_memory/document_store.py与测试test/document_stores/test_in_memory.py可以看出其内部实现注重正确性与边界处理——包括零向量 NaN 防护、空语料除零防护、BM25 统计的增量维护与回滚、datetime 严格比较开关等。适用边界同样清晰数据驻留内存、进程结束即消失官方不推荐用于生产。当你需要持久化、分布式或大规模检索时应切换到仓库 文档存储集成列表 中的其他实现如 Elasticsearch、Qdrant、Weaviate 等而在原型验证、CI 测试与本地实验场景下InMemoryDocumentStore是启动最快、心智负担最低的选择。【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表