【学习记录】本地 RAG 检索器加载 FAISS 索引并实现语义搜索在前一篇文章中我们构建了 PDF → 文本 → 向量 → FAISS 索引的完整流水线。本文展示如何使用该索引进行语义检索加载已保存的 FAISS 索引和 LlamaIndex 存储上下文创建一个不依赖 LLM 的检索器Retriever接受用户查询返回最相似的文本片段及其相关度分数。代码完全独立适合集成到问答系统或进一步分析检索结果。 目录功能概述环境配置与依赖核心原理解析完整代码检索脚本执行方法运行效果示例注意事项总结一、功能概述本脚本实现了以下功能加载 Embedding 模型使用与构建索引时相同的 HuggingFace 中文嵌入模型BAAI/bge-small-zh-v1.5确保向量空间一致。读取 FAISS 索引从磁盘加载之前保存的 FAISS 索引文件vector_store.faiss。恢复 StorageContext利用 LlamaIndex 的持久化机制恢复文档存储docstore和索引结构。创建检索器基于索引构建一个检索器retriever支持top_k参数。交互式检索循环接受用户输入查询输出最相关的文本片段及其相关度分数L2 距离转换后的相似度。适用场景在 RAG 系统中可以先使用此脚本验证检索效果调试chunk_size、top_k等参数再接入 LLM 生成最终答案。二、环境配置与依赖Python 库依赖pipinstallllama-index-core llama-index-embeddings-huggingface llama-index-vector-stores-faiss faiss-cpu sentence-transformers库作用llama-index-coreLlamaIndex 核心提供索引加载、检索器等功能llama-index-embeddings-huggingfaceHuggingFace 嵌入模型适配器llama-index-vector-stores-faissFAISS 向量存储适配器faiss-cpuFAISS 库CPU 版本sentence-transformers嵌入模型依赖自动安装前置要求已经运行过上一篇文章中的构建脚本生成了./storage/faiss_index目录其中包含vector_store.faissFAISS 索引文件。docstore.json、index_store.json等 LlamaIndex 元数据文件。三、核心原理解析3.1 Embedding 模型一致性检索时使用的嵌入模型必须与构建索引时完全相同包括模型名称、维度。本脚本显式设置了相同的EMBED_MODEL和devicecpu保证查询向量与索引向量在同一空间。3.2 加载 FAISS 索引faiss_indexfaiss.read_index(faiss_path)faiss.read_index直接读取原生 FAISS 索引文件获得一个faiss.Index对象。通过faiss_index.ntotal可以查看索引中的向量总数。3.3 恢复 StorageContextvector_storeFaissVectorStore(faiss_indexfaiss_index)storage_contextStorageContext.from_defaults(persist_dirINDEX_DIR,vector_storevector_store)由于我们已经手动加载了 FAISS 索引需要将其包装成FaissVectorStore对象。StorageContext.from_defaults再从persist_dir中读取docstore.json和index_store.json恢复文档节点和索引结构。3.4 创建检索器retrieverindex.as_retriever(similarity_top_kTOP_K)检索器只执行相似度搜索不调用 LLM 生成答案。返回的每个节点带有score属性对于 L2 距离索引score是负的欧氏距离越接近 0 表示越相似实际使用中可转换为余弦相似度或直接使用。3.5 检索流程用户输入查询文本query。脚本调用Settings.embed_model.get_text_embedding(query)将查询转换为向量内部自动完成。FAISS 索引执行search操作返回top_k个最相似的向量索引及其距离。通过storage_context将索引映射回原始文本节点连同元数据一起返回。四、完整代码检索脚本创建文件search_index.py内容如下importosimportsysfromllama_index.coreimport(StorageContext,load_index_from_storage,Settings)fromllama_index.embeddings.huggingfaceimportHuggingFaceEmbeddingfromllama_index.vector_stores.faissimportFaissVectorStoreimportfaiss# # 配置与构建脚本保持一致# INDEX_DIR./storage/faiss_indexEMBED_MODELBAAI/bge-small-zh-v1.5TOP_K5# 检索 top-k 相似片段# # 初始化 Embedding 模型# print(加载 Embedding 模型...)Settings.embed_modelHuggingFaceEmbedding(model_nameEMBED_MODEL,devicecpu)# # 加载 FAISS 索引# faiss_pathos.path.join(INDEX_DIR,vector_store.faiss)ifnotos.path.exists(faiss_path):print(f错误索引文件不存在 -{faiss_path})sys.exit(1)print(读取 FAISS 索引...)faiss_indexfaiss.read_index(faiss_path)print(fFAISS 索引维度{faiss_index.d}, 向量数量{faiss_index.ntotal})vector_storeFaissVectorStore(faiss_indexfaiss_index)print(加载 StorageContext...)storage_contextStorageContext.from_defaults(persist_dirINDEX_DIR,vector_storevector_store)print(加载索引...)try:indexload_index_from_storage(storage_context)print(索引加载成功)exceptExceptionase:print(f索引加载失败{e})sys.exit(1)# 获取文档节点数量doc_countlen(index.docstore.docs)ifhasattr(index,docstore)else未知print(f索引中的文档节点数{doc_count})# # 创建检索器不包含 LLM 生成# retrieverindex.as_retriever(similarity_top_kTOP_K)# # 交互检索循环# print(\n*60)print(索引加载成功现在仅进行检索不调用 LLM。)print(输入 exit 或 quit 退出程序。)print(*60)whileTrue:queryinput(\n请输入检索查询).strip()ifquery.lower()in[exit,quit,q]:print(退出程序。)breakifnotquery:continueprint(\n检索中...)nodes_with_scoresretriever.retrieve(query)print(\n【检索到的文档片段】)ifnodes_with_scores:foridx,node_with_scoreinenumerate(nodes_with_scores,1):scorenode_with_score.scoreifhasattr(node_with_score,score)elseN/Anodenode_with_score.nodeprint(f\n片段{idx}(相关度:{score:.4f}))snippetnode.text[:800]...iflen(node.text)800elsenode.textprint(f文本:\n{snippet})ifnode.metadata:print(f元数据:{node.metadata})print(-*40)else:print(未检索到任何片段可能索引为空或查询无匹配。)print(\n*60)五、执行方法5.1 确保索引已存在首先运行上一篇文章中的构建脚本build_index.py生成./storage/faiss_index目录。5.2 运行检索脚本在终端中执行python search_index.py5.3 交互示例加载 Embedding 模型... 读取 FAISS 索引... FAISS 索引维度512, 向量数量126 加载 StorageContext... 加载索引... 索引加载成功 索引中的文档节点数126 索引加载成功现在仅进行检索不调用 LLM。 输入 exit 或 quit 退出程序。 请输入检索查询医疗器械分类规则 检索中... 【检索到的文档片段】 片段 1 (相关度: 0.8234) 文本: 医疗器械按照风险程度分为三类第一类是风险较低第二类是中度风险第三类是较高风险... 元数据: {source: YY/T0664-2020} ---------------------------------------- 片段 2 (相关度: 0.7651) ...六、运行效果示例假设索引中包含某医疗器械标准文档的内容查询“分类规则”会返回相关段落及其相关度分数。score对于 L2 距离索引实际是负的欧氏距离越大表示越相似。用户可以根据分数阈值过滤低相关片段。七、注意事项问题说明解决方案模型不一致检索时使用的嵌入模型与构建时不同会导致向量空间不匹配检索结果完全错误。确保EMBED_MODEL与构建脚本完全一致。索引路径错误脚本中INDEX_DIR必须指向正确的目录。使用绝对路径或确认相对路径正确。内存不足加载大型 FAISS 索引可能占用大量内存。使用faiss的read_index时可设置mmap模式但 LlamaIndex 适配器可能需要全量加载。分数解释score不是标准的余弦相似度而是 FAISS 返回的距离转换值。可忽略具体数值仅用于排序。无检索结果可能查询与文档内容完全不相关或索引中文本过少。尝试更换查询词或增加chunk_size重新构建索引。八、总结本文提供了一个即用型语义检索脚本实现了✅ 加载 FAISS 索引和 LlamaIndex 存储。✅ 创建不依赖 LLM 的检索器。✅ 交互式查询展示相似文本片段及相关度分数。✅ 可直接集成到 RAG 系统的检索环节或用于调试分块策略。通过这个脚本你可以验证索引质量检查检索结果是否符合预期。调整TOP_K和CHUNK_SIZE参数观察召回效果变化。将检索到的节点传递给 LLM构建完整的问答系统。下一步可以结合上一篇文章的构建脚本形成一套完整的本地 RAG 知识库预处理与检索工具链。
本地 RAG 检索器:加载 FAISS 索引并实现语义搜索
发布时间:2026/6/16 12:30:12
【学习记录】本地 RAG 检索器加载 FAISS 索引并实现语义搜索在前一篇文章中我们构建了 PDF → 文本 → 向量 → FAISS 索引的完整流水线。本文展示如何使用该索引进行语义检索加载已保存的 FAISS 索引和 LlamaIndex 存储上下文创建一个不依赖 LLM 的检索器Retriever接受用户查询返回最相似的文本片段及其相关度分数。代码完全独立适合集成到问答系统或进一步分析检索结果。 目录功能概述环境配置与依赖核心原理解析完整代码检索脚本执行方法运行效果示例注意事项总结一、功能概述本脚本实现了以下功能加载 Embedding 模型使用与构建索引时相同的 HuggingFace 中文嵌入模型BAAI/bge-small-zh-v1.5确保向量空间一致。读取 FAISS 索引从磁盘加载之前保存的 FAISS 索引文件vector_store.faiss。恢复 StorageContext利用 LlamaIndex 的持久化机制恢复文档存储docstore和索引结构。创建检索器基于索引构建一个检索器retriever支持top_k参数。交互式检索循环接受用户输入查询输出最相关的文本片段及其相关度分数L2 距离转换后的相似度。适用场景在 RAG 系统中可以先使用此脚本验证检索效果调试chunk_size、top_k等参数再接入 LLM 生成最终答案。二、环境配置与依赖Python 库依赖pipinstallllama-index-core llama-index-embeddings-huggingface llama-index-vector-stores-faiss faiss-cpu sentence-transformers库作用llama-index-coreLlamaIndex 核心提供索引加载、检索器等功能llama-index-embeddings-huggingfaceHuggingFace 嵌入模型适配器llama-index-vector-stores-faissFAISS 向量存储适配器faiss-cpuFAISS 库CPU 版本sentence-transformers嵌入模型依赖自动安装前置要求已经运行过上一篇文章中的构建脚本生成了./storage/faiss_index目录其中包含vector_store.faissFAISS 索引文件。docstore.json、index_store.json等 LlamaIndex 元数据文件。三、核心原理解析3.1 Embedding 模型一致性检索时使用的嵌入模型必须与构建索引时完全相同包括模型名称、维度。本脚本显式设置了相同的EMBED_MODEL和devicecpu保证查询向量与索引向量在同一空间。3.2 加载 FAISS 索引faiss_indexfaiss.read_index(faiss_path)faiss.read_index直接读取原生 FAISS 索引文件获得一个faiss.Index对象。通过faiss_index.ntotal可以查看索引中的向量总数。3.3 恢复 StorageContextvector_storeFaissVectorStore(faiss_indexfaiss_index)storage_contextStorageContext.from_defaults(persist_dirINDEX_DIR,vector_storevector_store)由于我们已经手动加载了 FAISS 索引需要将其包装成FaissVectorStore对象。StorageContext.from_defaults再从persist_dir中读取docstore.json和index_store.json恢复文档节点和索引结构。3.4 创建检索器retrieverindex.as_retriever(similarity_top_kTOP_K)检索器只执行相似度搜索不调用 LLM 生成答案。返回的每个节点带有score属性对于 L2 距离索引score是负的欧氏距离越接近 0 表示越相似实际使用中可转换为余弦相似度或直接使用。3.5 检索流程用户输入查询文本query。脚本调用Settings.embed_model.get_text_embedding(query)将查询转换为向量内部自动完成。FAISS 索引执行search操作返回top_k个最相似的向量索引及其距离。通过storage_context将索引映射回原始文本节点连同元数据一起返回。四、完整代码检索脚本创建文件search_index.py内容如下importosimportsysfromllama_index.coreimport(StorageContext,load_index_from_storage,Settings)fromllama_index.embeddings.huggingfaceimportHuggingFaceEmbeddingfromllama_index.vector_stores.faissimportFaissVectorStoreimportfaiss# # 配置与构建脚本保持一致# INDEX_DIR./storage/faiss_indexEMBED_MODELBAAI/bge-small-zh-v1.5TOP_K5# 检索 top-k 相似片段# # 初始化 Embedding 模型# print(加载 Embedding 模型...)Settings.embed_modelHuggingFaceEmbedding(model_nameEMBED_MODEL,devicecpu)# # 加载 FAISS 索引# faiss_pathos.path.join(INDEX_DIR,vector_store.faiss)ifnotos.path.exists(faiss_path):print(f错误索引文件不存在 -{faiss_path})sys.exit(1)print(读取 FAISS 索引...)faiss_indexfaiss.read_index(faiss_path)print(fFAISS 索引维度{faiss_index.d}, 向量数量{faiss_index.ntotal})vector_storeFaissVectorStore(faiss_indexfaiss_index)print(加载 StorageContext...)storage_contextStorageContext.from_defaults(persist_dirINDEX_DIR,vector_storevector_store)print(加载索引...)try:indexload_index_from_storage(storage_context)print(索引加载成功)exceptExceptionase:print(f索引加载失败{e})sys.exit(1)# 获取文档节点数量doc_countlen(index.docstore.docs)ifhasattr(index,docstore)else未知print(f索引中的文档节点数{doc_count})# # 创建检索器不包含 LLM 生成# retrieverindex.as_retriever(similarity_top_kTOP_K)# # 交互检索循环# print(\n*60)print(索引加载成功现在仅进行检索不调用 LLM。)print(输入 exit 或 quit 退出程序。)print(*60)whileTrue:queryinput(\n请输入检索查询).strip()ifquery.lower()in[exit,quit,q]:print(退出程序。)breakifnotquery:continueprint(\n检索中...)nodes_with_scoresretriever.retrieve(query)print(\n【检索到的文档片段】)ifnodes_with_scores:foridx,node_with_scoreinenumerate(nodes_with_scores,1):scorenode_with_score.scoreifhasattr(node_with_score,score)elseN/Anodenode_with_score.nodeprint(f\n片段{idx}(相关度:{score:.4f}))snippetnode.text[:800]...iflen(node.text)800elsenode.textprint(f文本:\n{snippet})ifnode.metadata:print(f元数据:{node.metadata})print(-*40)else:print(未检索到任何片段可能索引为空或查询无匹配。)print(\n*60)五、执行方法5.1 确保索引已存在首先运行上一篇文章中的构建脚本build_index.py生成./storage/faiss_index目录。5.2 运行检索脚本在终端中执行python search_index.py5.3 交互示例加载 Embedding 模型... 读取 FAISS 索引... FAISS 索引维度512, 向量数量126 加载 StorageContext... 加载索引... 索引加载成功 索引中的文档节点数126 索引加载成功现在仅进行检索不调用 LLM。 输入 exit 或 quit 退出程序。 请输入检索查询医疗器械分类规则 检索中... 【检索到的文档片段】 片段 1 (相关度: 0.8234) 文本: 医疗器械按照风险程度分为三类第一类是风险较低第二类是中度风险第三类是较高风险... 元数据: {source: YY/T0664-2020} ---------------------------------------- 片段 2 (相关度: 0.7651) ...六、运行效果示例假设索引中包含某医疗器械标准文档的内容查询“分类规则”会返回相关段落及其相关度分数。score对于 L2 距离索引实际是负的欧氏距离越大表示越相似。用户可以根据分数阈值过滤低相关片段。七、注意事项问题说明解决方案模型不一致检索时使用的嵌入模型与构建时不同会导致向量空间不匹配检索结果完全错误。确保EMBED_MODEL与构建脚本完全一致。索引路径错误脚本中INDEX_DIR必须指向正确的目录。使用绝对路径或确认相对路径正确。内存不足加载大型 FAISS 索引可能占用大量内存。使用faiss的read_index时可设置mmap模式但 LlamaIndex 适配器可能需要全量加载。分数解释score不是标准的余弦相似度而是 FAISS 返回的距离转换值。可忽略具体数值仅用于排序。无检索结果可能查询与文档内容完全不相关或索引中文本过少。尝试更换查询词或增加chunk_size重新构建索引。八、总结本文提供了一个即用型语义检索脚本实现了✅ 加载 FAISS 索引和 LlamaIndex 存储。✅ 创建不依赖 LLM 的检索器。✅ 交互式查询展示相似文本片段及相关度分数。✅ 可直接集成到 RAG 系统的检索环节或用于调试分块策略。通过这个脚本你可以验证索引质量检查检索结果是否符合预期。调整TOP_K和CHUNK_SIZE参数观察召回效果变化。将检索到的节点传递给 LLM构建完整的问答系统。下一步可以结合上一篇文章的构建脚本形成一套完整的本地 RAG 知识库预处理与检索工具链。