style: 格式化代码

This commit is contained in:
John Richard
2025-10-02 19:38:39 +08:00
parent d5627b0661
commit ecb02cae31
111 changed files with 2344 additions and 2316 deletions

View File

@@ -12,7 +12,7 @@ from src.common.data_models.info_data_model import InterestScore
from src.chat.interest_system import bot_interest_manager
from src.common.logger import get_logger
from src.config.config import global_config
from src.plugins.built_in.affinity_flow_chatter.relationship_tracker import ChatterRelationshipTracker
logger = get_logger("chatter_interest_scoring")
# 定义颜色
@@ -45,7 +45,7 @@ class ChatterInterestScoringSystem:
self.probability_boost_per_no_reply = (
affinity_config.no_reply_threshold_adjustment / affinity_config.max_no_reply_count
) # 每次不回复增加的概率
# 用户关系数据
self.user_relationships: Dict[str, float] = {} # user_id -> relationship_score

View File

@@ -65,7 +65,7 @@ class ChatterPlanExecutor:
if not plan.decided_actions:
logger.info("没有需要执行的动作。")
return {"executed_count": 0, "results": []}
# 像hfc一样提前打印将要执行的动作
action_types = [action.action_type for action in plan.decided_actions]
logger.info(f"选择动作: {', '.join(action_types) if action_types else ''}")
@@ -150,17 +150,19 @@ class ChatterPlanExecutor:
for i, action_info in enumerate(unique_actions):
is_last_action = i == total_actions - 1
if total_actions > 1:
logger.info(f"[多重回复] 正在执行第 {i+1}/{total_actions} 个回复...")
logger.info(f"[多重回复] 正在执行第 {i + 1}/{total_actions} 个回复...")
# 传递 clear_unread 参数
result = await self._execute_single_reply_action(action_info, plan, clear_unread=is_last_action)
results.append(result)
if total_actions > 1:
logger.info(f"[多重回复] 所有回复任务执行完毕。")
logger.info("[多重回复] 所有回复任务执行完毕。")
return {"results": results}
async def _execute_single_reply_action(self, action_info: ActionPlannerInfo, plan: Plan, clear_unread: bool = True) -> Dict[str, any]:
async def _execute_single_reply_action(
self, action_info: ActionPlannerInfo, plan: Plan, clear_unread: bool = True
) -> Dict[str, any]:
"""执行单个回复动作"""
start_time = time.time()
success = False
@@ -201,7 +203,7 @@ class ChatterPlanExecutor:
execution_result = await self.action_manager.execute_action(
action_name=action_info.action_type, **action_params
)
# 从返回结果中提取真正的回复文本
if isinstance(execution_result, dict):
reply_content = execution_result.get("reply_text", "")
@@ -233,7 +235,9 @@ class ChatterPlanExecutor:
"error_message": error_message,
"execution_time": execution_time,
"reasoning": action_info.reasoning,
"reply_content": reply_content[:200] + "..." if reply_content and len(reply_content) > 200 else reply_content,
"reply_content": reply_content[:200] + "..."
if reply_content and len(reply_content) > 200
else reply_content,
}
async def _execute_other_actions(self, other_actions: List[ActionPlannerInfo], plan: Plan) -> Dict[str, any]:

View File

@@ -100,7 +100,7 @@ class ChatterPlanFilter:
# 预解析 action_type 来进行判断
thinking = item.get("thinking", "未提供思考过程")
actions_obj = item.get("actions", {})
# 处理actions字段可能是字典或列表的情况
if isinstance(actions_obj, dict):
action_type = actions_obj.get("action_type", "no_action")
@@ -116,14 +116,12 @@ class ChatterPlanFilter:
if action_type in reply_action_types:
if not reply_action_added:
final_actions.extend(
await self._parse_single_action(item, used_message_id_list, plan)
)
final_actions.extend(await self._parse_single_action(item, used_message_id_list, plan))
reply_action_added = True
else:
# 非回复类动作直接添加
final_actions.extend(await self._parse_single_action(item, used_message_id_list, plan))
if thinking and thinking != "未提供思考过程":
logger.info(f"\n{SAKURA_PINK}思考: {thinking}{RESET_COLOR}\n")
plan.decided_actions = self._filter_no_actions(final_actions)
@@ -154,6 +152,7 @@ class ChatterPlanFilter:
schedule_block = ""
# 优先检查是否被吵醒
from src.chat.message_manager.message_manager import message_manager
angry_prompt_addition = ""
wakeup_mgr = message_manager.wakeup_manager
@@ -161,7 +160,7 @@ class ChatterPlanFilter:
# 检查1: 直接从 wakeup_manager 获取
if wakeup_mgr.is_in_angry_state():
angry_prompt_addition = wakeup_mgr.get_angry_prompt_addition()
# 检查2: 如果上面没获取到,再从 mood_manager 确认
if not angry_prompt_addition:
chat_mood_for_check = mood_manager.get_mood_by_chat_id(plan.chat_id)
@@ -274,7 +273,9 @@ class ChatterPlanFilter:
is_group_chat = plan.chat_type == ChatType.GROUP
chat_context_description = "你现在正在一个群聊中"
if not is_group_chat and plan.target_info:
chat_target_name = plan.target_info.get("person_name") or plan.target_info.get("user_nickname") or "对方"
chat_target_name = (
plan.target_info.get("person_name") or plan.target_info.get("user_nickname") or "对方"
)
chat_context_description = f"你正在和 {chat_target_name} 私聊"
action_options_block = await self._build_action_options(plan.available_actions)
@@ -315,12 +316,12 @@ class ChatterPlanFilter:
"""构建已读/未读历史消息块"""
try:
# 从message_manager获取真实的已读/未读消息
from src.chat.message_manager.message_manager import message_manager
from src.chat.utils.utils import assign_message_ids
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat
# 获取聊天流的上下文
from src.plugin_system.apis.chat_api import get_chat_manager
chat_manager = get_chat_manager()
chat_stream = chat_manager.get_stream(plan.chat_id)
if not chat_stream:
@@ -333,6 +334,7 @@ class ChatterPlanFilter:
read_messages = stream_context.context.history_messages # 已读消息存储在history_messages中
if not read_messages:
from src.common.data_models.database_data_model import DatabaseMessages
# 如果内存中没有已读消息(比如刚启动),则从数据库加载最近的上下文
fallback_messages_dicts = await get_raw_msg_before_timestamp_with_chat(
chat_id=plan.chat_id,
@@ -414,7 +416,7 @@ class ChatterPlanFilter:
processed_plain_text=msg_dict.get("processed_plain_text", ""),
key_words=msg_dict.get("key_words", "[]"),
is_mentioned=msg_dict.get("is_mentioned", False),
**{"user_info": user_info_dict} # 通过kwargs传入user_info
**{"user_info": user_info_dict}, # 通过kwargs传入user_info
)
else:
# 如果没有user_info字段使用平铺的字段flatten()方法返回的格式)
@@ -425,13 +427,12 @@ class ChatterPlanFilter:
user_platform=msg_dict.get("user_platform", ""),
processed_plain_text=msg_dict.get("processed_plain_text", ""),
key_words=msg_dict.get("key_words", "[]"),
is_mentioned=msg_dict.get("is_mentioned", False)
is_mentioned=msg_dict.get("is_mentioned", False),
)
# 计算消息兴趣度
interest_score_obj = await chatter_interest_scoring_system._calculate_single_message_score(
message=db_message,
bot_nickname=global_config.bot.nickname
message=db_message, bot_nickname=global_config.bot.nickname
)
interest_score = interest_score_obj.total_score
@@ -454,7 +455,7 @@ class ChatterPlanFilter:
try:
# 从新的actions结构中获取动作信息
actions_obj = action_json.get("actions", {})
# 处理actions字段可能是字典或列表的情况
actions_to_process = []
if isinstance(actions_obj, dict):
@@ -463,19 +464,23 @@ class ChatterPlanFilter:
actions_to_process.extend(actions_obj)
if not actions_to_process:
actions_to_process.append({"action_type": "no_action", "reason": "actions格式错误"})
actions_to_process.append({"action_type": "no_action", "reason": "actions格式错误"})
for single_action_obj in actions_to_process:
if not isinstance(single_action_obj, dict):
continue
action = single_action_obj.get("action_type", "no_action")
reasoning = single_action_obj.get("reasoning", "未提供原因") # 兼容旧的reason字段
reasoning = single_action_obj.get("reasoning", "未提供原因") # 兼容旧的reason字段
action_data = single_action_obj.get("action_data", {})
# 为了向后兼容如果action_data不存在则从顶层字段获取
if not action_data:
action_data = {k: v for k, v in single_action_obj.items() if k not in ["action_type", "reason", "reasoning", "thinking"]}
action_data = {
k: v
for k, v in single_action_obj.items()
if k not in ["action_type", "reason", "reasoning", "thinking"]
}
# 保留原始的thinking字段如果有
thinking = action_json.get("thinking", "")
@@ -501,7 +506,9 @@ class ChatterPlanFilter:
# reply动作必须有目标消息使用最新消息作为兜底
target_message_dict = self._get_latest_message(message_id_list)
if target_message_dict:
logger.info(f"[{action}] 使用最新消息作为目标: {target_message_dict.get('message_id')}")
logger.info(
f"[{action}] 使用最新消息作为目标: {target_message_dict.get('message_id')}"
)
else:
logger.error(f"[{action}] 无法找到任何目标消息降级为no_action")
action = "no_action"
@@ -509,15 +516,21 @@ class ChatterPlanFilter:
elif action in ["poke_user", "set_emoji_like"]:
# 这些动作可以尝试其他策略
target_message_dict = self._find_poke_notice(message_id_list) or self._get_latest_message(message_id_list)
target_message_dict = self._find_poke_notice(
message_id_list
) or self._get_latest_message(message_id_list)
if target_message_dict:
logger.info(f"[{action}] 使用替代消息作为目标: {target_message_dict.get('message_id')}")
logger.info(
f"[{action}] 使用替代消息作为目标: {target_message_dict.get('message_id')}"
)
else:
# 其他动作使用最新消息或跳过
target_message_dict = self._get_latest_message(message_id_list)
if target_message_dict:
logger.info(f"[{action}] 使用最新消息作为目标: {target_message_dict.get('message_id')}")
logger.info(
f"[{action}] 使用最新消息作为目标: {target_message_dict.get('message_id')}"
)
else:
# 如果LLM没有指定target_message_id进行特殊处理
if action == "poke_user":
@@ -614,7 +627,7 @@ class ChatterPlanFilter:
query_text=query,
user_id="system", # 系统查询
scope_id="system",
limit=5
limit=5,
)
if not enhanced_memories:
@@ -627,7 +640,9 @@ class ChatterPlanFilter:
memory_type = memory_chunk.memory_type.value if memory_chunk.memory_type else "unknown"
retrieved_memories.append((memory_type, content))
memory_statements = [f"关于'{topic}', 你记得'{memory_item}'" for topic, memory_item in retrieved_memories]
memory_statements = [
f"关于'{topic}', 你记得'{memory_item}'" for topic, memory_item in retrieved_memories
]
except Exception as e:
logger.warning(f"增强记忆系统检索失败,使用默认回复: {e}")
@@ -648,12 +663,17 @@ class ChatterPlanFilter:
if action_name == "set_emoji_like" and p_name == "emoji":
# 特殊处理set_emoji_like的emoji参数
from src.plugins.built_in.social_toolkit_plugin.qq_emoji_list import qq_face
emoji_options = [re.search(r"\[表情:(.+?)\]", name).group(1) for name in qq_face.values() if re.search(r"\[表情:(.+?)\]", name)]
emoji_options = [
re.search(r"\[表情:(.+?)\]", name).group(1)
for name in qq_face.values()
if re.search(r"\[表情:(.+?)\]", name)
]
example_value = f"<从'{', '.join(emoji_options[:10])}...'中选择一个>"
else:
example_value = f"<{p_desc}>"
params_json_list.append(f' "{p_name}": "{example_value}"')
# 基础动作信息
action_description = action_info.description
action_require = "\n".join(f"- {req}" for req in action_info.action_require)
@@ -666,11 +686,11 @@ class ChatterPlanFilter:
# 将参数列表合并到JSON示例中
if params_json_list:
# 移除最后一行的逗号
json_example_lines.extend([line.rstrip(',') for line in params_json_list])
json_example_lines.extend([line.rstrip(",") for line in params_json_list])
json_example_lines.append(' "reason": "<执行该动作的详细原因>"')
json_example_lines.append(" }")
# 使用逗号连接内部元素,除了最后一个
json_parts = []
for i, line in enumerate(json_example_lines):
@@ -678,14 +698,14 @@ class ChatterPlanFilter:
if line.strip() in ["{", "}"]:
json_parts.append(line)
continue
# 检查是否是最后一个需要逗号的元素
is_last_item = True
for next_line in json_example_lines[i+1:]:
for next_line in json_example_lines[i + 1 :]:
if next_line.strip() not in ["}"]:
is_last_item = False
break
if not is_last_item:
json_parts.append(f"{line},")
else:
@@ -713,7 +733,7 @@ class ChatterPlanFilter:
# 1. 标准化处理:去除可能的格式干扰
original_id = str(message_id).strip()
normalized_id = original_id.strip('<>"\'').strip()
normalized_id = original_id.strip("<>\"'").strip()
if not normalized_id:
return None
@@ -731,12 +751,13 @@ class ChatterPlanFilter:
# 处理包含在文本中的ID格式 (如 "消息m123" -> 提取 m123)
import re
# 尝试提取各种格式的ID
id_patterns = [
r'm\d+', # m123格式
r'\d+', # 纯数字格式
r'buffered-[a-f0-9-]+', # buffered-xxxx格式
r'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}', # UUID格式
r"m\d+", # m123格式
r"\d+", # 纯数字格式
r"buffered-[a-f0-9-]+", # buffered-xxxx格式
r"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}", # UUID格式
]
for pattern in id_patterns:
@@ -771,12 +792,12 @@ class ChatterPlanFilter:
# 4. 尝试模糊匹配(数字部分匹配)
for candidate in candidate_ids:
# 提取数字部分进行模糊匹配
number_part = re.sub(r'[^0-9]', '', candidate)
number_part = re.sub(r"[^0-9]", "", candidate)
if number_part:
for item in message_id_list:
if isinstance(item, dict):
item_id = item.get("id", "")
item_number = re.sub(r'[^0-9]', '', item_id)
item_number = re.sub(r"[^0-9]", "", item_id)
# 数字部分匹配
if item_number == number_part:
@@ -787,7 +808,7 @@ class ChatterPlanFilter:
message_obj = item.get("message")
if isinstance(message_obj, dict):
orig_mid = message_obj.get("message_id") or message_obj.get("id")
orig_number = re.sub(r'[^0-9]', '', str(orig_mid)) if orig_mid else ""
orig_number = re.sub(r"[^0-9]", "", str(orig_mid)) if orig_mid else ""
if orig_number == number_part:
logger.debug(f"模糊匹配成功(消息对象): {candidate} -> {orig_mid}")
return message_obj

View File

@@ -4,7 +4,6 @@
"""
from dataclasses import asdict
import time
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
from src.plugins.built_in.affinity_flow_chatter.plan_executor import ChatterPlanExecutor
@@ -90,15 +89,16 @@ class ChatterActionPlanner:
try:
# 在规划前,先进行动作修改
from src.chat.planner_actions.action_modifier import ActionModifier
action_modifier = ActionModifier(self.action_manager, self.chat_id)
await action_modifier.modify_actions()
# 1. 生成初始 Plan
initial_plan = await self.generator.generate(context.chat_mode)
# 确保Plan中包含所有当前可用的动作
initial_plan.available_actions = self.action_manager.get_using_actions()
unread_messages = context.get_unread_messages() if context else []
# 2. 使用新的兴趣度管理系统进行评分
score = 0.0
@@ -117,7 +117,9 @@ class ChatterActionPlanner:
message_interest = interest_score.total_score
message.interest_value = message_interest
message.should_reply = message_interest > global_config.affinity_flow.non_reply_action_interest_threshold
message.should_reply = (
message_interest > global_config.affinity_flow.non_reply_action_interest_threshold
)
interest_updates.append(
{

View File

@@ -596,7 +596,7 @@ class ChatterRelationshipTracker:
quality = response_data.get("interaction_quality", "medium")
# 更新数据库
await self._update_user_relationship_in_db(user_id, new_text, new_score)
await self._update_user_relationship_in_db(user_id, new_text, new_score)
# 更新缓存
self.user_relationship_cache[user_id] = {

View File

@@ -268,7 +268,7 @@ class EmojiAction(BaseAction):
if not success:
logger.error(f"{self.log_prefix} 表情包发送失败")
await self.store_action_info(
action_build_into_prompt=True, action_prompt_display=f"发送了一个表情包,但失败了", action_done=False
action_build_into_prompt=True, action_prompt_display="发送了一个表情包,但失败了", action_done=False
)
return False, "表情包发送失败"
@@ -279,7 +279,7 @@ class EmojiAction(BaseAction):
logger.error(f"{self.log_prefix} 添加表情到历史记录时出错: {e}")
await self.store_action_info(
action_build_into_prompt=True, action_prompt_display=f"发送了一个表情包", action_done=True
action_build_into_prompt=True, action_prompt_display="发送了一个表情包", action_done=True
)
return True, f"发送表情包: {emoji_description}"

View File

@@ -47,9 +47,9 @@ class SearchKnowledgeFromLPMMTool(BaseTool):
knowledge_parts = []
for i, item in enumerate(knowledge_info["knowledge_items"]):
knowledge_parts.append(f"- {item.get('content', 'N/A')}")
knowledge_text = "\n".join(knowledge_parts)
summary = knowledge_info.get('summary', '无总结')
summary = knowledge_info.get("summary", "无总结")
content = f"关于 '{query}', 你知道以下信息:\n{knowledge_text}\n\n总结: {summary}"
else:
content = f"关于 '{query}',你的知识库里好像没有相关的信息呢"

View File

@@ -87,6 +87,7 @@ class MaiZoneRefactoredPlugin(BasePlugin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
async def on_plugin_loaded(self):
"""插件加载完成后的回调,初始化服务并启动后台任务"""
# --- 注册权限节点 ---

View File

@@ -140,5 +140,7 @@ class CookieService:
self._save_cookies_to_file(qq_account, cookies)
return cookies
logger.error(f"{qq_account} 获取Cookie的所有方法均失败。请确保Napcat HTTP服务或Adapter连接至少有一个正常工作或存在有效的本地Cookie文件。")
logger.error(
f"{qq_account} 获取Cookie的所有方法均失败。请确保Napcat HTTP服务或Adapter连接至少有一个正常工作或存在有效的本地Cookie文件。"
)
return None

View File

@@ -290,9 +290,7 @@ class QZoneService:
comment_content = comment.get("content", "")
try:
reply_content = await self.content_service.generate_comment_reply(
content, comment_content, nickname
)
reply_content = await self.content_service.generate_comment_reply(content, comment_content, nickname)
if reply_content:
success = await api_client["reply"](fid, qq_account, nickname, reply_content, comment_tid)
if success:
@@ -532,7 +530,9 @@ class QZoneService:
async def _get_api_client(self, qq_account: str, stream_id: Optional[str]) -> Optional[Dict]:
cookies = await self.cookie_service.get_cookies(qq_account, stream_id)
if not cookies:
logger.error("获取API客户端失败未能获取到Cookie。请检查Napcat连接是否正常或是否存在有效的本地Cookie文件。")
logger.error(
"获取API客户端失败未能获取到Cookie。请检查Napcat连接是否正常或是否存在有效的本地Cookie文件。"
)
return None
p_skey = cookies.get("p_skey") or cookies.get("p_skey".upper())
@@ -726,7 +726,8 @@ class QZoneService:
return {"pic_bo": picbo, "richval": richval}
except Exception as e:
logger.error(
f"从上传结果中提取图片参数失败: {e}, 上传结果: {upload_result}", exc_info=True
f"从上传结果中提取图片参数失败: {e}, 上传结果: {upload_result}",
exc_info=True,
)
return None
else:
@@ -764,7 +765,9 @@ class QZoneService:
json_data = orjson.loads(res_text)
if json_data.get("code") != 0:
logger.warning(f"获取说说列表API返回错误: code={json_data.get('code')}, message={json_data.get('message')}")
logger.warning(
f"获取说说列表API返回错误: code={json_data.get('code')}, message={json_data.get('message')}"
)
return []
feeds_list = []
@@ -797,7 +800,7 @@ class QZoneService:
for c in commentlist:
if not isinstance(c, dict):
continue
# 添加主评论
comments.append(
{
@@ -822,7 +825,7 @@ class QZoneService:
"parent_tid": c.get("tid"), # 父ID是主评论的ID
}
)
feeds_list.append(
{
"tid": msg.get("tid", ""),

View File

@@ -835,7 +835,7 @@ class MessageHandler:
if music:
tag = music.get("tag", "未知来源")
logger.debug(f"检测到【{tag}】音乐分享消息 (music view),开始提取信息")
title = music.get("title", "未知歌曲")
desc = music.get("desc", "未知艺术家")
jump_url = music.get("jumpUrl", "")
@@ -853,7 +853,7 @@ class MessageHandler:
artist = parts[1]
else:
artist = desc
formatted_content = (
f"这是一张来自【{tag}】的音乐分享卡片:\n"
f"歌曲: {song_title}\n"
@@ -870,12 +870,12 @@ class MessageHandler:
if news and "网易云音乐" in news.get("tag", ""):
tag = news.get("tag")
logger.debug(f"检测到【{tag}】音乐分享消息 (news view),开始提取信息")
title = news.get("title", "未知歌曲")
desc = news.get("desc", "未知艺术家")
jump_url = news.get("jumpUrl", "")
preview_url = news.get("preview", "")
formatted_content = (
f"这是一张来自【{tag}】的音乐分享卡片:\n"
f"标题: {title}\n"

View File

@@ -3,7 +3,6 @@ import time
import random
import websockets as Server
import uuid
import asyncio
from maim_message import (
UserInfo,
GroupInfo,
@@ -96,7 +95,9 @@ class SendHandler:
logger.error("无法识别的消息类型")
return
logger.info("尝试发送到napcat")
logger.debug(f"准备发送到napcat的消息体: action='{action}', {id_name}='{target_id}', message='{processed_message}'")
logger.debug(
f"准备发送到napcat的消息体: action='{action}', {id_name}='{target_id}', message='{processed_message}'"
)
response = await self.send_message_to_napcat(
action,
{

View File

@@ -6,7 +6,6 @@ import urllib3
import ssl
import io
import asyncio
import time
from asyncio import Lock
@@ -75,7 +74,7 @@ async def get_group_info(websocket: Server.ServerConnection, group_id: int) -> d
except Exception as e:
logger.error(f"获取群信息失败: {e}")
return None
data = socket_response.get("data")
if data:
await set_to_cache(cache_key, data)
@@ -114,7 +113,7 @@ async def get_member_info(websocket: Server.ServerConnection, group_id: int, use
cached_data = await get_from_cache(cache_key)
if cached_data:
return cached_data
logger.debug(f"获取群成员信息中 (无缓存): group={group_id}, user={user_id}")
request_uuid = str(uuid.uuid4())
payload = json.dumps(
@@ -133,7 +132,7 @@ async def get_member_info(websocket: Server.ServerConnection, group_id: int, use
except Exception as e:
logger.error(f"获取成员信息失败: {e}")
return None
data = socket_response.get("data")
if data:
await set_to_cache(cache_key, data)
@@ -203,7 +202,7 @@ async def get_self_info(websocket: Server.ServerConnection) -> dict | None:
except Exception as e:
logger.error(f"获取自身信息失败: {e}")
return None
data = response.get("data")
if data:
await set_to_cache(cache_key, data)

View File

@@ -260,7 +260,6 @@ class ManagementCommand(PlusCommand):
except Exception as e:
await self.send_text(f"❌ 强制重载过程中发生错误: {str(e)}")
async def _add_dir(self, dir_path: str):
"""添加插件目录"""
await self.send_text(f"📁 正在添加插件目录: `{dir_path}`")
@@ -501,13 +500,13 @@ class PluginManagementPlugin(BasePlugin):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# 注册权限节点
async def on_plugin_loaded(self):
await permission_api.register_permission_node(
"plugin.management.admin",
"插件管理:可以管理插件和组件的加载、卸载、启用、禁用等操作",
"plugin_management",
False,
"plugin.management.admin",
"插件管理:可以管理插件和组件的加载、卸载、启用、禁用等操作",
"plugin_management",
False,
)
def get_plugin_components(self) -> List[Tuple[PlusCommandInfo, Type[PlusCommand]]]:

View File

@@ -1,27 +1,23 @@
from typing import List, Tuple, Union, Type, Optional
from typing import List, Tuple, Type
from src.common.logger import get_logger
from src.config.official_configs import AffinityFlowConfig
from src.plugin_system.base.base_plugin import BasePlugin
from src.plugin_system import (
BasePlugin,
ConfigField,
register_plugin,
plugin_manage_api,
component_manage_api,
ComponentInfo,
ComponentType,
EventHandlerInfo,
EventType,
BaseEventHandler,
)
from .proacive_thinker_event import ProactiveThinkerEventHandler
logger = get_logger(__name__)
@register_plugin
class ProactiveThinkerPlugin(BasePlugin):
"""一个主动思考的插件,但现在还只是个空壳子"""
plugin_name: str = "proactive_thinker"
enable_plugin: bool = False
dependencies: list[str] = []
@@ -33,6 +29,7 @@ class ProactiveThinkerPlugin(BasePlugin):
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
},
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -42,4 +39,3 @@ class ProactiveThinkerPlugin(BasePlugin):
(ProactiveThinkerEventHandler.get_handler_info(), ProactiveThinkerEventHandler)
]
return components

View File

@@ -2,7 +2,7 @@ import asyncio
import random
import time
from datetime import datetime
from typing import List, Union, Type, Optional
from typing import List, Union
from maim_message import UserInfo
@@ -69,7 +69,7 @@ class ColdStartTask(AsyncTask):
# 创建 UserInfo 对象,这是创建聊天流的必要信息
user_info = UserInfo(platform=platform, user_id=str(user_id), user_nickname=user_nickname)
# 【关键步骤】主动创建聊天流。
# 创建后,该用户就进入了机器人的“好友列表”,后续将由 ProactiveThinkingTask 接管
stream = await self.chat_manager.get_or_create_stream(platform, user_info)
@@ -175,10 +175,12 @@ class ProactiveThinkingTask(AsyncTask):
# 2. 【核心逻辑】检查聊天冷却时间是否足够长
time_since_last_active = time.time() - stream.last_active_time
if time_since_last_active > next_interval:
logger.info(f"【日常唤醒】聊天流 {stream.stream_id} 已冷却 {time_since_last_active:.2f} 秒,触发主动对话。")
logger.info(
f"【日常唤醒】聊天流 {stream.stream_id} 已冷却 {time_since_last_active:.2f} 秒,触发主动对话。"
)
await self.executor.execute(stream_id=stream.stream_id, start_mode="wake_up")
# 【关键步骤】在触发后,立刻更新活跃时间并保存。
# 这可以防止在同一个检查周期内,对同一个目标因为意外的延迟而发送多条消息。
stream.update_active_time()

View File

@@ -3,7 +3,16 @@ from typing import Optional, Dict, Any
from datetime import datetime
from src.common.logger import get_logger
from src.plugin_system.apis import chat_api, person_api, schedule_api, send_api, llm_api, message_api, generator_api, database_api
from src.plugin_system.apis import (
chat_api,
person_api,
schedule_api,
send_api,
llm_api,
message_api,
generator_api,
database_api,
)
from src.config.config import global_config, model_config
from src.person_info.person_info import get_person_info_manager
@@ -39,17 +48,16 @@ class ProactiveThinkerExecutor:
# 2. 决策阶段
decision_result = await self._make_decision(context, start_mode)
if not decision_result or not decision_result.get("should_reply"):
reason = decision_result.get("reason", "未提供") if decision_result else "决策过程返回None"
logger.info(f"决策结果为:不回复。原因: {reason}")
await database_api.store_action_info(
chat_stream=self._get_stream_from_id(stream_id),
action_name="proactive_decision",
action_prompt_display=f"主动思考决定不回复,原因: {reason}",
action_done = True,
action_data=decision_result
)
chat_stream=self._get_stream_from_id(stream_id),
action_name="proactive_decision",
action_prompt_display=f"主动思考决定不回复,原因: {reason}",
action_done=True,
action_data=decision_result,
)
return
# 3. 规划与执行阶段
@@ -59,15 +67,17 @@ class ProactiveThinkerExecutor:
chat_stream=self._get_stream_from_id(stream_id),
action_name="proactive_decision",
action_prompt_display=f"主动思考决定回复,原因: {reason},话题:{topic}",
action_done = True,
action_data=decision_result
action_done=True,
action_data=decision_result,
)
logger.info(f"决策结果为:回复。话题: {topic}")
plan_prompt = self._build_plan_prompt(context, start_mode, topic, reason)
is_success, response, _, _ = await llm_api.generate_with_model(prompt=plan_prompt, model_config=model_config.model_task_config.utils)
is_success, response, _, _ = await llm_api.generate_with_model(
prompt=plan_prompt, model_config=model_config.model_task_config.utils
)
if is_success and response:
stream = self._get_stream_from_id(stream_id)
if stream:
@@ -104,33 +114,41 @@ class ProactiveThinkerExecutor:
if not user_info or not user_info.platform or not user_info.user_id:
logger.warning(f"Stream {stream_id} 的 user_info 不完整")
return None
person_id = person_api.get_person_id(user_info.platform, int(user_info.user_id))
person_info_manager = get_person_info_manager()
# 获取日程
schedules = await schedule_api.ScheduleAPI.get_today_schedule()
schedule_context = "\n".join([f"- {s['title']} ({s['start_time']}-{s['end_time']})" for s in schedules]) if schedules else "今天没有日程安排。"
schedule_context = (
"\n".join([f"- {s['title']} ({s['start_time']}-{s['end_time']})" for s in schedules])
if schedules
else "今天没有日程安排。"
)
# 获取关系信息
short_impression = await person_info_manager.get_value(person_id, "short_impression") or ""
impression = await person_info_manager.get_value(person_id, "impression") or ""
attitude = await person_info_manager.get_value(person_id, "attitude") or 50
# 获取最近聊天记录
recent_messages = await message_api.get_recent_messages(stream_id, limit=10)
recent_chat_history = await message_api.build_readable_messages_to_str(recent_messages) if recent_messages else ""
recent_chat_history = (
await message_api.build_readable_messages_to_str(recent_messages) if recent_messages else ""
)
# 获取最近的动作历史
action_history = await database_api.db_query(
database_api.MODEL_MAPPING["ActionRecords"],
filters={"chat_id": stream_id, "action_name": "proactive_decision"},
limit=3,
order_by=["-time"]
order_by=["-time"],
)
action_history_context = ""
if isinstance(action_history, list):
action_history_context = "\n".join([f"- {a['action_data']}" for a in action_history if isinstance(a, dict)]) or ""
action_history_context = (
"\n".join([f"- {a['action_data']}" for a in action_history if isinstance(a, dict)]) or ""
)
return {
"person_id": person_id,
@@ -138,47 +156,43 @@ class ProactiveThinkerExecutor:
"schedule_context": schedule_context,
"recent_chat_history": recent_chat_history,
"action_history_context": action_history_context,
"relationship": {
"short_impression": short_impression,
"impression": impression,
"attitude": attitude
},
"relationship": {"short_impression": short_impression, "impression": impression, "attitude": attitude},
"persona": {
"core": global_config.personality.personality_core,
"side": global_config.personality.personality_side,
"identity": global_config.personality.identity,
},
"current_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
"current_time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
}
async def _make_decision(self, context: Dict[str, Any], start_mode: str) -> Optional[Dict[str, Any]]:
"""
决策模块:判断是否应该主动发起对话,以及聊什么话题
"""
persona = context['persona']
user_info = context['user_info']
relationship = context['relationship']
persona = context["persona"]
user_info = context["user_info"]
relationship = context["relationship"]
prompt = f"""
# 角色
你的名字是{global_config.bot.nickname},你的人设如下:
- 核心人设: {persona['core']}
- 侧面人设: {persona['side']}
- 身份: {persona['identity']}
- 核心人设: {persona["core"]}
- 侧面人设: {persona["side"]}
- 身份: {persona["identity"]}
# 任务
现在是 {context['current_time']},你需要根据当前的情境,决定是否要主动向用户 '{user_info.user_nickname}' 发起对话。
现在是 {context["current_time"]},你需要根据当前的情境,决定是否要主动向用户 '{user_info.user_nickname}' 发起对话。
# 情境分析
1. **启动模式**: {start_mode} ({'初次见面/很久未见' if start_mode == 'cold_start' else '日常唤醒'})
1. **启动模式**: {start_mode} ({"初次见面/很久未见" if start_mode == "cold_start" else "日常唤醒"})
2. **你的日程**:
{context['schedule_context']}
{context["schedule_context"]}
3. **你和Ta的关系**:
- 简短印象: {relationship['short_impression']}
- 详细印象: {relationship['impression']}
- 好感度: {relationship['attitude']}/100
- 简短印象: {relationship["short_impression"]}
- 详细印象: {relationship["impression"]}
- 好感度: {relationship["attitude"]}/100
4. **最近的聊天摘要**:
{context['recent_chat_history']}
{context["recent_chat_history"]}
# 决策指令
请综合以上所有信息做出决策。你的决策需要以JSON格式输出包含以下字段
@@ -204,9 +218,11 @@ class ProactiveThinkerExecutor:
请输出你的决策:
"""
is_success, response, _, _ = await llm_api.generate_with_model(prompt=prompt, model_config=model_config.model_task_config.utils)
is_success, response, _, _ = await llm_api.generate_with_model(
prompt=prompt, model_config=model_config.model_task_config.utils
)
if not is_success:
return {"should_reply": False, "reason": "决策模型生成失败"}
@@ -222,17 +238,17 @@ class ProactiveThinkerExecutor:
"""
根据启动模式和决策话题,构建最终的规划提示词
"""
persona = context['persona']
user_info = context['user_info']
relationship = context['relationship']
persona = context["persona"]
user_info = context["user_info"]
relationship = context["relationship"]
if start_mode == "cold_start":
prompt = f"""
# 角色
你的名字是{global_config.bot.nickname},你的人设如下:
- 核心人设: {persona['core']}
- 侧面人设: {persona['side']}
- 身份: {persona['identity']}
- 核心人设: {persona["core"]}
- 侧面人设: {persona["side"]}
- 身份: {persona["identity"]}
# 任务
你需要主动向一个新朋友 '{user_info.user_nickname}' 发起对话。这是你们的第一次交流,或者很久没聊了。
@@ -240,9 +256,9 @@ class ProactiveThinkerExecutor:
# 决策上下文
- **决策理由**: {reason}
- **你和Ta的关系**:
- 简短印象: {relationship['short_impression']}
- 详细印象: {relationship['impression']}
- 好感度: {relationship['attitude']}/100
- 简短印象: {relationship["short_impression"]}
- 详细印象: {relationship["impression"]}
- 好感度: {relationship["attitude"]}/100
# 对话指引
- 你的目标是“破冰”,让对话自然地开始。
@@ -254,26 +270,26 @@ class ProactiveThinkerExecutor:
prompt = f"""
# 角色
你的名字是{global_config.bot.nickname},你的人设如下:
- 核心人设: {persona['core']}
- 侧面人设: {persona['side']}
- 身份: {persona['identity']}
- 核心人设: {persona["core"]}
- 侧面人设: {persona["side"]}
- 身份: {persona["identity"]}
# 任务
现在是 {context['current_time']},你需要主动向你的朋友 '{user_info.user_nickname}' 发起对话。
现在是 {context["current_time"]},你需要主动向你的朋友 '{user_info.user_nickname}' 发起对话。
# 决策上下文
- **决策理由**: {reason}
# 情境分析
1. **你的日程**:
{context['schedule_context']}
{context["schedule_context"]}
2. **你和Ta的关系**:
- 详细印象: {relationship['impression']}
- 好感度: {relationship['attitude']}/100
- 详细印象: {relationship["impression"]}
- 好感度: {relationship["attitude"]}/100
3. **最近的聊天摘要**:
{context['recent_chat_history']}
{context["recent_chat_history"]}
4. **你最近的相关动作**:
{context['action_history_context']}
{context["action_history_context"]}
# 对话指引
- 你决定和Ta聊聊关于“{topic}”的话题。

View File

@@ -16,8 +16,6 @@ from src.person_info.person_info import get_person_info_manager
from dateutil.parser import parse as parse_datetime
from src.manager.async_task_manager import AsyncTask, async_task_manager
from src.plugin_system.apis import send_api, llm_api, generator_api
from src.plugin_system.base.component_types import ComponentType
from typing import Optional
from src.chat.message_receive.chat_stream import ChatStream
import asyncio
import datetime