Merge branch 'dev' into dev

This commit is contained in:
SengokuCola
2025-06-02 23:09:11 +08:00
committed by GitHub
13 changed files with 144 additions and 192 deletions

View File

@@ -3,6 +3,9 @@
## [0.7.1] -2025-6-2 ## [0.7.1] -2025-6-2
- 修复关键词功能并且在focus中可用 - 修复关键词功能并且在focus中可用
- 更新planner架构大大加快速度和表现效果建议使用simple规划器 - 更新planner架构大大加快速度和表现效果建议使用simple规划器
- 为normal加入使用action的能力
- 修复emoji配置项无效问题
- 修复log出错问题 - 修复log出错问题
- 修复focus吞第一条消息问题 - 修复focus吞第一条消息问题

View File

@@ -71,7 +71,6 @@ class APIBotConfig:
max_emoji_num: int # 最大表情符号数量 max_emoji_num: int # 最大表情符号数量
max_reach_deletion: bool # 达到最大数量时是否删除 max_reach_deletion: bool # 达到最大数量时是否删除
check_interval: int # 检查表情包的时间间隔(分钟) check_interval: int # 检查表情包的时间间隔(分钟)
save_pic: bool # 是否保存图片
save_emoji: bool # 是否保存表情包 save_emoji: bool # 是否保存表情包
steal_emoji: bool # 是否偷取表情包 steal_emoji: bool # 是否偷取表情包
enable_check: bool # 是否启用表情包过滤 enable_check: bool # 是否启用表情包过滤

View File

@@ -602,9 +602,9 @@ class EmojiManager:
continue continue
# 检查是否需要处理表情包(数量超过最大值或不足) # 检查是否需要处理表情包(数量超过最大值或不足)
if (self.emoji_num > self.emoji_num_max and global_config.emoji.do_replace) or ( if global_config.emoji.steal_emoji and ((self.emoji_num > self.emoji_num_max and global_config.emoji.do_replace) or (
self.emoji_num < self.emoji_num_max self.emoji_num < self.emoji_num_max
): )):
try: try:
# 获取目录下所有图片文件 # 获取目录下所有图片文件
files_to_process = [ files_to_process = [

View File

@@ -18,30 +18,28 @@ class WorkingMemoryInfo(InfoBase):
self.data["talking_message"] = message self.data["talking_message"] = message
def set_working_memory(self, working_memory: List[str]) -> None: def set_working_memory(self, working_memory: List[str]) -> None:
"""设置工作记忆 """设置工作记忆列表
Args: Args:
working_memory (str): 工作记忆内容 working_memory (List[str]): 工作记忆内容列表
""" """
self.data["working_memory"] = working_memory self.data["working_memory"] = working_memory
def add_working_memory(self, working_memory: str) -> None: def add_working_memory(self, working_memory: str) -> None:
"""添加工作记忆 """添加一条工作记忆
Args: Args:
working_memory (str): 工作记忆内容 working_memory (str): 工作记忆内容,格式为"记忆要点:xxx"
""" """
working_memory_list = self.data.get("working_memory", []) working_memory_list = self.data.get("working_memory", [])
# print(f"working_memory_list: {working_memory_list}")
working_memory_list.append(working_memory) working_memory_list.append(working_memory)
# print(f"working_memory_list: {working_memory_list}")
self.data["working_memory"] = working_memory_list self.data["working_memory"] = working_memory_list
def get_working_memory(self) -> List[str]: def get_working_memory(self) -> List[str]:
"""获取工作记忆 """获取所有工作记忆
Returns: Returns:
List[str]: 工作记忆内容 List[str]: 工作记忆内容列表,每条记忆格式为"记忆要点:xxx"
""" """
return self.data.get("working_memory", []) return self.data.get("working_memory", [])
@@ -53,33 +51,32 @@ class WorkingMemoryInfo(InfoBase):
""" """
return self.type return self.type
def get_data(self) -> Dict[str, str]: def get_data(self) -> Dict[str, List[str]]:
"""获取所有信息数据 """获取所有信息数据
Returns: Returns:
Dict[str, str]: 包含所有信息数据的字典 Dict[str, List[str]]: 包含所有信息数据的字典
""" """
return self.data return self.data
def get_info(self, key: str) -> Optional[str]: def get_info(self, key: str) -> Optional[List[str]]:
"""获取特定属性的信息 """获取特定属性的信息
Args: Args:
key: 要获取的属性键名 key: 要获取的属性键名
Returns: Returns:
Optional[str]: 属性值,如果键不存在则返回 None Optional[List[str]]: 属性值,如果键不存在则返回 None
""" """
return self.data.get(key) return self.data.get(key)
def get_processed_info(self) -> Dict[str, str]: def get_processed_info(self) -> str:
"""获取处理后的信息 """获取处理后的信息
Returns: Returns:
Dict[str, str]: 处理后的信息数据 str: 处理后的信息数据,所有记忆要点按行拼接
""" """
all_memory = self.get_working_memory() all_memory = self.get_working_memory()
# print(f"all_memory: {all_memory}")
memory_str = "" memory_str = ""
for memory in all_memory: for memory in all_memory:
memory_str += f"{memory}\n" memory_str += f"{memory}\n"

View File

@@ -45,7 +45,6 @@ def init_prompt():
"selected_memory_ids": ["id1", "id2", ...], "selected_memory_ids": ["id1", "id2", ...],
"new_memory": "true" or "false", "new_memory": "true" or "false",
"merge_memory": [["id1", "id2"], ["id3", "id4"],...] "merge_memory": [["id1", "id2"], ["id3", "id4"],...]
}} }}
``` ```
""" """
@@ -104,11 +103,10 @@ class WorkingMemoryProcessor(BaseProcessor):
all_memory = working_memory.get_all_memories() all_memory = working_memory.get_all_memories()
memory_prompts = [] memory_prompts = []
for memory in all_memory: for memory in all_memory:
# memory_content = memory.data
memory_summary = memory.summary memory_summary = memory.summary
memory_id = memory.id memory_id = memory.id
memory_brief = memory_summary.get("brief") memory_brief = memory_summary.get("brief")
memory_keypoints = memory_summary.get("key_points", []) memory_points = memory_summary.get("points", [])
memory_single_prompt = f"记忆id:{memory_id},记忆摘要:{memory_brief}\n" memory_single_prompt = f"记忆id:{memory_id},记忆摘要:{memory_brief}\n"
memory_prompts.append(memory_single_prompt) memory_prompts.append(memory_single_prompt)
@@ -122,11 +120,11 @@ class WorkingMemoryProcessor(BaseProcessor):
memory_str=memory_choose_str, memory_str=memory_choose_str,
) )
print(f"prompt: {prompt}")
# 调用LLM处理记忆 # 调用LLM处理记忆
content = "" content = ""
try: try:
# logger.debug(f"{self.log_prefix} 处理工作记忆的prompt: {prompt}")
content, _ = await self.llm_model.generate_response_async(prompt=prompt) content, _ = await self.llm_model.generate_response_async(prompt=prompt)
if not content: if not content:
logger.warning(f"{self.log_prefix} LLM返回空结果处理工作记忆失败。") logger.warning(f"{self.log_prefix} LLM返回空结果处理工作记忆失败。")
@@ -159,13 +157,12 @@ class WorkingMemoryProcessor(BaseProcessor):
for memory_id in selected_memory_ids: for memory_id in selected_memory_ids:
memory = await working_memory.retrieve_memory(memory_id) memory = await working_memory.retrieve_memory(memory_id)
if memory: if memory:
# memory_content = memory.data
memory_summary = memory.summary memory_summary = memory.summary
memory_id = memory.id memory_id = memory.id
memory_brief = memory_summary.get("brief") memory_brief = memory_summary.get("brief")
memory_keypoints = memory_summary.get("key_points", []) memory_points = memory_summary.get("points", [])
for keypoint in memory_keypoints: for point in memory_points:
memory_str += f"记忆要点:{keypoint}\n" memory_str += f"记忆要点:{point}\n"
working_memory_info = WorkingMemoryInfo() working_memory_info = WorkingMemoryInfo()
if memory_str: if memory_str:
@@ -216,9 +213,7 @@ class WorkingMemoryProcessor(BaseProcessor):
merged_memory = await working_memory.merge_memory(memory_id1, memory_id2) merged_memory = await working_memory.merge_memory(memory_id1, memory_id2)
logger.debug(f"{self.log_prefix} 异步合并记忆成功: {memory_id1}{memory_id2}...") logger.debug(f"{self.log_prefix} 异步合并记忆成功: {memory_id1}{memory_id2}...")
logger.debug(f"{self.log_prefix} 合并后的记忆梗概: {merged_memory.summary.get('brief')}") logger.debug(f"{self.log_prefix} 合并后的记忆梗概: {merged_memory.summary.get('brief')}")
logger.debug(f"{self.log_prefix} 合并后的记忆详情: {merged_memory.summary.get('detailed')}") logger.debug(f"{self.log_prefix} 合并后的记忆要点: {merged_memory.summary.get('points')}")
logger.debug(f"{self.log_prefix} 合并后的记忆要点: {merged_memory.summary.get('key_points')}")
logger.debug(f"{self.log_prefix} 合并后的记忆事件: {merged_memory.summary.get('events')}")
except Exception as e: except Exception as e:
logger.error(f"{self.log_prefix} 异步合并记忆失败: {e}") logger.error(f"{self.log_prefix} 异步合并记忆失败: {e}")

View File

@@ -136,7 +136,8 @@ class ActionPlanner(BasePlanner):
self_info = info.get_processed_info() self_info = info.get_processed_info()
elif isinstance(info, StructuredInfo): elif isinstance(info, StructuredInfo):
structured_info = info.get_processed_info() structured_info = info.get_processed_info()
# print(f"structured_info: {structured_info}") else:
extra_info.append(info.get_processed_info())
# elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo # elif not isinstance(info, ActionInfo): # 跳过已处理的ActionInfo
# extra_info.append(info.get_processed_info()) # extra_info.append(info.get_processed_info())
@@ -217,6 +218,15 @@ class ActionPlanner(BasePlanner):
action_data["identity"] = self_info action_data["identity"] = self_info
extra_info_block = "\n".join(extra_info)
extra_info_block += f"\n{structured_info}"
if extra_info or structured_info:
extra_info_block = f"以下是一些额外的信息,现在请你阅读以下内容,进行决策\n{extra_info_block}\n以上是一些额外的信息,现在请你阅读以下内容,进行决策"
else:
extra_info_block = ""
action_data["extra_info_block"] = extra_info_block
# 对于reply动作不需要额外处理因为相关字段已经在上面的循环中添加到action_data # 对于reply动作不需要额外处理因为相关字段已经在上面的循环中添加到action_data
if extracted_action not in current_available_actions: if extracted_action not in current_available_actions:
@@ -254,8 +264,12 @@ class ActionPlanner(BasePlanner):
action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning} action_result = {"action_type": action, "action_data": action_data, "reasoning": reasoning}
plan_result = { plan_result = {
"action_result": action_result, "action_result": action_result,
# "extra_info_block": extra_info_block,
"current_mind": current_mind, "current_mind": current_mind,
"observed_messages": observed_messages, "observed_messages": observed_messages,
"action_prompt": prompt, "action_prompt": prompt,
@@ -284,7 +298,7 @@ class ActionPlanner(BasePlanner):
if running_memorys: if running_memorys:
memory_str = "以下是当前在聊天中,你回忆起的记忆:\n" memory_str = "以下是当前在聊天中,你回忆起的记忆:\n"
for running_memory in running_memorys: for running_memory in running_memorys:
memory_str += f"{running_memory['topic']}: {running_memory['content']}\n" memory_str += f"{running_memory['content']}\n"
chat_context_description = "你现在正在一个群聊中" chat_context_description = "你现在正在一个群聊中"
chat_target_name = None # Only relevant for private chat_target_name = None # Only relevant for private

View File

@@ -32,6 +32,10 @@ def init_prompt():
""" """
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中: 你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中:
{style_habbits} {style_habbits}
请你根据情景使用以下句法:
{grammar_habbits}
{extra_info_block}
{time_block} {time_block}
你现在正在群里聊天,以下是群里正在进行的聊天内容: 你现在正在群里聊天,以下是群里正在进行的聊天内容:
@@ -41,9 +45,7 @@ def init_prompt():
{chat_target} {chat_target}
{identity},在这聊天中,"{target_message}"引起了你的注意,你想要在群里发言或者回复这条消息。 {identity},在这聊天中,"{target_message}"引起了你的注意,你想要在群里发言或者回复这条消息。
你需要使用合适的语和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。 你需要使用合适的语言习惯和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。
请你根据情景使用以下句法:
{grammar_habbits}
{config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。只输出回复内容。 {config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。只输出回复内容。
{keywords_reaction_prompt} {keywords_reaction_prompt}
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。 请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
@@ -55,8 +57,7 @@ def init_prompt():
Prompt( Prompt(
""" """
你可以参考以下的语言习惯,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中: {extra_info_block}
{style_habbits}
{time_block} {time_block}
你现在正在聊天,以下是你和对方正在进行的聊天内容: 你现在正在聊天,以下是你和对方正在进行的聊天内容:
@@ -67,8 +68,10 @@ def init_prompt():
{chat_target} {chat_target}
{identity},在这聊天中,"{target_message}"引起了你的注意,你想要发言或者回复这条消息。 {identity},在这聊天中,"{target_message}"引起了你的注意,你想要发言或者回复这条消息。
你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。 你需要使用合适的语法和句法,参考聊天内容,组织一条日常且口语化的回复。注意不要复读你说过的话。
请你根据情景使用以下句法 你可以参考以下的语言习惯和句法,如果情景合适就使用,不要盲目使用,不要生硬使用,而是结合到表达中
{style_habbits}
{grammar_habbits} {grammar_habbits}
{config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。只输出回复内容。 {config_expression_style},请注意不要输出多余内容(包括前后缀,冒号和引号,括号()表情包at或 @等 )。只输出回复内容。
{keywords_reaction_prompt} {keywords_reaction_prompt}
请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。 请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。
@@ -263,6 +266,7 @@ class DefaultReplyer:
target_message = action_data.get("target", "") target_message = action_data.get("target", "")
identity = action_data.get("identity", "") identity = action_data.get("identity", "")
extra_info_block = action_data.get("extra_info_block", "")
# 3. 构建 Prompt # 3. 构建 Prompt
with Timer("构建Prompt", {}): # 内部计时器,可选保留 with Timer("构建Prompt", {}): # 内部计时器,可选保留
@@ -270,6 +274,7 @@ class DefaultReplyer:
chat_stream=self.chat_stream, # Pass the stream object chat_stream=self.chat_stream, # Pass the stream object
# in_mind_reply=in_mind_reply, # in_mind_reply=in_mind_reply,
identity=identity, identity=identity,
extra_info_block=extra_info_block,
reason=reason, reason=reason,
sender_name=sender_name_for_prompt, # Pass determined name sender_name=sender_name_for_prompt, # Pass determined name
target_message=target_message, target_message=target_message,
@@ -330,6 +335,7 @@ class DefaultReplyer:
chat_stream, chat_stream,
sender_name, sender_name,
# in_mind_reply, # in_mind_reply,
extra_info_block,
identity, identity,
target_message, target_message,
config_expression_style, config_expression_style,
@@ -425,6 +431,7 @@ class DefaultReplyer:
grammar_habbits=grammar_habbits_str, grammar_habbits=grammar_habbits_str,
chat_target=chat_target_1, chat_target=chat_target_1,
chat_info=chat_talking_prompt, chat_info=chat_talking_prompt,
extra_info_block=extra_info_block,
time_block=time_block, time_block=time_block,
# bot_name=global_config.bot.nickname, # bot_name=global_config.bot.nickname,
# prompt_personality="", # prompt_personality="",
@@ -444,6 +451,7 @@ class DefaultReplyer:
grammar_habbits=grammar_habbits_str, grammar_habbits=grammar_habbits_str,
chat_target=chat_target_1, chat_target=chat_target_1,
chat_info=chat_talking_prompt, chat_info=chat_talking_prompt,
extra_info_block=extra_info_block,
time_block=time_block, time_block=time_block,
# bot_name=global_config.bot.nickname, # bot_name=global_config.bot.nickname,
# prompt_personality="", # prompt_personality="",

View File

@@ -7,14 +7,14 @@ import string
class MemoryItem: class MemoryItem:
"""记忆项类,用于存储单个记忆的所有相关信息""" """记忆项类,用于存储单个记忆的所有相关信息"""
def __init__(self, data: Any, from_source: str = "", tags: Optional[List[str]] = None): def __init__(self, data: Any, from_source: str = "", brief: str = ""):
""" """
初始化记忆项 初始化记忆项
Args: Args:
data: 记忆数据 data: 记忆数据
from_source: 数据来源 from_source: 数据来源
tags: 数据标签列表 brief: 记忆内容主题
""" """
# 生成可读ID时间戳_随机字符串 # 生成可读ID时间戳_随机字符串
timestamp = int(time.time()) timestamp = int(time.time())
@@ -23,11 +23,10 @@ class MemoryItem:
self.data = data self.data = data
self.data_type = type(data) self.data_type = type(data)
self.from_source = from_source self.from_source = from_source
self.tags = set(tags) if tags else set() self.brief = brief
self.timestamp = time.time() self.timestamp = time.time()
# 修改summary的结构说明用于存储可能的总结信息 # 修改summary的结构说明用于存储可能的总结信息
# summary结构{ # summary结构{
# "brief": "记忆内容主题",
# "detailed": "记忆内容概括", # "detailed": "记忆内容概括",
# "keypoints": ["关键概念1", "关键概念2"], # "keypoints": ["关键概念1", "关键概念2"],
# "events": ["事件1", "事件2"] # "events": ["事件1", "事件2"]
@@ -47,23 +46,6 @@ class MemoryItem:
# 格式: [(操作类型, 时间戳, 当时精简次数, 当时强度), ...] # 格式: [(操作类型, 时间戳, 当时精简次数, 当时强度), ...]
self.history = [("create", self.timestamp, self.compress_count, self.memory_strength)] self.history = [("create", self.timestamp, self.compress_count, self.memory_strength)]
def add_tag(self, tag: str) -> None:
"""添加标签"""
self.tags.add(tag)
def remove_tag(self, tag: str) -> None:
"""移除标签"""
if tag in self.tags:
self.tags.remove(tag)
def has_tag(self, tag: str) -> bool:
"""检查是否有特定标签"""
return tag in self.tags
def has_all_tags(self, tags: List[str]) -> bool:
"""检查是否有所有指定的标签"""
return all(tag in self.tags for tag in tags)
def matches_source(self, source: str) -> bool: def matches_source(self, source: str) -> bool:
"""检查来源是否匹配""" """检查来源是否匹配"""
return self.from_source == source return self.from_source == source
@@ -103,9 +85,9 @@ class MemoryItem:
current_time = time.time() current_time = time.time()
self.history.append((operation_type, current_time, self.compress_count, self.memory_strength)) self.history.append((operation_type, current_time, self.compress_count, self.memory_strength))
def to_tuple(self) -> Tuple[Any, str, Set[str], float, str]: def to_tuple(self) -> Tuple[Any, str, float, str]:
"""转换为元组格式(为了兼容性)""" """转换为元组格式(为了兼容性)"""
return (self.data, self.from_source, self.tags, self.timestamp, self.id) return (self.data, self.from_source, self.timestamp, self.id)
def is_memory_valid(self) -> bool: def is_memory_valid(self) -> bool:
"""检查记忆是否有效强度是否大于等于1""" """检查记忆是否有效强度是否大于等于1"""

View File

@@ -71,14 +71,13 @@ class MemoryManager:
return memory_item.id return memory_item.id
async def push_with_summary(self, data: T, from_source: str = "", tags: Optional[List[str]] = None) -> MemoryItem: async def push_with_summary(self, data: T, from_source: str = "") -> MemoryItem:
""" """
推送一段有类型的信息到工作记忆中,并自动生成总结 推送一段有类型的信息到工作记忆中,并自动生成总结
Args: Args:
data: 要存储的数据 data: 要存储的数据
from_source: 数据来源 from_source: 数据来源
tags: 数据标签列表
Returns: Returns:
包含原始数据和总结信息的字典 包含原始数据和总结信息的字典
@@ -88,11 +87,8 @@ class MemoryManager:
# 先生成总结 # 先生成总结
summary = await self.summarize_memory_item(data) summary = await self.summarize_memory_item(data)
# 准备标签
memory_tags = list(tags) if tags else []
# 创建记忆项 # 创建记忆项
memory_item = MemoryItem(data, from_source, memory_tags) memory_item = MemoryItem(data, from_source, brief=summary.get("brief", ""))
# 将总结信息保存到记忆项中 # 将总结信息保存到记忆项中
memory_item.set_summary(summary) memory_item.set_summary(summary)
@@ -103,7 +99,7 @@ class MemoryManager:
return memory_item return memory_item
else: else:
# 非字符串类型,直接创建并推送记忆项 # 非字符串类型,直接创建并推送记忆项
memory_item = MemoryItem(data, from_source, tags) memory_item = MemoryItem(data, from_source)
self.push_item(memory_item) self.push_item(memory_item)
return memory_item return memory_item
@@ -136,7 +132,6 @@ class MemoryManager:
self, self,
data_type: Optional[Type] = None, data_type: Optional[Type] = None,
source: Optional[str] = None, source: Optional[str] = None,
tags: Optional[List[str]] = None,
start_time: Optional[float] = None, start_time: Optional[float] = None,
end_time: Optional[float] = None, end_time: Optional[float] = None,
memory_id: Optional[str] = None, memory_id: Optional[str] = None,
@@ -150,7 +145,6 @@ class MemoryManager:
Args: Args:
data_type: 要查找的数据类型 data_type: 要查找的数据类型
source: 数据来源 source: 数据来源
tags: 必须包含的标签列表
start_time: 开始时间戳 start_time: 开始时间戳
end_time: 结束时间戳 end_time: 结束时间戳
memory_id: 特定记忆项ID memory_id: 特定记忆项ID
@@ -191,10 +185,6 @@ class MemoryManager:
if source is not None and not item.matches_source(source): if source is not None and not item.matches_source(source):
continue continue
# 检查标签是否匹配
if tags is not None and not item.has_all_tags(tags):
continue
# 检查时间范围 # 检查时间范围
if start_time is not None and item.timestamp < start_time: if start_time is not None and item.timestamp < start_time:
continue continue
@@ -226,25 +216,24 @@ class MemoryManager:
""" """
prompt = f"""请对以下内容进行总结,总结成记忆,输出两部分: prompt = f"""请对以下内容进行总结,总结成记忆,输出两部分:
1. 记忆内容主题精简20字以内让用户可以一眼看出记忆内容是什么 1. 记忆内容主题精简20字以内让用户可以一眼看出记忆内容是什么
2. key_points条,包含关键的概念、事件,每条都要包含解释或描述,谁在什么时候干了什么 2. content一到三条,包含关键的概念、事件,每条都要包含解释或描述,谁在什么时候干了什么
内容: 内容:
{content} {content}
请按以下JSON格式输出 请按以下JSON格式输出
{{ {{
"brief": "记忆内容主题20字以内", "brief": "记忆内容主题",
"key_points": [ "points": [
"要点1解释或描述", "内容",
"要点2解释或描述", "内容"
...
] ]
}} }}
请确保输出是有效的JSON格式不要添加任何额外的说明或解释。 请确保输出是有效的JSON格式不要添加任何额外的说明或解释。
""" """
default_summary = { default_summary = {
"brief": "主题未知的记忆", "brief": "主题未知的记忆",
"key_points": ["未知的要点"], "points": ["未知的要点"],
} }
try: try:
@@ -277,13 +266,13 @@ class MemoryManager:
json_result["brief"] = "主题未知的记忆" json_result["brief"] = "主题未知的记忆"
# 处理关键要点 # 处理关键要点
if "key_points" not in json_result or not isinstance(json_result["key_points"], list): if "points" not in json_result or not isinstance(json_result["points"], list):
json_result["key_points"] = ["未知的要点"] json_result["points"] = ["未知的要点"]
else: else:
# 确保key_points中的每个项目都是字符串 # 确保points中的每个项目都是字符串
json_result["key_points"] = [str(point) for point in json_result["key_points"] if point is not None] json_result["points"] = [str(point) for point in json_result["points"] if point is not None]
if not json_result["key_points"]: if not json_result["points"]:
json_result["key_points"] = ["未知的要点"] json_result["points"] = ["未知的要点"]
return json_result return json_result
@@ -328,15 +317,15 @@ class MemoryManager:
目前主题:{summary["brief"]} 目前主题:{summary["brief"]}
目前关键要点: 目前关键要点:
{chr(10).join([f"- {point}" for point in summary.get("key_points", [])])} {chr(10).join([f"- {point}" for point in summary.get("points", [])])}
请生成修改后的主题和关键要点,遵循以下格式: 请生成修改后的主题和关键要点,遵循以下格式:
```json ```json
{{ {{
"brief": "修改后的主题20字以内", "brief": "修改后的主题20字以内",
"key_points": [ "points": [
"修改后的要点1解释或描述", "修改后的要点",
"修改后的要点2解释或描述" "修改后的要点"
] ]
}} }}
``` ```
@@ -345,7 +334,7 @@ class MemoryManager:
# 定义默认的精简结果 # 定义默认的精简结果
default_refined = { default_refined = {
"brief": summary["brief"], "brief": summary["brief"],
"key_points": summary.get("key_points", ["未知的要点"])[:1], # 默认只保留第一个要点 "points": summary.get("points", ["未知的要点"])[:1], # 默认只保留第一个要点
} }
try: try:
@@ -377,13 +366,13 @@ class MemoryManager:
summary["brief"] = refined_data.get("brief", "主题未知的记忆") summary["brief"] = refined_data.get("brief", "主题未知的记忆")
# 更新关键要点 # 更新关键要点
key_points = refined_data.get("key_points", []) points = refined_data.get("points", [])
if isinstance(key_points, list) and key_points: if isinstance(points, list) and points:
# 确保所有要点都是字符串 # 确保所有要点都是字符串
summary["key_points"] = [str(point) for point in key_points if point is not None] summary["points"] = [str(point) for point in points if point is not None]
else: else:
# 如果key_points不是列表或为空使用默认值 # 如果points不是列表或为空使用默认值
summary["key_points"] = ["主要要点已遗忘"] summary["points"] = ["主要要点已遗忘"]
except Exception as e: except Exception as e:
logger.error(f"精简记忆出错: {str(e)}") logger.error(f"精简记忆出错: {str(e)}")
@@ -391,7 +380,7 @@ class MemoryManager:
# 出错时使用简化的默认精简 # 出错时使用简化的默认精简
summary["brief"] = summary["brief"] + " (已简化)" summary["brief"] = summary["brief"] + " (已简化)"
summary["key_points"] = summary.get("key_points", ["未知的要点"])[:1] summary["points"] = summary.get("points", ["未知的要点"])[:1]
except Exception as e: except Exception as e:
logger.error(f"精简记忆调用LLM出错: {str(e)}") logger.error(f"精简记忆调用LLM出错: {str(e)}")
@@ -492,9 +481,6 @@ class MemoryManager:
if not memory_item1 or not memory_item2: if not memory_item1 or not memory_item2:
raise ValueError("无法找到指定的记忆项") raise ValueError("无法找到指定的记忆项")
content1 = memory_item1.data
content2 = memory_item2.data
# 获取记忆的摘要信息(如果有) # 获取记忆的摘要信息(如果有)
summary1 = memory_item1.summary summary1 = memory_item1.summary
summary2 = memory_item2.summary summary2 = memory_item2.summary
@@ -510,55 +496,42 @@ class MemoryManager:
# 如果有摘要信息,添加到提示中 # 如果有摘要信息,添加到提示中
if summary1: if summary1:
prompt += f"记忆1主题{summary1['brief']}\n" prompt += f"记忆1主题{summary1['brief']}\n"
prompt += (
"记忆1关键要点\n" + "\n".join([f"- {point}" for point in summary1.get("key_points", [])]) + "\n\n" prompt += "记忆1关键要点\n" + "\n".join([f"- {point}" for point in summary1.get("points", [])]) + "\n\n"
)
if summary2: if summary2:
prompt += f"记忆2主题{summary2['brief']}\n" prompt += f"记忆2主题{summary2['brief']}\n"
prompt += ( prompt += "记忆2关键要点\n" + "\n".join([f"- {point}" for point in summary2.get("points", [])]) + "\n\n"
"记忆2关键要点\n" + "\n".join([f"- {point}" for point in summary2.get("key_points", [])]) + "\n\n"
)
# 添加记忆原始内容
prompt += f"""
记忆1原始内容
{content1}
记忆2原始内容
{content2}
prompt += """
请按以下JSON格式输出合并结果 请按以下JSON格式输出合并结果
```json ```json
{{ {
"content": "合并后的记忆内容文本(尽可能保留原信息,但去除重复)",
"brief": "合并后的主题20字以内", "brief": "合并后的主题20字以内",
"key_points": [ "points": [
"合并后的要点1解释或描述", "合并后的要点",
"合并后的要点2解释或描述", "合并后的要点"
"合并后的要点3解释或描述"
] ]
}} }
``` ```
请确保输出是有效的JSON格式不要添加任何额外的说明或解释。 请确保输出是有效的JSON格式不要添加任何额外的说明或解释。
""" """
# 默认合并结果 # 默认合并结果
default_merged = { default_merged = {
"content": f"{content1}\n\n{content2}",
"brief": f"合并:{summary1['brief']} + {summary2['brief']}", "brief": f"合并:{summary1['brief']} + {summary2['brief']}",
"key_points": [], "points": [],
} }
# 合并key_points # 合并points
if "key_points" in summary1: if "points" in summary1:
default_merged["key_points"].extend(summary1["key_points"]) default_merged["points"].extend(summary1["points"])
if "key_points" in summary2: if "points" in summary2:
default_merged["key_points"].extend(summary2["key_points"]) default_merged["points"].extend(summary2["points"])
# 确保列表不为空 # 确保列表不为空
if not default_merged["key_points"]: if not default_merged["points"]:
default_merged["key_points"] = ["合并的要点"] default_merged["points"] = ["合并的要点"]
try: try:
# 调用LLM合并记忆 # 调用LLM合并记忆
@@ -585,21 +558,17 @@ class MemoryManager:
logger.error(f"修复后的JSON不是字典类型: {type(merged_data)}") logger.error(f"修复后的JSON不是字典类型: {type(merged_data)}")
merged_data = default_merged merged_data = default_merged
# 确保所有必要字段都存在且类型正确
if "content" not in merged_data or not isinstance(merged_data["content"], str):
merged_data["content"] = default_merged["content"]
if "brief" not in merged_data or not isinstance(merged_data["brief"], str): if "brief" not in merged_data or not isinstance(merged_data["brief"], str):
merged_data["brief"] = default_merged["brief"] merged_data["brief"] = default_merged["brief"]
# 处理关键要点 # 处理关键要点
if "key_points" not in merged_data or not isinstance(merged_data["key_points"], list): if "points" not in merged_data or not isinstance(merged_data["points"], list):
merged_data["key_points"] = default_merged["key_points"] merged_data["points"] = default_merged["points"]
else: else:
# 确保key_points中的每个项目都是字符串 # 确保points中的每个项目都是字符串
merged_data["key_points"] = [str(point) for point in merged_data["key_points"] if point is not None] merged_data["points"] = [str(point) for point in merged_data["points"] if point is not None]
if not merged_data["key_points"]: if not merged_data["points"]:
merged_data["key_points"] = ["合并的要点"] merged_data["points"] = ["合并的要点"]
except Exception as e: except Exception as e:
logger.error(f"合并记忆时处理JSON出错: {str(e)}") logger.error(f"合并记忆时处理JSON出错: {str(e)}")
@@ -611,9 +580,6 @@ class MemoryManager:
merged_data = default_merged merged_data = default_merged
# 创建新的记忆项 # 创建新的记忆项
# 合并记忆项的标签
merged_tags = memory_item1.tags.union(memory_item2.tags)
# 取两个记忆项中更强的来源 # 取两个记忆项中更强的来源
merged_source = ( merged_source = (
memory_item1.from_source memory_item1.from_source
@@ -621,13 +587,13 @@ class MemoryManager:
else memory_item2.from_source else memory_item2.from_source
) )
# 创建新的记忆项 # 创建新的记忆项使用空字符串作为data
merged_memory = MemoryItem(data=merged_data["content"], from_source=merged_source, tags=list(merged_tags)) merged_memory = MemoryItem(data="", from_source=merged_source, brief=merged_data["brief"])
# 设置合并后的摘要 # 设置合并后的摘要
summary = { summary = {
"brief": merged_data["brief"], "brief": merged_data["brief"],
"key_points": merged_data["key_points"], "points": merged_data["points"],
} }
merged_memory.set_summary(summary) merged_memory.set_summary(summary)

View File

@@ -51,19 +51,18 @@ class WorkingMemory:
except Exception as e: except Exception as e:
print(f"自动衰减记忆时出错: {str(e)}") print(f"自动衰减记忆时出错: {str(e)}")
async def add_memory(self, content: Any, from_source: str = "", tags: Optional[List[str]] = None): async def add_memory(self, content: Any, from_source: str = ""):
""" """
添加一段记忆到指定聊天 添加一段记忆到指定聊天
Args: Args:
content: 记忆内容 content: 记忆内容
from_source: 数据来源 from_source: 数据来源
tags: 数据标签列表
Returns: Returns:
包含记忆信息的字典 包含记忆信息的字典
""" """
memory = await self.memory_manager.push_with_summary(content, from_source, tags) memory = await self.memory_manager.push_with_summary(content, from_source)
if len(self.memory_manager.get_all_items()) > self.max_memories_per_chat: if len(self.memory_manager.get_all_items()) > self.max_memories_per_chat:
self.remove_earliest_memory() self.remove_earliest_memory()

View File

@@ -200,37 +200,37 @@ class ImageManager:
logger.debug(f"描述是{description}") logger.debug(f"描述是{description}")
# 根据配置决定是否保存图片 # 根据配置决定是否保存图片
if global_config.emoji.save_pic:
# 生成文件名和路径
current_timestamp = time.time()
filename = f"{int(current_timestamp)}_{image_hash[:8]}.{image_format}"
image_dir = os.path.join(self.IMAGE_DIR, "image")
os.makedirs(image_dir, exist_ok=True)
file_path = os.path.join(image_dir, filename)
# 生成文件名和路径
current_timestamp = time.time()
filename = f"{int(current_timestamp)}_{image_hash[:8]}.{image_format}"
image_dir = os.path.join(self.IMAGE_DIR, "image")
os.makedirs(image_dir, exist_ok=True)
file_path = os.path.join(image_dir, filename)
try:
# 保存文件
with open(file_path, "wb") as f:
f.write(image_bytes)
# 保存到数据库 (Images表)
try: try:
# 保存文件 img_obj = Images.get((Images.emoji_hash == image_hash) & (Images.type == "image"))
with open(file_path, "wb") as f: img_obj.path = file_path
f.write(image_bytes) img_obj.description = description
img_obj.timestamp = current_timestamp
# 保存到数据库 (Images表) img_obj.save()
try: except Images.DoesNotExist:
img_obj = Images.get((Images.emoji_hash == image_hash) & (Images.type == "image")) Images.create(
img_obj.path = file_path emoji_hash=image_hash,
img_obj.description = description path=file_path,
img_obj.timestamp = current_timestamp type="image",
img_obj.save() description=description,
except Images.DoesNotExist: timestamp=current_timestamp,
Images.create( )
emoji_hash=image_hash, logger.trace(f"保存图片元数据: {file_path}")
path=file_path, except Exception as e:
type="image", logger.error(f"保存图片文件或元数据失败: {str(e)}")
description=description,
timestamp=current_timestamp,
)
logger.trace(f"保存图片元数据: {file_path}")
except Exception as e:
logger.error(f"保存图片文件或元数据失败: {str(e)}")
# 保存描述到数据库 (ImageDescriptions表) # 保存描述到数据库 (ImageDescriptions表)
self._save_description_to_db(image_hash, description, "image") self._save_description_to_db(image_hash, description, "image")

View File

@@ -208,15 +208,6 @@ class EmojiConfig(ConfigBase):
check_interval: int = 120 check_interval: int = 120
"""表情包检查间隔(分钟)""" """表情包检查间隔(分钟)"""
save_pic: bool = True
"""是否保存图片"""
save_emoji: bool = True
"""是否保存表情包"""
cache_emoji: bool = True
"""是否缓存表情包"""
steal_emoji: bool = True steal_emoji: bool = True
"""是否偷取表情包,让麦麦可以发送她保存的这些表情包""" """是否偷取表情包,让麦麦可以发送她保存的这些表情包"""

View File

@@ -1,5 +1,5 @@
[inner] [inner]
version = "2.9.1" version = "2.10.0"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读---- #----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
#如果你想要修改配置文件请在修改后将version的值进行变更 #如果你想要修改配置文件请在修改后将version的值进行变更
@@ -116,9 +116,7 @@ working_memory_processor = false # 是否启用工作记忆处理器,不稳定
max_reg_num = 40 # 表情包最大注册数量 max_reg_num = 40 # 表情包最大注册数量
do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包 do_replace = true # 开启则在达到最大数量时删除(替换)表情包,关闭则达到最大数量时不会继续收集表情包
check_interval = 120 # 检查表情包(注册,破损,删除)的时间间隔(分钟) check_interval = 120 # 检查表情包(注册,破损,删除)的时间间隔(分钟)
save_pic = true # 是否保存图片 steal_emoji = true # 是否偷取表情包,让麦麦可以将一些表情包据为己有
cache_emoji = true # 是否缓存表情包
steal_emoji = true # 是否偷取表情包,让麦麦可以发送她保存的这些表情包
content_filtration = false # 是否启用表情包过滤,只有符合该要求的表情包才会被保存 content_filtration = false # 是否启用表情包过滤,只有符合该要求的表情包才会被保存
filtration_prompt = "符合公序良俗" # 表情包过滤要求,只有符合该要求的表情包才会被保存 filtration_prompt = "符合公序良俗" # 表情包过滤要求,只有符合该要求的表情包才会被保存