""" 深度思考的对话实现,需要多步骤的思考和校验,才能最后回答用户的问题,至多校验三次 """ 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")