Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox-Core into dev
This commit is contained in:
@@ -494,6 +494,14 @@ class StreamLoopManager:
|
|||||||
logger.debug(f"流 {stream_id} 未读消息为空,跳过 chatter 处理")
|
logger.debug(f"流 {stream_id} 未读消息为空,跳过 chatter 处理")
|
||||||
return True # 返回 True 表示处理完成(虽然没有实际处理)
|
return True # 返回 True 表示处理完成(虽然没有实际处理)
|
||||||
|
|
||||||
|
# 🔇 静默群组检查:在静默群组中,只有提到 Bot 名字/别名才响应
|
||||||
|
if await self._should_skip_for_mute_group(stream_id, unread_messages):
|
||||||
|
# 清空未读消息,不触发 chatter
|
||||||
|
from .message_manager import message_manager
|
||||||
|
await message_manager.clear_stream_unread_messages(stream_id)
|
||||||
|
logger.debug(f"🔇 流 {stream_id} 在静默列表中且未提及Bot,跳过处理")
|
||||||
|
return True
|
||||||
|
|
||||||
logger.debug(f"流 {stream_id} 有 {len(unread_messages)} 条未读消息,开始处理")
|
logger.debug(f"流 {stream_id} 有 {len(unread_messages)} 条未读消息,开始处理")
|
||||||
|
|
||||||
# 设置触发用户ID,以实现回复保护
|
# 设置触发用户ID,以实现回复保护
|
||||||
@@ -537,6 +545,70 @@ class StreamLoopManager:
|
|||||||
# 无论成功或失败,都要设置处理状态为未处理
|
# 无论成功或失败,都要设置处理状态为未处理
|
||||||
self._set_stream_processing_status(stream_id, False)
|
self._set_stream_processing_status(stream_id, False)
|
||||||
|
|
||||||
|
async def _should_skip_for_mute_group(self, stream_id: str, unread_messages: list) -> bool:
|
||||||
|
"""检查是否应该因静默群组而跳过处理
|
||||||
|
|
||||||
|
在静默群组中,只有当消息提及 Bot(@、回复、包含名字/别名)时才响应。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
stream_id: 流ID
|
||||||
|
unread_messages: 未读消息列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True 表示应该跳过,False 表示正常处理
|
||||||
|
"""
|
||||||
|
if global_config is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 获取静默群组列表
|
||||||
|
mute_group_list = getattr(global_config.message_receive, "mute_group_list", [])
|
||||||
|
if not mute_group_list:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 获取 chat_stream 来检查群组信息
|
||||||
|
chat_manager = get_chat_manager()
|
||||||
|
chat_stream = await chat_manager.get_stream(stream_id)
|
||||||
|
|
||||||
|
if not chat_stream or not chat_stream.group_info:
|
||||||
|
# 不是群聊,不适用静默规则
|
||||||
|
return False
|
||||||
|
|
||||||
|
group_id = str(chat_stream.group_info.group_id)
|
||||||
|
if group_id not in mute_group_list:
|
||||||
|
# 不在静默列表中
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 在静默列表中,检查是否有消息提及 Bot
|
||||||
|
bot_name = getattr(global_config.bot, "nickname", "")
|
||||||
|
bot_aliases = getattr(global_config.bot, "alias_names", [])
|
||||||
|
bot_qq = str(getattr(global_config.bot, "qq_account", ""))
|
||||||
|
|
||||||
|
# 构建需要检测的关键词列表
|
||||||
|
mention_keywords = [bot_name] + list(bot_aliases) if bot_name else list(bot_aliases)
|
||||||
|
mention_keywords = [k for k in mention_keywords if k] # 过滤空字符串
|
||||||
|
|
||||||
|
for msg in unread_messages:
|
||||||
|
# 检查是否被 @ 或回复
|
||||||
|
if getattr(msg, "is_at", False) or getattr(msg, "is_mentioned", False):
|
||||||
|
logger.debug(f"🔇 静默群组 {group_id}: 消息被@或回复,允许响应")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 检查消息内容是否包含 Bot 名字或别名
|
||||||
|
content = getattr(msg, "processed_plain_text", "") or getattr(msg, "display_message", "") or ""
|
||||||
|
for keyword in mention_keywords:
|
||||||
|
if keyword and keyword in content:
|
||||||
|
logger.debug(f"🔇 静默群组 {group_id}: 消息包含关键词 '{keyword}',允许响应")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 没有任何消息提及 Bot
|
||||||
|
logger.debug(f"🔇 静默群组 {group_id}: {len(unread_messages)} 条消息均未提及Bot,跳过")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"检查静默群组时出错: {stream_id}, error={e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def _set_stream_processing_status(self, stream_id: str, is_processing: bool) -> None:
|
def _set_stream_processing_status(self, stream_id: str, is_processing: bool) -> None:
|
||||||
"""设置流的处理状态"""
|
"""设置流的处理状态"""
|
||||||
try:
|
try:
|
||||||
@@ -643,6 +715,18 @@ class StreamLoopManager:
|
|||||||
if global_config is None:
|
if global_config is None:
|
||||||
raise RuntimeError("Global config is not initialized")
|
raise RuntimeError("Global config is not initialized")
|
||||||
|
|
||||||
|
# 私聊使用最小间隔,快速响应
|
||||||
|
try:
|
||||||
|
chat_manager = get_chat_manager()
|
||||||
|
chat_stream = await chat_manager.get_stream(stream_id)
|
||||||
|
if chat_stream and not chat_stream.group_info:
|
||||||
|
# 私聊:有消息时几乎立即响应,空转时稍微等待
|
||||||
|
min_interval = 0.1 if has_messages else 3.0
|
||||||
|
logger.debug(f"流 {stream_id} 私聊模式,使用最小间隔: {min_interval:.2f}s")
|
||||||
|
return min_interval
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"检查流 {stream_id} 是否为私聊失败: {e}")
|
||||||
|
|
||||||
# 基础间隔
|
# 基础间隔
|
||||||
base_interval = getattr(global_config.chat, "distribution_interval", 5.0)
|
base_interval = getattr(global_config.chat, "distribution_interval", 5.0)
|
||||||
|
|
||||||
|
|||||||
@@ -178,13 +178,16 @@ class KokoroFlowChatter(BaseChatter):
|
|||||||
# 10. 执行动作
|
# 10. 执行动作
|
||||||
exec_results = []
|
exec_results = []
|
||||||
has_reply = False
|
has_reply = False
|
||||||
|
|
||||||
for action in plan_response.actions:
|
for action in plan_response.actions:
|
||||||
|
action_data = action.params.copy()
|
||||||
|
|
||||||
result = await self.action_manager.execute_action(
|
result = await self.action_manager.execute_action(
|
||||||
action_name=action.type,
|
action_name=action.type,
|
||||||
chat_id=self.stream_id,
|
chat_id=self.stream_id,
|
||||||
target_message=target_message,
|
target_message=target_message,
|
||||||
reasoning=plan_response.thought,
|
reasoning=plan_response.thought,
|
||||||
action_data=action.params,
|
action_data=action_data,
|
||||||
thinking_id=None,
|
thinking_id=None,
|
||||||
log_prefix="[KFC]",
|
log_prefix="[KFC]",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -180,7 +180,14 @@ def build_actions_module(available_actions: Optional[dict[str, ActionInfo]] = No
|
|||||||
if not available_actions:
|
if not available_actions:
|
||||||
return _get_default_actions_block()
|
return _get_default_actions_block()
|
||||||
|
|
||||||
action_blocks = []
|
# 核心限制说明(放在最前面)
|
||||||
|
action_blocks = [
|
||||||
|
"""⚠️ **输出限制(必须遵守)**:
|
||||||
|
1. `actions` 数组里**只能有一个** `kfc_reply`,不能写多个
|
||||||
|
2. `kfc_reply` 的 `content` 要简洁,像发微信一样,**不要写长篇大论**
|
||||||
|
3. 系统会自动把你的回复拆分成多条消息发送,你不需要自己分段
|
||||||
|
"""
|
||||||
|
]
|
||||||
|
|
||||||
for action_name, action_info in available_actions.items():
|
for action_name, action_info in available_actions.items():
|
||||||
description = action_info.description or f"执行 {action_name}"
|
description = action_info.description or f"执行 {action_name}"
|
||||||
@@ -188,6 +195,10 @@ def build_actions_module(available_actions: Optional[dict[str, ActionInfo]] = No
|
|||||||
# 构建动作块
|
# 构建动作块
|
||||||
action_block = f"### `{action_name}` - {description}"
|
action_block = f"### `{action_name}` - {description}"
|
||||||
|
|
||||||
|
# 对 kfc_reply 特殊处理,再次强调限制
|
||||||
|
if action_name == "kfc_reply":
|
||||||
|
action_block += "\n(只能有一个,内容写完整)"
|
||||||
|
|
||||||
# 参数说明(如果有)
|
# 参数说明(如果有)
|
||||||
if action_info.action_parameters:
|
if action_info.action_parameters:
|
||||||
params_lines = [f" - `{name}`: {desc}" for name, desc in action_info.action_parameters.items()]
|
params_lines = [f" - `{name}`: {desc}" for name, desc in action_info.action_parameters.items()]
|
||||||
@@ -213,26 +224,22 @@ def build_actions_module(available_actions: Optional[dict[str, ActionInfo]] = No
|
|||||||
|
|
||||||
def _get_default_actions_block() -> str:
|
def _get_default_actions_block() -> str:
|
||||||
"""获取默认的内置动作描述块"""
|
"""获取默认的内置动作描述块"""
|
||||||
return """### `kfc_reply` - 发消息
|
return """⚠️ **输出限制(必须遵守)**:
|
||||||
发送文字回复。**注意:只能有一个 kfc_reply 动作,把你想说的话都写在一条消息里。**
|
1. `actions` 数组里**只能有一个** `kfc_reply`,不能写多个
|
||||||
|
2. `kfc_reply` 的 `content` 要简洁,像发微信一样,**不要写长篇大论**
|
||||||
|
3. 系统会自动把你的回复拆分成多条消息发送,你不需要自己分段
|
||||||
|
|
||||||
|
### `kfc_reply` - 发消息
|
||||||
```json
|
```json
|
||||||
{"type": "kfc_reply", "content": "你要说的话,全部写在这里"}
|
{"type": "kfc_reply", "content": "你想说的话"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `poke_user` - 戳一戳
|
### `poke_user` - 戳一戳
|
||||||
戳对方一下
|
|
||||||
```json
|
```json
|
||||||
{"type": "poke_user"}
|
{"type": "poke_user"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### `update_internal_state` - 更新你的心情
|
|
||||||
更新你现在的心情状态
|
|
||||||
```json
|
|
||||||
{"type": "update_internal_state", "mood": "开心"}
|
|
||||||
```
|
|
||||||
|
|
||||||
### `do_nothing` - 不做任何事
|
### `do_nothing` - 不做任何事
|
||||||
想了想,决定现在不作回应
|
|
||||||
```json
|
```json
|
||||||
{"type": "do_nothing"}
|
{"type": "do_nothing"}
|
||||||
```"""
|
```"""
|
||||||
@@ -262,13 +269,16 @@ def build_output_module(context_data: Optional[dict[str, str]] = None) -> str:
|
|||||||
|
|
||||||
# JSON 输出格式说明(更自然的思考引导)
|
# JSON 输出格式说明(更自然的思考引导)
|
||||||
json_format = """### 输出格式(JSON)
|
json_format = """### 输出格式(JSON)
|
||||||
|
|
||||||
|
⚠️ **核心规则**:actions 中只能有**一个** `kfc_reply`动作!不能有多个`kfc_reply`动作!想说的话全写在一条消息里,系统会自动拆分发送。
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"thought": "你心里的真实想法,像日记一样自然",
|
"thought": "你心里的真实想法,像日记一样自然",
|
||||||
"expected_user_reaction": "猜猜对方看到会怎么想",
|
"expected_user_reaction": "猜猜对方看到会怎么想",
|
||||||
"max_wait_seconds": "预估的等待时间(秒)",
|
"max_wait_seconds": "预估的等待时间(秒)",
|
||||||
"actions": [
|
"actions": [
|
||||||
{"type": "kfc_reply", "content": "你要说的话"}
|
{"type": "kfc_reply", "content": "你想说的所有话,写在这里面"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -280,7 +290,7 @@ def build_output_module(context_data: Optional[dict[str, str]] = None) -> str:
|
|||||||
|
|
||||||
关于 thought(内心想法):
|
关于 thought(内心想法):
|
||||||
- 写你真正在想的,不是在分析任务
|
- 写你真正在想的,不是在分析任务
|
||||||
- 像心里嘀咕一样,比如"这家伙又来撒娇了~" "有点困了但还想再聊会儿"
|
- 像心里嘀咕一样,比如"这家伙又来撒娇了" "有点困了但还想再聊会儿"
|
||||||
- 不要写"根据设定""我需要""我应该"这种规划性的话
|
- 不要写"根据设定""我需要""我应该"这种规划性的话
|
||||||
- 就是你作为这个人,此刻心里在想什么
|
- 就是你作为这个人,此刻心里在想什么
|
||||||
|
|
||||||
@@ -350,10 +360,10 @@ def build_system_prompt(
|
|||||||
"## 3. 现在的情况",
|
"## 3. 现在的情况",
|
||||||
build_context_module(session, chat_stream, context_data),
|
build_context_module(session, chat_stream, context_data),
|
||||||
"",
|
"",
|
||||||
"## 5. 你能做的事",
|
"## 4. 你能做的事",
|
||||||
build_actions_module(available_actions),
|
build_actions_module(available_actions),
|
||||||
"",
|
"",
|
||||||
"## 6. 怎么回复",
|
"## 5. 怎么回复",
|
||||||
build_output_module(context_data),
|
build_output_module(context_data),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user