refactor(reaction): 重构关键词反应系统为通用反应规则系统

将原有的 `keyword_reaction` 系统重构为一个更通用、更强大的 `reaction` 系统。新系统统一了关键词和正则表达式规则,并增加了按聊天流ID(`chat_stream_id`)进行规则作用域限定的功能。

主要变更包括:
- **统一配置模型**:将 `KeywordReactionConfig` 和 `KeywordRuleConfig` 合并重构为 `ReactionConfig` 和 `ReactionRuleConfig`,提供了更清晰、统一的规则定义方式。
- **增加作用域控制**:新的 `ReactionRuleConfig` 增加了 `chat_stream_id` 字段,允许规则被限定在全局范围或特定的聊天会话中。
- **简化逻辑实现**:`DefaultReplyer` 中的实现被重构,以适应新的配置模型,能够筛选并应用适用范围内的规则。
- **更新配置文件模板**:`bot_config_template.toml` 已更新,以反映新的 `[[reaction.rules]]` 配置结构,并提供了详细的注释说明。
- **新增辅助方法**:在 `ChatStream` 中添加了 `get_raw_id()` 方法,用于获取未哈希的原始聊天流ID,以支持新系统的作用域匹配。

BREAKING CHANGE: 关键词反应功能的配置结构已完全改变。旧的 `[keyword_reaction]` 配置不再兼容。用户需要将原有的 `keyword_rules` 和 `regex_rules` 迁移到新的 `[[reaction.rules]]` 格式。
This commit is contained in:
minecraft1024a
2025-10-26 12:16:37 +08:00
parent 0ed35ff33b
commit ccbac891b6
5 changed files with 105 additions and 72 deletions

View File

@@ -796,44 +796,63 @@ class DefaultReplyer:
async def build_keywords_reaction_prompt(self, target: str | None) -> str:
"""构建关键词反应提示
该方法根据配置的关键词和正则表达式规则,
检查目标消息内容是否触发了任何反应。
如果匹配成功,它会生成一个包含所有触发反应的提示字符串,
用于指导LLM的回复。
Args:
target: 目标消息内容
Returns:
str: 关键词反应提示字符串
str: 关键词反应提示字符串,如果没有触发任何反应则为空字符串
"""
# 关键词检测与反应
keywords_reaction_prompt = ""
if target is None:
return ""
reaction_prompt = ""
try:
# 添加None检查防止NoneType错误
if target is None:
return keywords_reaction_prompt
current_chat_stream_id_str = self.chat_stream.get_raw_id()
# 2. 筛选适用的规则(全局规则 + 特定于当前聊天的规则)
applicable_rules = []
for rule in global_config.reaction.rules:
if rule.chat_stream_id == "" or rule.chat_stream_id == current_chat_stream_id_str:
applicable_rules.append(rule) # noqa: PERF401
# 处理关键词规则
for rule in global_config.keyword_reaction.keyword_rules:
if any(keyword in target for keyword in rule.keywords):
logger.info(f"检测到关键词规则:{rule.keywords},触发反应:{rule.reaction}")
keywords_reaction_prompt += f"{rule.reaction}"
# 3. 遍历适用规则并执行匹配
for rule in applicable_rules:
matched = False
if rule.rule_type == "keyword":
if any(keyword in target for keyword in rule.patterns):
logger.info(f"检测到关键词规则:{rule.patterns},触发反应:{rule.reaction}")
reaction_prompt += f"{rule.reaction}"
matched = True
elif rule.rule_type == "regex":
for pattern_str in rule.patterns:
try:
pattern = re.compile(pattern_str)
if result := pattern.search(target):
reaction = rule.reaction
# 替换命名捕获组
for name, content in result.groupdict().items():
reaction = reaction.replace(f"[{name}]", content)
logger.info(f"匹配到正则表达式:{pattern_str},触发反应:{reaction}")
reaction_prompt += f"{reaction}"
matched = True
break # 一个正则规则里只要有一个 pattern 匹配成功即可
except re.error as e:
logger.error(f"正则表达式编译错误: {pattern_str}, 错误信息: {e!s}")
continue
if matched:
# 如果需要每条消息只触发一个反应规则,可以在这里 break
pass
# 处理正则表达式规则
for rule in global_config.keyword_reaction.regex_rules:
for pattern_str in rule.regex:
try:
pattern = re.compile(pattern_str)
if result := pattern.search(target):
reaction = rule.reaction
for name, content in result.groupdict().items():
reaction = reaction.replace(f"[{name}]", content)
logger.info(f"匹配到正则表达式:{pattern_str},触发反应:{reaction}")
keywords_reaction_prompt += f"{reaction}"
break
except re.error as e:
logger.error(f"正则表达式编译错误: {pattern_str}, 错误信息: {e!s}")
continue
except Exception as e:
logger.error(f"关键词检测与反应时发生异常: {e!s}", exc_info=True)
return keywords_reaction_prompt
return reaction_prompt
async def build_notice_block(self, chat_id: str) -> str:
"""构建notice信息块