AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


大模型RAG实战|向量数据库:Elasticsearch实现混合检索(附完整代码)
发布日期:2024-06-11 08:22:59 浏览次数: 2620



此前文章中,我介绍了使用LlamaIndex,如何构建知识库,实现各类文档和网页的加载、转换、索引与存储。


当时,我们采用的向量数据库是Chroma,作为LlamaIndex中的向量存储(Vector Store)。Chroma是一个非常简单易用的嵌入式向量数据库,在开发和测试场景非常受欢迎。


但是,如果是生产级系统,我们必须考虑能力更强、可扩展的向量数据库,比如国内使用较多的Milvus,以及国外使用较多的Weaviate。


LlamaIndex支持超过20种向量数据库,在官网文档上给出了列表,并标注了各个向量数据库是否支持下列5个特性,包括:元数据过滤、混合检索、可删除、文档存储、支持异步。


详情请参考官方文档:

https://docs.llamaindex.ai/en/stable/module_guides/storing/vector_stores/


 1 

选择向量数据库


我的前一篇文章《QAnything技术栈解析》中提到,QAnything采用了混合检索技术,提升检索的准确性。


最初,QAnything使用了Milvus做向量存储和向量语义检索,后来引入了Elasticsearch做BM25关键词检索,从而实现了混合检索。


Elasticsearch作为一个分布式的搜索和数据分析引擎,在全文检索和海量非结构化数据存储等场景,已经得到了广泛应用。现在,Elasticsearch也可以用于向量存储,并支持以上包括混合检索在内的全部5个特性。


因此,在生产环境,使用Elasticsearch作为向量数据库并使用其混合检索功能,是一个合适的选择。


系统技术栈如下表所示:


数据框架
LlamaIndex
前端框架
Streamlit
大模型工具
Ollama
大模型
Gemma 2B
嵌入模型
BAAI/bge-small-zh-v1.5
文本分割器
SpacyTextSplitter
文档存储
MongoDB
向量存储
ElasticSearch


以下,我将结合代码实例,讲解如何在LlamaIndex框架中使用Elasticsearch,并给出一套完整可运行的本地知识库问答系统。


 2 

代码实现示例


示例1:安装和配置ES


我们使用ES做向量存储(Vector Stores)。首先,使用ES官方Docker镜像,在本机安装和运行ES。


    docker run -p 9200:9200 \-e "discovery.type=single-node" \-e "xpack.security.enabled=false" \-e "xpack.license.self_generated.type=trial" \docker.elastic.co/elasticsearch/elasticsearch:8.13.2


    然后,配置ES,作为向量数据库,并通过retrieval_strategy配置使用混合检索(hybrid=true)。


      # 向量数据库: Elasticsearch
      from llama_index.vector_stores.elasticsearch import ElasticsearchStorefrom llama_index.vector_stores.elasticsearch import AsyncDenseVectorStrategy
      ES_URI = "http://localhost:9200"
      es_vector_store = ElasticsearchStore(es_url=ES_URI,index_name="my_index",retrieval_strategy=AsyncDenseVectorStrategy(hybrid=True), # 使用混合检索)


      示例2:配置MongoDB


      我们选用MongoDB做文档存储(Document Stores)和索引存储(Index Stores)。你可以通过Docker安装和运行MongoDB,然后做如下代码配置。


        # 文档存储: MongoDB
        from llama_index.storage.docstore.mongodb import MongoDocumentStorefrom llama_index.storage.index_store.mongodb import MongoIndexStorefrom llama_index.core import StorageContext
        MONGO_URI = "mongodb://localhost:27017"
        mongo_doc_store = MongoDocumentStore.from_uri(uri=MONGO_URI)mongo_index_store = MongoIndexStore.from_uri(uri=MONGO_URI)
        storage_context = StorageContext.from_defaults(docstore=mongo_doc_store,index_store=mongo_index_store,vector_store=es_vector_store,)


        示例3:配置本地模型


        请到ollama.com安装Ollama,并下载大模型,比如:Llama 3, Phi 3, Mistral, Gemma等。为了测试方便,我们选用速度更快、效果较好的Gemma 2B模型,共1.7GB。


          # Ollama本地LLM: gemma:2b
          from llama_index.llms.ollama import Ollamallm_ollama = Ollama(model="gemma:2b", request_timeout=600.0)


          嵌入模型我们继续使用智源的BAAI/bge-small-zh-v1.5。


            # 本地嵌入模型: bge-small-zh-v1.5
            from llama_index.embeddings.huggingface import HuggingFaceEmbeddingbge_embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-zh-v1.5")


            第一次运行时会自动从Hugging Face下载模型,请提前设置使用国内镜像站点。


              export HF_ENDPOINT=https://hf-mirror.com


              将配置好的大模型和嵌入模型,挂载在LlamaIndex全局设置上。


                # LlamaIndex全局配置
                from llama_index.core import SettingsSettings.llm = llm_ollamaSettings.embed_model = bge_embed_model


                示例4:配置文本分割器


                文本分割器Spacy对中文支持较好。我们可以通过Langchain引入和使用。


                  # 文本分割器: SpacyTextSplitter
                  from llama_index.core.node_parser import LangchainNodeParserfrom langchain.text_splitter import SpacyTextSplitterspacy_text_splitter = LangchainNodeParser(SpacyTextSplitter(pipeline="zh_core_web_sm", chunk_size = 512,chunk_overlap = 128))


                  示例5:加载网页信息


                  此前文章介绍过,我们可以用LlamaIndex的SimpleDirectoryReader读取本地文件夹“data”中的文件。


                    # 加载文件信息: SimpleDirectoryReader
                    from llama_index.core import SimpleDirectoryReaderdocuments = SimpleDirectoryReader(input_dir="./data", recursive=True).load_data() print(f"Loaded {len(documents)} Files")


                    为了测试方便,本程序中,我们用SimpleWebPageReader读取网页信息


                      # 加载网页信息: SimpleWebPageReader
                      from llama_index.readers.web import SimpleWebPageReader
                      pages = ["https://mp.weixin.qq.com/s/prnDzOQ8HjUmonNp0jhRfw",]documents = SimpleWebPageReader(html_to_text=True).load_data(pages)print(f"Loaded {len(documents)} Web Pages")


                      示例6:配置数据转换管道


                      通过配置数据转换管道,可以实现数据转换并行处理以及知识库的去重,即默认的策略为更新插入(upserts)。


                        # 数据转换管道
                        from llama_index.core.ingestion import IngestionPipelinepipeline = IngestionPipeline(transformations=[spacy_text_splitter,bge_embed_model,],docstore=mongo_doc_store,vector_store=es_vector_store,)


                        接下来,运行数据转换管道,将分片后的文本向量化之后,存储到Elasticsearch向量数据库中。


                          # 生成索引存入向量数据库
                          nodes = pipeline.run(documents=documents)print(f"Ingested {len(nodes)} Nodes")print(f"Load {len(pipeline.docstore.docs)} documents into docstore")


                          示例7:创建索引


                          然后,基于上述生成的nodes,创建向量存储索引。


                            # 创建向量存储索引 
                            from llama_index.core import VectorStoreIndexindex = VectorStoreIndex(nodes, storage_context=storage_context)


                            示例8:定制中文Prompt模板


                            为了避免大模型用英文回答中文问题,我们需要定制LlamaIndex的Prompt模板。


                              # 定制中文Prompt
                              from llama_index.core import PromptTemplate
                              text_qa_template_str = ("以下为上下文信息\n""---------------------\n""{context_str}\n""---------------------\n""请根据上下文信息回答我的问题或回复我的指令。前面的上下文信息可能有用,也可能没用,你需要从我给出的上下文信息中选出与我的问题最相关的那些,来为你的回答提供依据。回答一定要忠于原文,简洁但不丢信息,不要胡乱编造。我的问题或指令是什么语种,你就用什么语种回复。\n""问题:{query_str}\n""你的回复:")


                              示例9:创建查询引擎


                              基于索引,创建查询引擎(Query Engine)。现在,可以针对知识库中的内容进行提问了。


                                # 创建查询引擎并查询
                                query_engine = index.as_query_engine(text_qa_template = text_qa_template,top_k=1,)
                                your_question = "什么是流程?"print(f"问题:{your_question}")response = query_engine.query(your_question)print(f"回答:{response}")


                                示例10:前端UI使用Streamlit


                                我们使用Streamlit构建一个Web UI,用于对话。


                                  # 使用Streamlit构建Web UI
                                  import streamlit as st
                                  TITLE = "本地大模型知识库问答"st.set_page_config(page_title=TITLE, page_icon="?", layout="centered", initial_sidebar_state="auto", menu_items=None)st.header(TITLE)st.info("By 大卫", icon="?")
                                  if "messages" not in st.session_state.keys(): # 初始化聊天历史记录st.session_state.messages = [{"role": "assistant", "content": "关于文档里的内容,请随便问"}]
                                  # 提示用户输入问题,并将问题添加到消息历史记录if prompt := st.chat_input("Your question"): st.session_state.messages.append({"role": "user", "content": prompt})
                                  # 显示此前的问答记录for message in st.session_state.messages: with st.chat_message(message["role"]):st.write(message["content"])
                                  # 生成回答if st.session_state.messages[-1]["role"] != "assistant": with st.chat_message("assistant"):with st.spinner("Thinking..."):response = query_engine.query(prompt)st.write(response.response)message = {"role": "assistant", "content": response.response}st.session_state.messages.append(message)


                                  以上全部代码,合起来形成一个文件app.py,可以通过以下命令运行。


                                    streamlit run app.py


                                    请注意提前通过pip命令,安装llama-index、streamlit、zh-core-web-sm、html2text等本程序运行所需的组件。


                                    53AI,企业落地应用大模型首选服务商

                                    产品:大模型应用平台+智能体定制开发+落地咨询服务

                                    承诺:先做场景POC验证,看到效果再签署服务协议。零风险落地应用大模型,已交付160+中大型企业

                                    联系我们

                                    售前咨询
                                    186 6662 7370
                                    预约演示
                                    185 8882 0121

                                    微信扫码

                                    与创始人交个朋友

                                    回到顶部

                                     
                                    扫码咨询