ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

给 Haystack 管道装一道去重闸门:CacheChecker 增量索引实操

给 Haystack 管道装一道去重闸门:CacheChecker 增量索引实操 给 Haystack 管道装一道去重闸门CacheChecker 增量索引实操【免费下载链接】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 里的CacheChecker就是为这事准备的入库前先查一把已存在的内容直接放行。它既能独立调用也能嵌进任意处理链路。先跑个最小例子感受下行为from haystack import Document from haystack.document_stores.in_memory import InMemoryDocumentStore from haystack.components.caching import CacheChecker docstore InMemoryDocumentStore() docstore.write_documents([ Document(contentdoc1, meta{url: https://example.com/1}), Document(contentdoc2, meta{url: https://example.com/2}), Document(contentdoc3, meta{url: https://example.com/1}), ]) checker CacheChecker(docstore, cache_fieldurl) result checker.run(items[https://example.com/1, https://example.com/5]) print(result[hits], result[misses])跑完你会发现hits里拿到的是Document对象url 为/1的两条都算命中misses里拿到的还是你传进去的原始字符串/5——两个输出装的东西不一样后面接组件时要分清。两个参数怎么定构造CacheChecker只需要两个参数都很直白参数类型 / 必填作用document_storeDocumentStore必填命中查询跑在这个存储上cache_fieldstr必填拿哪个元数据键的值来判断命中document_store决定你去哪查——内存、Elasticsearch 都行只要这个实现支持 Document Store 元数据过滤。cache_field决定拿什么查它指向文档meta里的某个键。cache_field的取值是这套东西成败的关键。优先挑稳定 唯一的标识URL、文件路径、业务主键同一份内容对应的值永远不变。反例是时间戳、随机 ID——每跑一次生成新值cache_field每次都对不上旧文档缓存命中率永远是零组件等于白挂。还有个细节meta里压根没有这个键的文档天然不会命中任何items值写入端和读取端的键名必须对齐。一次 run 内部的三步判定run(items[...])内部对每个值做三步把这个值翻译成一个三段式过滤器{field: cache_field, operator: , value: 该值}拿过滤器调一次filter_documents按条件从存储里捞文档按结果分桶捞到了把捞到的文档并进hits没捞到把原值丢进misses。核心循环其实就这么点for item in items: filters {field: self.cache_field, operator: , value: item} found self.document_store.filter_documents(filtersfilters) if found: hits.extend(found) else: misses.append(item)注意这里的设计取舍组件自己一行匹配逻辑都没写过滤全在存储层发生。也就是说任何实现了filter_documents的 Document Store 都能直接拿来用组件不绑定具体存储。拿默认的InMemoryDocumentStore佐证它的filter_documents就是拿document_matches_filter在内存里逐条比对元数据haystack/document_stores/in_memory/document_store.py。这里有个坑提前说好命中不去重。多个文档共享同一个cache_field值时比如上面 doc1/doc3 同 URL它们全部进hitsitems里若带重复值同一文档会被反复并入。下游需要值 → 文档唯一映射的场景要自己拿hits按id去重一遍。工程化三件事序列化、异步与资源释放序列化。to_dict走default_to_dict把document_store递归序列化和cache_field都存进init_parametersfrom_dict走default_from_dict恢复。两个参数缺一不可缺任何一个抛TypeErrormissing 2 required positional argumentsdocument_store的type指向导入不了的模块则抛ImportError且报错里带模块名——YAML 管道里写错存储类名时这个报错能直接定位问题。异步。run_async和run语义完全一致唯一差别是把过滤调用换成filter_documents_async并对每个 item 逐次await。前提是存储层实现了这个方法源码用hasattr检查不满足直接抛TypeError: ... does not provide async support。InMemoryDocumentStore的filter_documents_async只是把同步的filter_documents丢进线程池执行所以可以直接用自建存储时这是最容易漏的一环。资源释放。close/close_async透传存储层同名方法存储不支持就静默跳过不报错。对持有远程数据库连接的存储来说管道收尾时调一下能避免连接泄漏。把 CacheChecker 塞进增量索引管道现在把闸门焊进一条完整的增量索引管道转换 → 清洗 → 拆分 → 写入cache_field取meta.file_path只有misses进处理链hits在这里被拦下。from haystack import Pipeline, Document from haystack.components.converters import TextFileToDocument from haystack.components.preprocessors import DocumentCleaner, DocumentSplitter from haystack.components.writers import DocumentWriter from haystack.components.caching import CacheChecker from haystack.document_stores.in_memory import InMemoryDocumentStore docstore InMemoryDocumentStore() pipeline Pipeline() pipeline.add_component(instanceCacheChecker(docstore, cache_fieldmeta.file_path), namecache_checker) pipeline.add_component(instanceTextFileToDocument(), nametext_file_converter) pipeline.add_component(instanceDocumentCleaner(), namecleaner) pipeline.add_component( instanceDocumentSplitter(split_bysentence, split_length250, split_overlap30), namesplitter, ) pipeline.add_component(instanceDocumentWriter(document_storedocstore), namewriter) pipeline.connect(cache_checker.misses, text_file_converter.sources) pipeline.connect(text_file_converter.documents, cleaner.documents) pipeline.connect(cleaner.documents, splitter.documents) pipeline.connect(splitter.documents, writer.documents) # 第一遍文件还没入库misses 非空处理链完整执行 pipeline.run({cache_checker: {items: [code_of_conduct_1.txt]}}) # 第二遍同一文件路径已被首次运行写入 meta.file_path全部判为命中 result pipeline.run({cache_checker: {items: [code_of_conduct_1.txt]}}) print(result[cache_checker][misses]) # []数据流走一遍就清楚了cache_checker拿meta.file_path逐个查存储命中即短路未命中进入处理链text_file_converter把未命中的文件转成Documentcleaner清洗空白字符splitter按句子切块块长 250、重叠 30writer把结果写回同一个docstore。第二次跑同一批文件时misses是空列表转换、清洗、拆分、写入全部不执行——这就是增量的来源而不是管道里多了什么跳过标志。hits输出端没人接也完全合法检查本身照样会发生。配置上盯住两点cache_field的取值必须和转换器实际写进meta的键对得上上面是meta.file_path键名对不上等于每次全 miss缓存键必须稳定。想按内容版本做缓存就把内容哈希塞进 meta 当键拿运行时间当键等于把闸门焊死在常开状态。【免费下载链接】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),仅供参考
返回列表