feat:移除reply动作,合并tool配置项
This commit is contained in:
@@ -18,11 +18,12 @@ from src.chat.chat_loop.hfc_utils import CycleDetail
|
||||
from src.person_info.relationship_builder_manager import relationship_builder_manager
|
||||
from src.person_info.person_info import get_person_info_manager
|
||||
from src.plugin_system.base.component_types import ActionInfo, ChatMode
|
||||
from src.plugin_system.apis import generator_api, send_api, message_api
|
||||
from src.plugin_system.apis import generator_api, send_api, message_api, database_api
|
||||
from src.chat.willing.willing_manager import get_willing_manager
|
||||
from src.mais4u.mai_think import mai_thinking_manager
|
||||
from maim_message.message_base import GroupInfo
|
||||
from src.mais4u.constant_s4u import ENABLE_S4U
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
from src.chat.chat_loop.hfc_utils import send_typing, stop_typing
|
||||
|
||||
ERROR_LOOP_INFO = {
|
||||
"loop_plan_info": {
|
||||
@@ -254,44 +255,19 @@ class HeartFChatting:
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
return f"{person_name}:{message_data.get('processed_plain_text')}"
|
||||
|
||||
async def send_typing(self):
|
||||
group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心")
|
||||
|
||||
chat = await get_chat_manager().get_or_create_stream(
|
||||
platform="amaidesu_default",
|
||||
user_info=None,
|
||||
group_info=group_info,
|
||||
)
|
||||
|
||||
await send_api.custom_to_stream(
|
||||
message_type="state", content="typing", stream_id=chat.stream_id, storage_message=False
|
||||
)
|
||||
|
||||
async def stop_typing(self):
|
||||
group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心")
|
||||
|
||||
chat = await get_chat_manager().get_or_create_stream(
|
||||
platform="amaidesu_default",
|
||||
user_info=None,
|
||||
group_info=group_info,
|
||||
)
|
||||
|
||||
await send_api.custom_to_stream(
|
||||
message_type="state", content="stop_typing", stream_id=chat.stream_id, storage_message=False
|
||||
)
|
||||
|
||||
async def _observe(self, message_data: Optional[Dict[str, Any]] = None):
|
||||
# sourcery skip: hoist-statement-from-if, merge-comparisons, reintroduce-else
|
||||
if not message_data:
|
||||
message_data = {}
|
||||
action_type = "no_action"
|
||||
reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
||||
# 创建新的循环信息
|
||||
cycle_timers, thinking_id = self.start_cycle()
|
||||
|
||||
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考[模式:{self.loop_mode}]")
|
||||
|
||||
if ENABLE_S4U:
|
||||
await self.send_typing()
|
||||
await send_typing()
|
||||
|
||||
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
||||
loop_start_time = time.time()
|
||||
@@ -310,7 +286,7 @@ class HeartFChatting:
|
||||
# 如果normal,开始一个回复生成进程,先准备好回复(其实是和planer同时进行的)
|
||||
if self.loop_mode == ChatMode.NORMAL:
|
||||
reply_to_str = await self.build_reply_to_str(message_data)
|
||||
gen_task = asyncio.create_task(self._generate_response(message_data, available_actions, reply_to_str))
|
||||
gen_task = asyncio.create_task(self._generate_response(message_data, available_actions, reply_to_str, "chat.replyer.normal"))
|
||||
|
||||
with Timer("规划器", cycle_timers):
|
||||
plan_result, target_message = await self.action_planner.plan(mode=self.loop_mode)
|
||||
@@ -326,7 +302,7 @@ class HeartFChatting:
|
||||
action_data["loop_start_time"] = loop_start_time
|
||||
|
||||
if self.loop_mode == ChatMode.NORMAL:
|
||||
if action_type == "no_action":
|
||||
if action_type == "reply":
|
||||
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复")
|
||||
elif is_parallel:
|
||||
logger.info(
|
||||
@@ -335,45 +311,86 @@ class HeartFChatting:
|
||||
else:
|
||||
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定执行{action_type}动作")
|
||||
|
||||
if action_type == "no_action":
|
||||
action_message: Dict[str, Any] = message_data or target_message # type: ignore
|
||||
if action_type == "no_action" or (self.loop_mode == ChatMode.FOCUS and action_type == "reply"):
|
||||
# 等待回复生成完毕
|
||||
gather_timeout = global_config.chat.thinking_timeout
|
||||
try:
|
||||
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
||||
except asyncio.TimeoutError:
|
||||
response_set = None
|
||||
if action_type == "no_action":
|
||||
gather_timeout = global_config.chat.thinking_timeout
|
||||
try:
|
||||
response_set = await asyncio.wait_for(gen_task, timeout=gather_timeout)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"{self.log_prefix} 回复生成超时>{global_config.chat.thinking_timeout}s,已跳过")
|
||||
response_set = None
|
||||
|
||||
if response_set:
|
||||
content = " ".join([item[1] for item in response_set if item[0] == "text"])
|
||||
if response_set:
|
||||
content = " ".join([item[1] for item in response_set if item[0] == "text"])
|
||||
|
||||
# 模型炸了,没有回复内容生成
|
||||
if not response_set:
|
||||
logger.warning(f"{self.log_prefix}模型未生成回复内容")
|
||||
return False
|
||||
elif action_type not in ["no_action"] and not is_parallel:
|
||||
logger.info(
|
||||
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
||||
)
|
||||
return False
|
||||
|
||||
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定的回复内容: {content}")
|
||||
|
||||
# 发送回复 (不再需要传入 chat)
|
||||
reply_text = await self._send_response(response_set, reply_to_str, loop_start_time,message_data)
|
||||
# 模型炸了或超时,没有回复内容生成
|
||||
if not response_set:
|
||||
logger.warning(f"{self.log_prefix}模型未生成回复内容")
|
||||
return False
|
||||
elif action_type not in ["no_action"] and not is_parallel:
|
||||
logger.info(
|
||||
f"{self.log_prefix}{global_config.bot.nickname} 原本想要回复:{content},但选择执行{action_type},不发表回复"
|
||||
)
|
||||
return False
|
||||
else:
|
||||
logger.info(f"{self.log_prefix}{global_config.bot.nickname} 决定进行回复 (focus模式)")
|
||||
|
||||
# 构建reply_to字符串
|
||||
reply_to_str = await self.build_reply_to_str(action_message)
|
||||
|
||||
# 生成回复
|
||||
with Timer("回复生成", cycle_timers):
|
||||
response_set = await self._generate_response(action_message, available_actions, reply_to_str, request_type="chat.replyer.focus")
|
||||
|
||||
if not response_set:
|
||||
logger.warning(f"{self.log_prefix}模型未生成回复内容")
|
||||
return False
|
||||
|
||||
# 发送回复
|
||||
with Timer("回复发送", cycle_timers):
|
||||
reply_text = await self._send_response(response_set, reply_to_str, loop_start_time, action_message)
|
||||
|
||||
# 存储reply action信息 (focus模式)
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_id = person_info_manager.get_person_id(
|
||||
action_message.get("chat_info_platform", ""),
|
||||
action_message.get("user_id", ""),
|
||||
)
|
||||
person_name = await person_info_manager.get_value(person_id, "person_name")
|
||||
action_prompt_display = f"你对{person_name}进行了回复:{reply_text}"
|
||||
|
||||
await database_api.store_action_info(
|
||||
chat_stream=self.chat_stream,
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=action_prompt_display,
|
||||
action_done=True,
|
||||
thinking_id=thinking_id,
|
||||
action_data={"reply_text": reply_text, "reply_to": reply_to_str},
|
||||
action_name="reply",
|
||||
)
|
||||
|
||||
|
||||
# 构建循环信息
|
||||
loop_info = {
|
||||
"loop_plan_info": {
|
||||
"action_result": plan_result.get("action_result", {}),
|
||||
},
|
||||
"loop_action_info": {
|
||||
"action_taken": True,
|
||||
"reply_text": reply_text,
|
||||
"command": "",
|
||||
"taken_time": time.time(),
|
||||
},
|
||||
}
|
||||
|
||||
success = True
|
||||
command = ""
|
||||
|
||||
|
||||
if ENABLE_S4U:
|
||||
await self.stop_typing()
|
||||
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)
|
||||
|
||||
return True
|
||||
|
||||
else:
|
||||
action_message: Dict[str, Any] = message_data or target_message # type: ignore
|
||||
|
||||
# 动作执行计时
|
||||
with Timer("动作执行", cycle_timers):
|
||||
success, reply_text, command = await self._handle_action(
|
||||
@@ -392,11 +409,11 @@ class HeartFChatting:
|
||||
},
|
||||
}
|
||||
|
||||
if loop_info["loop_action_info"]["command"] == "stop_focus_chat":
|
||||
logger.info(f"{self.log_prefix} 麦麦决定停止专注聊天")
|
||||
return False
|
||||
# 停止该聊天模式的循环
|
||||
|
||||
if ENABLE_S4U:
|
||||
await stop_typing()
|
||||
await mai_thinking_manager.get_mai_think(self.stream_id).do_think_after_response(reply_text)
|
||||
|
||||
self.end_cycle(loop_info, cycle_timers)
|
||||
self.print_cycle_info(cycle_timers)
|
||||
|
||||
@@ -406,13 +423,11 @@ class HeartFChatting:
|
||||
# 管理no_reply计数器:当执行了非no_reply动作时,重置计数器
|
||||
if action_type != "no_reply" and action_type != "no_action":
|
||||
# 导入NoReplyAction并重置计数器
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
NoReplyAction.reset_consecutive_count()
|
||||
logger.info(f"{self.log_prefix} 执行了{action_type}动作,重置no_reply计数器")
|
||||
return True
|
||||
elif action_type == "no_action":
|
||||
# 当执行回复动作时,也重置no_reply计数器
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
# 当执行回复动作时,也重置no_reply计数器s
|
||||
NoReplyAction.reset_consecutive_count()
|
||||
logger.info(f"{self.log_prefix} 执行了回复动作,重置no_reply计数器")
|
||||
|
||||
@@ -551,7 +566,7 @@ class HeartFChatting:
|
||||
return False
|
||||
|
||||
async def _generate_response(
|
||||
self, message_data: dict, available_actions: Optional[Dict[str, ActionInfo]], reply_to: str
|
||||
self, message_data: dict, available_actions: Optional[Dict[str, ActionInfo]], reply_to: str, request_type: str = "chat.replyer.normal"
|
||||
) -> Optional[list]:
|
||||
"""生成普通回复"""
|
||||
try:
|
||||
@@ -559,8 +574,8 @@ class HeartFChatting:
|
||||
chat_stream=self.chat_stream,
|
||||
reply_to=reply_to,
|
||||
available_actions=available_actions,
|
||||
enable_tool=global_config.tool.enable_in_normal_chat,
|
||||
request_type="chat.replyer.normal",
|
||||
enable_tool=global_config.tool.enable_tool,
|
||||
request_type=request_type,
|
||||
)
|
||||
|
||||
if not success or not reply_set:
|
||||
@@ -589,7 +604,7 @@ class HeartFChatting:
|
||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复"
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
logger.info(
|
||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import time
|
||||
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from src.config.config import global_config
|
||||
from src.common.message_repository import count_messages
|
||||
from src.common.logger import get_logger
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||
from src.plugin_system.apis import send_api
|
||||
from maim_message.message_base import GroupInfo
|
||||
|
||||
from src.common.message_repository import count_messages
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
@@ -106,3 +109,30 @@ def get_recent_message_stats(minutes: float = 30, chat_id: Optional[str] = None)
|
||||
bot_reply_count = count_messages(bot_filter)
|
||||
|
||||
return {"bot_reply_count": bot_reply_count, "total_message_count": total_message_count}
|
||||
|
||||
|
||||
async def send_typing():
|
||||
group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心")
|
||||
|
||||
chat = await get_chat_manager().get_or_create_stream(
|
||||
platform="amaidesu_default",
|
||||
user_info=None,
|
||||
group_info=group_info,
|
||||
)
|
||||
|
||||
await send_api.custom_to_stream(
|
||||
message_type="state", content="typing", stream_id=chat.stream_id, storage_message=False
|
||||
)
|
||||
|
||||
async def stop_typing():
|
||||
group_info = GroupInfo(platform="amaidesu_default", group_id="114514", group_name="内心")
|
||||
|
||||
chat = await get_chat_manager().get_or_create_stream(
|
||||
platform="amaidesu_default",
|
||||
user_info=None,
|
||||
group_info=group_info,
|
||||
)
|
||||
|
||||
await send_api.custom_to_stream(
|
||||
message_type="state", content="stop_typing", stream_id=chat.stream_id, storage_message=False
|
||||
)
|
||||
@@ -312,7 +312,7 @@ class ActionPlanner:
|
||||
by_what = "聊天内容和用户的最新消息"
|
||||
target_prompt = ""
|
||||
no_action_block = """重要说明:
|
||||
- 'no_action' 表示只进行普通聊天回复,不执行任何额外动作
|
||||
- 'reply' 表示只进行普通聊天回复,不执行任何额外动作
|
||||
- 其他action表示在普通回复的基础上,执行相应的额外动作"""
|
||||
|
||||
chat_context_description = "你现在正在一个群聊中"
|
||||
|
||||
@@ -151,7 +151,6 @@ class DefaultReplyer:
|
||||
|
||||
async def generate_reply_with_context(
|
||||
self,
|
||||
reply_data: Optional[Dict[str, Any]] = None,
|
||||
reply_to: str = "",
|
||||
extra_info: str = "",
|
||||
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||
@@ -160,29 +159,24 @@ class DefaultReplyer:
|
||||
) -> Tuple[bool, Optional[str], Optional[str]]:
|
||||
"""
|
||||
回复器 (Replier): 核心逻辑,负责生成回复文本。
|
||||
(已整合原 HeartFCGenerator 的功能)
|
||||
"""
|
||||
prompt = None
|
||||
if available_actions is None:
|
||||
available_actions = {}
|
||||
try:
|
||||
if not reply_data:
|
||||
reply_data = {
|
||||
"reply_to": reply_to,
|
||||
"extra_info": extra_info,
|
||||
}
|
||||
for key, value in reply_data.items():
|
||||
if not value:
|
||||
logger.debug(f"回复数据跳过{key},生成回复时将忽略。")
|
||||
|
||||
# 3. 构建 Prompt
|
||||
with Timer("构建Prompt", {}): # 内部计时器,可选保留
|
||||
prompt = await self.build_prompt_reply_context(
|
||||
reply_data=reply_data, # 传递action_data
|
||||
reply_to = reply_to,
|
||||
extra_info=extra_info,
|
||||
available_actions=available_actions,
|
||||
enable_timeout=enable_timeout,
|
||||
enable_tool=enable_tool,
|
||||
)
|
||||
|
||||
if not prompt:
|
||||
logger.warning("构建prompt失败,跳过回复生成")
|
||||
return False, None, None
|
||||
|
||||
# 4. 调用 LLM 生成回复
|
||||
content = None
|
||||
@@ -282,14 +276,13 @@ class DefaultReplyer:
|
||||
traceback.print_exc()
|
||||
return False, None
|
||||
|
||||
async def build_relation_info(self, reply_data=None):
|
||||
async def build_relation_info(self, reply_to: str = ""):
|
||||
if not global_config.relationship.enable_relationship:
|
||||
return ""
|
||||
|
||||
relationship_fetcher = relationship_fetcher_manager.get_fetcher(self.chat_stream.stream_id)
|
||||
if not reply_data:
|
||||
if not reply_to:
|
||||
return ""
|
||||
reply_to = reply_data.get("reply_to", "")
|
||||
sender, text = self._parse_reply_target(reply_to)
|
||||
if not sender or not text:
|
||||
return ""
|
||||
@@ -381,7 +374,7 @@ class DefaultReplyer:
|
||||
|
||||
return memory_str
|
||||
|
||||
async def build_tool_info(self, chat_history, reply_data: Optional[Dict], enable_tool: bool = True):
|
||||
async def build_tool_info(self, chat_history, reply_to: str = "", enable_tool: bool = True):
|
||||
"""构建工具信息块
|
||||
|
||||
Args:
|
||||
@@ -395,10 +388,9 @@ class DefaultReplyer:
|
||||
if not enable_tool:
|
||||
return ""
|
||||
|
||||
if not reply_data:
|
||||
if not reply_to:
|
||||
return ""
|
||||
|
||||
reply_to = reply_data.get("reply_to", "")
|
||||
sender, text = self._parse_reply_target(reply_to)
|
||||
|
||||
if not text:
|
||||
@@ -577,7 +569,8 @@ class DefaultReplyer:
|
||||
|
||||
async def build_prompt_reply_context(
|
||||
self,
|
||||
reply_data: Dict[str, Any],
|
||||
reply_to: str,
|
||||
extra_info: str = "",
|
||||
available_actions: Optional[Dict[str, ActionInfo]] = None,
|
||||
enable_timeout: bool = False,
|
||||
enable_tool: bool = True,
|
||||
@@ -602,8 +595,6 @@ class DefaultReplyer:
|
||||
chat_id = chat_stream.stream_id
|
||||
person_info_manager = get_person_info_manager()
|
||||
is_group_chat = bool(chat_stream.group_info)
|
||||
reply_to = reply_data.get("reply_to", "none")
|
||||
extra_info_block = reply_data.get("extra_info", "") or reply_data.get("extra_info_block", "")
|
||||
|
||||
if global_config.mood.enable_mood:
|
||||
chat_mood = mood_manager.get_mood_by_chat_id(chat_id)
|
||||
@@ -612,6 +603,13 @@ class DefaultReplyer:
|
||||
mood_prompt = ""
|
||||
|
||||
sender, target = self._parse_reply_target(reply_to)
|
||||
person_info_manager = get_person_info_manager()
|
||||
person_id = person_info_manager.get_person_id_by_person_name(sender)
|
||||
user_id = person_info_manager.get_value_sync(person_id, "user_id")
|
||||
platform = chat_stream.platform
|
||||
if user_id == global_config.bot.qq_account and platform == global_config.bot.platform:
|
||||
logger.warning("选取了自身作为回复对象,跳过构建prompt")
|
||||
return ""
|
||||
|
||||
target = replace_user_references_sync(target, chat_stream.platform, replace_bot_name=True)
|
||||
|
||||
@@ -630,21 +628,6 @@ class DefaultReplyer:
|
||||
limit=global_config.chat.max_context_size * 2,
|
||||
)
|
||||
|
||||
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=chat_id,
|
||||
timestamp=time.time(),
|
||||
limit=global_config.chat.max_context_size,
|
||||
)
|
||||
chat_talking_prompt = build_readable_messages(
|
||||
message_list_before_now,
|
||||
replace_bot_name=True,
|
||||
merge_messages=False,
|
||||
timestamp_mode="normal_no_YMD",
|
||||
read_mark=0.0,
|
||||
truncate=True,
|
||||
show_actions=True,
|
||||
)
|
||||
|
||||
message_list_before_short = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=chat_id,
|
||||
timestamp=time.time(),
|
||||
@@ -664,10 +647,10 @@ class DefaultReplyer:
|
||||
self._time_and_run_task(
|
||||
self.build_expression_habits(chat_talking_prompt_short, target), "expression_habits"
|
||||
),
|
||||
self._time_and_run_task(self.build_relation_info(reply_data), "relation_info"),
|
||||
self._time_and_run_task(self.build_relation_info(reply_to), "relation_info"),
|
||||
self._time_and_run_task(self.build_memory_block(chat_talking_prompt_short, target), "memory_block"),
|
||||
self._time_and_run_task(
|
||||
self.build_tool_info(chat_talking_prompt_short, reply_data, enable_tool=enable_tool), "tool_info"
|
||||
self.build_tool_info(chat_talking_prompt_short, reply_to, enable_tool=enable_tool), "tool_info"
|
||||
),
|
||||
self._time_and_run_task(get_prompt_info(target, threshold=0.38), "prompt_info"),
|
||||
)
|
||||
@@ -700,8 +683,8 @@ class DefaultReplyer:
|
||||
|
||||
keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target)
|
||||
|
||||
if extra_info_block:
|
||||
extra_info_block = f"以下是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策\n{extra_info_block}\n以上是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策"
|
||||
if extra_info:
|
||||
extra_info_block = f"以下是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策\n{extra_info}\n以上是你在回复时需要参考的信息,现在请你阅读以下内容,进行决策"
|
||||
else:
|
||||
extra_info_block = ""
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ class ChatConfig(ConfigBase):
|
||||
选择普通模型的概率为 1 - reasoning_normal_model_probability
|
||||
"""
|
||||
|
||||
thinking_timeout: int = 30
|
||||
thinking_timeout: int = 40
|
||||
"""麦麦最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢)"""
|
||||
|
||||
talk_frequency: float = 1
|
||||
@@ -299,11 +299,8 @@ class ExpressionConfig(ConfigBase):
|
||||
class ToolConfig(ConfigBase):
|
||||
"""工具配置类"""
|
||||
|
||||
enable_in_normal_chat: bool = False
|
||||
"""是否在普通聊天中启用工具"""
|
||||
|
||||
enable_in_focus_chat: bool = True
|
||||
"""是否在专注聊天中启用工具"""
|
||||
enable_tool: bool = False
|
||||
"""是否在聊天中启用工具"""
|
||||
|
||||
@dataclass
|
||||
class VoiceConfig(ConfigBase):
|
||||
|
||||
@@ -107,10 +107,14 @@ async def generate_reply(
|
||||
return False, [], None
|
||||
|
||||
logger.debug("[GeneratorAPI] 开始生成回复")
|
||||
|
||||
if not reply_to:
|
||||
reply_to = action_data.get("reply_to", "")
|
||||
if not extra_info and action_data:
|
||||
extra_info = action_data.get("extra_info", "")
|
||||
|
||||
# 调用回复器生成回复
|
||||
success, content, prompt = await replyer.generate_reply_with_context(
|
||||
reply_data=action_data or {},
|
||||
reply_to=reply_to,
|
||||
extra_info=extra_info,
|
||||
available_actions=available_actions,
|
||||
|
||||
@@ -24,11 +24,6 @@
|
||||
"is_built_in": true,
|
||||
"plugin_type": "action_provider",
|
||||
"components": [
|
||||
{
|
||||
"type": "action",
|
||||
"name": "reply",
|
||||
"description": "参与聊天回复,发送文本进行表达"
|
||||
},
|
||||
{
|
||||
"type": "action",
|
||||
"name": "no_reply",
|
||||
|
||||
@@ -61,7 +61,6 @@ class NoReplyAction(BaseAction):
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行不回复动作"""
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
reason = self.action_data.get("reason", "")
|
||||
|
||||
@@ -18,7 +18,6 @@ from src.common.logger import get_logger
|
||||
# 导入API模块 - 标准Python包方式
|
||||
from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
from src.plugins.built_in.core_actions.emoji import EmojiAction
|
||||
from src.plugins.built_in.core_actions.reply import ReplyAction
|
||||
|
||||
logger = get_logger("core_actions")
|
||||
|
||||
@@ -52,10 +51,9 @@ class CoreActionsPlugin(BasePlugin):
|
||||
config_schema: dict = {
|
||||
"plugin": {
|
||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||
"config_version": ConfigField(type=str, default="0.4.0", description="配置文件版本"),
|
||||
"config_version": ConfigField(type=str, default="0.5.0", description="配置文件版本"),
|
||||
},
|
||||
"components": {
|
||||
"enable_reply": ConfigField(type=bool, default=True, description="是否启用回复动作"),
|
||||
"enable_no_reply": ConfigField(type=bool, default=True, description="是否启用不回复动作"),
|
||||
"enable_emoji": ConfigField(type=bool, default=True, description="是否启用发送表情/图片动作"),
|
||||
},
|
||||
@@ -74,8 +72,6 @@ class CoreActionsPlugin(BasePlugin):
|
||||
|
||||
# --- 根据配置注册组件 ---
|
||||
components = []
|
||||
if self.get_config("components.enable_reply", True):
|
||||
components.append((ReplyAction.get_action_info(), ReplyAction))
|
||||
if self.get_config("components.enable_no_reply", True):
|
||||
components.append((NoReplyAction.get_action_info(), NoReplyAction))
|
||||
if self.get_config("components.enable_emoji", True):
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
# 导入新插件系统
|
||||
from src.plugin_system import BaseAction, ActionActivationType, ChatMode
|
||||
from src.config.config import global_config
|
||||
import random
|
||||
import time
|
||||
from typing import Tuple
|
||||
import asyncio
|
||||
import re
|
||||
import traceback
|
||||
|
||||
# 导入依赖的系统组件
|
||||
from src.common.logger import get_logger
|
||||
|
||||
# 导入API模块 - 标准Python包方式
|
||||
from src.plugin_system.apis import generator_api, message_api
|
||||
# 注释:不再需要导入NoReplyAction,因为计数器管理已移至heartFC_chat.py
|
||||
# from src.plugins.built_in.core_actions.no_reply import NoReplyAction
|
||||
from src.person_info.person_info import get_person_info_manager
|
||||
from src.mais4u.mai_think import mai_thinking_manager
|
||||
from src.mais4u.constant_s4u import ENABLE_S4U
|
||||
|
||||
logger = get_logger("reply_action")
|
||||
|
||||
|
||||
class ReplyAction(BaseAction):
|
||||
"""回复动作 - 参与聊天回复"""
|
||||
|
||||
# 激活设置
|
||||
focus_activation_type = ActionActivationType.NEVER
|
||||
normal_activation_type = ActionActivationType.NEVER
|
||||
mode_enable = ChatMode.FOCUS
|
||||
parallel_action = False
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "reply"
|
||||
action_description = ""
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {}
|
||||
|
||||
# 动作使用场景
|
||||
action_require = [""]
|
||||
|
||||
# 关联类型
|
||||
associated_types = ["text"]
|
||||
|
||||
def _parse_reply_target(self, target_message: str) -> tuple:
|
||||
sender = ""
|
||||
target = ""
|
||||
# 添加None检查,防止NoneType错误
|
||||
if target_message is None:
|
||||
return sender, target
|
||||
if ":" in target_message or ":" in target_message:
|
||||
# 使用正则表达式匹配中文或英文冒号
|
||||
parts = re.split(pattern=r"[::]", string=target_message, maxsplit=1)
|
||||
if len(parts) == 2:
|
||||
sender = parts[0].strip()
|
||||
target = parts[1].strip()
|
||||
return sender, target
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行回复动作"""
|
||||
logger.debug(f"{self.log_prefix} 决定进行回复")
|
||||
start_time = self.action_data.get("loop_start_time", time.time())
|
||||
|
||||
user_id = self.user_id
|
||||
platform = self.platform
|
||||
# logger.info(f"{self.log_prefix} 用户ID: {user_id}, 平台: {platform}")
|
||||
person_id = get_person_info_manager().get_person_id(platform, user_id) # type: ignore
|
||||
# logger.info(f"{self.log_prefix} 人物ID: {person_id}")
|
||||
person_name = get_person_info_manager().get_value_sync(person_id, "person_name")
|
||||
reply_to = f"{person_name}:{self.action_message.get('processed_plain_text', '')}" # type: ignore
|
||||
logger.info(f"{self.log_prefix} 决定进行回复,目标: {reply_to}")
|
||||
|
||||
try:
|
||||
if prepared_reply := self.action_data.get("prepared_reply", ""):
|
||||
reply_text = prepared_reply
|
||||
else:
|
||||
try:
|
||||
success, reply_set, _ = await asyncio.wait_for(
|
||||
generator_api.generate_reply(
|
||||
extra_info="",
|
||||
reply_to=reply_to,
|
||||
chat_id=self.chat_id,
|
||||
request_type="chat.replyer.focus",
|
||||
enable_tool=global_config.tool.enable_in_focus_chat,
|
||||
),
|
||||
timeout=global_config.chat.thinking_timeout,
|
||||
)
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"{self.log_prefix} 回复生成超时 ({global_config.chat.thinking_timeout}s)")
|
||||
return False, "timeout"
|
||||
|
||||
# 检查从start_time以来的新消息数量
|
||||
# 获取动作触发时间或使用默认值
|
||||
current_time = time.time()
|
||||
new_message_count = message_api.count_new_messages(
|
||||
chat_id=self.chat_id, start_time=start_time, end_time=current_time
|
||||
)
|
||||
|
||||
# 根据新消息数量决定是否使用reply_to
|
||||
need_reply = new_message_count >= random.randint(2, 4)
|
||||
if need_reply:
|
||||
logger.info(
|
||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,使用引用回复"
|
||||
)
|
||||
else:
|
||||
logger.debug(
|
||||
f"{self.log_prefix} 从思考到回复,共有{new_message_count}条新消息,不使用引用回复"
|
||||
)
|
||||
|
||||
# 构建回复文本
|
||||
reply_text = ""
|
||||
first_replied = False
|
||||
reply_to_platform_id = f"{platform}:{user_id}"
|
||||
for reply_seg in reply_set:
|
||||
data = reply_seg[1]
|
||||
if not first_replied:
|
||||
if need_reply:
|
||||
await self.send_text(
|
||||
content=data, reply_to=reply_to, reply_to_platform_id=reply_to_platform_id, typing=False
|
||||
)
|
||||
else:
|
||||
await self.send_text(content=data, reply_to_platform_id=reply_to_platform_id, typing=False)
|
||||
first_replied = True
|
||||
else:
|
||||
await self.send_text(content=data, reply_to_platform_id=reply_to_platform_id, typing=True)
|
||||
reply_text += data
|
||||
|
||||
# 存储动作记录
|
||||
reply_text = f"你对{person_name}进行了回复:{reply_text}"
|
||||
|
||||
if ENABLE_S4U:
|
||||
await mai_thinking_manager.get_mai_think(self.chat_id).do_think_after_response(reply_text)
|
||||
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=False,
|
||||
action_prompt_display=reply_text,
|
||||
action_done=True,
|
||||
)
|
||||
|
||||
# 注释:重置NoReplyAction的连续计数器现在由heartFC_chat.py统一管理
|
||||
# NoReplyAction.reset_consecutive_count()
|
||||
|
||||
return success, reply_text
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"{self.log_prefix} 回复动作执行失败: {e}")
|
||||
traceback.print_exc()
|
||||
return False, f"回复失败: {str(e)}"
|
||||
@@ -58,7 +58,7 @@ focus_value = 1
|
||||
willing_amplifier = 1 # 麦麦回复意愿
|
||||
|
||||
max_context_size = 25 # 上下文长度
|
||||
thinking_timeout = 20 # 麦麦一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢)
|
||||
thinking_timeout = 40 # 麦麦一次回复最长思考规划时间,超过这个时间的思考会放弃(往往是api反应太慢)
|
||||
replyer_random_probability = 0.5 # 首要replyer模型被选择的概率
|
||||
|
||||
mentioned_bot_inevitable_reply = true # 提及 bot 大概率回复
|
||||
@@ -107,8 +107,7 @@ ban_msgs_regex = [
|
||||
willing_mode = "classical" # 回复意愿模式 —— 经典模式:classical,mxp模式:mxp,自定义模式:custom(需要你自己实现)
|
||||
|
||||
[tool]
|
||||
enable_in_normal_chat = false # 是否在普通聊天中启用工具
|
||||
enable_in_focus_chat = true # 是否在专注聊天中启用工具
|
||||
enable_tool = false # 是否在普通聊天中启用工具
|
||||
|
||||
[emoji]
|
||||
emoji_chance = 0.6 # 麦麦激活表情包动作的概率
|
||||
|
||||
Reference in New Issue
Block a user