diff --git a/main/ds_chat.py b/main/ds_chat.py new file mode 100644 index 0000000..b770df3 --- /dev/null +++ b/main/ds_chat.py @@ -0,0 +1,235 @@ +""" +深度思考的对话实现,需要多步骤的思考和校验,才能最后回答用户的问题,至多校验三次 +""" + + +import datetime +from init import llm +import json +import json5 +from full_index_search import full_search,adjust_ctx_size,jieba_fenci,full_search_logic,one_doc_search +from k_database import QA + +#深度思考 +def DS_chat(query,history): + + try: + i=0 + yield gen_event_data({"step":i,"rsp":f"{query}--检查是否需要补全或重写?"}) + result =DS_chat0(query,history) + new_query = deal_llm_think_json_data(result) + + print(new_query) + yield gen_event_data({"step":i,"rsp":new_query}) + + seg_list = jieba_fenci(new_query) + yield gen_event_data({"rsp":f"搜索关键词 {seg_list}"}) + + #至多三次深度回答 + for i in range(1,4): + + for result in search_ctx_chat(new_query,seg_list): + try: + data = deal_llm_think_json_data(result) + answer = json.loads(data) + except Exception as e: + print(e,data) + yield gen_event_data({"step":i,"rsp":f"{data}"}) + break + if answer["code"]==100: #中间ixnxi + yield gen_event_data({"step":i,"rsp":answer["content"]}) + else: + break + #其它结果 + if answer["code"]==200 or answer["code"]==202: + yield gen_event_data({"step":i,"rsp":answer["content"]}) + #QA.create(question=new_query,answer=answer["content"],ref="深度思考") + return answer["content"] + elif answer["code"]==201:#单个文件回答 + yield gen_event_data({"step":i,"rsp":f'找到相关文件: {answer["name"]}'}) + answer["question"]=new_query + yield from one_doc_chat(answer) + #QA.create(question=new_query,answer=answer["name"],ref="深度思考") + return answer["name"] + elif answer["code"]==404: + yield gen_event_data({"step":i,"rsp":f'重新搜索关键字:{answer["keywords"]}'}) + seg_list = answer["keywords"] + else:#出错 + break + #end for + except Exception as e : + yield gen_event_data({"step":-1,"rsp":f"对话出现错误:{e}"}) + + if i==3: + yield gen_event_data({"step":i,"rsp":"对不起,没有找到你想要的答案!"}) + #QA.create(question=query,answer="对不起,没有找到你想要的答案!",ref="深度思考") + return "对不起,没有找到你想要的答案!" + + +def gen_event_data(json_data): + json_str = "data: " + json.dumps(json_data) + "\n\n" + return json_str.encode("utf-8") + + +def deal_llm_think_json_data(data): + #print("==============================") + #print(data) + #去掉思考过程 + if data.find("")>=0 and data.find("")>0: + end = data.find("") + data = data[end+8:] + if data.find("```json") >=0: + begin = data.find("{") + end = data.rfind("}") + data = data[begin:end+1] + return data + + + +#多轮对话,生成新问题 +def DS_chat0(query,history): + # 获取当前日期和星期 + today = datetime.datetime.now() + # 获取今天是星期几,使用weekday()方法(周一=0, 周日=6) + weekday_number = today.weekday() + + system_prompt=f""" + 今天是{today.strftime('%Y-%m-%d')} 星期{weekday_number+1}。 + 你是一个支持多轮问答的智能助手,你需要根据历史对话信息和本次问题来判断需不需改写问题,重新生成一个更加准确的问题. + + 并按照如下步骤来分析执行改写情况 + 1.特别是本次问题用他、她或它来指代历史对话中对象的情景,如果历史对话中有明确的对象,就需要用历史对话中明确的对象来本次问题的代词。 + + 如:历史对话中有["中国的国土面积是多少",...], + 本次问题是"他的人口有多少",则需要将本次问题中的“他”换成历史对话中的“中国”,生成新的问题"中国的人口有多少" + + 2.如果多多条历史记录的,就用最近对话中对象来指代,如: + + 示例1 + 历史对话:["杭州是个美丽的城市","他的GDP是多少"] + 本次问题: 他在2024年的常驻人口是多少 + 生成问题: 杭州在2024年的常驻人口是多少 + + 示例2 + 历史对话:["杭州是个美丽的城市","他的GDP是多少","他在2024年的常驻人口是多少","厦门也是一个美丽的城市"] + 本次问题:他的旅游忘记是什么时候 + 生成问题:厦门的旅游旺季是什么时候 + + 3.其它需要改写的情况,改写后语句的关键字可以更有利于后续检索的 + + + 切记!!!不输出其它任何信息!!! + + 历史对话:(时间越早排序越靠前) + ``` + {history} + ``` + + 本次问题: + {query} + + 生成问题: + """ + messages = [{'role': 'user', 'content': system_prompt}] + response = llm.chat( + messages=messages, + stream=False, + ) # get a new response from the model where it can see the function response + + return response[0]["content"] + +#检索上下文回答 +def search_ctx_chat(query,seg_list): + + #全文检索相关知识 + context,meta = full_search_logic(query,30,seg_list) + context = adjust_ctx_size(context) + count = len(context) + ctx = [] + for i in range(count): + ctx.append({"name":meta[i][0],"path":meta[i][1],"base":meta[i][2]}) + + yield json.dumps({"code":100,"content":f"找到{count}个相关信息{ctx}"}) + + system_prompt=f""" + 你是一个知识问答的助手,你可以根据提供的上下文信息来回答用户的问题,并能按如下步骤来深度思考准确回答问题: + 1.首先判断上下文和问题的相关性,如果上下文足以回答问题的,按如下json格式进行输出 + {{"code":200,"content":"回答的内容"}} + 2.如果提供的上下文不足以准确回答信息,但某个上下文和上下文提供的文件对回答问题的相关性已经具备,需要进一步检索该文件内容来回答信息的。 + 可以按如下json格式回答信息 + {{"code":201,"name":"对应的文件名","path":"文件路径","base":"知识库"}} + 3.如果提供的上下文和文件完全不足以回答问题时,你可以调整优化问题关键字列表,让助手重新检索上下文,如: + A.删除掉一些和问题相关度不高的关键字,让检索的范围变大,找到更合适的上下文 + B.根据问题的情况更换、调整或生成一些新关键字,重新检索,以便更好的回答问题 + C.使用更合适的同义词替换现有关键字 + 最后输出json格式格式如下: + {{"code":404,"keywords":["关键词1","关键词2","关键词n"]}} + 4.如果这个问题是一个常识性的问题,不需要上下文,只使用大模型内部知识就可以回答的,可以按如下json方式进行输出 + {{"code":202,"content":"回答的内容"}} + + 5.一定按json格式进行输出,回答的内容中有特殊符号如"的需要进行转化为\",不要影响json的输出的格式。 + + 上下文: + ``` + {context} + ``` + + 上下文对应的文件名称和路径信息: + ``` + {ctx} + ``` + + 问题: + {query} + + 问题关键字: + {seg_list} + + """ + messages = [{'role': 'user', 'content': system_prompt}] + + + response = llm.chat( + messages=messages, + stream=False, + ) # get a new response from the model where it can see the function response + + yield response[0]["content"] + + +def one_doc_chat(chat): + query = chat["question"] + context= one_doc_search(chat["question"],chat["base"],chat["name"],chat["path"]) + + context = adjust_ctx_size(context) + + system_prompt=f""" + 你是一个知识问答的助手。 + 你可以根据提供的上下文信息来回答用户的问题,上下文中排在前面的可信度越高,越应该优先采纳。 + 如果提供的上下文不足以回答问题时,你可以回答'对不起,暂时没有找到相关的信息,无法回答你的问题' + + 上下文: + ``` + {context} + ``` + + 问题: + {query} + + """ + messages = [{'role': 'user', 'content': system_prompt}] + responses = llm.chat( + messages=messages, + stream=True, + ) # get a new response from the model where it can see the function response + json_str="" + try: + for response in responses: + json_str = "data: " + json.dumps({'rsp':response[0]["content"]}) + "\n\n" + yield json_str.encode("utf-8") + + #QA.create(question=query,answer=response[0]["content"],ref="深度思考") + return response[0]["content"] + except Exception as e: + json_str = "data: " + json.dumps({"rsp": f"大模型运行出现错误:{e}, 请检查配置是否正确或者网络通信是否通畅!\n如果是本地大模型还请检查大模型是否正常启动!"}) + "\n\n" + yield json_str.encode("utf-8")