源码:https://github.com/datawhalechina/llm-universe
源码勘误:源码中,构建向量数据库部分,数据库的存储地址有误,请留意。
本文以智普为例
使用llm api
- 顺利调用接口:在各家大模型官网上获取api key,按照官方文档示例调用接口。
- 了解提示词工程。
搭建向量数据库
- 词向量:词向量(Embeddings)是一种将非结构化数据,如单词、句子或者整个文档,转化为实数向量的技术。这些实数向量可以被计算机更好地理解和处理。生成词向量的方式:可以调用各公司的词向量接口,或在本地用嵌入模型;本文使用公司接口。
- 向量数据库:向量数据库是一种专门用于存储和检索向量数据(embedding)的数据库系统。它与传统的基于关系模型的数据库不同,它主要关注的是向量数据的特性和相似性。本文使用chroma
- 搭建向量数据库:先用langchain加载、存储、分割(因为大模型有渡人文本长度限制)数据(中间可以加入数据清洗),再将处理好的数据存入向量数据库并持久化。
构建rag应用
- 前置步骤为加载向量数据库、构建一个llm;
- 构建检索链,核心是from langchain.chains import RetrievalQA,RetrievalQA.from_chain_type的参数中包含了向量数据库,使得llm的回答定制化。
部署应用程序
使用streamlit完成轻松部署,下面给出使用智普时streamlit_app.py的代码:
import streamlit as st
from langchain_openai import ChatOpenAI
import os
from langchain_core.output_parsers import StrOutputParser
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
import sys
sys.path.append("../C3 搭建知识库") # 将父目录放入系统路径中
from zhipuai_embedding import ZhipuAIEmbeddings
from langchain.vectorstores.chroma import Chroma
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) # read local .env file
## copy自llm接入langchain
from zhipuai_llm import ZhipuAILLM
# 获取环境变量 API_KEY
api_key = os.environ["ZHIPUAI_API_KEY"] #填写控制台中获取的 APIKey 信息
zhipuai_model = ZhipuAILLM(model="chatglm_std", temperature=0, api_key=api_key)
#export OPENAI_API_KEY=
#os.environ["OPENAI_API_BASE"] = 'https://api.chatgptid.net/v1'
# zhipuai_api_key = os.environ['ZHIPUAI_API_KEY']
def generate_response(input_text, api_key):
zhipuai_model = ZhipuAILLM(model="chatglm_std", temperature=0, api_key=api_key)
output = zhipuai_model(input_text)
output_parser = StrOutputParser()
output = output_parser.invoke(output)
#st.info(output)
return output
def get_vectordb():
# 定义 Embeddings
embedding = ZhipuAIEmbeddings()
# 向量数据库持久化路径
persist_directory = '../../data_base/vector_db/chroma'
# 加载数据库
vectordb = Chroma(
persist_directory=persist_directory, # 允许我们将persist_directory目录保存到磁盘上
embedding_function=embedding
)
return vectordb
#带有历史记录的问答链
def get_chat_qa_chain(question:str,api_key:str):
vectordb = get_vectordb()
zhipuai_model = ZhipuAILLM(model="chatglm_std", temperature=0, api_key=api_key)
memory = ConversationBufferMemory(
memory_key="chat_history", # 与 prompt 的输入变量保持一致。
return_messages=True # 将以消息列表的形式返回聊天记录,而不是单个字符串
)
retriever=vectordb.as_retriever()
qa = ConversationalRetrievalChain.from_llm(
zhipuai_model,
retriever=retriever,
memory=memory
)
result = qa({"question": question})
return result['answer']
#不带历史记录的问答链
def get_qa_chain(question:str,api_key:str):
vectordb = get_vectordb()
zhipuai_model = ZhipuAILLM(model="chatglm_std", temperature=0, api_key=api_key)
template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
"""
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
template=template)
qa_chain = RetrievalQA.from_chain_type(zhipuai_model,
retriever=vectordb.as_retriever(),
return_source_documents=True,
chain_type_kwargs={"prompt":QA_CHAIN_PROMPT})
result = qa_chain({"query": question})
return result["result"]
# Streamlit 应用程序界面
def main():
st.title('🦜🔗 动手学大模型应用开发')
api_key = st.sidebar.text_input('zhipuai API Key', type='password')
# 添加一个选择按钮来选择不同的模型
#selected_method = st.sidebar.selectbox("选择模式", ["qa_chain", "chat_qa_chain", "None"])
selected_method = st.radio(
"你想选择哪种模式进行对话?",
["None", "qa_chain", "chat_qa_chain"],
captions = ["不使用检索问答的普通模式", "不带历史记录的检索问答模式", "带历史记录的检索问答模式"])
# 用于跟踪对话历史
if 'messages' not in st.session_state:
st.session_state.messages = []
messages = st.container(height=300)
if prompt := st.chat_input("Say something"):
# 将用户输入添加到对话历史中
st.session_state.messages.append({"role": "user", "text": prompt})
if selected_method == "None":
# 调用 respond 函数获取回答
answer = generate_response(prompt, api_key)
elif selected_method == "qa_chain":
answer = get_qa_chain(prompt,api_key)
elif selected_method == "chat_qa_chain":
answer = get_chat_qa_chain(prompt,api_key)
# 检查回答是否为 None
if answer is not None:
# 将LLM的回答添加到对话历史中
st.session_state.messages.append({"role": "assistant", "text": answer})
# 显示整个对话历史
for message in st.session_state.messages:
if message["role"] == "user":
messages.chat_message("user").write(message["text"])
elif message["role"] == "assistant":
messages.chat_message("assistant").write(message["text"])
if __name__ == "__main__":
main()
可以在vscode终端切换到streamlit_app.py所在文件夹并运行。以上代码可以在本地运行成功。