了解检索增强生成(RAG):
检索增强生成(RAG)是一种策略,它通过在大型语言模型(LLM)的文本生成过程中融入外部知识检索功能,来提升模型的功能。RAG并不依赖于模型内置的预训练知识,而是主动从知识库中获取相关内容,从而生成更准确、上下文更丰富的响应。这种方法在提供最新且领域特定的响应方面尤其有价值。
大型语言模型的局限性:
随着技术的进步,我们经常遇到新的模型,但这些模型都存在一些局限性:
1)知识有限:所有模型都是用固定的数据集进行训练的,之后无法获取新数据。
2)幻觉:当大型语言模型(LLM)对某个主题信息不足或上下文模糊时,它会“编造”内容,这些内容听起来往往合理但实际上是错误的。它更注重流畅性而非事实正确性。没有验证机制来检查这种幻觉。
3)上下文长度限制:LLM有有限的“注意力跨度”或上下文窗口,即它们一次只能高效处理这么多文本。这导致它们无法将长文档的前后部分联系在一起或记住这些内容。
4)缺乏透明度:LLM是“黑箱”,即其内部决策过程通常不透明,很难辨别为何会产生特定输出。这种不透明性使得依赖LLM输出成为一项挑战,尤其是在需要理解为何提出某个建议的高风险情况下。
何时以及为何选择RAG而非微调?
在需要模型重新训练的情况下,通常更倾向于使用RAG。
1)频繁更新和重新训练:通过使用RAG,可以消除重新训练模型的需求。重新训练需要大量的计算能力和资源。RAG可以高效地从知识库中检索所需信息,并将其解析给LLM。
2)可扩展性:为特定用例扩展大型语言模型(LLM)通常需要频繁重新训练并整合新的大规模知识库,这可能资源密集且耗时。然而,通过利用检索增强生成(RAG)技术,可以消除这一繁琐过程。RAG只需将新信息附加到现有知识库中,即可实现无缝集成,无需昂贵的重新训练。这种方法不仅提高了效率,还确保了LLM保持最新数据,使其成为适应不断变化的用例的可扩展且成本效益高的解决方案。
3)减少幻觉并提高准确性:RAG从知识库中检索领域特定信息,并将用户查询组合成LLM的提示。这减少了幻觉并提高了响应的准确性。
构建RAG聊天机器人涉及检索和生成组件。现在,让我们深入探讨如何使用ollama、Streamlit和Deepseek R1在本地构建RAG聊天机器人。
向量数据库和嵌入:
嵌入用于以向量的形式表示数据。可以将大量文本数据转换为携带语义信息的复杂向量,使模型能够理解不同数据之间的关系和相似性。通常通过将大量数据通过机器学习模型(如Word2Vec、GloVe、BERT、OpenAI的text-embedding-small、AWS Bedrock嵌入等)来创建嵌入。在我们的示例中,我们将使用Ollama中本地可用的mxbai-embed-large或nomic-embed-text。
向量数据库是一种专门用于存储和查询向量嵌入的数据库。与传统处理结构化数据(如数字、字符串)的数据库不同,向量数据库针对高维向量数据进行了优化。它们支持快速的相似性搜索。典型的向量数据库示例包括Pinecone、Chromadb、Weaviate、Faiss、pgvector等。在本教程中,我们将使用Chromadb来存储我们的向量嵌入。
让我们从下载Ollama开始实施。Ollama适用于Windows和Mac系统。我在这个项目中使用的是Windows机器,但Mac系统的实施方式也非常相似。
1) 安装Ollama。打开你的命令提示符/终端,并使用以下命令检查安装是否完成。
ollama serve
2) 让我们开始构建向量数据库和嵌入。在本教程中,我们将使用“mxbai-embed-large”嵌入。我们可以通过以下命令来获取这个嵌入模型。
ollama pull nomic-embed-text
3) 接下来,我们使用以下命令在Ollama上拉取deepseek R1 7b模型。
ollama pull ollama run deepseek-r1:7b
4) 接下来,我们开始导入依赖项。
下面列出所需的依赖项:
a) Langchain:LangChain是一个开源框架,用于使用大型语言模型(LLM)构建AI应用程序,支持检索增强生成(RAG)、聊天机器人、工作流、内存管理、提示优化、代理、问答系统、推荐和代码生成。
b) Chroma DB:Chroma DB是一个开源向量数据库,用于存储和检索向量嵌入。
c) Streamlit:Streamlit是一个免费的开源Python库,旨在帮助数据科学家和AI/ML工程师以最少的编码工作量快速创建和部署交互式数据应用程序。
chromadb==0.6.3
langchain-community==0.2.16
streamlit==1.41.1
上述要求可以列在requirements.txt文件中,并使用pip install -r /path/to/requirements.txt命令在Python环境中安装。
5) 接下来,我们导入我们的嵌入模型:
from langchain_community.embeddings.ollama import OllamaEmbeddings
def get_embedding_function():
embeddings = OllamaEmbeddings(model="nomic-embed-text")
return embeddings
6) 接下来,我们加载文档,对其进行分块,并使用嵌入模型对其进行向量化,然后存储在向量数据库中。
from langchain_community.document_loaders import PyPDFLoader
from langchain.document_loaders.pdf import PyPDFDirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.vectorstores.chroma import Chroma
def load_documents():
document_loader = PyPDFLoader(data_path)
return document_loader.load()
def split_documents(documents: list[Document]):
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=80,
length_function=len,
is_separator_regex=False,
)
return text_splitter.split_documents(documents)
def add_to_db(chunks: list[Document]):
db = Chroma(
persist_directory=CHROMA_PATH, embedding_function=get_embedding_function()
)
chunks_with_ids = calculate_chunk_ids(chunks) # Giving each chunk an ID
existing_items = db.get(include=[]) # IDs are always included by default
existing_ids = set(existing_items["ids"])
print(f"Number of existing documents in DB: {len(existing_ids)}")
# Only add documents that don't exist in the DB.
new_chunks = []
for chunk in chunks_with_ids:
if chunk.metadata["id"] not in existing_ids:
new_chunks.append(chunk)
7) 向量信息检索:
聊天机器人中的用户查询通过嵌入模型获得查询的向量表示。之后,执行相似性搜索以从向量数据库中找到最相似的信息。通过提示工程,将从相似性搜索中获得的所有内容传递给大型语言模型(LLM)。
# Search the DB.
results = db.similarity_search_with_score(query_text, k=5)
context = "\n\n---\n\n".join([doc.page_content for doc, _score in results])
8) 提示工程:
提示工程是设计有效输入提示以引导大型语言模型(LLM)生成期望输出的艺术。它是撰写清晰、具体且上下文相关的指令的过程,以最大限度地提高人工智能生成输出的准确性和相关性。
对于基于检索增强生成(RAG)的模型,提示由相似性搜索中的上下文和用户查询组成。
from langchain.prompts import ChatPromptTemplate
PROMPT = """
Answer the question based only on the following context:
{context}
---
Answer the question based on the above context, If the above context does
not have any information about the question asked, Please tell that you do not have
any information regarding the question asked: {question}
"""
prompt_template = ChatPromptTemplate.from_template(PROMPT)
prompt = prompt_template.format(context=context, question=query)
9) 现在我们已经加载、分块并将数据添加到了向量数据库中。接下来,让我们构建查询函数,该函数涉及将用户提示与上下文结合后调用大型语言模型(LLM)。
from langchain.vectorstores.chroma import Chroma
from langchain_community.llms.ollama import Ollama
CHROMA_PATH = "chroma"
def query_rag(query_text: str):
# Prepare the DB.
embedding_function = get_embedding_function()
db = Chroma(persist_directory=CHROMA_PATH, embedding_function=embedding_function)
llm_model = Ollama(model="deepseek-r1")
response_text = llm_model.invoke(prompt)
return response_text
10) 现在,让我们来完成这个拼图的最后一块,用Streamlit构建一个聊天机器人:
import streamlit as st
st.set_page_config(page_title="FactBot: No Hallucinations, Just Facts")
with st.sidebar:
st.title('FactBot')
# Function for generating LLM response
def generate_response(input):
result = query_rag(input)
return result
# Store LLM generated responses
if "messages" not in st.session_state.keys():
st.session_state.messages = [{"role": "assistant", "content":
"Welcome, get ready to be mind blown by AI"}]
# Display chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.write(message["content"])
# User-provided prompt
if input := st.chat_input():
st.session_state.messages.append({"role": "user", "content": input})
with st.chat_message("user"):
st.write(input)
# Generate a new response if last message is not from assistant
if st.session_state.messages[-1]["role"] != "assistant":
with st.chat_message("assistant"):
with st.spinner("Getting your answer from superior intelligence.."):
response = generate_response(input)
st.write(response)
message = {"role": "assistant", "content": response}
st.session_state.messages.append(message)
要运行Streamlit应用程序,请使用命令:streamlit run /path/to_your_chatbotFile/chatbot.py