From 2f9718441a195907d0fb23de4e1bfd5d3f8366dc Mon Sep 17 00:00:00 2001 From: tcmofashi Date: Mon, 26 May 2025 10:40:31 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E5=90=AF=E7=94=A8message=E4=B8=AD?= =?UTF-8?q?=E7=9A=84format=5Finfo=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../info_processors/action_processor.py | 28 +++++++++++++++++-- .../focus_chat/planners/action_manager.py | 2 ++ .../planners/actions/base_action.py | 2 ++ .../planners/actions/reply_action.py | 3 ++ src/chat/message_receive/chat_stream.py | 9 ++++++ .../test_plugin/actions/mute_action.py | 5 ++-- .../test_plugin/actions/online_action.py | 5 ++-- 7 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/chat/focus_chat/info_processors/action_processor.py b/src/chat/focus_chat/info_processors/action_processor.py index b53d4568..89970cd9 100644 --- a/src/chat/focus_chat/info_processors/action_processor.py +++ b/src/chat/focus_chat/info_processors/action_processor.py @@ -5,6 +5,8 @@ from src.chat.focus_chat.info.action_info import ActionInfo from .base_processor import BaseProcessor from src.common.logger_manager import get_logger from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation +from src.chat.heart_flow.observation.chatting_observation import ChattingObservation +from src.chat.message_receive.chat_stream import ChatStream, chat_manager from typing import Dict from src.chat.models.utils_model import LLMRequest from src.config.config import global_config @@ -50,10 +52,12 @@ class ActionProcessor(BaseProcessor): # 处理Observation对象 if observations: + action_info = ActionInfo() + all_actions = None for obs in observations: if isinstance(obs, HFCloopObservation): # 创建动作信息 - action_info = ActionInfo() + all_actions = obs.all_actions action_changes = await self.analyze_loop_actions(obs) if action_changes["add"] or action_changes["remove"]: action_info.set_action_changes(action_changes) @@ -64,7 +68,27 @@ class ActionProcessor(BaseProcessor): if action_changes["remove"]: reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复") action_info.set_reason(" | ".join(reasons)) - processed_infos.append(action_info) + if isinstance(obs, ChattingObservation) and all_actions is not None: + action_changes = {"add": [], "remove": []} + # 检查动作的关联类型 + chat_context = chat_manager.get_stream(obs.chat_id).context + for action_name in all_actions.keys(): + data = all_actions[action_name] + if data.get("associated_types"): + if not chat_context.check_types(data["associated_types"]): + action_changes["remove"].append(action_name) + logger.debug(f"{self.log_prefix} 动作 {action_name} 关联类型不匹配,移除该动作") + if len(action_changes["remove"]) > 0: + action_info.set_action_changes(action_changes) + # 设置变更原因 + reasons = [] + if action_info.get_reason(): + reasons.append(action_info.get_reason()) + if action_changes["remove"]: + reasons.append(f"移除动作{action_changes['remove']}因为关联类型不匹配") + action_info.set_reason(" | ".join(reasons)) + + processed_infos.append(action_info) return processed_infos diff --git a/src/chat/focus_chat/planners/action_manager.py b/src/chat/focus_chat/planners/action_manager.py index a10c4884..6cf8de6d 100644 --- a/src/chat/focus_chat/planners/action_manager.py +++ b/src/chat/focus_chat/planners/action_manager.py @@ -59,6 +59,7 @@ class ActionManager: action_description: str = getattr(action_class, "action_description", "") action_parameters: dict[str:str] = getattr(action_class, "action_parameters", {}) action_require: list[str] = getattr(action_class, "action_require", []) + associated_types: list[str] = getattr(action_class, "associated_types", []) is_default: bool = getattr(action_class, "default", False) if action_name and action_description: @@ -67,6 +68,7 @@ class ActionManager: "description": action_description, "parameters": action_parameters, "require": action_require, + "associated_types": associated_types, } # 添加到所有已注册的动作 diff --git a/src/chat/focus_chat/planners/actions/base_action.py b/src/chat/focus_chat/planners/actions/base_action.py index 82d25967..87cd96e2 100644 --- a/src/chat/focus_chat/planners/actions/base_action.py +++ b/src/chat/focus_chat/planners/actions/base_action.py @@ -66,6 +66,8 @@ class BaseAction(ABC): self.action_parameters: dict = {} self.action_require: list[str] = [] + self.associated_types: list[str] = [] + self.default: bool = False self.action_data = action_data diff --git a/src/chat/focus_chat/planners/actions/reply_action.py b/src/chat/focus_chat/planners/actions/reply_action.py index 45a4340d..3eacf518 100644 --- a/src/chat/focus_chat/planners/actions/reply_action.py +++ b/src/chat/focus_chat/planners/actions/reply_action.py @@ -36,6 +36,9 @@ class ReplyAction(BaseAction): "避免重复或评价自己的发言,不要和自己聊天", "注意:回复尽量简短一些。可以参考贴吧,知乎和微博的回复风格,回复不要浮夸,不要用夸张修辞,平淡一些。不要有额外的符号,尽量简单简短", ] + + associated_types: list[str] = ["text", "emoji"] + default = True def __init__( diff --git a/src/chat/message_receive/chat_stream.py b/src/chat/message_receive/chat_stream.py index 1f2ebbf8..edbc733a 100644 --- a/src/chat/message_receive/chat_stream.py +++ b/src/chat/message_receive/chat_stream.py @@ -38,6 +38,15 @@ class ChatMessageContext: """获取最后一条消息""" return self.message + def check_types(self, types: list) -> bool: + """检查消息类型""" + if not self.message.message_info.format_info.accept_format: + return False + for t in types: + if t not in self.message.message_info.format_info.accept_format: + return False + return True + class ChatStream: """聊天流对象,存储一个完整的聊天上下文""" diff --git a/src/plugins/test_plugin/actions/mute_action.py b/src/plugins/test_plugin/actions/mute_action.py index a956169e..89fa387f 100644 --- a/src/plugins/test_plugin/actions/mute_action.py +++ b/src/plugins/test_plugin/actions/mute_action.py @@ -26,6 +26,7 @@ class MuteAction(PluginAction): "当你想回避某个话题时使用", ] default = True # 不是默认动作,需要手动添加到使用集 + associated_types = ["command",'text'] async def process(self) -> Tuple[bool, str]: """处理测试动作""" @@ -41,8 +42,8 @@ class MuteAction(PluginAction): try: await self.send_message( - type="text", - data=f"[command]mute,{user_id},{duration}", + type="command", + data={"name": "GROUP_BAN", "args": {"qq_id": f"{user_id}", "duration": f"{duration}"}}, # target = target ) diff --git a/src/plugins/test_plugin/actions/online_action.py b/src/plugins/test_plugin/actions/online_action.py index 7f667431..c6a2fe6c 100644 --- a/src/plugins/test_plugin/actions/online_action.py +++ b/src/plugins/test_plugin/actions/online_action.py @@ -18,6 +18,7 @@ class CheckOnlineAction(PluginAction): "mode参数为type时查看在线系统类型分布", ] default = False # 不是默认动作,需要手动添加到使用集 + associated_types = ["text"] async def process(self) -> Tuple[bool, str]: """处理测试动作""" @@ -30,9 +31,9 @@ class CheckOnlineAction(PluginAction): try: if mode == "type": - await self.send_message("#online detail") + await self.send_message("text", "#online detail") elif mode == "version": - await self.send_message("#online") + await self.send_message("text", "#online") except Exception as e: logger.error(f"{self.log_prefix} 执行online动作时出错: {e}") From 407d278f842d339f8ce4f10737295a3b380ec0fe Mon Sep 17 00:00:00 2001 From: tcmofashi Date: Mon, 26 May 2025 12:38:03 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E6=81=A2=E5=A4=8Dactionprocessor?= =?UTF-8?q?=EF=BC=8C=E5=A2=9E=E5=8A=A0=E4=B8=8Eformatinfo=E7=9A=84?= =?UTF-8?q?=E8=81=94=E5=8A=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/chat/focus_chat/heartFC_chat.py | 5 +- .../info_processors/action_processor.py | 70 +++++++++++-------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/chat/focus_chat/heartFC_chat.py b/src/chat/focus_chat/heartFC_chat.py index a0144294..7463791b 100644 --- a/src/chat/focus_chat/heartFC_chat.py +++ b/src/chat/focus_chat/heartFC_chat.py @@ -16,6 +16,7 @@ from src.chat.focus_chat.info.info_base import InfoBase from src.chat.focus_chat.info_processors.chattinginfo_processor import ChattingInfoProcessor from src.chat.focus_chat.info_processors.mind_processor import MindProcessor from src.chat.focus_chat.info_processors.working_memory_processor import WorkingMemoryProcessor +from src.chat.focus_chat.info_processors.action_processor import ActionProcessor from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation from src.chat.heart_flow.observation.working_observation import WorkingMemoryObservation from src.chat.focus_chat.info_processors.tool_processor import ToolProcessor @@ -39,6 +40,7 @@ PROCESSOR_CLASSES = { "ToolProcessor": (ToolProcessor, "tool_use_processor"), "WorkingMemoryProcessor": (WorkingMemoryProcessor, "working_memory_processor"), "SelfProcessor": (SelfProcessor, "self_identify_processor"), + "ActionProcessor": (ActionProcessor, "action_processor"), # 这个处理器不需要配置键名,默认启用 } @@ -425,10 +427,7 @@ class HeartFChatting: self.all_observations = observations with Timer("回忆", cycle_timers): - logger.debug(f"{self.log_prefix} 开始回忆") running_memorys = await self.memory_activator.activate_memory(observations) - logger.debug(f"{self.log_prefix} 回忆完成") - print(running_memorys) with Timer("执行 信息处理器", cycle_timers): all_plan_info = await self._process_processors(observations, running_memorys, cycle_timers) diff --git a/src/chat/focus_chat/info_processors/action_processor.py b/src/chat/focus_chat/info_processors/action_processor.py index 04a4dc5b..45247fe1 100644 --- a/src/chat/focus_chat/info_processors/action_processor.py +++ b/src/chat/focus_chat/info_processors/action_processor.py @@ -54,39 +54,47 @@ class ActionProcessor(BaseProcessor): if observations: action_info = ActionInfo() all_actions = None + hfc_obs = None + chat_obs = None for obs in observations: if isinstance(obs, HFCloopObservation): - # 创建动作信息 - all_actions = obs.all_actions - action_changes = await self.analyze_loop_actions(obs) - if action_changes["add"] or action_changes["remove"]: - action_info.set_action_changes(action_changes) - # 设置变更原因 - reasons = [] - if action_changes["add"]: - reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复") - if action_changes["remove"]: - reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复") - action_info.set_reason(" | ".join(reasons)) - if isinstance(obs, ChattingObservation) and all_actions is not None: - action_changes = {"add": [], "remove": []} - # 检查动作的关联类型 - chat_context = chat_manager.get_stream(obs.chat_id).context - for action_name in all_actions.keys(): - data = all_actions[action_name] - if data.get("associated_types"): - if not chat_context.check_types(data["associated_types"]): - action_changes["remove"].append(action_name) - logger.debug(f"{self.log_prefix} 动作 {action_name} 关联类型不匹配,移除该动作") - if len(action_changes["remove"]) > 0: - action_info.set_action_changes(action_changes) - # 设置变更原因 - reasons = [] - if action_info.get_reason(): - reasons.append(action_info.get_reason()) - if action_changes["remove"]: - reasons.append(f"移除动作{action_changes['remove']}因为关联类型不匹配") - action_info.set_reason(" | ".join(reasons)) + hfc_obs = obs + if isinstance(obs, ChattingObservation): + chat_obs = obs + if hfc_obs: + obs = hfc_obs + # 创建动作信息 + all_actions = obs.all_actions + action_changes = await self.analyze_loop_actions(obs) + if action_changes["add"] or action_changes["remove"]: + action_info.set_action_changes(action_changes) + # 设置变更原因 + reasons = [] + if action_changes["add"]: + reasons.append(f"添加动作{action_changes['add']}因为检测到大量无回复") + if action_changes["remove"]: + reasons.append(f"移除动作{action_changes['remove']}因为检测到连续回复") + action_info.set_reason(" | ".join(reasons)) + if chat_obs and all_actions is not None: + obs = chat_obs + action_changes = {"add": [], "remove": []} + # 检查动作的关联类型 + chat_context = chat_manager.get_stream(obs.chat_id).context + for action_name in all_actions.keys(): + data = all_actions[action_name] + if data.get("associated_types"): + if not chat_context.check_types(data["associated_types"]): + action_changes["remove"].append(action_name) + logger.debug(f"{self.log_prefix} 动作 {action_name} 关联类型不匹配,移除该动作") + if len(action_changes["remove"]) > 0: + action_info.set_action_changes(action_changes) + # 设置变更原因 + reasons = [] + if action_info.get_reason(): + reasons.append(action_info.get_reason()) + if action_changes["remove"]: + reasons.append(f"移除动作{action_changes['remove']}因为关联类型不匹配") + action_info.set_reason(" | ".join(reasons)) processed_infos.append(action_info) From a92ffc12df2f0f380913b1aaf1a4e63cffb42d98 Mon Sep 17 00:00:00 2001 From: tcmofashi Date: Mon, 26 May 2025 18:48:01 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=8F=90=E4=BE=9B=E4=BA=86tts=5Fac?= =?UTF-8?q?tion=EF=BC=8C=E9=80=82=E9=85=8D=E6=9C=80=E6=96=B0=E7=9A=84tts?= =?UTF-8?q?=5Fadapter=E5=8F=AF=E5=AE=9E=E7=8E=B0focus=20chat=E4=B8=8B?= =?UTF-8?q?=E4=B8=BB=E5=8A=A8=E5=8F=91=E9=80=81=E8=AF=AD=E9=9F=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/tts_plgin/__init__.py | 0 src/plugins/tts_plgin/actions/__init__.py | 1 + src/plugins/tts_plgin/actions/tts_action.py | 73 +++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 src/plugins/tts_plgin/__init__.py create mode 100644 src/plugins/tts_plgin/actions/__init__.py create mode 100644 src/plugins/tts_plgin/actions/tts_action.py diff --git a/src/plugins/tts_plgin/__init__.py b/src/plugins/tts_plgin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/plugins/tts_plgin/actions/__init__.py b/src/plugins/tts_plgin/actions/__init__.py new file mode 100644 index 00000000..b800907b --- /dev/null +++ b/src/plugins/tts_plgin/actions/__init__.py @@ -0,0 +1 @@ +from . import tts_action \ No newline at end of file diff --git a/src/plugins/tts_plgin/actions/tts_action.py b/src/plugins/tts_plgin/actions/tts_action.py new file mode 100644 index 00000000..a029d035 --- /dev/null +++ b/src/plugins/tts_plgin/actions/tts_action.py @@ -0,0 +1,73 @@ +from src.common.logger_manager import get_logger +from src.chat.focus_chat.planners.actions.plugin_action import PluginAction, register_action +from typing import Tuple + +logger = get_logger("tts_action") + + +@register_action +class TTSAction(PluginAction): + """TTS语音转换动作处理类""" + + action_name = "tts_action" + action_description = "将文本转换为语音进行播放,适用于需要语音输出的场景" + action_parameters = { + "text": "需要转换为语音的文本内容,必填,内容应当适合语音播报,语句流畅、清晰", + } + action_require = [ + "当需要发送语音信息时使用", + "当用户明确要求使用语音功能时使用", + "当表达内容更适合用语音而不是文字传达时使用", + "当用户想听到语音回答而非阅读文本时使用", + ] + default = True # 设为默认动作 + associated_types = ["tts_text"] + + async def process(self) -> Tuple[bool, str]: + """处理TTS文本转语音动作""" + logger.info(f"{self.log_prefix} 执行TTS动作: {self.reasoning}") + + # 获取要转换的文本 + text = self.action_data.get("text") + + if not text: + logger.error(f"{self.log_prefix} 执行TTS动作时未提供文本内容") + return False, "执行TTS动作失败:未提供文本内容" + + # 确保文本适合TTS使用 + processed_text = self._process_text_for_tts(text) + + try: + # 发送TTS消息 + await self.send_message(type="tts_text", data=processed_text) + + logger.info(f"{self.log_prefix} TTS动作执行成功,文本长度: {len(processed_text)}") + return True, "TTS动作执行成功" + + except Exception as e: + logger.error(f"{self.log_prefix} 执行TTS动作时出错: {e}") + return False, f"执行TTS动作时出错: {e}" + + def _process_text_for_tts(self, text: str) -> str: + """ + 处理文本使其更适合TTS使用 + - 移除不必要的特殊字符和表情符号 + - 修正标点符号以提高语音质量 + - 优化文本结构使语音更流畅 + """ + # 这里可以添加文本处理逻辑 + # 例如:移除多余的标点、表情符号,优化语句结构等 + + # 简单示例实现 + processed_text = text + + # 移除多余的标点符号 + import re + + processed_text = re.sub(r"([!?,.;:。!?,、;:])\1+", r"\1", processed_text) + + # 确保句子结尾有合适的标点 + if not any(processed_text.endswith(end) for end in [".", "?", "!", "。", "!", "?"]): + processed_text = processed_text + "。" + + return processed_text From 5a312a942124b5fcde0e5bdec9dd6c8b477ea479 Mon Sep 17 00:00:00 2001 From: tcmofashi Date: Mon, 26 May 2025 18:50:25 +0800 Subject: [PATCH 4/4] fix: pass ruff --- src/chat/focus_chat/info_processors/action_processor.py | 2 +- src/plugins/tts_plgin/actions/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chat/focus_chat/info_processors/action_processor.py b/src/chat/focus_chat/info_processors/action_processor.py index 45247fe1..3ef38914 100644 --- a/src/chat/focus_chat/info_processors/action_processor.py +++ b/src/chat/focus_chat/info_processors/action_processor.py @@ -6,7 +6,7 @@ from .base_processor import BaseProcessor from src.common.logger_manager import get_logger from src.chat.heart_flow.observation.hfcloop_observation import HFCloopObservation from src.chat.heart_flow.observation.chatting_observation import ChattingObservation -from src.chat.message_receive.chat_stream import ChatStream, chat_manager +from src.chat.message_receive.chat_stream import chat_manager from typing import Dict from src.llm_models.utils_model import LLMRequest from src.config.config import global_config diff --git a/src/plugins/tts_plgin/actions/__init__.py b/src/plugins/tts_plgin/actions/__init__.py index b800907b..00737d90 100644 --- a/src/plugins/tts_plgin/actions/__init__.py +++ b/src/plugins/tts_plgin/actions/__init__.py @@ -1 +1 @@ -from . import tts_action \ No newline at end of file +from . import tts_action # noqa