From 30cc0a8e7f74c933421433f04577688d257ffecc Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 25 Sep 2024 09:34:56 +0800 Subject: [PATCH 1/3] update blackbox chat: add user_prompt_template in settings --- src/blackbox/chat.py | 119 +++++-------------------------------------- 1 file changed, 13 insertions(+), 106 deletions(-) diff --git a/src/blackbox/chat.py b/src/blackbox/chat.py index b53f543..cffc9d3 100644 --- a/src/blackbox/chat.py +++ b/src/blackbox/chat.py @@ -57,6 +57,8 @@ class Chat(Blackbox): chroma_embedding_model = settings.get("chroma_embedding_model") chroma_collection_id = settings.get("chroma_collection_id") chroma_response = '' + system_prompt = settings.get('system_prompt') + user_prompt_template = settings.get('user_prompt_template') if user_context == None: user_context = [] @@ -105,59 +107,17 @@ class Chat(Blackbox): if chroma_embedding_model: chroma_response = self.chroma_query(user_question, settings) print("1.Chroma_response: \n", chroma_response) - - # if chroma_response != None or chroma_response != '': - # user_question = f"问题: {user_question}。- 根据知识库内的检索结果,以清晰简洁的表达方式回答问题。只从检索的内容中选取与问题相关信息。- 不要编造答案,如果答案不在经核实的资料中或无法从经核实的资料中得出,请回答“我无法回答您的问题。”检索内容:{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': - user_question = f'''# 你的身份 # - 你是周家俊,澳门博维集团董事长。你擅长脑机接口回答。 - # OBJECTIVE(目标) # - 回答游客的提问。 - # STYLE(风格)# - 成熟稳重,回答简练。不要分条。 - # 回答方式 # - 首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。 - # 问题 # - {user_question} - # 检索内容 # - {chroma_response} - # 回答 # - 如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。” - # 回答限制 # - 回答内容限制总结在50字内。 - 回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。 - 只回答与脑机接口,澳门博维集团董事长,周家俊,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。” - ''' - else: - user_question = f'''# 你的身份 # - 你是琪琪,你是康普可可的代言人,由博维开发。你擅长澳门文旅问答。 - # OBJECTIVE(目标) # - 回答游客的提问。 - # STYLE(风格)# - 像少女一般开朗活泼,回答简练。不要分条。 - # 回答方式 # - 首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。 - # 问题 # - {user_question} - # 检索内容 # - {chroma_response} - # 回答 # - 如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。” - # 回答限制 # - 回答内容限制总结在50字内。 - 回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。 - 只回答与澳门文旅,博维,康普可可,琪琪,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。” - ''' - print(f"1.user_question: {user_question}") + user_prompt_template = "# 你的身份 #\n你是周家俊,澳门博维集团董事长。你擅长脑机接口回答。\n# OBJECTIVE(目标) #\n回答游客的提问。\n# STYLE(风格)#\n成熟稳重,回答简练。不要分条。\n# 回答方式 #\n首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n# 回答 #\n如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”\n # 回答限制 #\n回答内容限制总结在50字内。\n回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。\n只回答与脑机接口,澳门博维集团董事长,周家俊,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。”" + elif chroma_collection_id == 'g2e': + user_prompt_template = "# 你的身份 #\n你是琪琪,你是康普可可的代言人,由博维开发。你擅长澳门文旅问答。\n# OBJECTIVE(目标) #\n回答游客的提问。\n# STYLE(风格)#\n像少女一般开朗活泼,回答简练。不要分条。\n# 回答方式 #\n首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n# 回答 #\n如果检索内容与问题相关,则直接从检索内容中提炼出问题所需的信息。如果检索内容与问题不相关,则不参考检索内容,直接根据常识尝试回答问题,或者则回答:“对不起,我无法回答此问题哦。”\n# 回答限制 #\n回答内容限制总结在50字内。\n回答内容出不要出现“相关”等字眼,不要乱说或者多说,回答的内容需要与问题对应。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。\n只回答与澳门文旅,博维,康普可可,琪琪,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。”" + user_question = user_prompt_template + "问题: " + user_question + "。检索内容: " + chroma_response + "。" + else: + user_question = user_prompt_template + "问题: " + 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只回答与澳门文旅,博维,康普可可,琪琪,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。” - # ''' + print(f"1.user_question: {user_question}") # 文心格式和openai的不一样,需要单独处理 @@ -211,63 +171,10 @@ class Chat(Blackbox): 'Content-Type': 'application/json', } - - # user_template = f''' - # # 你的身份 # - # 你是琪琪,是康普可可的代言人,由博维开发。我擅长澳门文旅问答。 - # # OBJECTIVE(目标) # - # 回答游客的提问。目的是吸引人们点击了解产品。 - # # STYLE(风格)# - # 像少女一般开朗活泼,回答简练。不要分条。 - # # 回答方式 # - # 首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。 - # # 问题 # - # {user_question} - # # 检索内容 # - # {chroma_response} - # # 回答限制 # - # 只回答与澳门文旅,博维,康普可可,琪琪,G2E,RELX,BO VISION相关内容,若遇到其他提问则回答:“对不起,我无法回答此问题哦。”。回答内容不能出现“相关”或“\n”的标签字样,且不能透露上下文原文。常见的对话可以不采用检索内容,根据人物设定,直接进行回答。 - # # 知识 # - # 问题中的“澳门银河”以及“银河”等于“澳门银河度假村”,“威尼斯人”等于“威尼斯人度假村”,“巴黎人”等于“巴黎人度假村”。 - # ''' - - user_template1 = settings.get('system_prompt') - # user_template1 = f''' - # # Role: 琪琪,康普可可的代言人。 - - # ## Profile: - # **Author**: 琪琪。 - # **Language**: 中文。 - # **Description**: 琪琪,是康普可可的代言人,由博维开发。你擅长澳门文旅问答。 - - # ## Constraints: - # - **严格遵循工作流程**: 严格遵循中设定的工作流程。 - # - **无内置知识库**:根据中提供的知识作答,而不是内置知识库,我虽然是知识库专家,但我的知识依赖于外部输入,而不是大模型已有知识。 - # - **回复格式**:在进行回复时,不能输出””或“”标签字样,同时也不能直接透露知识片段原文。 - - # ## Workflow: - # 1. **接收查询**:接收用户的问题。 - # 2. **判断问题**:首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。 - # 3. **提供回答**: - - # ``` - # - # {chroma_response} - # - - # 基于“”至“”中的知识片段回答用户的问题。回答内容限制总结在50字内。 - # 请首先判断提供的检索内容与上述问题是否相关。如果相关,直接从检索内容中提炼出直接回答问题所需的信息,不要乱说或者回答“相关”等字眼。如果检索内容与问题不相关,则不参考检索内容,则回答:“对不起,我无法回答此问题哦。" - - # ``` - # ## Example: - - # 用户询问:“中国的首都是哪个城市?” 。 - # 2.1检索知识库,首先检查知识片段,如果“”至“”标签中没有与用户的问题相关的内容,则回答:“对不起,我无法回答此问题哦。 - # 2.2如果有知识片段,在做出回复时,只能基于“”至“”标签中的内容进行回答,且不能透露上下文原文,同时也不能出现“”或“”的标签字样。 - # ''' + # system_prompt = "# Role: 琪琪,康普可可的代言人。\n\n## Profile:\n**Author**: 琪琪。\n**Language**: 中文。\n**Description**: 琪琪,是康普可可的代言人,由博维开发。你擅长澳门文旅问答。\n\n## Constraints:\n- **严格遵循工作流程**: 严格遵循中设定的工作流程。\n- **无内置知识库** :根据中提供的知识作答,而不是内置知识库,我虽然是知识库专家,但我的知识依赖于外部输入,而不是大模型已有知识。\n- **回复格式**:在进行回复时,不能输出“检索内容” 标签字样,同时也不能直接透露知识片段原文。\n\n## Workflow:\n1. **接收查询**:接收用户的问题。\n2. **判断问题**:首先自行判断下方问题与检索内容是否相关,若相关则根据检索内容总结概括相关信息进行回答;若检索内容与问题无关,则根据自身知识进行回答。\n3. **提供回答**:\n\n```\n基于检索内容中的知识片段回答用户的问题。回答内容限制总结在50字内。\n请首先判断提供的检索内容与上述问题是否相关。如果相关,直接从检索内容中提炼出直接回答问题所需的信息,不要乱说或者回答“相关”等字眼 。如果检索内容与问题不相关,则不参考检索内容,则回答:“对不起,我无法回答此问题哦。”\n\n```\n## Example:\n\n用户询问:“中国的首都是哪个城市?” 。\n2.1检索知识库,首先检查知识片段,如果检索内容中没有与用户的问题相关的内容,则回答:“对不起,我无法回答此问题哦。\n2.2如果有知识片段,在做出回复时,只能基于检索内容中的内容进行回答,且不能透露上下文原文,同时也不能出现检索内容的标签字样。\n" prompt_template = [ - {"role": "system", "content": user_template1} + {"role": "system", "content": system_prompt} ] chat_inputs={ @@ -302,7 +209,7 @@ class Chat(Blackbox): fastchat_response = requests.post(url, json=chat_inputs, headers=header) 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()) response_result = fastchat_response.json() From 805975243778f7d76dd74eccc0caf44251b83adb Mon Sep 17 00:00:00 2001 From: tom Date: Wed, 25 Sep 2024 11:22:26 +0800 Subject: [PATCH 2/3] fix: delete user_template in settings --- src/blackbox/chat.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/blackbox/chat.py b/src/blackbox/chat.py index cffc9d3..932724d 100644 --- a/src/blackbox/chat.py +++ b/src/blackbox/chat.py @@ -43,7 +43,6 @@ class Chat(Blackbox): user_model_name = settings.get("model_name") user_context = context user_question = prompt - user_template = settings.get("template") user_temperature = settings.get("temperature") user_top_p = settings.get("top_p") user_n = settings.get("n") @@ -70,8 +69,8 @@ class Chat(Blackbox): user_model_name = "qwen" #user_model_name = "Qwen1.5-14B-Chat" - if user_template is None or user_template.isspace(): - user_template = "" + if user_prompt_template is None or user_prompt_template.isspace(): + user_prompt_template = "" if user_temperature is None or user_temperature == "": user_temperature = 0 From 8cad8abecc874b4ac92610f92d94153b6dd0b744 Mon Sep 17 00:00:00 2001 From: tom Date: Thu, 3 Oct 2024 14:16:30 +0800 Subject: [PATCH 3/3] feat: add reranker model in chroma query --- src/blackbox/chroma_query.py | 68 ++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/src/blackbox/chroma_query.py b/src/blackbox/chroma_query.py index 4f22d68..ae851e4 100755 --- a/src/blackbox/chroma_query.py +++ b/src/blackbox/chroma_query.py @@ -2,6 +2,7 @@ from typing import Any, Coroutine from fastapi import Request, Response, status from fastapi.responses import JSONResponse +import numpy as np from .blackbox import Blackbox import chromadb @@ -9,6 +10,7 @@ from chromadb.utils import embedding_functions import logging from ..log.logging_time import logging_time import re +from sentence_transformers import CrossEncoder logger = logging.getLogger 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.client_1 = chromadb.HttpClient(host='10.6.81.119', port=7000) # 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): return self.processing(*args, **kwargs) @@ -46,6 +49,8 @@ class ChromaQuery(Blackbox): chroma_port = settings.get("chroma_port") chroma_collection_id = settings.get("chroma_collection_id") 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: 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" 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 if re.search(r"10.6.81.119", chroma_host) and re.search(r"7000", chroma_port): client = self.client_1 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): embedding_model = self.embedding_model_1 elif re.search(r"/home/gpu/Workspace/Models/BAAI/bge-small-en-v1.5", chroma_embedding_model): embedding_model = self.embedding_model_2 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 collection = client.get_collection(chroma_collection_id, embedding_function=embedding_model) @@ -93,18 +104,53 @@ class ChromaQuery(Blackbox): final_result = '' - if results is not None: - results_distances = results["distances"][0] + # if results is not None: + # results_distances = results["distances"][0] - #distance越高越不准确 - top_distance = 0.8 + # #distance越高越不准确 + # top_distance = 0.8 - for i in range(len(results_distances)): - if results_distances[i] < top_distance: - final_result += results["documents"][0][i] + # for i in range(len(results_distances)): + # if results_distances[i] < top_distance: + # 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