feat(notice): 实现全局notice消息管理系统

添加全局notice管理器,将notice消息与普通消息分离处理。主要功能包括:

- 创建 GlobalNoticeManager 单例类,支持公共和特定聊天流作用域
- 在 message_manager 中集成notice检测和处理逻辑
- 扩展数据库模型和消息类,添加notice相关字段
- 在提示词生成器中添加notice信息块展示
- 配置系统支持notice相关参数设置
- 适配器插件增强notice类型识别和配置

notice消息特点:
- 默认不触发聊天流程,只记录到全局管理器
- 可在提示词中展示最近的系统通知
- 支持按类型设置不同的生存时间
- 支持公共notice(所有聊天可见)和流特定notice

BREAKING CHANGE: 数据库消息表结构变更,需要添加 is_public_notice 和 notice_type 字段
This commit is contained in:
Windpicker-owo
2025-10-19 22:45:19 +08:00
parent 803e54b920
commit 1eda54cb8f
15 changed files with 915 additions and 10 deletions

View File

@@ -95,6 +95,8 @@ def init_prompt():
### 📬 未读历史消息(动作执行对象)
{unread_history_prompt}
{notice_block}
## 表达方式
- *你需要参考你的回复风格:*
{reply_style}
@@ -177,6 +179,8 @@ If you need to use the search tool, please directly call the function "lpmm_sear
{relation_info_block}
{extra_info_block}
{notice_block}
{cross_context_block}
{identity}
如果有人说你是人机,你可以用一种阴阳怪气的口吻来回应
@@ -792,6 +796,55 @@ class DefaultReplyer:
return keywords_reaction_prompt
async def build_notice_block(self, chat_id: str) -> str:
"""构建notice信息块
使用全局notice管理器获取notice消息并格式化展示
Args:
chat_id: 聊天ID即stream_id
Returns:
str: 格式化的notice信息文本如果没有notice或未启用则返回空字符串
"""
try:
logger.debug(f"开始构建notice块chat_id={chat_id}")
# 检查是否启用notice in prompt
if not hasattr(global_config, 'notice'):
logger.debug("notice配置不存在")
return ""
if not global_config.notice.notice_in_prompt:
logger.debug("notice_in_prompt配置未启用")
return ""
# 使用全局notice管理器获取notice文本
from src.chat.message_manager.message_manager import message_manager
limit = getattr(global_config.notice, 'notice_prompt_limit', 5)
logger.debug(f"获取notice文本limit={limit}")
notice_text = message_manager.get_notice_text(chat_id, limit)
if notice_text and notice_text.strip():
# 添加标题和格式化
notice_lines = []
notice_lines.append("## 📢 最近的系统通知")
notice_lines.append("")
notice_lines.append(notice_text)
notice_lines.append("")
result = "\n".join(notice_lines)
logger.info(f"notice块构建成功chat_id={chat_id}, 长度={len(result)}")
return result
else:
logger.debug(f"没有可用的notice文本chat_id={chat_id}")
return ""
except Exception as e:
logger.error(f"构建notice块失败chat_id={chat_id}: {e}", exc_info=True)
return ""
async def _time_and_run_task(self, coroutine, name: str) -> tuple[str, Any, float]:
"""计时并运行异步任务的辅助函数
@@ -1273,7 +1326,7 @@ class DefaultReplyer:
from src.chat.utils.prompt import Prompt
# 并行执行六个构建任务
# 并行执行任务
tasks = {
"expression_habits": asyncio.create_task(
self._time_and_run_task(
@@ -1301,6 +1354,9 @@ class DefaultReplyer:
"cross_context",
)
),
"notice_block": asyncio.create_task(
self._time_and_run_task(self.build_notice_block(chat_id), "notice_block")
),
}
# 设置超时
@@ -1319,6 +1375,7 @@ class DefaultReplyer:
"tool_info": "",
"prompt_info": "",
"cross_context": "",
"notice_block": "",
}
logger.info(f"为超时任务 {task_name} 提供默认值")
return task_name, default_values[task_name], timeout
@@ -1352,6 +1409,7 @@ class DefaultReplyer:
tool_info = results_dict["tool_info"]
prompt_info = results_dict["prompt_info"]
cross_context_block = results_dict["cross_context"]
notice_block = results_dict["notice_block"]
# 检查是否为视频分析结果,并注入引导语
if target and ("[视频内容]" in target or "好的,我将根据您提供的" in target):
@@ -1513,6 +1571,7 @@ class DefaultReplyer:
tool_info_block=tool_info,
knowledge_prompt=prompt_info,
cross_context_block=cross_context_block,
notice_block=notice_block,
keywords_reaction_prompt=keywords_reaction_prompt,
extra_info_block=extra_info_block,
time_block=time_block,
@@ -1655,6 +1714,9 @@ class DefaultReplyer:
else:
reply_target_block = ""
# 构建notice_block
notice_block = await self.build_notice_block(chat_id)
if is_group_chat:
await global_prompt_manager.get_prompt_async("chat_target_group1")
await global_prompt_manager.get_prompt_async("chat_target_group2")
@@ -1686,6 +1748,7 @@ class DefaultReplyer:
# 添加已构建的表达习惯和关系信息
expression_habits_block=expression_habits_block,
relation_info_block=relation_info,
notice_block=notice_block,
bot_name=global_config.bot.nickname,
bot_nickname=",".join(global_config.bot.alias_names) if global_config.bot.alias_names else "",
)