ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Feast RAG 实战:用 Docling 解析 PDF 并以 Milvus 向量检索构建 LLM 上下文

2026/9/17 20:49:18 拓冰建站 浏览量
Feast RAG 实战:用 Docling 解析 PDF 并以 Milvus 向量检索构建 LLM 上下文 Feast RAG 实战用 Docling 解析 PDF 并以 Milvus 向量检索构建 LLM 上下文【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast本文基于 Feast 官方示例examples/rag-docling讲解如何用 Feast 支撑一个完整的 RAGRetrieval-Augmented Generation检索增强生成应用先用 Docling 把 PDF 转成可被 LLM 使用的文本块与向量再以 MilvusMilvus Lite作为向量在线存储通过 Feast 的声明式特性定义实现写入时转换 相似度检索最终把检索结果注入 LLM 提示词。读完本文你可以掌握 Feast 向量索引字段的声明方式、on_demand_feature_view的文档转换机制以及retrieve_online_documents_v2的检索用法。为什么选择 Feast 来做 RAG示例的 README 给出了使用 Feast 做 RAG 的五点理由这也是本示例的技术定位在线检索特征实时访问预计算好的文档嵌入embedding和其他结构化数据声明式特性定义在一个 Python 文件中定义 Feature View 与 Entity让数据科学家以 Feast 的标准工作流交付可扩展的 RAG 应用向量搜索借助 Feast 对 Milvus 等向量数据库的集成按相似度度量如余弦检索相关文档结构化与非结构化上下文混合既检索嵌入也检索传统结构化字段为 LLM 提示词注入更丰富的上下文版本化与可复用跨团队协作共享可发现、可版本化的特征转换逻辑。项目结构与数据准备示例目录结构如下见 README 的 Project Structure 一节data/演示数据。README 特别提示docling_samples.parquet需要运行 docling-demo.ipynb 自行构建metadata_samples.parquet则已随仓库提供位于 feature_repo/dataexample_repo.py定义 Feast 的 Feature View 与 Entityfeature_store.yaml配置离线/在线存储本演示使用本地文件 Milvus Lite。两个 notebook 各司其职docling-demo.ipynb用 Docling 从 PDF 抽取文本并落成 Parquet 文件docling-quickstart.ipynb用 Feast 摄入文本数据并向在线存储写入与检索。docling-demo.ipynb 的完整流程该 notebook 完成了从原始 PDF 到可摄入 Feast 的 Parquet 表的 ETL 链路下载一批测试 PDF来自 Docling 项目的公开测试集配置PdfPipelineOptions并开启generate_page_images构建分块与嵌入组件使用sentence-transformers/all-MiniLM-L6-v2模型输出 384 维向量配合AutoTokenizer与HybridChunker(tokenizer..., max_tokens64, merge_peersTrue)——64 的 token 上限是演示用途的小值生产中可放宽转换与分块DocumentConverter.convert_all()批量转换 PDF对每个成功转换的文档用chunker.chunk()分块、chunker.serialize()序列化为 Markdown 文本再对每个 chunk 生成归一化嵌入normalize_embeddingsTrue生成稳定主键generate_chunk_id()对file_name raw_chunk_markdown做 SHA-256作为 chunk 的唯一 ID落盘chunk 表含vector、chunk_id、created时间戳列写入feature_repo/data/docling_samples.parquet原始 PDF 字节表写入feature_repo/data/metadata_samples.parquet。这份 Parquet 表正是后面 FeastFileSource的数据来源而 PDF 字节表则用于演示写入时现场转换。声明式特性定义example_repo.py 逐段解析example_repo.py 是本示例的核心展示了 Feast 中文档 → 特征的两类声明方式。全局组件嵌入模型、分块器与 Entity文件头部加载了与 notebook 相同的 tokenizer、嵌入模型和HybridChunker注意这里在模块导入时即完成加载说明转换逻辑会在 Feast 注册/运行环境中执行EMBED_MODEL_ID sentence-transformers/all-MiniLM-L6-v2 MAX_TOKENS 64 # 演示用的小 token 上限 tokenizer AutoTokenizer.from_pretrained(EMBED_MODEL_ID) embedding_model SentenceTransformer(EMBED_MODEL_ID) chunker HybridChunker(tokenizertokenizer, max_tokensMAX_TOKENS, merge_peersTrue)并定义了两个实体与两个数据来源chunk Entity( namechunk_id, value_typeValueType.STRING, join_keys[chunk_id], ) document Entity( namedocument_id, value_typeValueType.STRING, join_keys[document_id], ) source FileSource( file_formatParquetFormat(), path./data/docling_samples.parquet, timestamp_fieldcreated, ) input_request_pdf RequestSource( namepdf_request_source, schema[ Field(namedocument_id, dtypeString), Field(namepdf_bytes, dtypePdfBytes), Field(namefile_name, dtypeString), ], )两个值得注意的点chunk与document是不同粒度的实体——chunk 表以chunk_id为主键而按需转换的文档表以document_id为主键一个文档转换后产生多个 chunk 行RequestSource中的PdfBytes是 Feast 类型系统的一等类型从源码 sdk/python/feast/types.py 可以看到PdfBytes PrimitiveFeastType.PDF_BYTES的定义即 Feast 原生支持把 PDF 二进制当作特征字段传递这是写入时转换能够跑通的基础。FeatureView预计算好的文档嵌入docling_example_feature_view FeatureView( namedocling_feature_view, entities[chunk], schema[ Field(namefile_name, dtypeString), Field(nameraw_chunk_markdown, dtypeString), Field( namevector, dtypeArray(Float64), vector_indexTrue, # 声明该字段为向量索引 vector_search_metricCOSINE, # 检索度量余弦 ), Field(namechunk_id, dtypeString), ], sourcesource, ttltimedelta(hours2), )vector_indexTrue让 Feast 在创建在线存储 collection 时为vector字段建立向量索引vector_search_metricCOSINE声明该视图默认使用余弦相似度检索。ttltimedelta(hours2)控制在线存储中数据的存活时间。OnDemandFeatureView用 Docling 在写入时转换 PDFon_demand_feature_view( entities[chunk, document], sources[input_request_pdf], schema[ Field(namedocument_id, dtypeString), Field(namechunk_id, dtypeString), Field(namechunk_text, dtypeString), Field( namevector, dtypeArray(Float64), vector_indexTrue, vector_search_metricL2, # 注意这里声明为 L2 ), ], modepython, write_to_online_storeTrue, singletonTrue, ) def docling_transform_docs(inputs: dict[str, Any]): document_ids, chunks, embeddings, chunk_ids [], [], [], [] buf io.BytesIO(inputs[pdf_bytes]) doc_source DocumentStream(nameinputs[file_name], streambuf) converter DocumentConverter() result converter.convert(doc_source) for i, chunk in enumerate(chunker.chunk(dl_docresult.document)): raw_chunk chunker.serialize(chunkchunk) embedding embed_text(raw_chunk) chunk_id fchunk-{i} document_ids.append(inputs[document_id]) chunks.append(raw_chunk) chunk_ids.append(chunk_id) embeddings.append(embedding) return { document_id: document_ids, chunk_id: chunk_ids, vector: embeddings, chunk_text: chunks, }这个函数接收RequestSource定义的输入document_id、pdf_bytes、file_name内部完成整条 Docling 链路BytesIO包装原始 PDF 字节 →DocumentStream→DocumentConverter().convert()→HybridChunker分块序列化 → 逐块生成嵌入最后按 schema 返回一个文档展开为多行 chunk的字典。装饰器参数的含义modepython转换以 Python 函数执行write_to_online_storeTrue转换结果直接写入在线存储Milvus立即可被向量检索命中singletonTrue从源码结构看这表示该按需视图按请求整体执行而非逐实体行执行。feature_store.yaml本地 Milvus Lite 配置feature_store.yaml 完整内容如下project: rag provider: local registry: data/registry.db online_store: type: milvus path: data/online_store.db embedding_dim: 384 index_type: FLAT offline_store: type: file entity_key_serialization_version: 3 auth: type: no_auth参数说明provider: local使用本地 provider配合下面显式的online_store/offline_store配置online_store.type: milvus且给出path而非服务端地址即使用Milvus Lite数据落盘为本地data/online_store.db无需部署独立 Milvus 服务embedding_dim: 384必须与嵌入模型维度一致all-MiniLM-L6-v2输出 384 维这是示例中vector字段能建索引的前提index_type: FLAT暴力精确索引适合演示数据量offline_store.type: file离线存储即本地 Parquet 文件与FileSource的./data/docling_samples.parquet对应。需要说明的一点docling-quickstart.ipynb 的部分说明文字提及 Sqlite 在线存储但仓库中实际的feature_store.yaml配置为 Milvus Lite执行时以该 YAML 为准。部署与摄入feast apply 与 write_to_online_store 的三种用法在feature_repo/目录下执行feast apply即可扫描example_repo.py中的定义、注册 Feature View/Entity 并按 YAML 配置初始化存储本例中是在 Milvus Lite 中创建 collection。随后用 docling-quickstart.ipynb 演示了向在线存储写入数据的三种姿势对应不同的数据形态直接写入无转换的 FeatureView预计算好的 chunk 表store.write_to_online_store(feature_view_namedocling_feature_view, dfdf)写入已预转换的数据关闭写入时转换store.write_to_online_store( feature_view_namedocling_transform_docs, dfdf[df[document_id] ! doc-1], transform_on_writeFalse, )写入原始 PDF 字节开启写入时转换默认行为——这是本示例的亮点把metadata_samples.parquet中某文档的原始 PDF 字节传入由docling_transform_docs在写入链路中现场执行 Docling 解析、分块与嵌入store.write_to_online_store( feature_view_namedocling_transform_docs, dfmdf[mdf[document_id] doc-1], transform_on_writeTrue, # 默认值 )transform_on_write参数在 SDK 中的签名为write_to_online_store(..., transform_on_write: bool True)见 sdk/python/feast/feature_store.py。写入完成后可以通过底层 Milvus 客户端校验数据确实落库notebook 中的做法pymilvus_client store._provider._online_store._connect(store.config) COLLECTION_NAME [c for c in pymilvus_client.list_collections() if docling_transform_docs in c][0] milvus_query_result pymilvus_client.query( collection_nameCOLLECTION_NAME, filterdocument_id doc-1, limit1000, )同时可观察到在线存储以entity_keydocument_id的序列化形式feature_store.yaml中entity_key_serialization_version: 3指定了序列化版本建索引这与 Feast 的 entity key 机制一致。向量检索retrieve_online_documents_v2推理时的核心一步是把查询文本嵌入后通过 Feast SDK 的向量检索 API 从在线存储取出 top-k 文档块query_embedding embed_text(sample_query) context_data store.retrieve_online_documents_v2( features[ docling_feature_view:vector, docling_feature_view:file_name, docling_feature_view:raw_chunk_markdown, docling_feature_view:chunk_id, ], queryquery_embedding, top_k3, distance_metricCOSINE, ).to_df()该 API 既可以指向批量摄入的docling_feature_view也可以指向写入时转换的docling_transform_docs两个视图都能被同一检索路径命中。在源码层面该调用最终落到 Milvus 在线存储的实现 sdk/python/feast/infra/online_stores/milvus_online_store/milvus.py 的retrieve_online_documents_v2它会校验向量检索已在存储配置中启用config.online_store.vector_enabled、要求embedding与query_string关键词检索至少提供其一然后在 collection schema 中定位FLOAT_VECTOR/BINARY_VECTOR类型的字段作为 ANN 检索字段最后执行相似度搜索并附带created_ts、event_ts等内部字段返回。两类视图的关键差异notebook 中有专门说明docling_example_feature_view是普通FeatureView数据由离线 Parquet 直接写入docling_transform_docs是OnDemandFeatureViewFeast 负责在 Feature Server/写入链路中编排执行 Docling 转换函数转换完成后文档即可参与向量检索——这正是ingestion 时转换 PDF这一 README 目标 3 的实现方式。组装 LLM 上下文并生成回答检索结果经过去重与格式化后注入提示词def format_documents(context_df): output_context unique_documents context_df.drop_duplicates(subset[chunk_id])[chunk_text] for i, document_text in enumerate(unique_documents): output_context f****START DOCUMENT {i}****\n output_context fdocument {{ {document_text.strip()} }}\n output_context ****END DOCUMENT {i}****\n\n return output_context.strip()随后构造 system prompt要求模型仅基于给定文档回答、不知道就说不知道用openai客户端调用gpt-4o-mini回答示例问题Who are the authors of the paper?——这里的 PDF 正是 Docling 测试集中的学术论文检索命中的 chunk 里含有作者信息验证了端到端链路有效。小结与适用边界本示例展示了 Feast 在 RAG 场景中的完整分工Docling 负责非结构化文档解析Feast 负责声明式地管理文档特征的生命周期定义、注册、写入、检索Milvus Lite 提供零部署成本的向量存储两条数据路径都值得一试先离线批处理再摄入docling_feature_view或把原始 PDF 字节交给OnDemandFeatureView在写入时转换docling_transform_docs依赖PdfBytes类型与transform_on_write机制适用前提与限制嵌入模型、tokenizer 与 Docling 组件在example_repo.py模块导入时加载需要相应 Python 依赖sentence-transformers、docling、transformers等embedding_dim: 384与模型输出维度强绑定更换嵌入模型时需同步修改 YAMLFLAT索引与 2 小时 TTL 均为演示配置生产环境应按数据规模调整索引类型与在线存储选型Feast 亦支持 Faiss、Qdrant、Elasticsearch 等其他向量/检索型在线存储可参考 docs/reference/online-stores。【免费下载链接】feastThe Open Source Feature Store for AI/ML项目地址: https://gitcode.com/GitHub_Trending/fe/feast创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考