feat: 新增统一命名功能,自动替换prompt内唯一标识符,优化prompt效果
This commit is contained in:
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@@ -58,7 +58,8 @@ class ChattingObservation(Observation):
|
||||
for msg in mid_memory_by_id["messages"]:
|
||||
msg_str += f"{msg['detailed_plain_text']}"
|
||||
time_diff = int((datetime.now().timestamp() - mid_memory_by_id["created_at"]) / 60)
|
||||
mid_memory_str += f"距离现在{time_diff}分钟前:\n{msg_str}\n"
|
||||
# mid_memory_str += f"距离现在{time_diff}分钟前:\n{msg_str}\n"
|
||||
mid_memory_str += f"{msg_str}\n"
|
||||
except Exception as e:
|
||||
logger.error(f"获取mid_memory_id失败: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -3,8 +3,9 @@ import asyncio
|
||||
from src.plugins.moods.moods import MoodManager
|
||||
from src.plugins.models.utils_model import LLM_request
|
||||
from src.plugins.config.config import global_config
|
||||
import re
|
||||
import time
|
||||
from src.plugins.chat.message import UserInfo
|
||||
from src.plugins.chat.utils import parse_text_timestamps
|
||||
|
||||
# from src.plugins.schedule.schedule_generator import bot_schedule
|
||||
# from src.plugins.memory_system.Hippocampus import HippocampusManager
|
||||
@@ -37,11 +38,11 @@ def init_prompt():
|
||||
prompt += "{prompt_personality}\n"
|
||||
prompt += "刚刚你的想法是{current_thinking_info}。可以适当转换话题\n"
|
||||
prompt += "-----------------------------------\n"
|
||||
prompt += "现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{chat_observe_info}\n"
|
||||
prompt += "现在是{time_now},你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:\n{chat_observe_info}\n"
|
||||
prompt += "你现在{mood_info}\n"
|
||||
prompt += "你注意到{sender_name}刚刚说:{message_txt}\n"
|
||||
prompt += "现在你接下去继续思考,产生新的想法,不要分点输出,输出连贯的内心独白"
|
||||
prompt += "思考时可以想想如何对群聊内容进行回复。回复的要求是:平淡一些,简短一些,说中文,尽量不要说你说过的话\n"
|
||||
prompt += "思考时可以想想如何对群聊内容进行回复。回复的要求是:平淡一些,简短一些,说中文,尽量不要说你说过的话。如果你要回复,最好只回复一个人的一个话题\n"
|
||||
prompt += "请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要带有括号和动作描写"
|
||||
prompt += "记得结合上述的消息,生成内心想法,文字不要浮夸,注意你就是{bot_name},{bot_name}指的就是你。"
|
||||
Prompt(prompt, "sub_heartflow_prompt_before")
|
||||
@@ -49,7 +50,7 @@ def init_prompt():
|
||||
# prompt += f"你现在正在做的事情是:{schedule_info}\n"
|
||||
prompt += "{extra_info}\n"
|
||||
prompt += "{prompt_personality}\n"
|
||||
prompt += "现在你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:{chat_observe_info}\n"
|
||||
prompt += "现在是{time_now},你正在上网,和qq群里的网友们聊天,群里正在聊的话题是:\n{chat_observe_info}\n"
|
||||
prompt += "刚刚你的想法是{current_thinking_info}。"
|
||||
prompt += "你现在看到了网友们发的新消息:{message_new_info}\n"
|
||||
prompt += "你刚刚回复了群友们:{reply_info}"
|
||||
@@ -154,7 +155,7 @@ class SubHeartflow:
|
||||
await observation.observe()
|
||||
|
||||
async def do_thinking_before_reply(
|
||||
self, message_txt: str, sender_name: str, chat_stream: ChatStream, extra_info: str, obs_id: int = None
|
||||
self, message_txt: str, sender_info: UserInfo, chat_stream: ChatStream, extra_info: str, obs_id: int = None
|
||||
):
|
||||
current_thinking_info = self.current_mind
|
||||
mood_info = self.current_state.mood
|
||||
@@ -207,8 +208,10 @@ class SubHeartflow:
|
||||
# f"根据你和说话者{sender_name}的关系和态度进行回复,明确你的立场和情感。"
|
||||
# )
|
||||
relation_prompt_all = (await global_prompt_manager.get_prompt_async("relationship_prompt")).format(
|
||||
relation_prompt, sender_name
|
||||
relation_prompt, sender_info.user_nickname
|
||||
)
|
||||
|
||||
sender_name_sign = f"<{chat_stream.platform}:{sender_info.user_id}:{sender_info.user_nickname}:{sender_info.user_cardname}>"
|
||||
|
||||
# prompt = ""
|
||||
# # prompt += f"麦麦的总体想法是:{self.main_heartflow_info}\n\n"
|
||||
@@ -226,18 +229,24 @@ class SubHeartflow:
|
||||
# prompt += "请注意不要输出多余内容(包括前后缀,冒号和引号,括号, 表情,等),不要带有括号和动作描写"
|
||||
# prompt += f"记得结合上述的消息,生成内心想法,文字不要浮夸,注意你就是{self.bot_name},{self.bot_name}指的就是你。"
|
||||
|
||||
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_before")).format(
|
||||
extra_info_prompt,
|
||||
# prompt_schedule,
|
||||
relation_prompt_all,
|
||||
prompt_personality,
|
||||
current_thinking_info,
|
||||
time_now,
|
||||
chat_observe_info,
|
||||
mood_info,
|
||||
sender_name,
|
||||
sender_name_sign,
|
||||
message_txt,
|
||||
self.bot_name,
|
||||
)
|
||||
|
||||
prompt = await relationship_manager.convert_all_person_sign_to_person_name(prompt)
|
||||
prompt = parse_text_timestamps(prompt, mode="lite")
|
||||
|
||||
try:
|
||||
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
|
||||
@@ -285,16 +294,22 @@ class SubHeartflow:
|
||||
|
||||
message_new_info = chat_talking_prompt
|
||||
reply_info = reply_content
|
||||
|
||||
time_now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
|
||||
|
||||
prompt = (await global_prompt_manager.get_prompt_async("sub_heartflow_prompt_after")).format(
|
||||
extra_info_prompt,
|
||||
prompt_personality,
|
||||
time_now,
|
||||
chat_observe_info,
|
||||
current_thinking_info,
|
||||
message_new_info,
|
||||
reply_info,
|
||||
mood_info,
|
||||
)
|
||||
|
||||
prompt = await relationship_manager.convert_all_person_sign_to_person_name(prompt)
|
||||
prompt = parse_text_timestamps(prompt, mode="lite")
|
||||
|
||||
try:
|
||||
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
|
||||
@@ -308,48 +323,6 @@ class SubHeartflow:
|
||||
|
||||
self.last_reply_time = time.time()
|
||||
|
||||
async def judge_willing(self):
|
||||
# 开始构建prompt
|
||||
prompt_personality = "你"
|
||||
# person
|
||||
individuality = Individuality.get_instance()
|
||||
|
||||
personality_core = individuality.personality.personality_core
|
||||
prompt_personality += personality_core
|
||||
|
||||
personality_sides = individuality.personality.personality_sides
|
||||
random.shuffle(personality_sides)
|
||||
prompt_personality += f",{personality_sides[0]}"
|
||||
|
||||
identity_detail = individuality.identity.identity_detail
|
||||
random.shuffle(identity_detail)
|
||||
prompt_personality += f",{identity_detail[0]}"
|
||||
|
||||
# print("麦麦闹情绪了1")
|
||||
current_thinking_info = self.current_mind
|
||||
mood_info = self.current_state.mood
|
||||
# print("麦麦闹情绪了2")
|
||||
prompt = ""
|
||||
prompt += f"{prompt_personality}\n"
|
||||
prompt += "现在你正在上网,和qq群里的网友们聊天"
|
||||
prompt += f"你现在的想法是{current_thinking_info}。"
|
||||
prompt += f"你现在{mood_info}。"
|
||||
prompt += "现在请你思考,你想不想发言或者回复,请你输出一个数字,1-10,1表示非常不想,10表示非常想。"
|
||||
prompt += "请你用<>包裹你的回复意愿,输出<1>表示不想回复,输出<10>表示非常想回复。请你考虑,你完全可以不回复"
|
||||
try:
|
||||
response, reasoning_content = await self.llm_model.generate_response_async(prompt)
|
||||
# 解析willing值
|
||||
willing_match = re.search(r"<(\d+)>", response)
|
||||
except Exception as e:
|
||||
logger.error(f"意愿判断获取失败: {e}")
|
||||
willing_match = None
|
||||
if willing_match:
|
||||
self.current_state.willing = int(willing_match.group(1))
|
||||
else:
|
||||
self.current_state.willing = 0
|
||||
|
||||
return self.current_state.willing
|
||||
|
||||
def update_current_mind(self, response):
|
||||
self.past_mind.append(self.current_mind)
|
||||
self.current_mind = response
|
||||
|
||||
@@ -142,14 +142,18 @@ class MessageRecv(Message):
|
||||
|
||||
def _generate_detailed_text(self) -> str:
|
||||
"""生成详细文本,包含时间和用户信息"""
|
||||
time_str = time.strftime("%m-%d %H:%M:%S", time.localtime(self.message_info.time))
|
||||
# time_str = time.strftime("%m-%d %H:%M:%S", time.localtime(self.message_info.time))
|
||||
time = self.message_info.time
|
||||
user_info = self.message_info.user_info
|
||||
# name = (
|
||||
# f"{user_info.user_nickname}(ta的昵称:{user_info.user_cardname},ta的id:{user_info.user_id})"
|
||||
# if user_info.user_cardname != None
|
||||
# else f"{user_info.user_nickname}(ta的id:{user_info.user_id})"
|
||||
# )
|
||||
name = (
|
||||
f"{user_info.user_nickname}(ta的昵称:{user_info.user_cardname},ta的id:{user_info.user_id})"
|
||||
if user_info.user_cardname != None
|
||||
else f"{user_info.user_nickname}(ta的id:{user_info.user_id})"
|
||||
f"<{self.message_info.platform}:{user_info.user_id}:{user_info.user_nickname}:{user_info.user_cardname}>"
|
||||
)
|
||||
return f"[{time_str}] {name}: {self.processed_plain_text}\n"
|
||||
return f"[{time}] {name}: {self.processed_plain_text}\n"
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -239,14 +243,18 @@ class MessageProcessBase(Message):
|
||||
|
||||
def _generate_detailed_text(self) -> str:
|
||||
"""生成详细文本,包含时间和用户信息"""
|
||||
time_str = time.strftime("%m-%d %H:%M:%S", time.localtime(self.message_info.time))
|
||||
# time_str = time.strftime("%m-%d %H:%M:%S", time.localtime(self.message_info.time))
|
||||
time = self.message_info.time
|
||||
user_info = self.message_info.user_info
|
||||
# name = (
|
||||
# f"{user_info.user_nickname}(ta的昵称:{user_info.user_cardname},ta的id:{user_info.user_id})"
|
||||
# if user_info.user_cardname != None
|
||||
# else f"{user_info.user_nickname}(ta的id:{user_info.user_id})"
|
||||
# )
|
||||
name = (
|
||||
f"{user_info.user_nickname}(ta的昵称:{user_info.user_cardname},ta的id:{user_info.user_id})"
|
||||
if user_info.user_cardname != None
|
||||
else f"{user_info.user_nickname}(ta的id:{user_info.user_id})"
|
||||
f"<{self.message_info.platform}:{user_info.user_id}:{user_info.user_nickname}:{user_info.user_cardname}>"
|
||||
)
|
||||
return f"[{time_str}] {name}: {self.processed_plain_text}\n"
|
||||
return f"[{time}] {name}: {self.processed_plain_text}\n"
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -153,11 +153,11 @@ class MessageBuffer:
|
||||
# 更新当前消息的processed_plain_text
|
||||
if combined_text and combined_text[0] != message.processed_plain_text and is_update:
|
||||
if type == "text":
|
||||
message.processed_plain_text = "".join(combined_text)
|
||||
message.processed_plain_text = ",".join(combined_text)
|
||||
logger.debug(f"整合了{len(combined_text) - 1}条F消息的内容到当前消息")
|
||||
elif type == "emoji":
|
||||
combined_text.pop()
|
||||
message.processed_plain_text = "".join(combined_text)
|
||||
message.processed_plain_text = ",".join(combined_text)
|
||||
message.is_emoji = False
|
||||
logger.debug(f"整合了{len(combined_text) - 1}条F消息的内容,覆盖当前emoji消息")
|
||||
|
||||
|
||||
@@ -629,3 +629,141 @@ def count_messages_between(start_time: float, end_time: float, stream_id: str) -
|
||||
except Exception as e:
|
||||
logger.error(f"计算消息数量时出错: {str(e)}")
|
||||
return 0, 0
|
||||
|
||||
|
||||
def translate_timestamp_to_human_readable(timestamp: float, mode: str = "normal") -> str:
|
||||
"""将时间戳转换为人类可读的时间格式
|
||||
|
||||
Args:
|
||||
timestamp: 时间戳
|
||||
mode: 转换模式,"normal"为标准格式,"relative"为相对时间格式
|
||||
|
||||
Returns:
|
||||
str: 格式化后的时间字符串
|
||||
"""
|
||||
if mode == "normal":
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
|
||||
elif mode == "relative":
|
||||
now = time.time()
|
||||
diff = now - timestamp
|
||||
|
||||
if diff < 20:
|
||||
return "刚刚:"
|
||||
elif diff < 60:
|
||||
return f"{int(diff)}秒前:"
|
||||
elif diff < 1800:
|
||||
return f"{int(diff / 60)}分钟前:"
|
||||
elif diff < 3600:
|
||||
return f"{int(diff / 60)}分钟前:\n"
|
||||
elif diff < 86400:
|
||||
return f"{int(diff / 3600)}小时前:\n"
|
||||
elif diff < 604800:
|
||||
return f"{int(diff / 86400)}天前:\n"
|
||||
else:
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp)) + ":"
|
||||
|
||||
def parse_text_timestamps(text: str, mode: str = "normal") -> str:
|
||||
"""解析文本中的时间戳并转换为可读时间格式
|
||||
|
||||
Args:
|
||||
text: 包含时间戳的文本,时间戳应以[]包裹
|
||||
mode: 转换模式,传递给translate_timestamp_to_human_readable,"normal"或"relative"
|
||||
|
||||
Returns:
|
||||
str: 替换后的文本
|
||||
|
||||
转换规则:
|
||||
- normal模式: 将文本中所有时间戳转换为可读格式
|
||||
- lite模式:
|
||||
- 第一个和最后一个时间戳必须转换
|
||||
- 以5秒为间隔划分时间段,每段最多转换一个时间戳
|
||||
- 不转换的时间戳替换为空字符串
|
||||
"""
|
||||
# 匹配[数字]或[数字.数字]格式的时间戳
|
||||
pattern = r'\[(\d+(?:\.\d+)?)\]'
|
||||
|
||||
# 找出所有匹配的时间戳
|
||||
matches = list(re.finditer(pattern, text))
|
||||
|
||||
if not matches:
|
||||
return text
|
||||
|
||||
# normal模式: 直接转换所有时间戳
|
||||
if mode == "normal":
|
||||
result_text = text
|
||||
for match in matches:
|
||||
timestamp = float(match.group(1))
|
||||
readable_time = translate_timestamp_to_human_readable(timestamp, "normal")
|
||||
# 由于替换会改变文本长度,需要使用正则替换而非直接替换
|
||||
pattern_instance = re.escape(match.group(0))
|
||||
result_text = re.sub(pattern_instance, readable_time, result_text, count=1)
|
||||
return result_text
|
||||
else:
|
||||
# lite模式: 按5秒间隔划分并选择性转换
|
||||
result_text = text
|
||||
|
||||
# 提取所有时间戳及其位置
|
||||
timestamps = [(float(m.group(1)), m) for m in matches]
|
||||
timestamps.sort(key=lambda x: x[0]) # 按时间戳升序排序
|
||||
|
||||
if not timestamps:
|
||||
return text
|
||||
|
||||
# 获取第一个和最后一个时间戳
|
||||
first_timestamp, first_match = timestamps[0]
|
||||
last_timestamp, last_match = timestamps[-1]
|
||||
|
||||
# 将时间范围划分成5秒间隔的时间段
|
||||
time_segments = {}
|
||||
|
||||
# 对所有时间戳按15秒间隔分组
|
||||
for ts, match in timestamps:
|
||||
segment_key = int(ts // 15) # 将时间戳除以15取整,作为时间段的键
|
||||
if segment_key not in time_segments:
|
||||
time_segments[segment_key] = []
|
||||
time_segments[segment_key].append((ts, match))
|
||||
|
||||
# 记录需要转换的时间戳
|
||||
to_convert = []
|
||||
|
||||
# 从每个时间段中选择一个时间戳进行转换
|
||||
for segment, segment_timestamps in time_segments.items():
|
||||
# 选择这个时间段中的第一个时间戳
|
||||
to_convert.append(segment_timestamps[0])
|
||||
|
||||
# 确保第一个和最后一个时间戳在转换列表中
|
||||
first_in_list = False
|
||||
last_in_list = False
|
||||
|
||||
for ts, match in to_convert:
|
||||
if ts == first_timestamp:
|
||||
first_in_list = True
|
||||
if ts == last_timestamp:
|
||||
last_in_list = True
|
||||
|
||||
if not first_in_list:
|
||||
to_convert.append((first_timestamp, first_match))
|
||||
if not last_in_list:
|
||||
to_convert.append((last_timestamp, last_match))
|
||||
|
||||
# 创建需要转换的时间戳集合,用于快速查找
|
||||
to_convert_set = {match.group(0) for _, match in to_convert}
|
||||
|
||||
# 首先替换所有不需要转换的时间戳为空字符串
|
||||
for ts, match in timestamps:
|
||||
if match.group(0) not in to_convert_set:
|
||||
pattern_instance = re.escape(match.group(0))
|
||||
result_text = re.sub(pattern_instance, "", result_text, count=1)
|
||||
|
||||
# 按照时间戳原始顺序排序,避免替换时位置错误
|
||||
to_convert.sort(key=lambda x: x[1].start())
|
||||
|
||||
# 执行替换
|
||||
# 由于替换会改变文本长度,从后向前替换
|
||||
to_convert.reverse()
|
||||
for ts, match in to_convert:
|
||||
readable_time = translate_timestamp_to_human_readable(ts, "relative")
|
||||
pattern_instance = re.escape(match.group(0))
|
||||
result_text = re.sub(pattern_instance, readable_time, result_text, count=1)
|
||||
|
||||
return result_text
|
||||
|
||||
@@ -235,6 +235,7 @@ class ThinkFlowChat:
|
||||
do_reply = False
|
||||
if random() < reply_probability:
|
||||
try:
|
||||
|
||||
do_reply = True
|
||||
|
||||
# 回复前处理
|
||||
@@ -258,7 +259,7 @@ class ThinkFlowChat:
|
||||
await heartflow.get_subheartflow(chat.stream_id).do_observe()
|
||||
except Exception as e:
|
||||
logger.error(f"心流观察失败: {e}")
|
||||
traceback.print_exc()
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
info_catcher.catch_after_observe(timing_results["观察"])
|
||||
|
||||
@@ -329,13 +330,17 @@ class ThinkFlowChat:
|
||||
chat.stream_id
|
||||
).do_thinking_before_reply(
|
||||
message_txt=message.processed_plain_text,
|
||||
sender_name=message.message_info.user_info.user_nickname,
|
||||
sender_info=message.message_info.user_info,
|
||||
chat_stream=chat,
|
||||
obs_id=get_mid_memory_id,
|
||||
extra_info=tool_result_info,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"心流思考前脑内状态失败: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
# 确保变量被定义,即使在错误情况下
|
||||
current_mind = ""
|
||||
past_mind = ""
|
||||
|
||||
info_catcher.catch_afer_shf_step(timing_results["思考前脑内状态"], past_mind, current_mind)
|
||||
|
||||
@@ -373,6 +378,7 @@ class ThinkFlowChat:
|
||||
except Exception as e:
|
||||
logger.error(f"心流处理表情包失败: {e}")
|
||||
|
||||
# 思考后脑内状态更新
|
||||
try:
|
||||
with Timer("思考后脑内状态更新", timing_results):
|
||||
stream_id = message.chat_stream.stream_id
|
||||
@@ -387,9 +393,43 @@ class ThinkFlowChat:
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"心流思考后脑内状态更新失败: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 回复后处理
|
||||
await willing_manager.after_generate_reply_handle(message.message_info.message_id)
|
||||
|
||||
# 处理认识关系
|
||||
try:
|
||||
is_known = await relationship_manager.is_known_some_one(
|
||||
message.message_info.platform,
|
||||
message.message_info.user_info.user_id
|
||||
)
|
||||
if not is_known:
|
||||
logger.info(f"首次认识用户: {message.message_info.user_info.user_nickname}")
|
||||
await relationship_manager.first_knowing_some_one(
|
||||
message.message_info.platform,
|
||||
message.message_info.user_info.user_id,
|
||||
message.message_info.user_info.user_nickname,
|
||||
message.message_info.user_info.user_cardname or message.message_info.user_info.user_nickname,
|
||||
""
|
||||
)
|
||||
else:
|
||||
logger.debug(f"已认识用户: {message.message_info.user_info.user_nickname}")
|
||||
if not await relationship_manager.is_qved_name(
|
||||
message.message_info.platform,
|
||||
message.message_info.user_info.user_id
|
||||
):
|
||||
logger.info(f"更新已认识但未取名的用户: {message.message_info.user_info.user_nickname}")
|
||||
await relationship_manager.first_knowing_some_one(
|
||||
message.message_info.platform,
|
||||
message.message_info.user_info.user_id,
|
||||
message.message_info.user_info.user_nickname,
|
||||
message.message_info.user_info.user_cardname or message.message_info.user_info.user_nickname,
|
||||
""
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"处理认识关系失败: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"心流处理消息失败: {e}")
|
||||
|
||||
@@ -100,15 +100,17 @@ class ResponseGenerator:
|
||||
|
||||
info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
|
||||
|
||||
if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname:
|
||||
sender_name = (
|
||||
f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]"
|
||||
f"{message.chat_stream.user_info.user_cardname}"
|
||||
)
|
||||
elif message.chat_stream.user_info.user_nickname:
|
||||
sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
|
||||
else:
|
||||
sender_name = f"用户({message.chat_stream.user_info.user_id})"
|
||||
# if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname:
|
||||
# sender_name = (
|
||||
# f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]"
|
||||
# f"{message.chat_stream.user_info.user_cardname}"
|
||||
# )
|
||||
# elif message.chat_stream.user_info.user_nickname:
|
||||
# sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
|
||||
# else:
|
||||
# sender_name = f"用户({message.chat_stream.user_info.user_id})"
|
||||
|
||||
sender_name = f"<{message.chat_stream.user_info.platform}:{message.chat_stream.user_info.user_id}:{message.chat_stream.user_info.user_nickname}:{message.chat_stream.user_info.user_cardname}>"
|
||||
|
||||
# 构建prompt
|
||||
with Timer() as t_build_prompt:
|
||||
@@ -119,14 +121,7 @@ class ResponseGenerator:
|
||||
sender_name=sender_name,
|
||||
stream_id=message.chat_stream.stream_id,
|
||||
)
|
||||
elif mode == "simple":
|
||||
prompt = await prompt_builder._build_prompt_simple(
|
||||
message.chat_stream,
|
||||
message_txt=message.processed_plain_text,
|
||||
sender_name=sender_name,
|
||||
stream_id=message.chat_stream.stream_id,
|
||||
)
|
||||
logger.info(f"构建{mode}prompt时间: {t_build_prompt.human_readable}")
|
||||
logger.info(f"构建prompt时间: {t_build_prompt.human_readable}")
|
||||
|
||||
try:
|
||||
content, reasoning_content, self.current_model_name = await model.generate_response(prompt)
|
||||
@@ -141,49 +136,6 @@ class ResponseGenerator:
|
||||
|
||||
return content
|
||||
|
||||
async def _check_response_with_model(
|
||||
self, message: MessageRecv, content: str, model: LLM_request, thinking_id: str
|
||||
) -> str:
|
||||
_info_catcher = info_catcher_manager.get_info_catcher(thinking_id)
|
||||
|
||||
sender_name = ""
|
||||
if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname:
|
||||
sender_name = (
|
||||
f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]"
|
||||
f"{message.chat_stream.user_info.user_cardname}"
|
||||
)
|
||||
elif message.chat_stream.user_info.user_nickname:
|
||||
sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}"
|
||||
else:
|
||||
sender_name = f"用户({message.chat_stream.user_info.user_id})"
|
||||
|
||||
# 构建prompt
|
||||
with Timer() as t_build_prompt_check:
|
||||
prompt = await prompt_builder._build_prompt_check_response(
|
||||
message.chat_stream,
|
||||
message_txt=message.processed_plain_text,
|
||||
sender_name=sender_name,
|
||||
stream_id=message.chat_stream.stream_id,
|
||||
content=content,
|
||||
)
|
||||
logger.info(f"构建check_prompt: {prompt}")
|
||||
logger.info(f"构建check_prompt时间: {t_build_prompt_check.human_readable}")
|
||||
|
||||
try:
|
||||
checked_content, reasoning_content, self.current_model_name = await model.generate_response(prompt)
|
||||
|
||||
# info_catcher.catch_after_llm_generated(
|
||||
# prompt=prompt,
|
||||
# response=content,
|
||||
# reasoning_content=reasoning_content,
|
||||
# model_name=self.current_model_name)
|
||||
|
||||
except Exception:
|
||||
logger.exception("检查回复时出错")
|
||||
return None
|
||||
|
||||
return checked_content
|
||||
|
||||
async def _get_emotion_tags(self, content: str, processed_plain_text: str):
|
||||
"""提取情感标签,结合立场和情绪"""
|
||||
try:
|
||||
|
||||
@@ -8,7 +8,8 @@ from src.common.logger import get_module_logger
|
||||
from ....individuality.individuality import Individuality
|
||||
from src.heart_flow.heartflow import heartflow
|
||||
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
|
||||
|
||||
from src.plugins.person_info.relationship_manager import relationship_manager
|
||||
from src.plugins.chat.utils import parse_text_timestamps
|
||||
logger = get_module_logger("prompt")
|
||||
|
||||
|
||||
@@ -160,7 +161,10 @@ class PromptBuilder:
|
||||
prompt_ger=prompt_ger,
|
||||
moderation_prompt=await global_prompt_manager.get_prompt_async("moderation_prompt"),
|
||||
)
|
||||
|
||||
|
||||
prompt = await relationship_manager.convert_all_person_sign_to_person_name(prompt)
|
||||
prompt = parse_text_timestamps(prompt, mode="lite")
|
||||
|
||||
return prompt
|
||||
|
||||
async def _build_prompt_simple(
|
||||
|
||||
@@ -26,9 +26,9 @@ config_config = LogConfig(
|
||||
logger = get_module_logger("config", config=config_config)
|
||||
|
||||
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
||||
is_test = False
|
||||
mai_version_main = "0.6.2"
|
||||
mai_version_fix = ""
|
||||
is_test = True
|
||||
mai_version_main = "0.6.3"
|
||||
mai_version_fix = "snapshot-1"
|
||||
|
||||
if mai_version_fix:
|
||||
if is_test:
|
||||
|
||||
@@ -6,6 +6,9 @@ from typing import Any, Callable, Dict
|
||||
import datetime
|
||||
import asyncio
|
||||
import numpy as np
|
||||
from src.plugins.models.utils_model import LLM_request
|
||||
from src.plugins.config.config import global_config
|
||||
from src.individuality.individuality import Individuality
|
||||
|
||||
import matplotlib
|
||||
|
||||
@@ -13,6 +16,8 @@ matplotlib.use("Agg")
|
||||
import matplotlib.pyplot as plt
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
"""
|
||||
@@ -32,6 +37,8 @@ logger = get_module_logger("person_info")
|
||||
|
||||
person_info_default = {
|
||||
"person_id": None,
|
||||
"person_name": None,
|
||||
"name_reason": None,
|
||||
"platform": None,
|
||||
"user_id": None,
|
||||
"nickname": None,
|
||||
@@ -48,16 +55,46 @@ person_info_default = {
|
||||
|
||||
class PersonInfoManager:
|
||||
def __init__(self):
|
||||
self.person_name_list = {}
|
||||
self.qv_name_llm = LLM_request(
|
||||
model=global_config.llm_normal,
|
||||
max_tokens=256,
|
||||
request_type="qv_name",
|
||||
)
|
||||
if "person_info" not in db.list_collection_names():
|
||||
db.create_collection("person_info")
|
||||
db.person_info.create_index("person_id", unique=True)
|
||||
|
||||
# 初始化时读取所有person_name
|
||||
cursor = db.person_info.find(
|
||||
{"person_name": {"$exists": True}},
|
||||
{"person_id": 1, "person_name": 1, "_id": 0}
|
||||
)
|
||||
for doc in cursor:
|
||||
if doc.get("person_name"):
|
||||
self.person_name_list[doc["person_id"]] = doc["person_name"]
|
||||
logger.debug(f"已加载 {len(self.person_name_list)} 个用户名称")
|
||||
|
||||
def get_person_id(self, platform: str, user_id: int):
|
||||
"""获取唯一id"""
|
||||
#如果platform中存在-,就截取-后面的部分
|
||||
if "-" in platform:
|
||||
platform = platform.split("-")[1]
|
||||
|
||||
components = [platform, str(user_id)]
|
||||
key = "_".join(components)
|
||||
return hashlib.md5(key.encode()).hexdigest()
|
||||
|
||||
def is_person_known(self, platform: str, user_id: int):
|
||||
"""判断是否认识某人"""
|
||||
person_id = self.get_person_id(platform, user_id)
|
||||
document = db.person_info.find_one({"person_id": person_id})
|
||||
if document:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
async def create_person_info(self, person_id: str, data: dict = None):
|
||||
"""创建一个项"""
|
||||
if not person_id:
|
||||
@@ -88,6 +125,109 @@ class PersonInfoManager:
|
||||
Data[field_name] = value
|
||||
logger.debug(f"更新时{person_id}不存在,已新建")
|
||||
await self.create_person_info(person_id, Data)
|
||||
|
||||
async def has_one_field(self, person_id: str, field_name: str):
|
||||
"""判断是否存在某一个字段"""
|
||||
document = db.person_info.find_one({"person_id": person_id}, {field_name: 1})
|
||||
if document:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _extract_json_from_text(self, text: str) -> dict:
|
||||
"""从文本中提取JSON数据的高容错方法"""
|
||||
try:
|
||||
|
||||
# 尝试直接解析
|
||||
return json.loads(text)
|
||||
except json.JSONDecodeError:
|
||||
try:
|
||||
# 尝试找到JSON格式的部分
|
||||
json_pattern = r'\{[^{}]*\}'
|
||||
matches = re.findall(json_pattern, text)
|
||||
if matches:
|
||||
return json.loads(matches[0])
|
||||
|
||||
# 如果上面都失败了,尝试提取键值对
|
||||
nickname_pattern = r'"nickname"[:\s]+"([^"]+)"'
|
||||
reason_pattern = r'"reason"[:\s]+"([^"]+)"'
|
||||
|
||||
nickname_match = re.search(nickname_pattern, text)
|
||||
reason_match = re.search(reason_pattern, text)
|
||||
|
||||
if nickname_match:
|
||||
return {
|
||||
"nickname": nickname_match.group(1),
|
||||
"reason": reason_match.group(1) if reason_match else "未提供理由"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"JSON提取失败: {str(e)}")
|
||||
|
||||
# 如果所有方法都失败了,返回空结果
|
||||
return {"nickname": "", "reason": ""}
|
||||
|
||||
async def qv_person_name(self, person_id: str, user_nickname: str, user_cardname: str, user_avatar: str):
|
||||
"""给某个用户取名"""
|
||||
if not person_id:
|
||||
logger.debug("取名失败:person_id不能为空")
|
||||
return
|
||||
|
||||
old_name = await self.get_value(person_id, "person_name")
|
||||
old_reason = await self.get_value(person_id, "name_reason")
|
||||
|
||||
max_retries = 5 # 最大重试次数
|
||||
current_try = 0
|
||||
existing_names = ""
|
||||
while current_try < max_retries:
|
||||
individuality = Individuality.get_instance()
|
||||
prompt_personality = individuality.get_prompt(type="personality", x_person=2, level=1)
|
||||
bot_name = individuality.personality.bot_nickname
|
||||
|
||||
qv_name_prompt = f"你是{bot_name},你{prompt_personality}"
|
||||
qv_name_prompt += f"现在你想给一个用户取一个昵称,用户是的qq昵称是{user_nickname},"
|
||||
qv_name_prompt += f"用户的qq群昵称名是{user_cardname},"
|
||||
if user_avatar:
|
||||
qv_name_prompt += f"用户的qq头像是{user_avatar},"
|
||||
if old_name:
|
||||
qv_name_prompt += f"你之前叫他{old_name},是因为{old_reason},"
|
||||
|
||||
qv_name_prompt += "\n请根据以上用户信息,想想你叫他什么比较好,请最好使用用户的qq昵称,可以稍作修改"
|
||||
if existing_names:
|
||||
qv_name_prompt += f"\n请注意,以下名称已被使用,不要使用以下昵称:{existing_names}。\n"
|
||||
qv_name_prompt += "请用json给出你的想法,并给出理由,示例如下:"
|
||||
qv_name_prompt += '''{
|
||||
"nickname": "昵称",
|
||||
"reason": "理由"
|
||||
}'''
|
||||
logger.debug(f"取名提示词:{qv_name_prompt}")
|
||||
response = await self.qv_name_llm.generate_response(qv_name_prompt)
|
||||
logger.debug(f"取名回复:{response}")
|
||||
result = self._extract_json_from_text(response[0])
|
||||
|
||||
if not result["nickname"]:
|
||||
logger.error("生成的昵称为空,重试中...")
|
||||
current_try += 1
|
||||
continue
|
||||
|
||||
# 检查生成的昵称是否已存在
|
||||
if result["nickname"] not in self.person_name_list.values():
|
||||
# 更新数据库和内存中的列表
|
||||
await self.update_one_field(person_id, "person_name", result["nickname"])
|
||||
# await self.update_one_field(person_id, "nickname", user_nickname)
|
||||
# await self.update_one_field(person_id, "avatar", user_avatar)
|
||||
await self.update_one_field(person_id, "name_reason", result["reason"])
|
||||
|
||||
self.person_name_list[person_id] = result["nickname"]
|
||||
logger.debug(f"用户 {person_id} 的名称已更新为 {result['nickname']},原因:{result['reason']}")
|
||||
return result
|
||||
else:
|
||||
existing_names += f"{result['nickname']}、"
|
||||
|
||||
logger.debug(f"生成的昵称 {result['nickname']} 已存在,重试中...")
|
||||
current_try += 1
|
||||
|
||||
logger.error(f"在{max_retries}次尝试后仍未能生成唯一昵称")
|
||||
return None
|
||||
|
||||
async def del_one_document(self, person_id: str):
|
||||
"""删除指定 person_id 的文档"""
|
||||
|
||||
@@ -4,6 +4,8 @@ import math
|
||||
from bson.decimal128 import Decimal128
|
||||
from .person_info import person_info_manager
|
||||
import time
|
||||
import re
|
||||
import traceback
|
||||
|
||||
relationship_config = LogConfig(
|
||||
# 使用关系专用样式
|
||||
@@ -74,6 +76,61 @@ class RelationshipManager:
|
||||
return mood_value * coefficient
|
||||
else:
|
||||
return mood_value / coefficient
|
||||
|
||||
async def is_known_some_one(self, platform , user_id):
|
||||
"""判断是否认识某人"""
|
||||
is_known = person_info_manager.is_person_known(platform, user_id)
|
||||
return is_known
|
||||
|
||||
async def is_qved_name(self, platform , user_id):
|
||||
"""判断是否认识某人"""
|
||||
person_id = person_info_manager.get_person_id(platform, user_id)
|
||||
is_qved = await person_info_manager.has_one_field(person_id, "person_name")
|
||||
old_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
print(f"old_name: {old_name}")
|
||||
print(f"is_qved: {is_qved}")
|
||||
if is_qved and old_name != None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
async def first_knowing_some_one(self, platform , user_id, user_nickname, user_cardname, user_avatar):
|
||||
"""判断是否认识某人"""
|
||||
person_id = person_info_manager.get_person_id(platform,user_id)
|
||||
await person_info_manager.update_one_field(person_id, "nickname", user_nickname)
|
||||
# await person_info_manager.update_one_field(person_id, "user_cardname", user_cardname)
|
||||
# await person_info_manager.update_one_field(person_id, "user_avatar", user_avatar)
|
||||
await person_info_manager.qv_person_name(person_id, user_nickname, user_cardname, user_avatar)
|
||||
|
||||
async def convert_all_person_sign_to_person_name(self,input_text:str):
|
||||
"""将所有人的<platform:user_id:nickname:cardname>格式转换为person_name"""
|
||||
try:
|
||||
# 使用正则表达式匹配<platform:user_id:nickname:cardname>格式
|
||||
all_person = person_info_manager.person_name_list
|
||||
|
||||
pattern = r'<([^:]+):(\d+):([^:]+):([^>]+)>'
|
||||
matches = re.findall(pattern, input_text)
|
||||
|
||||
# 遍历匹配结果,将<platform:user_id:nickname:cardname>替换为person_name
|
||||
result_text = input_text
|
||||
for platform, user_id, nickname, cardname in matches:
|
||||
person_id = person_info_manager.get_person_id(platform, user_id)
|
||||
# 默认使用昵称作为人名
|
||||
person_name = nickname.strip() if nickname.strip() else cardname.strip()
|
||||
|
||||
if person_id in all_person:
|
||||
if all_person[person_id] != None:
|
||||
person_name = all_person[person_id]
|
||||
|
||||
print(f"将<{platform}:{user_id}:{nickname}:{cardname}>替换为{person_name}")
|
||||
|
||||
|
||||
result_text = result_text.replace(f"<{platform}:{user_id}:{nickname}:{cardname}>", person_name)
|
||||
|
||||
return result_text
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
return input_text
|
||||
|
||||
async def calculate_update_relationship_value(self, chat_stream: ChatStream, label: str, stance: str) -> tuple:
|
||||
"""计算并变更关系值
|
||||
|
||||
@@ -126,6 +126,7 @@ def main():
|
||||
"""主函数,启动心跳线程"""
|
||||
# 配置
|
||||
SERVER_URL = "http://hyybuth.xyz:10058"
|
||||
# SERVER_URL = "http://localhost:10058"
|
||||
HEARTBEAT_INTERVAL = 300 # 5分钟(秒)
|
||||
|
||||
# 创建并启动心跳线程
|
||||
|
||||
Reference in New Issue
Block a user