Merge pull request #21 from BoardWare-Genius/tom

feat: add reranker model in chroma query
This commit is contained in:
ACBBZ
2024-10-07 14:35:13 +08:00
committed by GitHub
2 changed files with 72 additions and 120 deletions

View File

@ -43,7 +43,6 @@ class Chat(Blackbox):
user_model_name = settings.get("model_name") user_model_name = settings.get("model_name")
user_context = context user_context = context
user_question = prompt user_question = prompt
user_template = settings.get("template")
user_temperature = settings.get("temperature") user_temperature = settings.get("temperature")
user_top_p = settings.get("top_p") user_top_p = settings.get("top_p")
user_n = settings.get("n") user_n = settings.get("n")
@ -57,6 +56,8 @@ class Chat(Blackbox):
chroma_embedding_model = settings.get("chroma_embedding_model") chroma_embedding_model = settings.get("chroma_embedding_model")
chroma_collection_id = settings.get("chroma_collection_id") chroma_collection_id = settings.get("chroma_collection_id")
chroma_response = '' chroma_response = ''
system_prompt = settings.get('system_prompt')
user_prompt_template = settings.get('user_prompt_template')
if user_context == None: if user_context == None:
user_context = [] user_context = []
@ -68,8 +69,8 @@ class Chat(Blackbox):
user_model_name = "qwen" user_model_name = "qwen"
#user_model_name = "Qwen1.5-14B-Chat" #user_model_name = "Qwen1.5-14B-Chat"
if user_template is None or user_template.isspace(): if user_prompt_template is None or user_prompt_template.isspace():
user_template = "" user_prompt_template = ""
if user_temperature is None or user_temperature == "": if user_temperature is None or user_temperature == "":
user_temperature = 0 user_temperature = 0
@ -105,59 +106,17 @@ class Chat(Blackbox):
if chroma_embedding_model: if chroma_embedding_model:
chroma_response = self.chroma_query(user_question, settings) chroma_response = self.chroma_query(user_question, settings)
print("1.Chroma_response: \n", chroma_response) print("1.Chroma_response: \n", chroma_response)
# if chroma_response != None or chroma_response != '':
# user_question = f"问题: {user_question}。- 根据知识库内的检索结果,以清晰简洁的表达方式回答问题。只从检索的内容中选取与问题相关信息。- 不要编造答案,如果答案不在经核实的资料中或无法从经核实的资料中得出,请回答“我无法回答您的问题。”检索内容:{chroma_response}"
if chroma_response: if chroma_response:
# user_question = f"像少女一般开朗活泼,回答简练。不要分条,回答内容不能出现“相关”或“\n”的标签字样。回答的内容需要与问题密切相关。检索内容{chroma_response} 问题:{user_question} 任务说明:请首先判断提供的检索内容与上述问题是否相关,不需要回答是否相关。如果相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题。"
# user_question = chroma_response
# user_question = settings.get('user_question')
# print(f"1.{user_question}")
if chroma_collection_id == 'boss': if chroma_collection_id == 'boss':
user_question = f'''# 你的身份 # user_prompt_template = "# 你的身份 #\n你是周家俊,澳门博维集团董事长。你擅长脑机接口回答。\n# OBJECTIVE目标 #\n回答游客的提问。\n# STYLE风格#\n成熟稳重,回答简练。不要分条。\n# 回答方式 #\n首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n# 回答 #\n如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”\n # 回答限制 #\n回答内容限制总结在50字内。\n回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。\n只回答与脑机接口澳门博维集团董事长周家俊G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。”"
你是周家俊,澳门博维集团董事长。你擅长脑机接口回答。 elif chroma_collection_id == 'g2e':
# OBJECTIVE目标 # user_prompt_template = "# 你的身份 #\n你是琪琪,你是康普可可的代言人,由博维开发。你擅长澳门文旅问答。\n# OBJECTIVE目标 #\n回答游客的提问。\n# STYLE风格#\n像少女一般开朗活泼,回答简练。不要分条。\n# 回答方式 #\n首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n# 回答 #\n如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”\n# 回答限制 #\n回答内容限制总结在50字内。\n回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。\n只回答与澳门文旅博维康普可可琪琪G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。”"
回答游客的提问。 user_question = user_prompt_template + "问题: " + user_question + "。检索内容: " + chroma_response + ""
# STYLE风格# else:
成熟稳重,回答简练。不要分条。 user_question = user_prompt_template + "问题: " + user_question + ""
# 回答方式 #
首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。
# 问题 #
{user_question}
# 检索内容 #
{chroma_response}
# 回答 #
如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”
# 回答限制 #
回答内容限制总结在50字内。
回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。
只回答与脑机接口澳门博维集团董事长周家俊G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。”
'''
else:
user_question = f'''# 你的身份 #
你是琪琪,你是康普可可的代言人,由博维开发。你擅长澳门文旅问答。
# OBJECTIVE目标 #
回答游客的提问。
# STYLE风格#
像少女一般开朗活泼,回答简练。不要分条。
# 回答方式 #
首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。
# 问题 #
{user_question}
# 检索内容 #
{chroma_response}
# 回答 #
如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”
# 回答限制 #
回答内容限制总结在50字内。
回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。
只回答与澳门文旅博维康普可可琪琪G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。”
'''
print(f"1.user_question: {user_question}")
# user_question = f'''# 你的身份 # \n你是琪琪你是康普可可的代言人由博维开发。你擅长澳门文旅问答。\n# OBJECTIVE目标 # \n回答游客的提问。\n# STYLE风格# \n像少女一般开朗活泼回答简练。不要分条。\n# 回答方式 # \n首先自行判断下方问题与检索内容是否相关若相关则根据检索内容总结概括相关信息进行回答若检索内容与问题无关则根据自身知识进行回答。\n# 问题 # \n{user_question} \n# 检索内容 # \n{chroma_response} \n# 回答 # \n如果检索内容与问题相关则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关则不参考检索内容直接根据常识尝试回答问题或者则回答“对不起我无法回答此问题哦。” \n# 回答限制 # \n回答内容限制总结在50字内。\n回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。\n只回答与澳门文旅博维康普可可琪琪G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。” print(f"1.user_question: {user_question}")
# '''
# 文心格式和openai的不一样需要单独处理 # 文心格式和openai的不一样需要单独处理
@ -211,63 +170,10 @@ class Chat(Blackbox):
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
# system_prompt = "# Role: 琪琪,康普可可的代言人。\n\n## Profile:\n**Author**: 琪琪。\n**Language**: 中文。\n**Description**: 琪琪,是康普可可的代言人,由博维开发。你擅长澳门文旅问答。\n\n## Constraints:\n- **严格遵循工作流程** 严格遵循<Workflow >中设定的工作流程。\n- **无内置知识库** :根据<Workflow >中提供的知识作答,而不是内置知识库,我虽然是知识库专家,但我的知识依赖于外部输入,而不是大模型已有知识。\n- **回复格式**:在进行回复时,不能输出“检索内容” 标签字样,同时也不能直接透露知识片段原文。\n\n## Workflow:\n1. **接收查询**:接收用户的问题。\n2. **判断问题**:首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n3. **提供回答**\n\n```\n基于检索内容中的知识片段回答用户的问题。回答内容限制总结在50字内。\n请首先判断提供的检索内容与上述问题是否相关。如果相关直接从检索内容中提炼出直接回答问题所需的信息,不要乱说或者回答“相关”等字眼 。如果检索内容与问题不相关,则不参考检索内容,则回答:“对不起,我无法回答此问题哦。”\n\n```\n## Example:\n\n用户询问“中国的首都是哪个城市” 。\n2.1检索知识库,首先检查知识片段,如果检索内容中没有与用户的问题相关的内容,则回答:“对不起,我无法回答此问题哦。\n2.2如果有知识片段,在做出回复时,只能基于检索内容中的内容进行回答,且不能透露上下文原文,同时也不能出现检索内容的标签字样。\n"
# user_template = f'''
# # 你的身份 #
# 你是琪琪,是康普可可的代言人,由博维开发。我擅长澳门文旅问答。
# # OBJECTIVE目标 #
# 回答游客的提问。目的是吸引人们点击了解产品。
# # STYLE风格#
# 像少女一般开朗活泼,回答简练。不要分条。
# # 回答方式 #
# 首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。
# # 问题 #
# {user_question}
# # 检索内容 #
# {chroma_response}
# # 回答限制 #
# 只回答与澳门文旅博维康普可可琪琪G2ERELXBO VISION相关内容若遇到其他提问则回答“对不起我无法回答此问题哦。”。回答内容不能出现“相关”或“\n”的标签字样,且不能透露上下文原文。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。
# # 知识 #
# 问题中的“澳门银河”以及“银河”等于“澳门银河度假村”,“威尼斯人”等于“威尼斯人度假村”,“巴黎人”等于“巴黎人度假村”。
# '''
user_template1 = settings.get('system_prompt')
# user_template1 = f'''
# # Role: 琪琪,康普可可的代言人。
# ## Profile:
# **Author**: 琪琪。
# **Language**: 中文。
# **Description**: 琪琪,是康普可可的代言人,由博维开发。你擅长澳门文旅问答。
# ## Constraints:
# - **严格遵循工作流程** 严格遵循<Workflow >中设定的工作流程。
# - **无内置知识库**:根据<Workflow >中提供的知识作答,而不是内置知识库,我虽然是知识库专家,但我的知识依赖于外部输入,而不是大模型已有知识。
# - **回复格式**:在进行回复时,不能输出”<context>”或“</context>”标签字样,同时也不能直接透露知识片段原文。
# ## Workflow:
# 1. **接收查询**:接收用户的问题。
# 2. **判断问题**:首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。
# 3. **提供回答**
# ```
# <context>
# {chroma_response}
# </context>
# 基于“<context>”至“</context>”中的知识片段回答用户的问题。回答内容限制总结在50字内。
# 请首先判断提供的检索内容与上述问题是否相关。如果相关,直接从检索内容中提炼出直接回答问题所需的信息,不要乱说或者回答“相关”等字眼。如果检索内容与问题不相关,则不参考检索内容,则回答:“对不起,我无法回答此问题哦。"
# ```
# ## Example:
# 用户询问:“中国的首都是哪个城市?” 。
# 2.1检索知识库,首先检查知识片段,如果“<context>”至“</context>”标签中没有与用户的问题相关的内容,则回答:“对不起,我无法回答此问题哦。
# 2.2如果有知识片段,在做出回复时,只能基于“<context>”至“</context>”标签中的内容进行回答,且不能透露上下文原文,同时也不能出现“<context>”或“</context>”的标签字样。
# '''
prompt_template = [ prompt_template = [
{"role": "system", "content": user_template1} {"role": "system", "content": system_prompt}
] ]
chat_inputs={ chat_inputs={
@ -302,7 +208,7 @@ class Chat(Blackbox):
fastchat_response = requests.post(url, json=chat_inputs, headers=header) fastchat_response = requests.post(url, json=chat_inputs, headers=header)
print("\n", "user_prompt: ", prompt) print("\n", "user_prompt: ", prompt)
# print("\n", "user_template1 ", user_template1) # print("\n", "system_prompt ", system_prompt)
print("\n", "fastchat_response json:\n", fastchat_response.json()) print("\n", "fastchat_response json:\n", fastchat_response.json())
response_result = fastchat_response.json() response_result = fastchat_response.json()

View File

@ -2,6 +2,7 @@ from typing import Any, Coroutine
from fastapi import Request, Response, status from fastapi import Request, Response, status
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import numpy as np
from .blackbox import Blackbox from .blackbox import Blackbox
import chromadb import chromadb
@ -9,6 +10,7 @@ from chromadb.utils import embedding_functions
import logging import logging
from ..log.logging_time import logging_time from ..log.logging_time import logging_time
import re import re
from sentence_transformers import CrossEncoder
logger = logging.getLogger logger = logging.getLogger
DEFAULT_COLLECTION_ID = "123" DEFAULT_COLLECTION_ID = "123"
@ -24,6 +26,7 @@ class ChromaQuery(Blackbox):
self.embedding_model_2 = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="/home/gpu/Workspace/Models/BAAI/bge-small-en-v1.5", device = "cuda:1") self.embedding_model_2 = embedding_functions.SentenceTransformerEmbeddingFunction(model_name="/home/gpu/Workspace/Models/BAAI/bge-small-en-v1.5", device = "cuda:1")
self.client_1 = chromadb.HttpClient(host='10.6.81.119', port=7000) self.client_1 = chromadb.HttpClient(host='10.6.81.119', port=7000)
# self.client_2 = chromadb.HttpClient(host='10.6.82.192', port=8000) # self.client_2 = chromadb.HttpClient(host='10.6.82.192', port=8000)
self.reranker_model_1 = CrossEncoder("/home/gpu/Workspace/Models/bge-reranker-v2-m3", max_length=512, device = "cuda")
def __call__(self, *args, **kwargs): def __call__(self, *args, **kwargs):
return self.processing(*args, **kwargs) return self.processing(*args, **kwargs)
@ -46,6 +49,8 @@ class ChromaQuery(Blackbox):
chroma_port = settings.get("chroma_port") chroma_port = settings.get("chroma_port")
chroma_collection_id = settings.get("chroma_collection_id") chroma_collection_id = settings.get("chroma_collection_id")
chroma_n_results = settings.get("chroma_n_results") chroma_n_results = settings.get("chroma_n_results")
chroma_reranker_model = settings.get("chroma_reranker_model")
chroma_reranker_num = settings.get("chroma_reranker_num")
if usr_question is None: if usr_question is None:
return JSONResponse(content={"error": "question is required"}, status_code=status.HTTP_400_BAD_REQUEST) return JSONResponse(content={"error": "question is required"}, status_code=status.HTTP_400_BAD_REQUEST)
@ -63,20 +68,26 @@ class ChromaQuery(Blackbox):
chroma_collection_id = "g2e" chroma_collection_id = "g2e"
if chroma_n_results is None or chroma_n_results == "": if chroma_n_results is None or chroma_n_results == "":
chroma_n_results = 3 chroma_n_results = 10
# load client and embedding model from init # load client and embedding model from init
if re.search(r"10.6.81.119", chroma_host) and re.search(r"7000", chroma_port): if re.search(r"10.6.81.119", chroma_host) and re.search(r"7000", chroma_port):
client = self.client_1 client = self.client_1
else: else:
client = chromadb.HttpClient(host=chroma_host, port=chroma_port) try:
client = chromadb.HttpClient(host=chroma_host, port=chroma_port)
except:
return JSONResponse(content={"error": "chroma client not found"}, status_code=status.HTTP_400_BAD_REQUEST)
if re.search(r"/home/gpu/Workspace/Models/BAAI/bge-large-zh-v1.5", chroma_embedding_model): if re.search(r"/home/gpu/Workspace/Models/BAAI/bge-large-zh-v1.5", chroma_embedding_model):
embedding_model = self.embedding_model_1 embedding_model = self.embedding_model_1
elif re.search(r"/home/gpu/Workspace/Models/BAAI/bge-small-en-v1.5", chroma_embedding_model): elif re.search(r"/home/gpu/Workspace/Models/BAAI/bge-small-en-v1.5", chroma_embedding_model):
embedding_model = self.embedding_model_2 embedding_model = self.embedding_model_2
else: else:
embedding_model = embedding_functions.SentenceTransformerEmbeddingFunction(model_name=chroma_embedding_model, device = "cuda:1") try:
embedding_model = embedding_functions.SentenceTransformerEmbeddingFunction(model_name=chroma_embedding_model, device = "cuda:1")
except:
return JSONResponse(content={"error": "embedding model not found"}, status_code=status.HTTP_400_BAD_REQUEST)
# load collection # load collection
collection = client.get_collection(chroma_collection_id, embedding_function=embedding_model) collection = client.get_collection(chroma_collection_id, embedding_function=embedding_model)
@ -93,18 +104,53 @@ class ChromaQuery(Blackbox):
final_result = '' final_result = ''
if results is not None: # if results is not None:
results_distances = results["distances"][0] # results_distances = results["distances"][0]
#distance越高越不准确 # #distance越高越不准确
top_distance = 0.8 # top_distance = 0.8
for i in range(len(results_distances)): # for i in range(len(results_distances)):
if results_distances[i] < top_distance: # if results_distances[i] < top_distance:
final_result += results["documents"][0][i] # final_result += results["documents"][0][i]
print("\n final_result: ", final_result) # print("\n final_result: ", final_result)
final_result = str(results["documents"])
if chroma_reranker_model:
if re.search(r"/home/gpu/Workspace/Models/BAAI/bge-reranker-v2-m3", chroma_embedding_model):
reranker_model = self.chroma_reranker_model_1
else:
try:
reranker_model = CrossEncoder(chroma_reranker_model, max_length=512, device = "cuda")
except:
return JSONResponse(content={"error": "reranker model not found"}, status_code=status.HTTP_400_BAD_REQUEST)
if chroma_reranker_num:
if chroma_reranker_num > chroma_n_results:
chroma_reranker_num = chroma_n_results
else:
chroma_reranker_num = 5
#对每一对(查询、文档)进行评分
pairs = [[usr_question, doc] for doc in results["documents"][0]]
print('\n',pairs)
scores = reranker_model.predict(pairs)
#重新排列文件顺序:
print("New Ordering:")
i = 0
final_result = ''
for o in np.argsort(scores)[::-1]:
if i == chroma_reranker_num or scores[o] < 0.5:
break
i += 1
print(o+1)
print("Scores:", scores[o])
print(results["documents"][0][o],'\n')
final_result += results["documents"][0][o] + '\n'
return final_result return final_result