在不断演进的人工智能领域,追求更智能、更响应、更情境意识的聊天机器人将我们带入了一个新时代的大门。欢迎来到RAG的世界——检索增强生成(RAG),一种革命性的方式,它结合了检索系统的丰富知识与生成模型的创意能力。RAG技术使聊天机器人通过访问知识库,可以有效处理各种用户查询。但要充分利用这种能力,我们需要一个能够匹配其速度和效率的存储方案。这时,向量数据库就显得尤为重要,提供了管理与检索海量数据的巨大飞跃,实现了质的飞跃。
在这篇文章中,我们将展示如何使用Google Gemini模型和MyScaleDB只需几分钟来构建一个使用RAG技术的聊天机器人。
为了开始我们的chatbot开发旅程,我们需要确保所需的依赖项已准备好。以下是所需工具的清单:
如果您的系统中已经安装了Python,您可以跳过这一步。否则,请按照下面的步骤进行。
要安装所有这些依赖包,请在终端中输入以下命令:
pip 安装 gemini-api, langchain 和 clickhouse-client
刚才的命令应安装所有开发聊天程序所需的软件包。现在我们开始吧。
我们正在为公司员工打造一个专属聊天机器人。这个聊天机器人将帮助员工解答关于公司政策的各种问题。从了解着装规范到弄清楚休假规定,聊天机器人将提供快速准确的信息。
第一步是使用LangChain的PyPDFLoader
模块加载数据文件并分割数据。
from langchain_community.document_loaders 导入 PyPDFLoader模块 loader = PyPDFLoader('Employee_Handbook.pdf') # 例如,加载员工手册 pages = loader.load_and_split() pages = pages[4:] # 跳过前面几页,因为这些页面不需要 text = "\n".join([doc.page_content for doc in pages]) # 将所有页面内容合并为一个字符串,每页内容之间用换行符分隔
我们加载文档并将其拆分成页面,跳过前面几页。接下来,我们将所有页面的文本拼接成一个单一的字符串。
说明:我们正在使用来自这个Kaggle仓库的手册。
接下来,我们将会把这段文本分成更小的块,让聊天机器人处理起来更方便。
import langchain_text_splitters from langchain_text_splitters导入RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 每个块的大小为500字符 chunk_overlap=150, # 块之间的重叠部分为150字符 length_function=len, # 使用len函数计算文本长度 is_separator_regex=False, # 分隔符不是正则表达式 ),这里我们创建了一个文本分割器对象,用于将文本分割成指定大小的块。\n docs = text_splitter.create_documents([text]),这里我们使用text_splitter将文本分割成一系列文档。\n for循环遍历每个文档并为其分配一个doc_id。\n for i, d in enumerate(docs): d.metadata = {"doc_id": i},这里我们为每个文档添加一个元数据项,用于标识文档的唯一ID。\n
我们使用 RecursiveCharacterTextSplitter
将文本每500个字符拆分为一段,每段之间保留150个字符的重叠,以确保连续性。
为了让我们的聊天机器人能够理解并检索相关信息,我们需要为每段文本生成嵌入向量。这些嵌入向量是文本的数值表示,捕捉文本的语义信息。
import os import google.generativeai as genai import pandas as pd os.environ["GEMINI_API_KEY"] = "your_key_here" # 此函数接受一个句子作为输入参数,并返回该句的嵌入 def get_embeddings(text): # 定义嵌入模型 model = 'models/embedding-001' # 获取嵌入 embedding = genai.embed_content(model=model, content=text, task_type="retrieval_document") return embedding['embedding'] # 从文档中获取页面内容并创建一个新的列表 content_list = [doc.page_content for doc in docs] # 一次处理一个页面内容 embeddings = [get_embeddings(content) for content in content_list] # 创建一个数据框以便将其上传到数据库 dataframe = pd.DataFrame({ 'page_content': content_list, 'embeddings': embeddings })
我们定义了一个名为 get_embeddings
的函数,该函数来使用 Google Gemini 为每段文本生成嵌入。这些嵌入存储在一个 DataFrame 中,以供进一步处理。
注意:我们正在使用 Gemini 模型中的 embedding-001
模型(来自 Gemini 模型),您可以在 这里 获取 Gemini API 密钥。
有了文本块及其对应的嵌入向量准备好之后,下一步就是将这些数据存储到MyScaleDB中。这将使我们能够高效地进行检索操作。我们先连接到MyScaleDB。
import clickhouse_connect client = clickhouse_connect.get_client( host='your_host_name', port='port_number', username='your_username', password='your_password_here' )
请参考快速入门指南获取您的MyScaleDB账户的凭据。请访问快速入门指南。
建立数据库连接后,下一步是创建一个表(因为MyScaleDB是一款基于SQL的矢量数据库),并将数据插入进去。
# 创建一个名为 'handbook' 的表 client.command(""" CREATE TABLE default.handbook ( id Int64, page_content String, embeddings Array(Float32), CONSTRAINT check_data_length CHECK length(embeddings) = 768 ) ENGINE = MergeTree() ORDER BY id """) # 这将确保每个嵌入向量的长度为 768 # 设置批处理大小为 10 batch_size = 10 # 计算总批次数 num_batches = len(dataframe) // batch_size for i in range(num_batches): # 计算当前批次的开始索引 start_idx = i * batch_size # 计算当前批次的结束索引 end_idx = start_idx + batch_size # 获取当前批次的数据 batch_data = dataframe[start_idx:end_idx] # 将当前批次的数据插入到 'handbook' 表中 client.insert("default.handbook", batch_data.to_records(index=False).tolist(), column_names=batch_data.columns.tolist()) # 打印(f"批次 {i+1}/{num_batches} 已插入.") print(f"批次 {i+1}/{num_batches} 已插入.") # 创建向量索引以实现快速检索数据 client.command(""" ALTER TABLE default.handbook ADD VECTOR INDEX vector_index embeddings TYPE MSTG -- 类型 MSTG,这是一种高效的向量索引类型 """)
数据分批插入以提高效率,并添加了向量索引(vector index)以实现快速相似搜索。
一旦数据被存储,下一步就是使用这些嵌入来检索与用户输入的查询最相关的文档。
def get_relevant_docs(user_query): # 将用户查询转换为向量嵌入 query_embeddings = get_embeddings(user_query) # 进行查询 results = client.query(f""" SELECT page_content, distance(embeddings, {query_embeddings}) as dist FROM default.handbook ORDER BY dist LIMIT 3 """) 相关文档列表 = [] # 遍历结果 for row in results.named_results(): 相关文档列表.append(row['page_content']) return 相关文档列表
此功能首先为用户的查询生成嵌入向量,然后根据嵌入向量的相似性从数据库中检索出最相关的三个文本段落。
这样一来,最后我们用找到的相关文件来生成回复用户的问题。
def make_rag_prompt(query, relevant_passage): relevant_passage = ' '.join(relevant_passage) prompt = ( f"你是一个乐于助人的助手,会使用下面提供的参考文本回答问题。" f"请用完整的句子作答,并确保所有人都能轻松理解。" f"保持友好的对话语气。如果文本不相关,你可以忽略它。\n\n" f"问题:'{query}'\n" f"文本:'{relevant_passage}'\n\n" f"回答:" ) return prompt import google.generativeai as genai def generate_response(user_prompt): model = genai.GenerativeModel('gemini-pro') answer = model.generate_content(user_prompt) return answer.text def generate_answer(query): relevant_text = get_relevant_docs(query) text = " ".join(relevant_text) prompt = make_rag_prompt(query, relevant_passage=relevant_text) answer = generate_response(prompt) return answer answer = generate_answer(query="工作着装规范是什么?") print(answer)
make_rag_prompt
函数创建一个用于聊天机器人的提示。generate_response
函数使用 Google Gemini 根据提示生成回复,而 generate_answer
函数则将所有这些步骤整合在一起,检索相关文档并根据用户查询生成回复。
注意:在本博文中,我们使用Gemini Pro 1.0,因为它在免费层中每分钟可以处理更多的请求。尽管Gemini还提供了更高级的模型,如Gemini 1.5 Pro和Gemini 1.5 Flash,这些模型在免费层中的限制更严格,对于广泛使用则成本更高。
以下是聊天机器人的几个回复:
当有人问聊天机器人办公室午餐时间时:
通过将这些步骤整合到您的聊天机器人的开发中,您可以利用 Google Gemini 和 MyScaleDB 的力量来构建一个高级且由 AI 驱动的聊天机器人。实验至关重要;不断调整您的聊天机器人以提升其性能。保持好奇心和创新精神,看着您的聊天机器人逐渐成为会话界的奇迹!
RAG 的出现彻底改变了聊天机器人的开发过程,通过整合如 Gemini 或 GPT 这样的大型语言模型。这些先进的 LLM 可以从向量数据库中检索相关信息,生成更准确、更符合事实且上下文相关的回答。这一转变不仅节省了开发时间和成本,还通过提供更智能且响应迅速的聊天机器人,显著提升了用户体验。
RAG模型的性能高度依赖于其向量数据库的效率。向量数据库快速检索相关文档的能力对提供快速响应至关重要。在扩展RAG系统时,保持这种高性能变得尤为重要。MyScaleDB因其高可扩展性及其快速查询响应而成为理想选择。新用户还能享受到500万免费的向量存储,这对于开发小型应用非常有帮助,不容错过。
如果你想与我们多聊一聊,欢迎加入 MyScale Discord 分享你的想法和建议!