大模型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等本程序运行所需的组件。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。