Files
k3GPT/main/ds_chat.py
2025-11-19 19:42:46 +08:00

236 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
深度思考的对话实现,需要多步骤的思考和校验,才能最后回答用户的问题,至多校验三次
"""
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("<think>")>=0 and data.find("</think>")>0:
end = data.find("</think>")
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")