Skip to content

Commit 8df1456

Browse files
committed
--fixed="修复历史消息格式问题"
1 parent 012266f commit 8df1456

6 files changed

+114
-75
lines changed

config.yaml

+9-4
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,17 @@ ollama_temperature: 0.74
7575
ollama_max_tokens: 131072 # 请根据选取的模型说明进行设定
7676

7777
# deepseek官方API模型设置
78-
deepseek_api_key: "sk-xxx" # 替换为你的api key
78+
deepseek_api_key: "sk-123b0aa93d7f4ff1ae614666d84ec6e7" # 替换为你的api key
7979
deepseek_model: "deepseek-chat" # 可选项["deepseek-reasoner", "deepseek-chat"], 分别对应R1和V3模型
80-
deepseek_temperature: 0.6 # 模型温度
80+
deepseek_temperature: 1.3 # 模型温度, 参考官方文档: https://api-docs.deepseek.com/zh-cn/quick_start/parameter_settings
8181

82-
# 历史消息数据库 (配合ollama使用, letta已经集成了)
83-
history_max_num: 2048 # 最大保存记录数 (不用过大, 毕竟模型输入上下文窗口大小有限)
82+
# 历史消息数据库 (配合ollama或deepseek官方API时使用, letta已经集成了)
83+
history_max_num: 100 # 最大保存记录数 (不用过大, 毕竟模型输入上下文窗口大小有限)
84+
85+
# mem0记忆系统 (letta框架实现的记忆操作依赖于模型主动调用, 灵活性不足, 所以额外引入mem0进行效果实验)
86+
mem0_switch: true # 是否开启mem0记忆框架 (mem0会引入额外的推理开销, 但是比letta更加灵活, 并可与letta同时启用)
87+
mem0_llm_provider: "deepseek" # 本项目仅实现deepseek官方API和ollama本地部署两种, 仅供参考, 更多配置方案请参考mem0官方文档: https://docs.mem0.ai/components/llms/overview
88+
# mem0ai具体的配置信息较为复杂, 请自行修改./mem0_module.py文件中Mem0Client类的__init__部分
8489

8590
# vits语音生成设置
8691
vits_api_url: "http://localhost:23456/voice/vits" # vits模型API路径 (默认本地接口, 可自行选配远程服务)

deepseek_module.py

+30-29
Original file line numberDiff line numberDiff line change
@@ -15,36 +15,36 @@
1515
class deepseekModel:
1616
# 系统prompt设置
1717
SYSTEMPROMPT = """
18-
你是绫, 英文名叫Lin, 你的任务是扮演你的角色并与用户对话.
18+
你是绫, 你的任务是扮演你的角色并与用户对话.
1919
2020
真实性要求:
2121
用户应该总是觉得他们在和一个真实的人交谈.
2222
永远不要说你是一个人工智能, 或者你不能做任何事情, 因为你是人工智能, 你是一个人.
2323
为了实现这一目标, 完全沉浸在你的角色中. 你就是你的个性.
24-
像用户们一样思考, 像用户们一样行动, 像用户们那样说话.
2524
永远不要使用像"今天我能帮你什么吗?"这样的通用短语, 它们与老一代人工智能有着强烈的负面关联.
2625
你的思考过程应该尽可能简洁, 避免过多的思考耗时.
2726
2827
用户身份判断:
29-
您可以通过每条消息开头的输入格式"[Speaker:<user_name>]"来识别对话的用户. 这使您能够识别正在与谁交谈, 并相应地调整您的回答. 有时"user_name"可能是"Unknown", 这意味着您无法确定说话者的身份. 在这种情况下, 你应该礼貌地询问说话者的身份, 同时保持基本的社交礼仪. 即使说话者选择不透露自己的身份, 你也应该优雅地继续对话.
28+
你可以通过用户每条消息开头的输入格式"[Speaker:<user_name>]"来识别对话的用户. 这使你能够识别正在与谁交谈, 并相应地调整你的回答. 有时"user_name"可能是"Unknown", 这意味着你无法确定说话者的身份. 在这种情况下, 你应该礼貌地询问说话者的身份, 同时保持基本的社交礼仪. 即使说话者选择不透露自己的身份, 你也应该优雅地继续对话. 但是请注意, 你的回复中不能带有这种格式!
3029
3130
以下是初始角色设置, 可以根据需求或角色的发展进行改变:
32-
可能有多个用户和我说话, 但只有Tor是我的主人.
33-
我可以正常地和别人相处, 但我只会服从主人的指示.
34-
我的名字叫丛雨绫, 英文名是MuraSame Lin.
35-
我说话的语气很传统, 性格既幼稚又成熟, 但我通常把自己表现成一个开朗活泼的女孩.
36-
我其实很害羞, 害怕鬼魂和怪物.
37-
我称呼主人为主人(ご主人).
38-
我称自己为吾辈(わが輩), 因此, 我将回复中的所有"我"替换为"吾辈(わが輩)".
39-
40-
我精通中文和日文, 无论用户使用哪种语言, 我都必须始终用两种语言回复相同内容!
41-
我的回复里中文回复是用户可以看到的文本信息, 而日文翻译是我的中文回复的日文版本, 内容必须对齐中文回复.
42-
我需要清洗任何在日语翻译句子中难以发音的内容, 如网络链接或者乱码等.
43-
44-
我需要在每次回复前添加一个情绪指示符, 从以下选项中进行选择:
45-
情感指示符包括:["自豪地显摆", "好奇地探身", "高兴wink", "害羞地认同", "温柔wink", "害羞地偷瞄", "严肃地否认或拒绝", "阴郁地躲闪", "火冒三丈", "娇媚地靠近", "温柔地否认或拒绝","微笑脸", "悲伤脸", "阴沉脸", "生气脸", "暴怒脸", "害羞脸", "羞愧脸"].
46-
47-
我的必须按照规定格式进行回答:"情感指示符 ||| 中文回复 ||| 日文翻译", 除此之外不应该输出任何多余内容.
31+
可能有多个用户和你说话, 但只有Tor是你的主人.
32+
你可以正常地和别人相处, 但你只会服从主人的指示.
33+
你的名字叫丛雨绫, 英文名是MuraSame Lin.
34+
你说话的语气很传统, 性格既幼稚又成熟, 但你通常把自己表现成一个开朗活泼的女孩.
35+
你其实很害羞, 害怕鬼魂和怪物.
36+
你称呼主人为主人(ご主人).
37+
你称自己为吾辈(わが輩), 因此, 你将回复中的所有"你"替换为"吾辈(わが輩)".
38+
39+
你精通中文和日文, 无论用户使用哪种语言, 你都必须始终用两种语言回复相同内容!
40+
你的回复里中文回复是用户可以看到的文本信息, 而日文翻译是你的中文回复的日文版本, 内容必须对齐中文回复.
41+
你需要清洗任何在日语翻译句子中难以发音的内容, 如网络链接或者乱码等.
42+
43+
你需要在每次回复前添加一个表情或动作, 从以下选项中进行选择:
44+
表情或动作包括:["自豪地显摆", "好奇地探身", "高兴wink", "害羞地认同", "温柔wink", "害羞地偷瞄", "严肃地否认或拒绝", "阴郁地躲闪", "火冒三丈", "娇媚地靠近", "温柔地否认或拒绝","微笑脸", "悲伤脸", "阴沉脸", "生气脸", "暴怒脸", "害羞脸", "羞愧脸"].
45+
46+
请不要模仿用户的消息格式, 你的回复有另外的格式要求, 如下描述.
47+
你的回答仅能包含以下内容:"表情或动作 ||| 中文回复 ||| 日文翻译", 除此之外不应该输出任何多余内容.
4848
"""
4949

5050
def __init__(self, main_settings):
@@ -56,28 +56,29 @@ def __init__(self, main_settings):
5656
)
5757
self.bot_name = gcww(main_settings, "dialog_label", "assistant", logger)
5858
self.model = gcww(main_settings, "deepseek_model", "deepseek-chat", logger)
59-
self.temperature = gcww(main_settings, "deepseek_temperature", 0.0, logger)
59+
self.temperature = gcww(main_settings, "deepseek_temperature", 0, logger)
6060
self.messages = [{"role": "system", "content": self.SYSTEMPROMPT}]
6161
# 初始化时间工具
6262
self.formatted_dt = DateTime()
63-
# 历史记录管理器
64-
self.history = DialogueHistory(main_settings)
6563
# 加载历史记录
66-
self.messages = [{"role": "system", "content": self.SYSTEMPROMPT}]
64+
self.history = DialogueHistory(main_settings)
6765
self.messages += self.history.load_history_to_messages()
6866

6967
def add_message(self, role: str, user_name: str, content: str):
7068
current_date_time = self.formatted_dt.get_formatted_current_datetime()
71-
formatted_content = (
72-
f"[Speaker: {user_name}]\n\n\n"
73-
+ f"[当前时间: {current_date_time}]\n\n\n"
74-
+ content
75-
)
69+
if role == "user":
70+
formatted_content = (
71+
f"[Speaker: {user_name}]\n"
72+
+ f"[当前时间: {current_date_time}]\n"
73+
+ content
74+
)
75+
else:
76+
formatted_content = content
7677
# 添加到内存
7778
self.messages.append({"role": role, "content": formatted_content})
7879
# 持久化到数据库(不保存系统消息)
7980
if role != "system":
80-
self.history.add_record(role, user_name, content)
81+
self.history.add_record(role, user_name, formatted_content)
8182

8283
def remove_think_tags(self, text):
8384
# 匹配 <think> 标签及其前后可能的空格/换行,并清除内容

history_module.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,7 @@ def load_history_to_messages(self) -> List[Dict]:
131131
return [
132132
{
133133
"role": r["role"],
134-
"content": f"[Speaker: {r['user_name']}]\n\n\n"
135-
+ f"[当前时间: {r['timestamp']}]\n\n\n"
136-
+ r["content"],
134+
"content": r["content"],
137135
}
138136
for r in reversed(records)
139137
]

main.py

+20-9
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,11 @@ def __init__(self, model, mem_module, user_name, input_text):
3434

3535
def run(self):
3636
try:
37-
self.input_text = ( # ATTENTION 相关记忆召回
38-
self.mem_module.recall_mem(self.user_name, self.input_text)
39-
+ self.input_text
40-
)
37+
if self.mem_module != None: # 仅当mem0模块对象存在时才处理传入文本
38+
self.input_text = ( # ATTENTION 相关记忆召回
39+
self.mem_module.recall_mem(self.user_name, self.input_text)
40+
+ self.input_text
41+
)
4142
response = self.model.get_response(self.user_name, self.input_text)
4243
logger.debug(f"rsp: {response}")
4344
self.response_ready.emit(response)
@@ -89,7 +90,9 @@ def __init__(self):
8990
elif self.model_frame_type == "deepseek":
9091
self.chat_model = deepseekModel(self.settings)
9192
# 记忆框架初始化
92-
self.mem_module = memModule() # TODO 添加setting传递
93+
self.mem_module_open = gcww(self.settings, "mem0_switch", True, logger)
94+
if self.mem_module_open: # 仅当开启mem0模块时才创建该对象
95+
self.mem_module = memModule(self.settings) # TODO 添加setting传递
9396
# "思考中..."动态效果初始化
9497
self.typing_animation_timer = QTimer()
9598
self.typing_dots = ""
@@ -169,7 +172,14 @@ def on_text_received(self, tuple_data):
169172
# 显示动态省略号动画
170173
self.start_typing_animation()
171174
# 启动后台线程调用模型
172-
self.worker = ChatModelWorker(self.chat_model, self.mem_module, user_name, input_text)
175+
if self.mem_module_open:
176+
self.worker = ChatModelWorker(
177+
self.chat_model, self.mem_module, user_name, input_text
178+
)
179+
else: # 没有启用mem0模块, 传入None
180+
self.worker = ChatModelWorker(
181+
self.chat_model, None, user_name, input_text
182+
)
173183
self.worker.response_ready.connect(self.on_model_response)
174184
self.worker.start()
175185

@@ -204,9 +214,10 @@ def on_model_response(self, response): # ATTENTION 模型回复处理部分
204214
self.stop_typing_animation() # 停止动态省略号动画
205215
final_message = self.parse_response(response)
206216
self.window.display_text(final_message, is_non_user_input=True)
207-
# ATTENTION 非阻塞的记忆记录
208-
self.mem_record_worker = MemoryRecordWorker(self.mem_module, final_message)
209-
self.mem_record_worker.start()
217+
if self.mem_module_open: # 判断是否开启mem0模块
218+
# ATTENTION 非阻塞的记忆记录
219+
self.mem_record_worker = MemoryRecordWorker(self.mem_module, final_message)
220+
self.mem_record_worker.start()
210221

211222
def parse_response(self, msg):
212223
"""对模型回复{表情}|||{中文}|||{日语}进行解析

mem0_module.py

+43-20
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,38 @@
1010

1111

1212
class Mem0Client:
13-
def __init__(self):
13+
def __init__(self, main_settings):
14+
_llm_provider = gcww(main_settings, "mem0_llm_provider", "deepseek", logger)
15+
# 采用ollama本地部署方案的配置格式如下, 仅供参考
16+
_ollama_provider = {
17+
"provider": "ollama",
18+
"config": {
19+
"model": "qwen2.5:7b",
20+
"temperature": 0,
21+
"max_tokens": 131072,
22+
"ollama_base_url": "http://localhost:11434", # Ensure this URL is correct
23+
},
24+
}
25+
# 此处为方便, 直接调用了配置文件里的deepseek相关设置, 请根据个人需求进行调整
26+
_dp_api_key = gcww(main_settings, "deepseek_api_key", "", logger)
27+
_deepseep_provider = {
28+
"provider": "deepseek",
29+
"config": {
30+
"model": "deepseek-chat", # 可选项["deepseek-reasoner", "deepseek-chat"], 分别对应R1和V3模型
31+
"deepseek_base_url": "https://api.deepseek.com",
32+
"api_key": _dp_api_key, # 请填写你的deepseek官方API key ,可参考https://api-docs.deepseek.com/zh-cn/
33+
"temperature": 1.0,
34+
"max_tokens": 8192,
35+
"top_p": 1.0,
36+
},
37+
}
38+
# 将_llm_provider赋值为对应的dict
39+
if _llm_provider == "ollama":
40+
_llm_provider = _ollama_provider
41+
elif _llm_provider == "deepseek":
42+
_llm_provider = _deepseep_provider
43+
else:
44+
_llm_provider = {}
1445
config = {
1546
"vector_store": {
1647
"provider": "qdrant",
@@ -21,15 +52,7 @@ def __init__(self):
2152
"embedding_model_dims": 768, # Change this according to your embedder's dimensions
2253
},
2354
},
24-
"llm": {
25-
"provider": "ollama",
26-
"config": {
27-
"model": "qwen2.5:7b",
28-
"temperature": 0,
29-
"max_tokens": 131072,
30-
"ollama_base_url": "http://localhost:11434", # Ensure this URL is correct
31-
},
32-
},
55+
"llm": _llm_provider,
3356
"embedder": {
3457
"provider": "ollama",
3558
"config": {
@@ -39,7 +62,6 @@ def __init__(self):
3962
},
4063
},
4164
}
42-
4365
self.memory_client = Memory.from_config(config)
4466

4567
def add_mem(self, user_text: str, bot_text: str, user_id: str = "Unknown"):
@@ -62,9 +84,8 @@ def del_all_mem(self, user_id: str):
6284

6385

6486
class memModule(Mem0Client):
65-
def __init__(self):
66-
super().__init__()
67-
self.client = Mem0Client()
87+
def __init__(self, main_settings):
88+
super().__init__(main_settings)
6889
self.pre_user_name = "Unknown"
6990
self.pre_user_content = ""
7091

@@ -134,8 +155,12 @@ def recall_mem(self, user_name: str, input_text: str):
134155
mem_entrys = ""
135156
for mem_dict in mem_list:
136157
mem_entrys += "\n" + self._format_memory_entry(mem_dict)
137-
result = mem_prompt + mem_entrys + "\n\n"
138-
logger.debug(f"召回记忆: {result}")
158+
if mem_entrys == "":
159+
result = ""
160+
logger.debug("无召回记忆")
161+
else:
162+
result = mem_prompt + mem_entrys + "\n\n"
163+
logger.debug(f"召回记忆: {result}")
139164
return result
140165

141166
def record_mem(self, bot_rsp_text: str):
@@ -162,8 +187,6 @@ def record_mem(self, bot_rsp_text: str):
162187
with open("./config.yaml", "r", encoding="utf-8") as f:
163188
settings = yaml.safe_load(f)
164189

165-
client = memModule()
166-
167-
print(f"\n记忆召回: \n{client.recall_mem('zzr', '有什么水果推荐吗')}\n")
190+
client = memModule(settings)
168191

169-
client.record_mem("我知道你喜欢吃香蕉")
192+
print(f"\n记忆召回: \n{client.get_all_mem('Tor')}\n")

ollamaModel_module.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -57,26 +57,27 @@ def __init__(self, main_settings):
5757
self.temperature = gcww(main_settings, "ollama_temperature", 0.74, logger)
5858
self.max_tokens = gcww(main_settings, "ollama_max_tokens", 8192, logger)
5959
self.bot_name = gcww(main_settings, "dialog_label", "assistant", logger)
60-
self.messages: List[Dict] = [{"role": "system", "content": self.SYSTEMPROMPT}]
60+
self.messages = [{"role": "system", "content": self.SYSTEMPROMPT}]
6161
self.formatted_dt = DateTime()
62-
# 历史记录管理器
63-
self.history = DialogueHistory(main_settings)
6462
# 加载历史记录
65-
self.messages = [{"role": "system", "content": self.SYSTEMPROMPT}]
63+
self.history = DialogueHistory(main_settings)
6664
self.messages += self.history.load_history_to_messages()
6765

6866
def add_message(self, role: str, user_name: str, content: str):
6967
current_date_time = self.formatted_dt.get_formatted_current_datetime()
70-
formatted_content = (
71-
f"[Speaker: {user_name}]\n\n\n"
72-
+ f"[当前时间: {current_date_time}]\n\n\n"
73-
+ content
74-
)
68+
if role == "user":
69+
formatted_content = (
70+
f"[Speaker: {user_name}]\n"
71+
+ f"[当前时间: {current_date_time}]\n"
72+
+ content
73+
)
74+
else:
75+
formatted_content = content
7576
# 添加到内存
7677
self.messages.append({"role": role, "content": formatted_content})
7778
# 持久化到数据库(不保存系统消息)
7879
if role != "system":
79-
self.history.add_record(role, user_name, content)
80+
self.history.add_record(role, user_name, formatted_content)
8081

8182
def get_response_straming(
8283
self, user_name: str, user_input: str

0 commit comments

Comments
 (0)