1.文本分割和向量化
......
def create_db(files=DEFAULT_DB_PATH, persist_directory=DEFAULT_PERSIST_PATH, embeddings="openai"):
"""
该函数用于加载 PDF 文件,切分文档,生成文档的嵌入向量,创建向量数据库。
参数:
file: 存放文件的路径。
embeddings: 用于生产 Embedding 的模型
返回:
vectordb: 创建的数据库。
"""
if files == None:
return "can't load empty file"
if type(files) != list:
files = [files]
loaders = []
[file_loader(file, loaders) for file in files]
docs = []
for loader in loaders:
if loader is not None:
docs.extend(loader.load())
# 切分文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=150)
split_docs = text_splitter.split_documents(docs)
....
....
....此处省略了其他代码
....
return vectordb
...........
2.向量数据库
def create_db(files=DEFAULT_DB_PATH, persist_directory=DEFAULT_PERSIST_PATH, embeddings="openai"):
"""
该函数用于加载 PDF 文件,切分文档,生成文档的嵌入向量,创建向量数据库。
参数:
file: 存放文件的路径。
embeddings: 用于生产 Embedding 的模型
返回:
vectordb: 创建的数据库。
"""
if files == None:
return "can't load empty file"
if type(files) != list:
files = [files]
loaders = []
[file_loader(file, loaders) for file in files]
docs = []
for loader in loaders:
if loader is not None:
docs.extend(loader.load())
# 切分文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=150)
split_docs = text_splitter.split_documents(docs)
if type(embeddings) == str:
embeddings = get_embedding(embedding=embeddings)
# 定义持久化路径
persist_directory = '../../data_base/vector_db/chroma'
# 加载数据库
vectordb = Chroma.from_documents(
documents=split_docs,
embedding=embeddings,
persist_directory=persist_directory # 允许我们将persist_directory目录保存到磁盘上
)
vectordb.persist()
return vectordb
2.1检索-Retriver和生成-Generator
question="什么是机器学习"
Copy to clipboardErrorCopied
sim_docs = vectordb.similarity_search(question,k=3)
print(f"检索到的内容数:{len(sim_docs)}")
for i, sim_doc in enumerate(sim_docs):
print(f"检索到的第{i}个内容: \n{sim_doc.page_content[:200]}", end="\n--------------\n")
2.2大模型llm调用
def model_to_llm(model:str=None, temperature:float=0.0, appid:str=None, api_key:str=None,Spark_api_secret:str=None,Wenxin_secret_key:str=None):
"""
星火:model,temperature,appid,api_key,api_secret
百度问心:model,temperature,api_key,api_secret
智谱:model,temperature,api_key
OpenAI:model,temperature,api_key
"""
if model in ["gpt-3.5-turbo", "gpt-3.5-turbo-16k-0613", "gpt-3.5-turbo-0613", "gpt-4", "gpt-4-32k"]:
if api_key == None:
api_key = parse_llm_api_key("openai")
llm = ChatOpenAI(model_name = model, temperature = temperature , openai_api_key = api_key)
elif model in ["ERNIE-Bot", "ERNIE-Bot-4", "ERNIE-Bot-turbo"]:
if api_key == None or Wenxin_secret_key == None:
api_key, Wenxin_secret_key = parse_llm_api_key("wenxin")
llm = Wenxin_LLM(model=model, temperature = temperature, api_key=api_key, secret_key=Wenxin_secret_key)
elif model in ["Spark-1.5", "Spark-2.0"]:
if api_key == None or appid == None and Spark_api_secret == None:
api_key, appid, Spark_api_secret = parse_llm_api_key("spark")
llm = Spark_LLM(model=model, temperature = temperature, appid=appid, api_secret=Spark_api_secret, api_key=api_key)
elif model in ["chatglm_pro", "chatglm_std", "chatglm_lite"]:
if api_key == None:
api_key = parse_llm_api_key("zhipuai")
llm = ZhipuAILLM(model=model, zhipuai_api_key=api_key, temperature = temperature)
else:
raise ValueError(f"model{model} not support!!!")
return llm
2.3promp和构建问答链
from langchain.prompts import PromptTemplate
# template = """基于以下已知信息,简洁和专业的来回答用户的问题。
# 如果无法从中得到答案,请说 "根据已知信息无法回答该问题" 或 "没有提供足够的相关信息",不允许在答案中添加编造成分。
# 答案请使用中文。
# 总是在回答的最后说"谢谢你的提问!"。
# 已知信息:{context}
# 问题: {question}"""
template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答
案。最多使用三句话。尽量使答案简明扼要。总是在回答的最后说"谢谢你的提问!"。
{context}
问题: {question}
有用的回答:"""
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],
template=template)
# 运行 chain
# 自定义 QA 链
self.qa_chain = RetrievalQA.from_chain_type(llm=self.llm,
retriever=self.retriever,
return_source_documents=True,
chain_type_kwargs={"prompt":self.QA_CHAIN_PROMPT})
3.完整带记忆的检索问答链条代码
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from langchain.vectorstores import Chroma
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
import sys
sys.path.append('/Users/lta/Desktop/llm-universe/project')
from qa_chain.model_to_llm import model_to_llm
from qa_chain.get_vectordb import get_vectordb
import re
class Chat_QA_chain_self:
""""
带历史记录的问答链
- model:调用的模型名称
- temperature:温度系数,控制生成的随机性
- top_k:返回检索的前k个相似文档
- chat_history:历史记录,输入一个列表,默认是一个空列表
- history_len:控制保留的最近 history_len 次对话
- file_path:建库文件所在路径
- persist_path:向量数据库持久化路径
- appid:星火
- api_key:星火、百度文心、OpenAI、智谱都需要传递的参数
- Spark_api_secret:星火秘钥
- Wenxin_secret_key:文心秘钥
- embeddings:使用的embedding模型
- embedding_key:使用的embedding模型的秘钥(智谱或者OpenAI)
"""
def __init__(self,model:str, temperature:float=0.0, top_k:int=4, chat_history:list=[], file_path:str=None, persist_path:str=None, appid:str=None, api_key:str=None, Spark_api_secret:str=None,Wenxin_secret_key:str=None, embedding = "openai",embedding_key:str=None):
self.model = model
self.temperature = temperature
self.top_k = top_k
self.chat_history = chat_history
#self.history_len = history_len
self.file_path = file_path
self.persist_path = persist_path
self.appid = appid
self.api_key = api_key
self.Spark_api_secret = Spark_api_secret
self.Wenxin_secret_key = Wenxin_secret_key
self.embedding = embedding
self.embedding_key = embedding_key
self.vectordb = get_vectordb(self.file_path, self.persist_path, self.embedding,self.embedding_key)
def clear_history(self):
"清空历史记录"
return self.chat_history.clear()
def change_history_length(self,history_len:int=1):
"""
保存指定对话轮次的历史记录
输入参数:
- history_len :控制保留的最近 history_len 次对话
- chat_history:当前的历史对话记录
输出:返回最近 history_len 次对话
"""
n = len(self.chat_history)
return self.chat_history[n-history_len:]
def answer(self, question:str=None,temperature = None, top_k = 4):
""""
核心方法,调用问答链
arguments:
- question:用户提问
"""
if len(question) == 0:
return "", self.chat_history
if len(question) == 0:
return ""
if temperature == None:
temperature = self.temperature
llm = model_to_llm(self.model, temperature, self.appid, self.api_key, self.Spark_api_secret,self.Wenxin_secret_key)
#self.memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
retriever = self.vectordb.as_retriever(search_type="similarity",
search_kwargs={'k': top_k}) #默认similarity,k=4
qa = ConversationalRetrievalChain.from_llm(
llm = llm,
retriever = retriever
)
#print(self.llm)
result = qa({"question": question,"chat_history": self.chat_history}) #result里有question、chat_history、answer
answer = result['answer']
answer = re.sub(r"\n", '<br/>', answer)
self.chat_history.append((question,answer)) #更新历史记录
return self.chat_history #返回本次回答和更新后的历史记录