This commit is contained in:
春河晴
2025-06-10 16:13:31 +09:00
parent 440e8bf7f3
commit 8d9a88a903
70 changed files with 1598 additions and 1642 deletions

View File

@@ -13,8 +13,6 @@ from src.chat.utils.prompt_builder import global_prompt_manager
from .normal_chat_generator import NormalChatGenerator
from ..message_receive.message import MessageSending, MessageRecv, MessageThinking, MessageSet
from src.chat.message_receive.message_sender import message_manager
from src.chat.utils.utils_image import image_path_to_base64
from src.chat.emoji_system.emoji_manager import emoji_manager
from src.chat.normal_chat.willing.willing_manager import willing_manager
from src.chat.normal_chat.normal_chat_utils import get_recent_message_stats
from src.config.config import global_config
@@ -69,7 +67,7 @@ class NormalChat:
self.on_switch_to_focus_callback = on_switch_to_focus_callback
self._disabled = False # 增加停用标志
logger.debug(f"[{self.stream_name}] NormalChat 初始化完成 (异步部分)。")
# 改为实例方法
@@ -193,7 +191,9 @@ class NormalChat:
return
timing_results = {}
reply_probability = 1.0 if is_mentioned and global_config.normal_chat.mentioned_bot_inevitable_reply else 0.0 # 如果被提及且开启了提及必回复则基础概率为1否则需要意愿判断
reply_probability = (
1.0 if is_mentioned and global_config.normal_chat.mentioned_bot_inevitable_reply else 0.0
) # 如果被提及且开启了提及必回复则基础概率为1否则需要意愿判断
# 意愿管理器设置当前message信息
willing_manager.setup(message, self.chat_stream, is_mentioned, interested_rate)
@@ -267,13 +267,17 @@ class NormalChat:
try:
# 获取发送者名称(动作修改已在并行执行前完成)
sender_name = self._get_sender_name(message)
no_action = {
"action_result": {"action_type": "no_action", "action_data": {}, "reasoning": "规划器初始化默认", "is_parallel": True},
"action_result": {
"action_type": "no_action",
"action_data": {},
"reasoning": "规划器初始化默认",
"is_parallel": True,
},
"chat_context": "",
"action_prompt": "",
}
# 检查是否应该跳过规划
if self.action_modifier.should_skip_planning():
@@ -288,7 +292,9 @@ class NormalChat:
reasoning = plan_result["action_result"]["reasoning"]
is_parallel = plan_result["action_result"].get("is_parallel", False)
logger.info(f"[{self.stream_name}] Planner决策: {action_type}, 理由: {reasoning}, 并行执行: {is_parallel}")
logger.info(
f"[{self.stream_name}] Planner决策: {action_type}, 理由: {reasoning}, 并行执行: {is_parallel}"
)
self.action_type = action_type # 更新实例属性
self.is_parallel_action = is_parallel # 新增:保存并行执行标志
@@ -307,7 +313,12 @@ class NormalChat:
else:
logger.warning(f"[{self.stream_name}] 额外动作 {action_type} 执行失败")
return {"action_type": action_type, "action_data": action_data, "reasoning": reasoning, "is_parallel": is_parallel}
return {
"action_type": action_type,
"action_data": action_data,
"reasoning": reasoning,
"is_parallel": is_parallel,
}
except Exception as e:
logger.error(f"[{self.stream_name}] Planner执行失败: {e}")
@@ -331,13 +342,19 @@ class NormalChat:
logger.error(f"[{self.stream_name}] 动作规划异常: {plan_result}")
elif plan_result:
logger.debug(f"[{self.stream_name}] 额外动作处理完成: {self.action_type}")
if not response_set or (
self.enable_planner and self.action_type not in ["no_action", "change_to_focus_chat"] and not self.is_parallel_action
self.enable_planner
and self.action_type not in ["no_action", "change_to_focus_chat"]
and not self.is_parallel_action
):
if not response_set:
logger.info(f"[{self.stream_name}] 模型未生成回复内容")
elif self.enable_planner and self.action_type not in ["no_action", "change_to_focus_chat"] and not self.is_parallel_action:
elif (
self.enable_planner
and self.action_type not in ["no_action", "change_to_focus_chat"]
and not self.is_parallel_action
):
logger.info(f"[{self.stream_name}] 模型选择其他动作(非并行动作)")
# 如果模型未生成回复,移除思考消息
container = await message_manager.get_container(self.stream_id) # 使用 self.stream_id
@@ -364,7 +381,6 @@ class NormalChat:
# 检查 first_bot_msg 是否为 None (例如思考消息已被移除的情况)
if first_bot_msg:
# 记录回复信息到最近回复列表中
reply_info = {
"time": time.time(),
@@ -396,7 +412,6 @@ class NormalChat:
await self._check_switch_to_focus()
pass
# with Timer("关系更新", timing_results):
# await self._update_relationship(message, response_set)
@@ -605,7 +620,7 @@ class NormalChat:
# 执行动作
result = await action_handler.handle_action()
success = False
if result and isinstance(result, tuple) and len(result) >= 2:
# handle_action返回 (success: bool, message: str)
success = result[0]

View File

@@ -35,7 +35,7 @@ class NormalChatActionModifier:
**kwargs: Any,
):
"""为Normal Chat修改可用动作集合
实现动作激活策略:
1. 基于关联类型的动态过滤
2. 基于激活类型的智能判定LLM_JUDGE转为概率激活
@@ -49,7 +49,7 @@ class NormalChatActionModifier:
reasons = []
merged_action_changes = {"add": [], "remove": []}
type_mismatched_actions = [] # 在外层定义避免作用域问题
self.action_manager.restore_default_actions()
# 第一阶段:基于关联类型的动态过滤
@@ -74,7 +74,7 @@ class NormalChatActionModifier:
# 第二阶段:应用激活类型判定
# 构建聊天内容 - 使用与planner一致的方式
chat_content = ""
if chat_stream and hasattr(chat_stream, 'stream_id'):
if chat_stream and hasattr(chat_stream, "stream_id"):
try:
# 获取消息历史使用与normal_chat_planner相同的方法
message_list_before_now = get_raw_msg_before_timestamp_with_chat(
@@ -82,7 +82,7 @@ class NormalChatActionModifier:
timestamp=time.time(),
limit=global_config.focus_chat.observation_context_size, # 使用相同的配置
)
# 构建可读的聊天上下文
chat_content = build_readable_messages(
message_list_before_now,
@@ -92,39 +92,41 @@ class NormalChatActionModifier:
read_mark=0.0,
show_actions=True,
)
logger.debug(f"{self.log_prefix} 成功构建聊天内容,长度: {len(chat_content)}")
except Exception as e:
logger.warning(f"{self.log_prefix} 构建聊天内容失败: {e}")
chat_content = ""
# 获取当前Normal模式下的动作集进行激活判定
current_actions = self.action_manager.get_using_actions_for_mode(ChatMode.NORMAL)
# print(f"current_actions: {current_actions}")
# print(f"chat_content: {chat_content}")
final_activated_actions = await self._apply_normal_activation_filtering(
current_actions,
chat_content,
message_content
current_actions, chat_content, message_content
)
# print(f"final_activated_actions: {final_activated_actions}")
# 统一处理所有需要移除的动作,避免重复移除
all_actions_to_remove = set() # 使用set避免重复
# 添加关联类型不匹配的动作
if type_mismatched_actions:
all_actions_to_remove.update(type_mismatched_actions)
# 添加激活类型判定未通过的动作
for action_name in current_actions.keys():
if action_name not in final_activated_actions:
all_actions_to_remove.add(action_name)
# 统计移除原因(避免重复)
activation_failed_actions = [name for name in current_actions.keys() if name not in final_activated_actions and name not in type_mismatched_actions]
activation_failed_actions = [
name
for name in current_actions.keys()
if name not in final_activated_actions and name not in type_mismatched_actions
]
if activation_failed_actions:
reasons.append(f"移除{activation_failed_actions}(激活类型判定未通过)")
@@ -146,7 +148,7 @@ class NormalChatActionModifier:
# 记录变更原因
if reasons:
logger.info(f"{self.log_prefix} 动作调整完成: {' | '.join(reasons)}")
# 获取最终的Normal模式可用动作并记录
final_actions = self.action_manager.get_using_actions_for_mode(ChatMode.NORMAL)
logger.debug(f"{self.log_prefix} 当前Normal模式可用动作: {list(final_actions.keys())}")
@@ -159,31 +161,31 @@ class NormalChatActionModifier:
) -> Dict[str, Any]:
"""
应用Normal模式的激活类型过滤逻辑
与Focus模式的区别
1. LLM_JUDGE类型转换为概率激活避免LLM调用
2. RANDOM类型保持概率激活
3. KEYWORD类型保持关键词匹配
4. ALWAYS类型直接激活
Args:
actions_with_info: 带完整信息的动作字典
chat_content: 聊天内容
Returns:
Dict[str, Any]: 过滤后激活的actions字典
"""
activated_actions = {}
# 分类处理不同激活类型的actions
always_actions = {}
random_actions = {}
keyword_actions = {}
for action_name, action_info in actions_with_info.items():
# 使用normal_activation_type
activation_type = action_info.get("normal_activation_type", ActionActivationType.ALWAYS)
if activation_type == ActionActivationType.ALWAYS:
always_actions[action_name] = action_info
elif activation_type == ActionActivationType.RANDOM or activation_type == ActionActivationType.LLM_JUDGE:
@@ -192,12 +194,12 @@ class NormalChatActionModifier:
keyword_actions[action_name] = action_info
else:
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
# 1. 处理ALWAYS类型直接激活
for action_name, action_info in always_actions.items():
activated_actions[action_name] = action_info
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: ALWAYS类型直接激活")
# 2. 处理RANDOM类型概率激活
for action_name, action_info in random_actions.items():
probability = action_info.get("random_probability", 0.3)
@@ -207,15 +209,10 @@ class NormalChatActionModifier:
logger.debug(f"{self.log_prefix}激活动作: {action_name},原因: RANDOM类型触发概率{probability}")
else:
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: RANDOM类型未触发概率{probability}")
# 3. 处理KEYWORD类型关键词匹配
for action_name, action_info in keyword_actions.items():
should_activate = self._check_keyword_activation(
action_name,
action_info,
chat_content,
message_content
)
should_activate = self._check_keyword_activation(action_name, action_info, chat_content, message_content)
if should_activate:
activated_actions[action_name] = action_info
keywords = action_info.get("activation_keywords", [])
@@ -225,7 +222,7 @@ class NormalChatActionModifier:
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: KEYWORD类型未匹配关键词{keywords}")
# print(f"keywords: {keywords}")
# print(f"chat_content: {chat_content}")
logger.debug(f"{self.log_prefix}Normal模式激活类型过滤完成: {list(activated_actions.keys())}")
return activated_actions
@@ -238,41 +235,40 @@ class NormalChatActionModifier:
) -> bool:
"""
检查是否匹配关键词触发条件
Args:
action_name: 动作名称
action_info: 动作信息
chat_content: 聊天内容(已经是格式化后的可读消息)
Returns:
bool: 是否应该激活此action
"""
activation_keywords = action_info.get("activation_keywords", [])
case_sensitive = action_info.get("keyword_case_sensitive", False)
if not activation_keywords:
logger.warning(f"{self.log_prefix}动作 {action_name} 设置为关键词触发但未配置关键词")
return False
# 使用构建好的聊天内容作为检索文本
search_text = chat_content +message_content
search_text = chat_content + message_content
# 如果不区分大小写,转换为小写
if not case_sensitive:
search_text = search_text.lower()
# 检查每个关键词
matched_keywords = []
for keyword in activation_keywords:
check_keyword = keyword if case_sensitive else keyword.lower()
if check_keyword in search_text:
matched_keywords.append(keyword)
# print(f"search_text: {search_text}")
# print(f"activation_keywords: {activation_keywords}")
if matched_keywords:
logger.debug(f"{self.log_prefix}动作 {action_name} 匹配到关键词: {matched_keywords}")
return True

View File

@@ -9,7 +9,7 @@ import time
from typing import List, Optional, Tuple, Dict, Any
from src.chat.message_receive.message import MessageRecv, MessageSending, MessageThinking, Seg
from src.chat.message_receive.message import UserInfo
from src.chat.message_receive.chat_stream import ChatStream,chat_manager
from src.chat.message_receive.chat_stream import ChatStream, chat_manager
from src.chat.message_receive.message_sender import message_manager
from src.config.config import global_config
from src.common.logger_manager import get_logger
@@ -37,7 +37,7 @@ class NormalChatExpressor:
self.chat_stream = chat_stream
self.stream_name = chat_manager.get_stream_name(self.chat_stream.stream_id) or self.chat_stream.stream_id
self.log_prefix = f"[{self.stream_name}]Normal表达器"
logger.debug(f"{self.log_prefix} 初始化完成")
async def create_thinking_message(

View File

@@ -25,9 +25,7 @@ class NormalChatGenerator:
request_type="normal.chat_2",
)
self.model_sum = LLMRequest(
model=global_config.model.memory_summary, temperature=0.7, request_type="relation"
)
self.model_sum = LLMRequest(model=global_config.model.memory_summary, temperature=0.7, request_type="relation")
self.current_model_type = "r1" # 默认使用 R1
self.current_model_name = "unknown model"
@@ -68,7 +66,6 @@ class NormalChatGenerator:
enable_planner: bool = False,
available_actions=None,
):
person_id = person_info_manager.get_person_id(
message.chat_stream.user_info.platform, message.chat_stream.user_info.user_id
)
@@ -103,7 +100,6 @@ class NormalChatGenerator:
logger.info(f"{message.processed_plain_text} 的回复:{content}")
except Exception:
logger.exception("生成回复时出错")
return None

View File

@@ -101,7 +101,7 @@ class NormalChatPlanner:
# 获取当前可用的动作使用Normal模式过滤
current_available_actions = self.action_manager.get_using_actions_for_mode(ChatMode.NORMAL)
# 注意:动作的激活判定现在在 normal_chat_action_modifier 中完成
# 这里直接使用经过 action_modifier 处理后的最终动作集
# 符合职责分离原则ActionModifier负责动作管理Planner专注于决策
@@ -110,7 +110,12 @@ class NormalChatPlanner:
if not current_available_actions:
logger.debug(f"{self.log_prefix}规划器: 没有可用动作返回no_action")
return {
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning, "is_parallel": True},
"action_result": {
"action_type": action,
"action_data": action_data,
"reasoning": reasoning,
"is_parallel": True,
},
"chat_context": "",
"action_prompt": "",
}
@@ -121,7 +126,7 @@ class NormalChatPlanner:
timestamp=time.time(),
limit=global_config.focus_chat.observation_context_size,
)
chat_context = build_readable_messages(
message_list_before_now,
replace_bot_name=True,
@@ -130,7 +135,7 @@ class NormalChatPlanner:
read_mark=0.0,
show_actions=True,
)
# 构建planner的prompt
prompt = await self.build_planner_prompt(
self_info_block=self_info,
@@ -141,7 +146,12 @@ class NormalChatPlanner:
if not prompt:
logger.warning(f"{self.log_prefix}规划器: 构建提示词失败")
return {
"action_result": {"action_type": action, "action_data": action_data, "reasoning": reasoning, "is_parallel": False},
"action_result": {
"action_type": action,
"action_data": action_data,
"reasoning": reasoning,
"is_parallel": False,
},
"chat_context": chat_context,
"action_prompt": "",
}
@@ -149,7 +159,7 @@ class NormalChatPlanner:
# 使用LLM生成动作决策
try:
content, (reasoning_content, model_name) = await self.planner_llm.generate_response_async(prompt)
# logger.info(f"{self.log_prefix}规划器原始提示词: {prompt}")
logger.info(f"{self.log_prefix}规划器原始响应: {content}")
logger.info(f"{self.log_prefix}规划器推理: {reasoning_content}")
@@ -201,8 +211,10 @@ class NormalChatPlanner:
if action in current_available_actions:
action_info = current_available_actions[action]
is_parallel = action_info.get("parallel_action", False)
logger.debug(f"{self.log_prefix}规划器决策动作:{action}, 动作信息: '{action_data}', 理由: {reasoning}, 并行执行: {is_parallel}")
logger.debug(
f"{self.log_prefix}规划器决策动作:{action}, 动作信息: '{action_data}', 理由: {reasoning}, 并行执行: {is_parallel}"
)
# 恢复到默认动作集
self.action_manager.restore_actions()
@@ -216,15 +228,15 @@ class NormalChatPlanner:
"action_data": action_data,
"reasoning": reasoning,
"timestamp": time.time(),
"model_name": model_name if 'model_name' in locals() else None
"model_name": model_name if "model_name" in locals() else None,
}
action_result = {
"action_type": action,
"action_data": action_data,
"action_type": action,
"action_data": action_data,
"reasoning": reasoning,
"is_parallel": is_parallel,
"action_record": json.dumps(action_record, ensure_ascii=False)
"action_record": json.dumps(action_record, ensure_ascii=False),
}
plan_result = {
@@ -248,24 +260,19 @@ class NormalChatPlanner:
# 添加特殊的change_to_focus_chat动作
action_options_text += "动作change_to_focus_chat\n"
action_options_text += (
"该动作的描述当聊天变得热烈、自己回复条数很多或需要深入交流时使用正常回复消息并切换到focus_chat模式\n"
)
action_options_text += "该动作的描述当聊天变得热烈、自己回复条数很多或需要深入交流时使用正常回复消息并切换到focus_chat模式\n"
action_options_text += "使用该动作的场景:\n"
action_options_text += "- 聊天上下文中自己的回复条数较多超过3-4条\n"
action_options_text += "- 对话进行得非常热烈活跃\n"
action_options_text += "- 用户表现出深入交流的意图\n"
action_options_text += "- 话题需要更专注和深入的讨论\n\n"
action_options_text += "输出要求:\n"
action_options_text += "{{"
action_options_text += " \"action\": \"change_to_focus_chat\""
action_options_text += ' "action": "change_to_focus_chat"'
action_options_text += "}}\n\n"
for action_name, action_info in current_available_actions.items():
action_description = action_info.get("description", "")
action_parameters = action_info.get("parameters", {})
@@ -276,15 +283,14 @@ class NormalChatPlanner:
print(action_parameters)
for param_name, param_description in action_parameters.items():
param_text += f' "{param_name}":"{param_description}"\n'
param_text = param_text.rstrip('\n')
param_text = param_text.rstrip("\n")
else:
param_text = ""
require_text = ""
for require_item in action_require:
require_text += f"- {require_item}\n"
require_text = require_text.rstrip('\n')
require_text = require_text.rstrip("\n")
# 构建单个动作的提示
action_prompt = await global_prompt_manager.format_prompt(
@@ -316,6 +322,4 @@ class NormalChatPlanner:
return ""
init_prompt()

View File

@@ -214,7 +214,6 @@ class PromptBuilder:
except Exception as e:
logger.error(f"关键词检测与反应时发生异常: {str(e)}", exc_info=True)
moderation_prompt_block = "请不要输出违法违规内容,不要输出色情,暴力,政治相关内容,如有敏感内容,请规避。"
# 构建action描述 (如果启用planner)

View File

@@ -42,9 +42,7 @@ class ClassicalWillingManager(BaseWillingManager):
self.chat_reply_willing[chat_id] = min(current_willing, 3.0)
reply_probability = min(
max((current_willing - 0.5), 0.01) * 2, 1
)
reply_probability = min(max((current_willing - 0.5), 0.01) * 2, 1)
# 检查群组权限(如果是群聊)
if (