大模型RAG实战|向量数据库:Elasticsearch实现混合检索(附完整代码)

大模型RAG实战系列文章,带你深入探索使用LlamaIndex框架,构建本地大模型知识库问答系统。本系列涵盖知识库管理、检索优化、模型本地部署等主题,通过代码与实例,讲解如何打造生产级系统,实现本地知识库的快速检索与智能问答。

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

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

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

详情请参考官方文档:

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

1、选择向量数据库

最初,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 ElasticsearchStore
from 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 MongoDocumentStore
from llama_index.storage.index_store.mongodb import MongoIndexStore
from 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 Ollama
llm_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 HuggingFaceEmbedding
bge_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 Settings
Settings.llm = llm_ollama
Settings.embed_model = bge_embed_model

示例4:配置文本分割器

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

# 文本分割器: SpacyTextSplitter

from llama_index.core.node_parser import LangchainNodeParser
from langchain.text_splitter import SpacyTextSplitter
spacy_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 SimpleDirectoryReader
documents = 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 IngestionPipeline
pipeline = 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 VectorStoreIndex
index = 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等本程序运行所需的组件。

参考资料:

  • https://docs.llamaindex.ai/en/stable/examples/vector_stores/ElasticsearchIndexDemo/
  • https://docs.llamaindex.ai/en/stable/examples/docstore/MongoDocstoreDemo/

如何学习AI大模型?

作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

一、全套AGI大模型学习路线

AI大模型时代的学习之旅:从基础到前沿,掌握人工智能的核心技能!

img

二、640套AI大模型报告合集

这套包含640份报告的合集,涵盖了AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,这套报告合集都将为您提供宝贵的信息和启示。

img

三、AI大模型经典PDF籍

随着人工智能技术的飞速发展,AI大模型已经成为了当今科技领域的一大热点。这些大型预训练模型,如GPT-3、BERT、XLNet等,以其强大的语言理解和生成能力,正在改变我们对人工智能的认识。 那以下这些PDF籍就是非常不错的学习资源。

img

四、AI大模型商业化落地方案

img

作为普通人,入局大模型时代需要持续学习和实践,不断提高自己的技能和认知水平,同时也需要有责任感和伦理意识,为人工智能的健康发展贡献力量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值