diff --git a/src/heart_flow/chat_state_info.py b/src/heart_flow/chat_state_info.py index 619f372fc..31eae2cd9 100644 --- a/src/heart_flow/chat_state_info.py +++ b/src/heart_flow/chat_state_info.py @@ -10,7 +10,7 @@ class ChatState(enum.Enum): class ChatStateInfo: def __init__(self): - self.chat_status: ChatState = ChatState.ABSENT + self.chat_status: ChatState = ChatState.CHAT self.current_state_time = 120 self.mood_manager = MoodManager() diff --git a/src/heart_flow/sub_heartflow.py b/src/heart_flow/sub_heartflow.py index 8acf615bf..a9c79df18 100644 --- a/src/heart_flow/sub_heartflow.py +++ b/src/heart_flow/sub_heartflow.py @@ -295,9 +295,6 @@ class SubHeartflow: def get_all_observations(self) -> list[Observation]: return self.observations - def clear_observations(self): - self.observations.clear() - def _get_primary_observation(self) -> Optional[ChattingObservation]: if self.observations and isinstance(self.observations[0], ChattingObservation): return self.observations[0] diff --git a/src/plugins/heartFC_chat/expressors/exprssion_learner.py b/src/plugins/heartFC_chat/expressors/exprssion_learner.py index db1d8a6f9..15c528c20 100644 --- a/src/plugins/heartFC_chat/expressors/exprssion_learner.py +++ b/src/plugins/heartFC_chat/expressors/exprssion_learner.py @@ -1,4 +1,5 @@ import time +import random from typing import List, Dict, Optional, Any, Tuple, Coroutine from src.common.logger_manager import get_logger from src.plugins.models.utils_model import LLMRequest @@ -8,6 +9,9 @@ from src.plugins.heartFC_chat.heartflow_prompt_builder import Prompt, global_pro import os import json + +MAX_EXPRESSION_COUNT = 300 + logger = get_logger("expressor") @@ -52,6 +56,18 @@ class ExpressionLearner: expressions: List[dict] = json.load(f) return expressions + def is_similar(self, s1: str, s2: str) -> bool: + """ + 判断两个字符串是否相似(只考虑长度大于5且有80%以上重合,不考虑子串) + """ + if not s1 or not s2: + return False + min_len = min(len(s1), len(s2)) + if min_len < 5: + return False + same = sum(1 for a, b in zip(s1, s2) if a == b) + return same / min_len > 0.8 + async def learn_and_store_expression(self) -> List[Tuple[str, str, str]]: """选择从当前到最近1小时内的随机10条消息,然后学习这些消息的表达方式""" logger.info("开始学习表达方式...") @@ -74,15 +90,40 @@ class ExpressionLearner: file_path = os.path.join(dir_path, "expressions.json") # 若已存在,先读出合并 if os.path.exists(file_path): - old_data: List[Dict[str, str]] = [] + old_data: List[Dict[str, str, str]] = [] try: with open(file_path, "r", encoding="utf-8") as f: old_data = json.load(f) except Exception: old_data = [] - expr_list = old_data + expr_list + else: + old_data = [] + # 超过最大数量时,20%概率移除count=1的项 + if len(old_data) >= MAX_EXPRESSION_COUNT: + delete = True + new_old_data = [] + for item in old_data: + if item.get("count", 1) == 1 and random.random() < 0.2: + continue # 20%概率移除 + new_old_data.append(item) + old_data = new_old_data + # 合并逻辑 + for new_expr in expr_list: + found = False + for old_expr in old_data: + if self.is_similar(new_expr["situation"], old_expr.get("situation", "")) and self.is_similar(new_expr["style"], old_expr.get("style", "")): + found = True + # 50%概率替换 + if random.random() < 0.5: + old_expr["situation"] = new_expr["situation"] + old_expr["style"] = new_expr["style"] + old_expr["count"] = old_expr.get("count", 1) + 1 + break + if not found: + new_expr["count"] = 1 + old_data.append(new_expr) with open(file_path, "w", encoding="utf-8") as f: - json.dump(expr_list, f, ensure_ascii=False, indent=2) + json.dump(old_data, f, ensure_ascii=False, indent=2) return expressions async def learn_expression(self) -> Optional[List[Tuple[str, str, str]]]: diff --git a/src/plugins/heartFC_chat/heartflow_prompt_builder.py b/src/plugins/heartFC_chat/heartflow_prompt_builder.py index 5c1b1d196..23eb0a416 100644 --- a/src/plugins/heartFC_chat/heartflow_prompt_builder.py +++ b/src/plugins/heartFC_chat/heartflow_prompt_builder.py @@ -86,7 +86,7 @@ def init_prompt(): {{ "action": "reply", "text": "你想表达的内容", - "emojis": "表情关键词", + "emojis": "描述当前使用表情包的场景", "target": "你想要回复的原始文本内容(非必须,仅文本,不包含发送者)", "reasoning": "你的决策理由", }} diff --git a/src/plugins/utils/chat_message_builder.py b/src/plugins/utils/chat_message_builder.py index 773d8f336..a1a6c85c0 100644 --- a/src/plugins/utils/chat_message_builder.py +++ b/src/plugins/utils/chat_message_builder.py @@ -8,6 +8,7 @@ from src.config.config import global_config from typing import List, Dict, Any, Tuple # 确保类型提示被导入 import time # 导入 time 模块以获取当前时间 import random +import re # 导入新的 repository 函数 from src.common.message_repository import find_messages, count_messages @@ -215,10 +216,43 @@ async def _build_readable_messages_internal( else: person_name = "某人" + # 检查是否有 回复 字段 + reply_pattern = r"回复<([^:<>]+):([^:<>]+)>" + match = re.search(reply_pattern, content) + if match: + aaa = match.group(1) + bbb = match.group(2) + reply_person_id = person_info_manager.get_person_id(platform, bbb) + reply_person_name = await person_info_manager.get_value(reply_person_id, "person_name") + if not reply_person_name: + reply_person_name = aaa + # 在内容前加上回复信息 + content = re.sub(reply_pattern, f"回复 {reply_person_name}", content, count=1) + + # 检查是否有 @ 字段 @<{member_info.get('nickname')}:{member_info.get('user_id')}> + at_pattern = r"@<([^:<>]+):([^:<>]+)>" + at_matches = list(re.finditer(at_pattern, content)) + if at_matches: + new_content = "" + last_end = 0 + for m in at_matches: + new_content += content[last_end:m.start()] + aaa = m.group(1) + bbb = m.group(2) + at_person_id = person_info_manager.get_person_id(platform, bbb) + at_person_name = await person_info_manager.get_value(at_person_id, "person_name") + if not at_person_name: + at_person_name = aaa + new_content += f"@{at_person_name}" + last_end = m.end() + new_content += content[last_end:] + content = new_content + message_details_raw.append((timestamp, person_name, content)) if not message_details_raw: - return "", [] + return "", [] + message_details_raw.sort(key=lambda x: x[0]) # 按时间戳(第一个元素)升序排序,越早的消息排在前面 diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 8eab299cb..303845f5f 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -66,11 +66,11 @@ time_zone = "Asia/Shanghai" # 给你的机器人设置时区,可以解决运 nonebot-qq="http://127.0.0.1:18002/api/message" [chat] #麦麦的聊天通用设置 -allow_focus_mode = true # 是否允许专注聊天状态 +allow_focus_mode = false # 是否允许专注聊天状态 # 是否启用heart_flowC(HFC)模式 # 启用后麦麦会自主选择进入heart_flowC模式(持续一段时间),进行主动的观察和回复,并给出回复,比较消耗token -base_normal_chat_num = 8 # 最多允许多少个群进行普通聊天 -base_focused_chat_num = 5 # 最多允许多少个群进行专注聊天 +base_normal_chat_num = 999 # 最多允许多少个群进行普通聊天 +base_focused_chat_num = 4 # 最多允许多少个群进行专注聊天 observation_context_size = 15 # 观察到的最长上下文大小,建议15,太短太长都会导致脑袋尖尖 message_buffer = true # 启用消息缓冲器?启用此项以解决消息的拆分问题,但会使麦麦的回复延迟