本章将通过多个案例实现私有数据集检索功能,虽然 LangChain 也可以实现向量检索,但在企业级项目中,通常会选用 LlamaIndex 这类专门面向检索场景的框架。LlamaIndex 是 MIT 协议开源、以 RAG 为核心的大模型应用开发框架。它充当大模型与私有数据之间的中间层,能够接入 PDF、Word 等各类本地或内网文档,对原始数据构建索引,以此解决大模型无法读取私有资料、容易产生幻觉的问题,可用于搭建知识问答、AI 智能体等应用。
一、环境部署 本项目需要部署两个轻量化GGUF量化模型,分别承担对话交互与语义向量检索能力,适配本地低资源部署场景。其中对话大模型负责承接用户提问、逻辑推理、文本生成等通用对话任务;Embedding向量嵌入模型负责将文本、语句等非结构化数据转化为低维稠密向量,让计算机能够量化计算文本间的语义相似度,实现精准检索、内容匹配、知识库召回等功能。
我们通过llama-server分别启动两个模型服务,绑定不同本地端口,独立提供对话推理和向量生成能力。
加载通义千问对话量化模型,绑定127.0.0.1本地11433端口,设置4096上下文窗口,满足日常对话、文本生成、问答推理需求。
CMD> llama-server.exe -m qwen2.5-1.5b-instruct-q4_k_m.gguf --host 127.0.0.1 --port 11433 -c 4096 load_model: loading model 'qwen2.5-1.5b-instruct-q4_k_m.gguf' llama_server: model loaded llama_server: listening on http://127.0.0.1:11433
加载向量量化模型,需额外配置专属参数开启向量生成能力 --embeddings 开启向量嵌入功能,--pooling mean 采用均值池化策略聚合文本向量,保证语义表征精准;同时限制批次参数提升本地部署稳定性。
CMD> llama-server.exe -m Qwen3-Embedding-0.6B-Q8_0.gguf --host 127.0.0.1 --port 11434 -c 4096 --embeddings --pooling mean -b 512 -ub 512 load_model: loading model 'Qwen3-Embedding-0.6B-Q8_0.gguf' llama_server: model loaded llama_server: listening on http://127.0.0.1:11434
批量安装RAG项目所需核心依赖,包含框架、向量数据库、文档解析、接口适配等全量包。
CMD> pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple CMD> pip config set global.trusted-host mirrors.tuna.tsinghua.edu.cn CMD> pip config list CMD> pip install llama-index CMD> pip install llama-index[complete] CMD> pip install chromadb CMD> pip install pinecone-client CMD> pip install weaviate-client CMD> pip install pypdf CMD> pip install docx2txt CMD> pip install python-pptx CMD> pip install llama-index-core llama-index-llms-openai-like CMD> pip install llama-index-embeddings-openai CMD> pip list Package Version ---------------------------------------- ----------- llama-index 0.14.24 llama-index-core 0.14.24 llama-index-embeddings-openai 0.6.0 llama-index-instrumentation 0.6.0 llama-index-llms-openai 0.7.10 llama-index-llms-openai-like 0.8.0 llama-index-workflows 2.23.3
测试Chat对话接口 通过LlamaIndex的OpenAI兼容接口,对接本地11433端口对话服务,实现基础大模型问答调用,验证对话服务可用性。本地llama-server兼容OpenAI接口格式,可直接通过通用类适配调用,无需额外改造服务端。
import osfrom llama_index.core import Settingsfrom llama_index.llms.openai_like import OpenAILikeos.environ["OPENAI_API_KEY" ] = "dummy" os.environ["OPENAI_BASE_URL" ] = "http://127.0.0.1:11433/v1" llm = OpenAILike( model="qwen2.5-1.5b-instruct-q4_k_m.gguf" , api_base=os.environ["OPENAI_BASE_URL" ], api_key=os.environ["OPENAI_API_KEY" ], is_chat_model=True , context_window=4096 ) if __name__ == "__main__" : Settings.llm = llm resp = llm.complete("你好,大模型!" ) print (resp)
代码运行输出提示信息:
测试Embedding向量接口 LlamaIndex无默认的本地llama-server向量模型适配接口,因此需要自定义Embedding调用类,对接11434端口向量服务,搭建完整的「文档加载-分块向量化-索引存储-语义检索-大模型生成」RAG流程。
在项目根目录新建 data 文件夹,创建 test.txt 测试文档,写入RAG相关测试语料,用于后续检索验证
1. 人工智能大模型能够理解人类自然语言 2. 大模型基于海量文本数据进行预训练 3. 向量数据库用来存储文本对应的向量嵌入 4. 向量检索通过计算向量相似度找到相似内容 5. 文本嵌入模型可以把句子转为多维向量 6. 语义检索不依赖关键词匹配,理解句子含义 7. 关键词检索只匹配字面文字,无法理解语义 8. RAG检索增强生成依靠检索外部知识库 9. RAG可以降低大模型产生幻觉的概率 10. 分块策略会影响知识库检索的效果
使用向量检索代码,先自定义对接本地 embedding 服务LocalLlamaServerEmbedding实现文本向量化,配置OpenAILike接入本地大模型作为LLM,设置全局 Settings 指定模型,运行时读取文档,加载文档后构建内存向量索引,生成查询引擎,最后发起查询让大模型基于检索到的文档片段总结文档核心内容。
运行逻辑: 自定义本地向量模型调用类 → 绑定本地对话LLM与向量Embedding模型 → 加载本地文档构建向量索引 → 语义检索+大模型总结生成答案import osimport requestsfrom typing import List from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndexfrom llama_index.core.embeddings import BaseEmbeddingfrom llama_index.llms.openai_like import OpenAILikeclass LocalLlamaServerEmbedding (BaseEmbedding ): api_base: str api_key: str = "dummy" def _get_embedding (self, text: str ) -> List [float ]: url = f"{self.api_base} /embeddings" payload = { "input" : text, "model" : "Qwen3-Embedding-0.6B-Q8_0.gguf" } headers = {"Authorization" : f"Bearer {self.api_key} " } resp = requests.post(url, json=payload, headers=headers) resp.raise_for_status() return resp.json()["data" ][0 ]["embedding" ] def _get_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) def _get_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) os.environ["OPENAI_API_KEY" ] = "dummy" os.environ["OPENAI_BASE_URL" ] = "http://127.0.0.1:11433/v1" llm = OpenAILike( model="qwen2.5-1.5b-instruct-q4_k_m.gguf" , api_base=os.environ["OPENAI_BASE_URL" ], api_key=os.environ["OPENAI_API_KEY" ], is_chat_model=True , context_window=1024 ) Settings.llm = llm Settings.embed_model = LocalLlamaServerEmbedding(api_base="http://127.0.0.1:11434/v1" ) if __name__ == "__main__" : documents = SimpleDirectoryReader( "./data/" , required_exts=[".pdf" , ".docx" , ".txt" ] ).load_data() index = VectorStoreIndex.from_documents(documents) query_engine = index.as_query_engine() response = query_engine.query("请总结文档里面的核心内容,使用汉语回复" ) print ("\n回答:" ) print (response)
代码运行输出提示信息:
回答: 文档的核心内容是关于人工智能大模型如何理解和处理自然语言 以及如何通过向量数据库和向量检索技术来处理和查找文本内容。
二、实际应用 使用向量数据库 该代码基于 Llama Index 框架,自定义对接本地 Qwen3 嵌入模型的 Embedding 类,使用本地 OpenAI 兼容接口的 Qwen2.5 大模型,实现向量索引的创建、持久化存储与加载,读取 pdf、docx、txt 文档构建向量库,通过查询引擎检索 top3 相关文本片段并交由大模型回答用户问题。
import osimport requestsfrom typing import List from llama_index.core.embeddings import BaseEmbeddingfrom llama_index.llms.openai_like import OpenAILikefrom llama_index.core import ( VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext, load_index_from_storage ) class LocalLlamaServerEmbedding (BaseEmbedding ): api_base: str api_key: str = "dummy" def _get_embedding (self, text: str ) -> List [float ]: url = f"{self.api_base} /embeddings" payload = { "input" : text, "model" : "Qwen3-Embedding-0.6B-Q8_0.gguf" } headers = {"Authorization" : f"Bearer {self.api_key} " } resp = requests.post(url, json=payload, headers=headers) resp.raise_for_status() return resp.json()["data" ][0 ]["embedding" ] def _get_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) def _get_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) class IndexManager : """索引管理器:负责向量索引的创建、持久化与加载""" def create_index (self, data_dir="./data/" , persist_dir="./storage/" ): """创建并持久化向量索引""" try : documents = SimpleDirectoryReader( data_dir, required_exts=[".pdf" , ".docx" , ".txt" ], recursive=False ).load_data() except Exception as err: print (f"[文档读取解析失败]错误信息:{err} " ) raise err print (f"成功读取文档片段数量:{len (documents)} " ) index = VectorStoreIndex.from_documents(documents) index.storage_context.persist(persist_dir=persist_dir) return index def load_index (self, persist_dir="storage" ): """加载磁盘上已持久化的索引""" storage_context = StorageContext.from_defaults(persist_dir=persist_dir) index = load_index_from_storage(storage_context) return index if __name__ == "__main__" : os.environ["OPENAI_API_KEY" ] = "dummy" os.environ["OPENAI_BASE_URL" ] = "http://127.0.0.1:11433/v1" llm = OpenAILike( model="qwen2.5-1.5b-instruct-q4_k_m.gguf" , api_base=os.environ["OPENAI_BASE_URL" ], api_key=os.environ["OPENAI_API_KEY" ], is_chat_model=True , context_window=1024 ) Settings.llm = llm Settings.embed_model = LocalLlamaServerEmbedding(api_base="http://127.0.0.1:11434/v1" ) manager = IndexManager() index = None try : index = manager.load_index() print ("索引加载成功" ) except Exception as e: print (f"索引加载失败:{e} ,创建新索引..." ) index = manager.create_index() query_engine = index.as_query_engine( similarity_top_k=3 , response_mode="compact" ) resp = query_engine.query("简单概述文档内容,并回答中文简体。" ) print (f"回答:{resp} \n" )
代码运行输出提示信息:
索引加载成功 回答: 文档内容主要介绍了人工智能大模型如何理解和处理自然语言,以及如何通过预训练和向量数据库来存储和检索文本数据。 文档中提到的步骤包括: 1. 人工智能大模型能够理解人类自然语言 2. 大模型基于海量文本数据进行预训练 3. 向量数据库用来存储文本对应的向量嵌入
使用文档检索 这段代码在向量数据库基础上做了封装优化,新增句子文本分片器,在 IndexManager 类中统一完成本地大模型与自定义 Embedding 的初始化,加载或重建向量索引,对外提供问答接口,返回问题答案同时附带检索到的文档源元数据,并且设置系统提示词约束大模型基于文档作答。
import osimport requestsfrom typing import List from llama_index.core.embeddings import BaseEmbeddingfrom llama_index.llms.openai_like import OpenAILikefrom llama_index.core.node_parser import SentenceSplitterfrom llama_index.core import ( VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext, load_index_from_storage ) class LocalLlamaServerEmbedding (BaseEmbedding ): api_base: str api_key: str = "dummy" def _get_embedding (self, text: str ) -> List [float ]: url = f"{self.api_base} /embeddings" payload = { "input" : text, "model" : "Qwen3-Embedding-0.6B-Q8_0.gguf" } headers = {"Authorization" : f"Bearer {self.api_key} " } resp = requests.post(url, json=payload, headers=headers) resp.raise_for_status() return resp.json()["data" ][0 ]["embedding" ] def _get_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) def _get_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) class IndexManager : def __init__ (self, data_dir="./data/" , persist_dir="./storage/" ): self.data_dir = data_dir self.persist_dir = persist_dir self.setup_llm() self.index = self.load_database() def setup_llm (self ): os.environ["OPENAI_API_KEY" ] = "dummy" os.environ["OPENAI_BASE_URL" ] = "http://127.0.0.1:11433/v1" llm = OpenAILike( model="qwen2.5-1.5b-instruct-q4_k_m.gguf" , api_base=os.environ["OPENAI_BASE_URL" ], api_key=os.environ["OPENAI_API_KEY" ], is_chat_model=True , context_window=1024 , temperature=0.1 , system_prompt="""你是一个企业文档助手。 请基于提供的文档内容回答问题,如果文档中没有相关信息,请明确说明。 回答要准确、简洁、专业。""" ) embed_model = LocalLlamaServerEmbedding( api_base="http://127.0.0.1:11434/v1" ) Settings.llm = llm Settings.embed_model = embed_model Settings.node_parser = SentenceSplitter(chunk_size=512 , chunk_overlap=50 ) def load_database (self ): if os.path.exists(self.persist_dir): try : storage_context = StorageContext.from_defaults( persist_dir=self.persist_dir ) index = load_index_from_storage(storage_context) print ("[+] 索引加载成功" ) return index except Exception as e: print (f"[-] 索引加载失败,原因:{e} ,重新创建..." ) documents = SimpleDirectoryReader( self.data_dir, required_exts=[".pdf" , ".docx" , ".txt" ], recursive=False ).load_data() print (f"[+] 读取文档片段数量:{len (documents)} " ) index = VectorStoreIndex.from_documents(documents) index.storage_context.persist(persist_dir=self.persist_dir) print ("[+] 索引创建并持久化完成" ) return index def get_query_engine (self, similarity_top_k=3 ): """获取默认查询引擎,可后续替换为混合检索引擎""" return self.index.as_query_engine(similarity_top_k=similarity_top_k) def query (self, question, top_k=3 ): query_engine = self.index.as_query_engine( similarity_top_k=top_k, response_mode="compact" ) response = query_engine.query(question) return { "question" : question, "answer" : str (response), "sources" : [node.metadata for node in response.source_nodes] } if __name__ == "__main__" : manager = IndexManager(data_dir="./data/" , persist_dir="./storage/" ) res = manager.query("请简单概述文章内容,15个字以内。" ) print ("问题:" , res["question" ]) print ("回答:" , res["answer" ]) print ("来源:" , res["sources" ]) query_engine = manager.get_query_engine() response = query_engine.query("请简述文档内容,中文回答。" ) print (f"回答:{response} " )
代码运行输出提示信息:
[+] 索引加载成功 问题: 请简单概述文章内容,15个字以内。 回答: RAG检索增强生成依靠检索外部知识库,可以降低大模型产生幻觉的概率,分块策略会影响知识库检索的效果。 来源: [ { 'file_path' : 'C: \\Users\\Admin\\data\\test.txt' , 'file_name' : 'test.txt' , 'file_type' : 'text/plain' , 'file_size' : 562, 'creation_date' : '2026-09-17' , 'last_modified_date' : '2026-09-17' } ] 回答:文档内容描述了人工智能大模型能够理解人类自然语言,大模型基于海量文本数据进行预训练,向量数据库用来存储文本对应的向量嵌入。
使用文档摘要 代码改用 LlamaIndex 的 SummaryIndex 摘要索引,复用本地 Qwen 大模型与自定义嵌入模型,读取本地文档后支持生成简洁摘要、详细摘要以及提取文档关键要点,采用 tree_summarize 模式对文档内容做汇总,适合对文档整体内容进行归纳提取。
import osimport requestsfrom typing import List from llama_index.core.embeddings import BaseEmbeddingfrom llama_index.llms.openai_like import OpenAILikefrom llama_index.core.node_parser import SentenceSplitterfrom llama_index.core import ( SummaryIndex, SimpleDirectoryReader, Settings ) class LocalLlamaServerEmbedding (BaseEmbedding ): api_base: str api_key: str = "dummy" def _get_embedding (self, text: str ) -> List [float ]: url = f"{self.api_base} /embeddings" payload = { "input" : text, "model" : "Qwen3‑Embedding‑0.6B‑Q8_0.gguf" } headers = {"Authorization" : f"Bearer {self.api_key} " } resp = requests.post(url, json=payload, headers=headers) resp.raise_for_status() return resp.json()["data" ][0 ]["embedding" ] def _get_text_embedding (self, text: str ) -> List [float ]: return self._get_embedding(text) def _get_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) async def _aget_query_embedding (self, query: str ) -> List [float ]: return self._get_embedding(query) class DocumentSummarizer : def __init__ (self ): self.setup_llm() def setup_llm (self ): os.environ["OPENAI_API_KEY" ] = "dummy" os.environ["OPENAI_BASE_URL" ] = "http://127.0.0.1:11433/v1" llm = OpenAILike( model="qwen2.5-1.5b-instruct-q4_k_m.gguf" , api_base=os.environ["OPENAI_BASE_URL" ], api_key=os.environ["OPENAI_API_KEY" ], is_chat_model=True , context_window=1024 , temperature=0.3 , system_prompt="你是一个专业的文档摘要助手,请提供准确、简洁的摘要。" ) embed_model = LocalLlamaServerEmbedding(api_base="http://127.0.0.1:11434/v1" ) Settings.llm = llm Settings.embed_model = embed_model Settings.node_parser = SentenceSplitter(chunk_size=512 , chunk_overlap=50 ) def summarize_documents (self, documents, summary_type="brief" ): try : summary_index = SummaryIndex.from_documents(documents) except Exception as e: print (f"[构建SummaryIndex失败]{e} " ) raise e if summary_type == "brief" : prompt = "请为这些文档生成一个简洁的摘要,突出主要观点。" elif summary_type == "detailed" : prompt = "请为这些文档生成一个详细的摘要,包括关键信息和重要细节。" else : prompt = "请为这些文档生成摘要。" query_engine = summary_index.as_query_engine( response_mode="tree_summarize" ) response = query_engine.query(prompt) return str (response) def extract_key_points (self, documents ): try : summary_index = SummaryIndex.from_documents(documents) except Exception as e: print (f"[构建SummaryIndex失败]{e} " ) raise e query_engine = summary_index.as_query_engine() response = query_engine.query( "请列出这些文档中的关键要点,以项目符号的形式呈现。" ) return str (response) if __name__ == "__main__" : summarizer = DocumentSummarizer() try : documents = SimpleDirectoryReader( "./data/" , required_exts=[".pdf" , ".docx" , ".txt" ], recursive=False ).load_data() except Exception as err: print (f"[文档读取解析失败]错误信息:{err} " ) raise err print (f"读取文档片段数:{len (documents)} " ) brief_summary = summarizer.summarize_documents(documents, "brief" ) print ("[简洁摘要]" ) print (brief_summary) detailed_summary = summarizer.summarize_documents(documents, "detailed" ) print ("[详细摘要]" ) print (detailed_summary) key_points = summarizer.extract_key_points(documents) print ("[关键要点]" ) print (key_points)
代码运行输出提示信息:
读取文档片段数:1 [简洁摘要] 这些文档的主要观点包括: 1. 人工智能大模型能够理解人类自然语言。 2. 大模型基于海量文本数据进行预训练。 3. 向量数据库用来存储文本对应的向量嵌入。 4. 向量检索通过计算向量相似度找到相似内容。 5. 文本嵌入模型可以把句子转为多维向量。 6. 语义检索不依赖关键词匹配,理解句子含义。 7. 关键词检索只匹配字面文字,无法理解语义。 8. RAG检索增强生成依靠检索外部知识库。 9. RAG可以降低大模型产生幻觉的概率。 10. 分块策略会影响知识库检索的效果。 [详细摘要] 这些文档主要讨论了人工智能大模型的几个关键点,包括:1. 人工智能大模型能够理解人类自然语言;2. 大模型基于海量文本数据进行预训练;3. 向量数据库用来存储文本对应的向量嵌入;4. 向量检索通过计算向量相似度 找到相似内容;5. 文本嵌入模型可以把句子转为多维向量;6. 语义检索不依赖关键词匹配,理解句子含义;7. 关键词检索只匹配字面文字,无法理解语义;8. RAG检索增强生成依靠检索外部知识库;9. RAG可以降低大模型产 生幻觉的概率;10. 分块策略会影响知识库检索的效果。 [关键要点] 1. 分块策略会影响知识库检索的效果