feat:添加黑话收集器

pull/1348/head
SengokuCola 2025-11-05 00:35:26 +08:00
parent 03e06c282c
commit 5bde31e512
5 changed files with 199 additions and 4 deletions

View File

@ -639,6 +639,83 @@ class DefaultReplyer:
prompt_personality = f"{prompt_personality};"
return f"你的名字是{bot_name}{bot_nickname},你{prompt_personality}"
def _parse_chat_prompt_config_to_chat_id(self, chat_prompt_str: str) -> Optional[tuple[str, str]]:
"""
解析聊天prompt配置字符串并生成对应的 chat_id prompt内容
Args:
chat_prompt_str: 格式为 "platform:id:type:prompt内容" 的字符串
Returns:
tuple: (chat_id, prompt_content)如果解析失败则返回 None
"""
try:
# 使用 split 分割但限制分割次数为3因为prompt内容可能包含冒号
parts = chat_prompt_str.split(":", 3)
if len(parts) != 4:
return None
platform = parts[0]
id_str = parts[1]
stream_type = parts[2]
prompt_content = parts[3]
# 判断是否为群聊
is_group = stream_type == "group"
# 使用与 ChatStream.get_stream_id 相同的逻辑生成 chat_id
import hashlib
if is_group:
components = [platform, str(id_str)]
else:
components = [platform, str(id_str), "private"]
key = "_".join(components)
chat_id = hashlib.md5(key.encode()).hexdigest()
return chat_id, prompt_content
except (ValueError, IndexError):
return None
def get_chat_prompt_for_chat(self, chat_id: str) -> str:
"""
根据聊天流ID获取匹配的额外prompt仅匹配group类型
Args:
chat_id: 聊天流ID哈希值
Returns:
str: 匹配的额外prompt内容如果没有匹配则返回空字符串
"""
if not global_config.experimental.chat_prompts:
return ""
for chat_prompt_str in global_config.experimental.chat_prompts:
if not isinstance(chat_prompt_str, str):
continue
# 解析配置字符串检查类型是否为group
parts = chat_prompt_str.split(":", 3)
if len(parts) != 4:
continue
stream_type = parts[2]
# 只匹配group类型
if stream_type != "group":
continue
result = self._parse_chat_prompt_config_to_chat_id(chat_prompt_str)
if result is None:
continue
config_chat_id, prompt_content = result
if config_chat_id == chat_id:
logger.debug(f"匹配到群聊prompt配置chat_id: {chat_id}, prompt: {prompt_content[:50]}...")
return prompt_content
return ""
async def build_prompt_reply_context(
self,
reply_message: Optional[DatabaseMessages] = None,
@ -820,6 +897,11 @@ class DefaultReplyer:
# 构建分离的对话 prompt
dialogue_prompt = self.build_chat_history_prompts(message_list_before_now_long, user_id, sender)
# 获取匹配的额外prompt
chat_prompt_content = self.get_chat_prompt_for_chat(chat_id)
chat_prompt_block = f"{chat_prompt_content}\n" if chat_prompt_content else ""
# 固定使用群聊回复模板
return await global_prompt_manager.format_prompt(
"replyer_prompt",
expression_habits_block=expression_habits_block,
@ -840,6 +922,7 @@ class DefaultReplyer:
keywords_reaction_prompt=keywords_reaction_prompt,
moderation_prompt=moderation_prompt_block,
question_block=question_block,
chat_prompt=chat_prompt_block,
), selected_expressions
async def build_prompt_rewrite_context(

View File

@ -536,6 +536,83 @@ class PrivateReplyer:
prompt_personality = f"{prompt_personality};"
return f"你的名字是{bot_name}{bot_nickname},你{prompt_personality}"
def _parse_chat_prompt_config_to_chat_id(self, chat_prompt_str: str) -> Optional[tuple[str, str]]:
"""
解析聊天prompt配置字符串并生成对应的 chat_id prompt内容
Args:
chat_prompt_str: 格式为 "platform:id:type:prompt内容" 的字符串
Returns:
tuple: (chat_id, prompt_content)如果解析失败则返回 None
"""
try:
# 使用 split 分割但限制分割次数为3因为prompt内容可能包含冒号
parts = chat_prompt_str.split(":", 3)
if len(parts) != 4:
return None
platform = parts[0]
id_str = parts[1]
stream_type = parts[2]
prompt_content = parts[3]
# 判断是否为群聊
is_group = stream_type == "group"
# 使用与 ChatStream.get_stream_id 相同的逻辑生成 chat_id
import hashlib
if is_group:
components = [platform, str(id_str)]
else:
components = [platform, str(id_str), "private"]
key = "_".join(components)
chat_id = hashlib.md5(key.encode()).hexdigest()
return chat_id, prompt_content
except (ValueError, IndexError):
return None
def get_chat_prompt_for_chat(self, chat_id: str) -> str:
"""
根据聊天流ID获取匹配的额外prompt仅匹配private类型
Args:
chat_id: 聊天流ID哈希值
Returns:
str: 匹配的额外prompt内容如果没有匹配则返回空字符串
"""
if not global_config.experimental.chat_prompts:
return ""
for chat_prompt_str in global_config.experimental.chat_prompts:
if not isinstance(chat_prompt_str, str):
continue
# 解析配置字符串检查类型是否为private
parts = chat_prompt_str.split(":", 3)
if len(parts) != 4:
continue
stream_type = parts[2]
# 只匹配private类型
if stream_type != "private":
continue
result = self._parse_chat_prompt_config_to_chat_id(chat_prompt_str)
if result is None:
continue
config_chat_id, prompt_content = result
if config_chat_id == chat_id:
logger.debug(f"匹配到私聊prompt配置chat_id: {chat_id}, prompt: {prompt_content[:50]}...")
return prompt_content
return ""
async def build_prompt_reply_context(
self,
reply_message: Optional[DatabaseMessages] = None,
@ -718,6 +795,10 @@ class PrivateReplyer:
# 其他情况(空内容等)
reply_target_block = f"现在对方说的:{target}。引起了你的注意"
# 获取匹配的额外prompt
chat_prompt_content = self.get_chat_prompt_for_chat(chat_id)
chat_prompt_block = f"{chat_prompt_content}\n" if chat_prompt_content else ""
if global_config.bot.qq_account == user_id and platform == global_config.bot.platform:
return await global_prompt_manager.format_prompt(
"private_replyer_self_prompt",
@ -738,6 +819,7 @@ class PrivateReplyer:
reply_style=global_config.personality.reply_style,
keywords_reaction_prompt=keywords_reaction_prompt,
moderation_prompt=moderation_prompt_block,
chat_prompt=chat_prompt_block,
), selected_expressions
else:
return await global_prompt_manager.format_prompt(
@ -758,6 +840,7 @@ class PrivateReplyer:
keywords_reaction_prompt=keywords_reaction_prompt,
moderation_prompt=moderation_prompt_block,
sender_name=sender,
chat_prompt=chat_prompt_block,
), selected_expressions
async def build_prompt_rewrite_context(

View File

@ -21,7 +21,7 @@ def init_replyer_prompt():
{reply_target_block}
{identity}
你正在群里聊天,现在请你读读之前的聊天记录然后给出日常且口语化的回复平淡一些{mood_state}
{chat_prompt}你正在群里聊天,现在请你读读之前的聊天记录然后给出日常且口语化的回复平淡一些{mood_state}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容不要回复的太有条理可以有个性
{reply_style}
请注意不要输出多余内容(包括前后缀冒号和引号括号表情等)只输出一句回复内容就好
@ -41,7 +41,7 @@ def init_replyer_prompt():
{reply_target_block}
{identity}
你正在和{sender_name}聊天,现在请你读读之前的聊天记录然后给出日常且口语化的回复平淡一些{mood_state}
{chat_prompt}你正在和{sender_name}聊天,现在请你读读之前的聊天记录然后给出日常且口语化的回复平淡一些{mood_state}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容不要回复的太有条理可以有个性
{reply_style}
请注意不要输出多余内容(包括前后缀冒号和引号括号表情等)只输出回复内容
@ -61,7 +61,7 @@ def init_replyer_prompt():
你现在想补充说明你刚刚自己的发言内容{target}原因是{reason}
请你根据聊天内容组织一条新回复注意{target} 是刚刚你自己的发言你要在这基础上进一步发言请按照你自己的角度来继续进行回复注意保持上下文的连贯性{mood_state}
{identity}
尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容不要回复的太有条理可以有个性
{chat_prompt}尽量简短一些{keywords_reaction_prompt}请注意把握聊天内容不要回复的太有条理可以有个性
{reply_style}
请注意不要输出多余内容(包括前后缀冒号和引号括号表情等)只输出回复内容
{moderation_prompt}不要输出多余内容(包括冒号和引号括号表情包at或 @等 )

View File

@ -656,6 +656,25 @@ class ExperimentalConfig(ConfigBase):
enable_friend_chat: bool = False
"""是否启用好友聊天"""
chat_prompts: list[str] = field(default_factory=lambda: [])
"""
为指定聊天添加额外的prompt配置列表
格式: ["platform:id:type:prompt内容", ...]
示例:
[
"qq:114514:group:这是一个摄影群,你精通摄影知识",
"qq:19198:group:这是一个二次元交流群",
"qq:114514:private:这是你与好朋友的私聊"
]
说明:
- platform: 平台名称 "qq"
- id: 群ID或用户ID
- type: "group" "private"
- prompt内容: 要添加的额外prompt文本
"""
@dataclass
class MaimMessageConfig(ConfigBase):

View File

@ -1,5 +1,5 @@
[inner]
version = "6.19.2"
version = "6.20.0"
#----以下是给开发人员阅读的,如果你只是部署了麦麦,不需要阅读----
#如果你想要修改配置文件请递增version的值
@ -241,6 +241,16 @@ enable = true
[experimental] #实验性功能
none = false # 暂无
# 为指定聊天添加额外的prompt配置
# 格式: ["platform:id:type:prompt内容", ...]
# 示例:
# chat_prompts = [
# "qq:114514:group:这是一个摄影群,你精通摄影知识",
# "qq:19198:group:这是一个二次元交流群",
# "qq:114514:private:这是你与好朋友的私聊"
# ]
chat_prompts = []
#此系统暂时移除,无效配置
[relationship]