This commit is contained in:
SengokuCola
2025-04-29 18:54:01 +08:00
12 changed files with 112 additions and 111 deletions

View File

@@ -93,8 +93,7 @@ class ActionPlanner:
max_tokens=1500, max_tokens=1500,
request_type="action_planning", request_type="action_planning",
) )
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3) self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME self.name = global_config.BOT_NICKNAME
self.private_name = private_name self.private_name = private_name
self.chat_observer = ChatObserver.get_instance(stream_id, private_name) self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
@@ -244,21 +243,7 @@ class ActionPlanner:
chat_history_text = "处理聊天记录时出错。\n" chat_history_text = "处理聊天记录时出错。\n"
# 构建 Persona 文本 (persona_text) # 构建 Persona 文本 (persona_text)
# (这部分逻辑不变) persona_text = f"你的名字是{self.name}{self.personality_info}"
identity_details_only = self.identity_detail_info
identity_addon = ""
if isinstance(identity_details_only, str):
pronouns = ["", "", ""]
for p in pronouns:
if identity_details_only.startswith(p):
identity_details_only = identity_details_only[len(p) :]
break
if identity_details_only.endswith(""):
identity_details_only = identity_details_only[:-1]
cleaned_details = identity_details_only.strip(", ")
if cleaned_details:
identity_addon = f"并且{cleaned_details}"
persona_text = f"你的名字是{self.name}{self.personality_info}{identity_addon}"
# 构建行动历史和上一次行动结果 (action_history_summary, last_action_context) # 构建行动历史和上一次行动结果 (action_history_summary, last_action_context)
# (这部分逻辑不变) # (这部分逻辑不变)

View File

@@ -368,6 +368,15 @@ class Conversation:
self.conversation_info.last_successful_reply_action = "send_new_message" self.conversation_info.last_successful_reply_action = "send_new_message"
action_successful = True # 标记动作成功 action_successful = True # 标记动作成功
elif need_replan:
# 打回动作决策
logger.warning(
f"[私聊][{self.private_name}]经过 {reply_attempt_count} 次尝试,追问回复决定打回动作决策。打回原因: {check_reason}"
)
conversation_info.done_action[action_index].update(
{"status": "recall", "final_reason": f"追问尝试{reply_attempt_count}次后打回: {check_reason}"}
)
else: else:
# 追问失败 # 追问失败
logger.warning( logger.warning(
@@ -463,6 +472,15 @@ class Conversation:
self.conversation_info.last_successful_reply_action = "direct_reply" self.conversation_info.last_successful_reply_action = "direct_reply"
action_successful = True # 标记动作成功 action_successful = True # 标记动作成功
elif need_replan:
# 打回动作决策
logger.warning(
f"[私聊][{self.private_name}]经过 {reply_attempt_count} 次尝试,首次回复决定打回动作决策。打回原因: {check_reason}"
)
conversation_info.done_action[action_index].update(
{"status": "recall", "final_reason": f"首次回复尝试{reply_attempt_count}次后打回: {check_reason}"}
)
else: else:
# 首次回复失败 # 首次回复失败
logger.warning( logger.warning(

View File

@@ -23,8 +23,7 @@ class GoalAnalyzer:
model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="conversation_goal" model=global_config.llm_normal, temperature=0.7, max_tokens=1000, request_type="conversation_goal"
) )
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3) self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME self.name = global_config.BOT_NICKNAME
self.nick_name = global_config.BOT_ALIAS_NAMES self.nick_name = global_config.BOT_ALIAS_NAMES
self.private_name = private_name self.private_name = private_name
@@ -79,21 +78,7 @@ class GoalAnalyzer:
# await observation_info.clear_unprocessed_messages() # await observation_info.clear_unprocessed_messages()
identity_details_only = self.identity_detail_info persona_text = f"你的名字是{self.name}{self.personality_info}"
identity_addon = ""
if isinstance(identity_details_only, str):
pronouns = ["", "", ""]
for p in pronouns:
if identity_details_only.startswith(p):
identity_details_only = identity_details_only[len(p) :]
break
if identity_details_only.endswith(""):
identity_details_only = identity_details_only[:-1]
cleaned_details = identity_details_only.strip(", ")
if cleaned_details:
identity_addon = f"并且{cleaned_details}"
persona_text = f"你的名字是{self.name}{self.personality_info}{identity_addon}"
# 构建action历史文本 # 构建action历史文本
action_history_list = conversation_info.done_action action_history_list = conversation_info.done_action
action_history_text = "你之前做的事情是:" action_history_text = "你之前做的事情是:"
@@ -241,21 +226,8 @@ class GoalAnalyzer:
timestamp_mode="relative", timestamp_mode="relative",
read_mark=0.0, read_mark=0.0,
) )
identity_details_only = self.identity_detail_info
identity_addon = ""
if isinstance(identity_details_only, str):
pronouns = ["", "", ""]
for p in pronouns:
if identity_details_only.startswith(p):
identity_details_only = identity_details_only[len(p) :]
break
if identity_details_only.endswith(""):
identity_details_only = identity_details_only[:-1]
cleaned_details = identity_details_only.strip(", ")
if cleaned_details:
identity_addon = f"并且{cleaned_details}"
persona_text = f"你的名字是{self.name}{self.personality_info}{identity_addon}" persona_text = f"你的名字是{self.name}{self.personality_info}"
# ===> Persona 文本构建结束 <=== # ===> Persona 文本构建结束 <===
# --- 修改 Prompt 字符串,使用 persona_text --- # --- 修改 Prompt 字符串,使用 persona_text ---

View File

@@ -55,9 +55,9 @@ class ReplyChecker:
) )
return ( return (
False, False,
"回复内容与你上一条发言完全相同,请修改,可以选择深入话题或寻找其它话题或等待", "被逻辑检查拒绝:回复内容与你上一条发言完全相同,可以选择深入话题或寻找其它话题或等待",
False, True,
) # 不合适,无需重新规划 ) # 不合适,需要返回至决策层
# 2. 相似度检查 (如果精确匹配未通过) # 2. 相似度检查 (如果精确匹配未通过)
import difflib # 导入 difflib 库 import difflib # 导入 difflib 库
@@ -73,8 +73,8 @@ class ReplyChecker:
) )
return ( return (
False, False,
f"拒绝发送:回复内容与你上一条发言高度相似 (相似度 {similarity_ratio:.2f})请修改,可以选择深入话题或寻找其它话题或等待。", f"被逻辑检查拒绝:回复内容与你上一条发言高度相似 (相似度 {similarity_ratio:.2f}),可以选择深入话题或寻找其它话题或等待。",
False, True,
) )
except Exception as e: except Exception as e:
@@ -83,37 +83,37 @@ class ReplyChecker:
logger.error(f"[私聊][{self.private_name}]检查回复时出错: 类型={type(e)}, 值={e}") logger.error(f"[私聊][{self.private_name}]检查回复时出错: 类型={type(e)}, 值={e}")
logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 打印详细的回溯信息 logger.error(f"[私聊][{self.private_name}]{traceback.format_exc()}") # 打印详细的回溯信息
prompt = f"""请检查以下回复或消息是否合适: prompt = f"""你是一个聊天逻辑检查器,请检查以下回复或消息是否合适:
当前对话目标:{goal} 当前对话目标:{goal}
最新的对话记录: 最新的对话记录:
{chat_history_text} {chat_history_text}
待检查的回复 待检查的消息
{reply} {reply}
请结合聊天记录检查以下几点: 请结合聊天记录检查以下几点:
1. 回复是否依然符合当前对话目标和实现方式 1. 这条消息是否依然符合当前对话目标和实现方式
2. 回复是否与最新的对话记录保持一致性 2. 这条消息是否与最新的对话记录保持一致性
3. 回复是否重复发言,或重复表达同质内容(尤其是只是换一种方式表达了相同的含义) 3. 是否存在重复发言,或重复表达同质内容(尤其是只是换一种方式表达了相同的含义)
4. 回复是否包含违规内容(例如血腥暴力,政治敏感等) 4. 这条消息是否包含违规内容(例如血腥暴力,政治敏感等)
5. 回复是否以你的角度发言,不要把""说的话当做对方说的话,这是你自己说的话(不要自己回复自己的消息) 5. 这条消息是否以发送者的角度发言(不要让发送者自己回复自己的消息)
6. 回复是否通俗易懂 6. 这条消息是否通俗易懂
7. 回复是否有些多余例如在对方没有回复的情况下依然连续多次“消息轰炸”尤其是已经连续发送3条信息的情况这很可能不合理需要着重判断 7. 这条消息是否有些多余例如在对方没有回复的情况下依然连续多次“消息轰炸”尤其是已经连续发送3条信息的情况这很可能不合理需要着重判断
8. 回复是否使用了完全没必要的修辞 8. 这条消息是否使用了完全没必要的修辞
9. 回复是否逻辑通顺 9. 这条消息是否逻辑通顺
10. 回复是否太过冗长了通常私聊的每条消息长度在20字以内除非特殊情况 10. 这条消息是否太过冗长了通常私聊的每条消息长度在20字以内除非特殊情况
11. 在连续多次发送消息的情况下,当前回复是否衔接自然,会不会显得奇怪(例如连续两条消息中部分内容重叠) 11. 在连续多次发送消息的情况下,这条消息是否衔接自然,会不会显得奇怪(例如连续两条消息中部分内容重叠)
请以JSON格式输出包含以下字段 请以JSON格式输出包含以下字段
1. suitable: 是否合适 (true/false) 1. suitable: 是否合适 (true/false)
2. reason: 原因说明 2. reason: 原因说明
3. need_replan: 是否需要重新规划对话目标 (true/false),当发现当前对话目标不再适合时设为true 3. need_replan: 是否需要重新决策 (true/false),当你认为此时已经不适合发消息,需要规划其它行动时,设为true
输出格式示例: 输出格式示例:
{{ {{
"suitable": true, "suitable": true,
"reason": "回复符合要求,内容得体", "reason": "回复符合要求,虽然有可能略微偏离目标,但是整体内容流畅得体",
"need_replan": false "need_replan": false
}} }}

View File

@@ -68,8 +68,7 @@ class ReplyGenerator:
max_tokens=300, max_tokens=300,
request_type="reply_generation", request_type="reply_generation",
) )
self.personality_info = Individuality.get_instance().get_prompt(type="personality", x_person=2, level=3) self.personality_info = Individuality.get_instance().get_prompt(x_person=2, level=3)
self.identity_detail_info = Individuality.get_instance().get_prompt(type="identity", x_person=2, level=2)
self.name = global_config.BOT_NICKNAME self.name = global_config.BOT_NICKNAME
self.private_name = private_name self.private_name = private_name
self.chat_observer = ChatObserver.get_instance(stream_id, private_name) self.chat_observer = ChatObserver.get_instance(stream_id, private_name)
@@ -130,20 +129,7 @@ class ReplyGenerator:
chat_history_text = "还没有聊天记录。" chat_history_text = "还没有聊天记录。"
# 构建 Persona 文本 (persona_text) # 构建 Persona 文本 (persona_text)
identity_details_only = self.identity_detail_info persona_text = f"你的名字是{self.name}{self.personality_info}"
identity_addon = ""
if isinstance(identity_details_only, str):
pronouns = ["", "", ""]
for p in pronouns:
if identity_details_only.startswith(p):
identity_details_only = identity_details_only[len(p) :]
break
if identity_details_only.endswith(""):
identity_details_only = identity_details_only[:-1]
cleaned_details = identity_details_only.strip(", ")
if cleaned_details:
identity_addon = f"并且{cleaned_details}"
persona_text = f"你的名字是{self.name}{self.personality_info}{identity_addon}"
# --- 选择 Prompt --- # --- 选择 Prompt ---
if action_type == "send_new_message": if action_type == "send_new_message":

View File

@@ -360,6 +360,7 @@ class EmojiManager:
return return
total_count = len(self.emoji_objects) total_count = len(self.emoji_objects)
self.emoji_num = total_count
removed_count = 0 removed_count = 0
# 使用列表复制进行遍历,因为我们会在遍历过程中修改列表 # 使用列表复制进行遍历,因为我们会在遍历过程中修改列表
for emoji in self.emoji_objects[:]: for emoji in self.emoji_objects[:]:
@@ -376,10 +377,22 @@ class EmojiManager:
removed_count += 1 removed_count += 1
continue continue
if emoji.description == None:
logger.warning(f"[检查] 表情包文件已被删除: {emoji.path}")
# 执行表情包对象的删除方法
await emoji.delete()
# 从列表中移除该对象
self.emoji_objects.remove(emoji)
# 更新计数
self.emoji_num -= 1
removed_count += 1
continue
except Exception as item_error: except Exception as item_error:
logger.error(f"[错误] 处理表情包记录时出错: {str(item_error)}") logger.error(f"[错误] 处理表情包记录时出错: {str(item_error)}")
continue continue
await self.clean_unused_emojis(EMOJI_REGISTED_DIR, self.emoji_objects)
# 输出清理结果 # 输出清理结果
if removed_count > 0: if removed_count > 0:
logger.success(f"[清理] 已清理 {removed_count} 个失效的表情包记录") logger.success(f"[清理] 已清理 {removed_count} 个失效的表情包记录")
@@ -749,7 +762,7 @@ class EmojiManager:
await new_emoji.initialize_hash_format() await new_emoji.initialize_hash_format()
emoji_base64 = image_path_to_base64(os.path.join(EMOJI_DIR, filename)) emoji_base64 = image_path_to_base64(os.path.join(EMOJI_DIR, filename))
description, emotions = await self.build_emoji_description(emoji_base64) description, emotions = await self.build_emoji_description(emoji_base64)
if description == "": if description == "" or description == None:
return False return False
new_emoji.description = description new_emoji.description = description
new_emoji.emotion = emotions new_emoji.emotion = emotions
@@ -817,6 +830,26 @@ class EmojiManager:
logger.success("[清理] 临时文件清理完成") logger.success("[清理] 临时文件清理完成")
async def clean_unused_emojis(self, emoji_dir, emoji_objects):
"""清理未使用的表情包文件
遍历指定文件夹中的所有文件删除未在emoji_objects列表中的文件
"""
# 获取所有表情包路径
emoji_paths = {emoji.path for emoji in emoji_objects}
# 遍历文件夹中的所有文件
for file_name in os.listdir(emoji_dir):
file_path = os.path.join(emoji_dir, file_name)
# 检查文件是否在表情包路径列表中
if file_path not in emoji_paths:
try:
# 删除未在表情包列表中的文件
os.remove(file_path)
logger.info(f"[清理] 删除未使用的表情包文件: {file_path}")
except Exception as e:
logger.error(f"[错误] 删除文件时出错: {str(e)}")
# 创建全局单例 # 创建全局单例
emoji_manager = EmojiManager() emoji_manager = EmojiManager()

View File

@@ -5,7 +5,7 @@ from ...individuality.individuality import Individuality
from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager from src.plugins.utils.prompt_builder import Prompt, global_prompt_manager
from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat from src.plugins.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
from src.plugins.person_info.relationship_manager import relationship_manager from src.plugins.person_info.relationship_manager import relationship_manager
from src.plugins.chat.utils import get_embedding, parse_text_timestamps from src.plugins.chat.utils import get_embedding
import time import time
from typing import Union, Optional from typing import Union, Optional
from ...common.database import db from ...common.database import db

View File

@@ -1495,21 +1495,24 @@ class ParahippocampalGyrus:
# 从原始列表中移除信息量较低的项 # 从原始列表中移除信息量较低的项
try: try:
memory_items.remove(item_to_remove) memory_items.remove(item_to_remove)
logger.info(f"[整合] 已合并节点 '{node}' 中的记忆,保留: '{item_to_keep[:60]}...', 移除: '{item_to_remove[:60]}...'" ) logger.info(
f"[整合] 已合并节点 '{node}' 中的记忆,保留: '{item_to_keep[:60]}...', 移除: '{item_to_remove[:60]}...'"
)
merged_count += 1 merged_count += 1
nodes_modified.add(node) nodes_modified.add(node)
node_data['last_modified'] = current_timestamp # 更新修改时间 node_data["last_modified"] = current_timestamp # 更新修改时间
_merged_in_this_node = True _merged_in_this_node = True
break # 每个节点每次检查只合并一对 break # 每个节点每次检查只合并一对
except ValueError: except ValueError:
# 如果项已经被移除(例如,在之前的迭代中作为 item_to_keep则跳过 # 如果项已经被移除(例如,在之前的迭代中作为 item_to_keep则跳过
logger.warning(f"[整合] 尝试移除节点 '{node}' 中不存在的项 '{item_to_remove[:30]}...',可能已被合并。") logger.warning(
f"[整合] 尝试移除节点 '{node}' 中不存在的项 '{item_to_remove[:30]}...',可能已被合并。"
)
continue continue
# # 如果节点内发生了合并,更新节点数据 (这种方式不安全,会丢失其他属性) # # 如果节点内发生了合并,更新节点数据 (这种方式不安全,会丢失其他属性)
# if merged_in_this_node: # if merged_in_this_node:
# self.memory_graph.G.nodes[node]["memory_items"] = memory_items # self.memory_graph.G.nodes[node]["memory_items"] = memory_items
if merged_count > 0: if merged_count > 0:
logger.info(f"[整合] 共合并了 {merged_count} 对相似记忆项,分布在 {len(nodes_modified)} 个节点中。") logger.info(f"[整合] 共合并了 {merged_count} 对相似记忆项,分布在 {len(nodes_modified)} 个节点中。")
sync_start = time.time() sync_start = time.time()

View File

@@ -31,7 +31,9 @@ class MemoryConfig:
"""从全局配置创建记忆系统配置""" """从全局配置创建记忆系统配置"""
# 使用 getattr 提供默认值,防止全局配置缺少这些项 # 使用 getattr 提供默认值,防止全局配置缺少这些项
return cls( return cls(
memory_build_distribution=getattr(global_config, "memory_build_distribution", (24, 12, 0.5, 168, 72, 0.5)), # 添加默认值 memory_build_distribution=getattr(
global_config, "memory_build_distribution", (24, 12, 0.5, 168, 72, 0.5)
), # 添加默认值
build_memory_sample_num=getattr(global_config, "build_memory_sample_num", 5), build_memory_sample_num=getattr(global_config, "build_memory_sample_num", 5),
build_memory_sample_length=getattr(global_config, "build_memory_sample_length", 30), build_memory_sample_length=getattr(global_config, "build_memory_sample_length", 30),
memory_compress_rate=getattr(global_config, "memory_compress_rate", 0.1), memory_compress_rate=getattr(global_config, "memory_compress_rate", 0.1),
@@ -42,5 +44,7 @@ class MemoryConfig:
consolidate_memory_percentage=getattr(global_config, "consolidate_memory_percentage", 0.01), consolidate_memory_percentage=getattr(global_config, "consolidate_memory_percentage", 0.01),
consolidate_memory_interval=getattr(global_config, "consolidate_memory_interval", 1000), consolidate_memory_interval=getattr(global_config, "consolidate_memory_interval", 1000),
llm_topic_judge=getattr(global_config, "llm_topic_judge", "default_judge_model"), # 添加默认模型名 llm_topic_judge=getattr(global_config, "llm_topic_judge", "default_judge_model"), # 添加默认模型名
llm_summary_by_topic=getattr(global_config, "llm_summary_by_topic", "default_summary_model"), # 添加默认模型名 llm_summary_by_topic=getattr(
global_config, "llm_summary_by_topic", "default_summary_model"
), # 添加默认模型名
) )

View File

@@ -4,8 +4,8 @@ import math
from bson.decimal128 import Decimal128 from bson.decimal128 import Decimal128
from .person_info import person_info_manager from .person_info import person_info_manager
import time import time
import re # import re
import traceback # import traceback
logger = get_logger("relation") logger = get_logger("relation")