加入了尚未完成或者说根本就没做de主动思考
This commit is contained in:
committed by
Windpicker-owo
parent
82ec73bf07
commit
111d23f608
@@ -44,6 +44,7 @@ from src.config.official_configs import (
|
|||||||
CommandConfig,
|
CommandConfig,
|
||||||
PlanningSystemConfig,
|
PlanningSystemConfig,
|
||||||
AffinityFlowConfig,
|
AffinityFlowConfig,
|
||||||
|
ProactiveThinkingConfig
|
||||||
)
|
)
|
||||||
|
|
||||||
from .api_ada_configs import (
|
from .api_ada_configs import (
|
||||||
@@ -433,6 +434,7 @@ class Config(ValidatedConfigBase):
|
|||||||
default_factory=lambda: CrossContextConfig(), description="跨群聊上下文共享配置"
|
default_factory=lambda: CrossContextConfig(), description="跨群聊上下文共享配置"
|
||||||
)
|
)
|
||||||
affinity_flow: AffinityFlowConfig = Field(default_factory=lambda: AffinityFlowConfig(), description="亲和流配置")
|
affinity_flow: AffinityFlowConfig = Field(default_factory=lambda: AffinityFlowConfig(), description="亲和流配置")
|
||||||
|
ProactiveThinking: ProactiveThinkingConfig = Field(default_factory=lambda: AffinityFlowConfig(), description="主动思考配置")
|
||||||
|
|
||||||
|
|
||||||
class APIAdapterConfig(ValidatedConfigBase):
|
class APIAdapterConfig(ValidatedConfigBase):
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ class ChatConfig(ValidatedConfigBase):
|
|||||||
mentioned_bot_inevitable_reply: bool = Field(default=False, description="提到机器人的必然回复")
|
mentioned_bot_inevitable_reply: bool = Field(default=False, description="提到机器人的必然回复")
|
||||||
at_bot_inevitable_reply: bool = Field(default=False, description="@机器人的必然回复")
|
at_bot_inevitable_reply: bool = Field(default=False, description="@机器人的必然回复")
|
||||||
allow_reply_self: bool = Field(default=False, description="是否允许回复自己说的话")
|
allow_reply_self: bool = Field(default=False, description="是否允许回复自己说的话")
|
||||||
talk_frequency_adjust: list[list[str]] = Field(default_factory=lambda: [], description="聊天频率调整")
|
|
||||||
focus_value: float = Field(default=1.0, description="专注值")
|
focus_value: float = Field(default=1.0, description="专注值")
|
||||||
focus_mode_quiet_groups: List[str] = Field(
|
focus_mode_quiet_groups: List[str] = Field(
|
||||||
default_factory=list,
|
default_factory=list,
|
||||||
@@ -121,19 +120,6 @@ class ChatConfig(ValidatedConfigBase):
|
|||||||
timestamp_display_mode: Literal["normal", "normal_no_YMD", "relative"] = Field(
|
timestamp_display_mode: Literal["normal", "normal_no_YMD", "relative"] = Field(
|
||||||
default="normal_no_YMD", description="时间戳显示模式"
|
default="normal_no_YMD", description="时间戳显示模式"
|
||||||
)
|
)
|
||||||
enable_proactive_thinking: bool = Field(default=False, description="启用主动思考")
|
|
||||||
proactive_thinking_interval: int = Field(default=1500, description="主动思考间隔")
|
|
||||||
The_scope_that_proactive_thinking_can_trigger: str = Field(default="all", description="主动思考可以触发的范围")
|
|
||||||
proactive_thinking_in_private: bool = Field(default=True, description="主动思考可以在私聊里面启用")
|
|
||||||
proactive_thinking_in_group: bool = Field(default=True, description="主动思考可以在群聊里面启用")
|
|
||||||
proactive_thinking_enable_in_private: List[str] = Field(
|
|
||||||
default_factory=list, description="启用主动思考的私聊范围,格式:platform:user_id,为空则不限制"
|
|
||||||
)
|
|
||||||
proactive_thinking_enable_in_groups: List[str] = Field(
|
|
||||||
default_factory=list, description="启用主动思考的群聊范围,格式:platform:group_id,为空则不限制"
|
|
||||||
)
|
|
||||||
delta_sigma: int = Field(default=120, description="采用正态分布随机时间间隔")
|
|
||||||
|
|
||||||
# 消息打断系统配置
|
# 消息打断系统配置
|
||||||
interruption_enabled: bool = Field(default=True, description="是否启用消息打断系统")
|
interruption_enabled: bool = Field(default=True, description="是否启用消息打断系统")
|
||||||
interruption_max_limit: int = Field(default=3, ge=0, description="每个聊天流的最大打断次数")
|
interruption_max_limit: int = Field(default=3, ge=0, description="每个聊天流的最大打断次数")
|
||||||
@@ -162,159 +148,6 @@ class ChatConfig(ValidatedConfigBase):
|
|||||||
default=10, ge=1, le=100, description="最大并发处理的消息流数量"
|
default=10, ge=1, le=100, description="最大并发处理的消息流数量"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_current_talk_frequency(self, chat_stream_id: Optional[str] = None) -> float:
|
|
||||||
"""
|
|
||||||
根据当前时间和聊天流获取对应的 talk_frequency
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat_stream_id: 聊天流ID,格式为 "platform:chat_id:type"
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 对应的频率值
|
|
||||||
"""
|
|
||||||
if not self.talk_frequency_adjust:
|
|
||||||
return self.talk_frequency
|
|
||||||
|
|
||||||
# 优先检查聊天流特定的配置
|
|
||||||
if chat_stream_id:
|
|
||||||
stream_frequency = self._get_stream_specific_frequency(chat_stream_id)
|
|
||||||
if stream_frequency is not None:
|
|
||||||
return stream_frequency
|
|
||||||
|
|
||||||
# 检查全局时段配置(第一个元素为空字符串的配置)
|
|
||||||
global_frequency = self._get_global_frequency()
|
|
||||||
return self.talk_frequency if global_frequency is None else global_frequency
|
|
||||||
|
|
||||||
def _get_time_based_frequency(self, time_freq_list: list[str]) -> Optional[float]:
|
|
||||||
"""
|
|
||||||
根据时间配置列表获取当前时段的频率
|
|
||||||
|
|
||||||
Args:
|
|
||||||
time_freq_list: 时间频率配置列表,格式为 ["HH:MM,frequency", ...]
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 频率值,如果没有配置则返回 None
|
|
||||||
"""
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
current_time = datetime.now().strftime("%H:%M")
|
|
||||||
current_hour, current_minute = map(int, current_time.split(":"))
|
|
||||||
current_minutes = current_hour * 60 + current_minute
|
|
||||||
|
|
||||||
# 解析时间频率配置
|
|
||||||
time_freq_pairs = []
|
|
||||||
for time_freq_str in time_freq_list:
|
|
||||||
try:
|
|
||||||
time_str, freq_str = time_freq_str.split(",")
|
|
||||||
hour, minute = map(int, time_str.split(":"))
|
|
||||||
frequency = float(freq_str)
|
|
||||||
minutes = hour * 60 + minute
|
|
||||||
time_freq_pairs.append((minutes, frequency))
|
|
||||||
except (ValueError, IndexError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not time_freq_pairs:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 按时间排序
|
|
||||||
time_freq_pairs.sort(key=lambda x: x[0])
|
|
||||||
|
|
||||||
# 查找当前时间对应的频率
|
|
||||||
current_frequency = None
|
|
||||||
for minutes, frequency in time_freq_pairs:
|
|
||||||
if current_minutes >= minutes:
|
|
||||||
current_frequency = frequency
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
# 如果当前时间在所有配置时间之前,使用最后一个时间段的频率(跨天逻辑)
|
|
||||||
if current_frequency is None and time_freq_pairs:
|
|
||||||
current_frequency = time_freq_pairs[-1][1]
|
|
||||||
|
|
||||||
return current_frequency
|
|
||||||
|
|
||||||
def _get_stream_specific_frequency(self, chat_stream_id: str):
|
|
||||||
"""
|
|
||||||
获取特定聊天流在当前时间的频率
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chat_stream_id: 聊天流ID(哈希值)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 频率值,如果没有配置则返回 None
|
|
||||||
"""
|
|
||||||
# 查找匹配的聊天流配置
|
|
||||||
for config_item in self.talk_frequency_adjust:
|
|
||||||
if not config_item or len(config_item) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
stream_config_str = config_item[0] # 例如 "qq:1026294844:group"
|
|
||||||
|
|
||||||
# 解析配置字符串并生成对应的 chat_id
|
|
||||||
config_chat_id = self._parse_stream_config_to_chat_id(stream_config_str)
|
|
||||||
if config_chat_id is None:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 比较生成的 chat_id
|
|
||||||
if config_chat_id != chat_stream_id:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 使用通用的时间频率解析方法
|
|
||||||
return self._get_time_based_frequency(config_item[1:])
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _parse_stream_config_to_chat_id(self, stream_config_str: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
解析流配置字符串并生成对应的 chat_id
|
|
||||||
|
|
||||||
Args:
|
|
||||||
stream_config_str: 格式为 "platform:id:type" 的字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 生成的 chat_id,如果解析失败则返回 None
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
parts = stream_config_str.split(":")
|
|
||||||
if len(parts) != 3:
|
|
||||||
return None
|
|
||||||
|
|
||||||
platform = parts[0]
|
|
||||||
id_str = parts[1]
|
|
||||||
stream_type = parts[2]
|
|
||||||
|
|
||||||
# 判断是否为群聊
|
|
||||||
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)
|
|
||||||
return hashlib.md5(key.encode()).hexdigest()
|
|
||||||
|
|
||||||
except (ValueError, IndexError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _get_global_frequency(self) -> Optional[float]:
|
|
||||||
"""
|
|
||||||
获取全局默认频率配置
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 频率值,如果没有配置则返回 None
|
|
||||||
"""
|
|
||||||
for config_item in self.talk_frequency_adjust:
|
|
||||||
if not config_item or len(config_item) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 检查是否为全局默认配置(第一个元素为空字符串)
|
|
||||||
if config_item[0] == "":
|
|
||||||
return self._get_time_based_frequency(config_item[1:])
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class MessageReceiveConfig(ValidatedConfigBase):
|
class MessageReceiveConfig(ValidatedConfigBase):
|
||||||
@@ -762,7 +595,6 @@ class SleepSystemConfig(ValidatedConfigBase):
|
|||||||
default="我准备睡觉了,请生成一句简短自然的晚安问候。", description="用于生成睡前消息的提示"
|
default="我准备睡觉了,请生成一句简短自然的晚安问候。", description="用于生成睡前消息的提示"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ContextGroup(ValidatedConfigBase):
|
class ContextGroup(ValidatedConfigBase):
|
||||||
"""上下文共享组配置"""
|
"""上下文共享组配置"""
|
||||||
|
|
||||||
@@ -824,3 +656,33 @@ class AffinityFlowConfig(ValidatedConfigBase):
|
|||||||
mention_bot_adjustment_threshold: float = Field(default=0.3, description="提及bot后的调整阈值")
|
mention_bot_adjustment_threshold: float = Field(default=0.3, description="提及bot后的调整阈值")
|
||||||
mention_bot_interest_score: float = Field(default=0.6, description="提及bot的兴趣分")
|
mention_bot_interest_score: float = Field(default=0.6, description="提及bot的兴趣分")
|
||||||
base_relationship_score: float = Field(default=0.5, description="基础人物关系分")
|
base_relationship_score: float = Field(default=0.5, description="基础人物关系分")
|
||||||
|
|
||||||
|
class ProactiveThinkingConfig(ValidatedConfigBase):
|
||||||
|
"""主动思考(主动发起对话)功能配置"""
|
||||||
|
|
||||||
|
# --- 总开关 ---
|
||||||
|
enable: bool = Field(default=False, description="是否启用主动发起对话功能")
|
||||||
|
|
||||||
|
# --- 触发时机 ---
|
||||||
|
interval: int = Field(default=1500, description="基础触发间隔(秒),AI会围绕这个时间点主动发起对话")
|
||||||
|
interval_sigma: int = Field(default=120, description="间隔随机化标准差(秒),让触发时间更自然。设为0则为固定间隔。")
|
||||||
|
talk_frequency_adjust: list[list[str]] = Field(
|
||||||
|
default_factory=lambda: [['', '8:00,1', '12:00,1.2', '18:00,1.5', '01:00,0.6']],
|
||||||
|
description='每日活跃度调整,格式:[["", "HH:MM,factor", ...], ["stream_id", ...]]'
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- 作用范围 ---
|
||||||
|
enable_in_private: bool = Field(default=True, description="是否允许在私聊中主动发起对话")
|
||||||
|
enable_in_group: bool = Field(default=True, description="是否允许在群聊中主动发起对话")
|
||||||
|
enabled_private_chats: List[str] = Field(
|
||||||
|
default_factory=list,
|
||||||
|
description='私聊白名单,为空则对所有私聊生效。格式: ["platform:user_id", ...]'
|
||||||
|
)
|
||||||
|
enabled_group_chats: List[str] = Field(
|
||||||
|
default_factory=list,
|
||||||
|
description='群聊白名单,为空则对所有群聊生效。格式: ["platform:group_id", ...]'
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- 冷启动配置 (针对私聊) ---
|
||||||
|
enable_cold_start: bool = Field(default=True, description="对于白名单中不活跃的私聊,是否允许进行一次“冷启动”问候")
|
||||||
|
cold_start_cooldown: int = Field(default=86400, description="冷启动后,该私聊的下一次主动思考需要等待的最小时间(秒)")
|
||||||
|
|||||||
0
src/plugins/built_in/proactive_thinker/__init__.py
Normal file
0
src/plugins/built_in/proactive_thinker/__init__.py
Normal file
25
src/plugins/built_in/proactive_thinker/_manifest.json
Normal file
25
src/plugins/built_in/proactive_thinker/_manifest.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 1,
|
||||||
|
"name": "MoFox-Bot主动思考",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "主动思考插件",
|
||||||
|
"author": {
|
||||||
|
"name": "MoFox-Studio",
|
||||||
|
"url": "https://github.com/MoFox-Studio"
|
||||||
|
},
|
||||||
|
"license": "GPL-v3.0-or-later",
|
||||||
|
|
||||||
|
"host_application": {
|
||||||
|
"min_version": "0.10.0"
|
||||||
|
},
|
||||||
|
"keywords": ["emoji", "reaction", "like", "表情", "回应", "点赞"],
|
||||||
|
"categories": ["Chat", "Integration"],
|
||||||
|
|
||||||
|
"default_locale": "zh-CN",
|
||||||
|
"locales_path": "_locales",
|
||||||
|
|
||||||
|
"plugin_info": {
|
||||||
|
"is_built_in": "true",
|
||||||
|
"plugin_type": "functional"
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/plugins/built_in/proactive_thinker/plugin.py
Normal file
45
src/plugins/built_in/proactive_thinker/plugin.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
from typing import List, Tuple, Union, Type, Optional
|
||||||
|
|
||||||
|
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 = True
|
||||||
|
dependencies: list[str] = []
|
||||||
|
python_dependencies: list[str] = []
|
||||||
|
config_file_name: str = "config.toml"
|
||||||
|
config_schema: dict = {
|
||||||
|
"plugin": {
|
||||||
|
"enabled": ConfigField(bool, default=False, description="是否启用插件"),
|
||||||
|
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_plugin_components(self) -> List[Tuple[EventHandlerInfo, Type[BaseEventHandler]]]:
|
||||||
|
"""返回插件的EventHandler组件"""
|
||||||
|
components: List[Tuple[EventHandlerInfo, Type[BaseEventHandler]]] = [
|
||||||
|
(ProactiveThinkerEventHandler.get_handler_info(), ProactiveThinkerEventHandler)
|
||||||
|
]
|
||||||
|
return components
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
from typing import List, Union, Type, Optional
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
from src.plugin_system import (
|
||||||
|
EventType,
|
||||||
|
BaseEventHandler,
|
||||||
|
HandlerResult,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ProactiveThinkerEventHandler(BaseEventHandler):
|
||||||
|
"""主动思考需要的启动时触发的事件处理器"""
|
||||||
|
|
||||||
|
handler_name: str = "proactive_thinker_on_start"
|
||||||
|
handler_description: str = "主动思考插件的启动事件处理器"
|
||||||
|
init_subscribe: List[Union[EventType, str]] = [EventType.ON_START]
|
||||||
|
|
||||||
|
async def execute(self, kwargs: dict | None) -> "HandlerResult":
|
||||||
|
"""执行事件处理"""
|
||||||
|
logger.info("ProactiveThinkerPlugin on_start event triggered.")
|
||||||
|
# 返回 (是否执行成功, 是否需要继续处理, 可选的返回消息)
|
||||||
|
return HandlerResult(success=True, continue_process=True, message=None)
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
# 主动聊天功能重构与设计方案
|
||||||
|
|
||||||
|
本文档旨在规划一个全新的、真正的“主动发起对话”功能。方案的核心是创建一个独立的、可配置的插件,并重构现有配置,使其更具模块化和可扩展性。
|
||||||
|
|
||||||
|
## 1. 配置文件重构 (`bot_config.toml`)
|
||||||
|
|
||||||
|
为了提高清晰度和模块化,我们将创建一个新的配置节 `[proactive_thinking]`。
|
||||||
|
|
||||||
|
### 1.1. 移除旧配置
|
||||||
|
|
||||||
|
以下配置项将从 `[chat]` 配置节中 **移除**:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# mmc/config/bot_config.toml
|
||||||
|
|
||||||
|
# 从 line 132 开始移除以下所有行
|
||||||
|
talk_frequency_adjust = [['', '8:00,1', '12:00,1.2', '18:00,1.5', '01:00,0.6'], ['qq:114514:group', '12:20,1', '16:10,2', '20:10,1', '00:10,0.3'], ['qq:1919810:private', '8:20,1', '12:10,2', '20:10,1.5', '00:10,0.2']]
|
||||||
|
# ... (所有 talk_frequency_adjust 的注释) ...
|
||||||
|
|
||||||
|
# 主动思考功能配置(仅在focus模式下生效)
|
||||||
|
|
||||||
|
enable_proactive_thinking = false
|
||||||
|
proactive_thinking_interval = 1500
|
||||||
|
# ... (所有 proactive_thinking 的注释和相关配置) ...
|
||||||
|
delta_sigma = 120
|
||||||
|
# ... (所有 delta_sigma 的注释和相关配置) ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2. 新增 `[proactive_thinking]` 配置节
|
||||||
|
|
||||||
|
在 `bot_config.toml` 文件 **末尾**,添加以下全新配置节:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# mmc/config/bot_config.toml
|
||||||
|
|
||||||
|
[proactive_thinking] # 主动思考(主动发起对话)功能配置
|
||||||
|
# --- 总开关 ---
|
||||||
|
enable = false # 是否启用主动发起对话功能
|
||||||
|
|
||||||
|
# --- 触发时机 ---
|
||||||
|
# 基础触发间隔(秒),AI会围绕这个时间点主动发起对话
|
||||||
|
interval = 1500 # 默认25分钟
|
||||||
|
# 间隔随机化标准差(秒),让触发时间更自然。设为0则为固定间隔。
|
||||||
|
interval_sigma = 120
|
||||||
|
# 每日活跃度调整,格式:[["", "HH:MM,factor", ...], ["stream_id", ...]]
|
||||||
|
# factor > 1.0 会缩短思考间隔,更活跃;factor < 1.0 会延长间隔。
|
||||||
|
talk_frequency_adjust = [['', '8:00,1', '12:00,1.2', '18:00,1.5', '01:00,0.6']]
|
||||||
|
|
||||||
|
# --- 作用范围 ---
|
||||||
|
enable_in_private = true # 是否允许在私聊中主动发起对话
|
||||||
|
enable_in_group = true # 是否允许在群聊中主动发起对话
|
||||||
|
# 私聊白名单,为空则对所有私聊生效
|
||||||
|
# 格式: ["platform:user_id", ...] e.g., ["qq:123456"]
|
||||||
|
enabled_private_chats = []
|
||||||
|
# 群聊白名单,为空则对所有群聊生效
|
||||||
|
# 格式: ["platform:group_id", ...] e.g., ["qq:7891011"]
|
||||||
|
enabled_group_chats = []
|
||||||
|
|
||||||
|
# --- 冷启动配置 (针对私聊) ---
|
||||||
|
# 对于白名单中不活跃的私聊,是否允许进行一次“冷启动”问候
|
||||||
|
enable_cold_start = true
|
||||||
|
# 冷启动后,该私聊的下一次主动思考需要等待的最小时间(秒)
|
||||||
|
cold_start_cooldown = 86400 # 默认24小时
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 新插件架构设计 (`proactive_initiation_chatter`)
|
||||||
|
|
||||||
|
我们将创建一个全新的插件来实现此功能。
|
||||||
|
|
||||||
|
### 2.1. 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
mmc/src/plugins/built_in/proactive_initiation_chatter/
|
||||||
|
├── __init__.py
|
||||||
|
├── _manifest.json
|
||||||
|
├── plugin.py # 插件主入口,负责启动和管理触发器
|
||||||
|
├── trigger_manager.py # 核心触发器,内置于插件中
|
||||||
|
├── initiation_chatter.py # Chatter实现,监听触发事件
|
||||||
|
└── initiation_planner.py # 规划器,负责决定“说什么”
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2. 核心组件设计
|
||||||
|
|
||||||
|
#### `plugin.py` - `ProactiveInitiationPlugin`
|
||||||
|
- **职责**: 作为插件的入口,它将在插件被加载时,读取 `[proactive_thinking]` 配置,并根据配置启动 `ProactiveTriggerManager`。
|
||||||
|
- **启动逻辑 (参考 `maizone_refactored`)**:
|
||||||
|
|
||||||
|
#### `trigger_manager.py` - `ProactiveTriggerManager`
|
||||||
|
- **职责**: 这是一个后台服务类,负责管理所有聊天流的触发计时器,并实现包括“冷启动”在内的所有复杂触发逻辑。
|
||||||
|
- **核心逻辑 (参考 `SchedulerService`)**:
|
||||||
|
- 维护一个异步主循环,定期检查所有符合条件的聊天流。
|
||||||
|
- 根据配置的间隔和活跃度调整,计算下次触发时间。
|
||||||
|
- 在触发时,调用 `InitiationPlanner` 来决定具体内容,并通过事件管理器派发 `ProactiveInitiationEvent` 或 `ColdStartInitiationEvent`。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 核心交互与依赖
|
||||||
|
|
||||||
|
新的 `proactive_initiation_chatter` 插件将与以下核心系统模块进行交互,以确保其决策的智能性和合规性:
|
||||||
|
|
||||||
|
- **`Config`**: `TriggerManager` 和 `Planner` 将从全局配置中读取 `[proactive_thinking]` 配置节,以获取所有行为参数。
|
||||||
|
- **`EventManager`**: `TriggerManager` 将通过事件管理器派发 `ProactiveInitiationEvent` 和 `ColdStartInitiationEvent` 事件。`InitiationChatter` 则会监听这些事件以触发执行。
|
||||||
|
- **`AsyncTaskManager`**: `ProactiveInitiationPlugin` 将使用此管理器来安全地在后台运行 `TriggerManager` 的主循环。
|
||||||
|
- **`ChatManager` (from `chat_stream.py`)**: 这是实现“冷启动”的核心。`TriggerManager` 将调用 `chat_manager.get_or_create_stream()` 方法来按需“唤醒”或创建不活跃的聊天流实例及其附带的空上下文。
|
||||||
|
- **`SleepManager`**: 在每次触发决策前,`TriggerManager` **必须**查询 `SleepManager` 以确认AI当前未处于睡眠状态。
|
||||||
|
- **`ScheduleManager` / `MonthlyPlanManager`**: `InitiationPlanner` 的“待办任务驱动”策略会查询这些管理器,以获取可作为聊天话题的日程或计划。
|
||||||
|
- **`MemoryManager` / `ContextManager`**: `InitiationPlanner` 的“记忆驱动”策略会查询长期记忆和短期上下文,以寻找关联性话题。
|
||||||
|
- **`RelationshipManager`**: `InitiationPlanner` 可以查询关系分数,作为执行某些话题策略的门槛。
|
||||||
|
|
||||||
|
## 4. 插件清单文件 (`_manifest.json`)
|
||||||
|
|
||||||
|
插件的清单文件将定义其元数据和依赖。
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"manifest_version": 1,
|
||||||
|
"name": "ProactiveInitiationChatter",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "Kilo Code",
|
||||||
|
"description": "一个真正的主动发起对话插件,由内置的、可高度配置的触发器驱动。",
|
||||||
|
"dependencies": [],
|
||||||
|
"python_dependencies": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 上下文获取与“唤醒”机制详解
|
||||||
|
|
||||||
|
本设计区分了“热启动”(针对活跃聊天)和“冷启动”(针对非活跃聊天)两种场景,并利用 `ChatManager` 的不同方法来优雅地处理。
|
||||||
|
|
||||||
|
### 热启动流程 (Hot Start - 针对活跃聊天)
|
||||||
|
|
||||||
|
这是最常见的场景。当一个聊天流近期有过对话,其实例存在于 `ChatManager` 的内存缓存中。
|
||||||
|
|
||||||
|
1. **获取现有上下文**: `ProactiveTriggerManager` 决定对一个活跃的 `stream_id` 发起对话时,它会调用 `chat_manager.get_stream(stream_id)`。
|
||||||
|
2. **返回缓存实例**: `ChatManager` 会直接从内存中返回缓存的 `ChatStream` 实例。
|
||||||
|
3. **传递丰富上下文**: 这个实例中包含了**完整的、包含近期对话历史**的 `stream_context`。
|
||||||
|
4. **智能决策**: `TriggerManager` 将这个**充满信息**的上下文派发给 `InitiationPlanner`。`Planner` 因此可以优先使用“记忆驱动”等高级策略,生成与前文高度相关的话题,使对话显得自然、连贯。
|
||||||
|
|
||||||
|
### 冷启动流程 (Cold Start - “唤醒”非活跃聊天)
|
||||||
|
|
||||||
|
针对在白名单中,但当前未加载到内存的私聊。
|
||||||
|
|
||||||
|
**核心方法:** `ChatManager.get_or_create_stream(platform, user_info, group_info)`
|
||||||
|
|
||||||
|
**唤醒流程:**
|
||||||
|
|
||||||
|
1. `ProactiveTriggerManager` 在主循环中识别到一个需要“冷启动”的私聊 `stream_id`。
|
||||||
|
2. `TriggerManager` 构造出必要的 `UserInfo` 对象。
|
||||||
|
3. 它调用 `get_chat_manager()`,然后执行核心的唤醒调用:
|
||||||
|
```python
|
||||||
|
# (伪代码)
|
||||||
|
chat_stream = await chat_manager.get_or_create_stream(...)
|
||||||
|
```
|
||||||
|
4. 此调用会从数据库加载或全新创建一个 `ChatStream` 实例,该实例内部会自动创建一个**不包含任何历史消息的空上下文**。
|
||||||
|
5. `TriggerManager` 将这个**空的 `StreamContext`** 连同 `ColdStartInitiationEvent` 事件一同派发出去,以触发通用的问候语。
|
||||||
|
|
||||||
|
此双轨制流程无需修改任何核心系统代码,仅通过合理调用现有接口即可实现,保证了方案的稳定性和兼容性。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
这份经过强化的设计文档详细说明了配置文件的修改方案、新插件的内部架构以及与核心系统的交互模式。请您审阅。如果这份蓝图符合您的预期,我们就可以准备将此计划交付实施。
|
||||||
|
|
||||||
|
另外附加:我计划在 InitiationPlanner 中实现一个策略选择系统。每次被 TriggerManager 触发时,它会评估多种“主动聊天策略”的“适宜度分数”,然后选择分数最高的策略来执行。
|
||||||
|
|
||||||
|
以下是我初步设计的几种策略:
|
||||||
|
|
||||||
|
ColdStartGreetingStrategy (冷启动问候策略)
|
||||||
|
|
||||||
|
触发条件:仅在 TriggerManager 派发 ColdStartInitiationEvent 事件时触发。
|
||||||
|
核心逻辑:生成一句通用的、友好的问候语,比如“你好呀!”或者“最近怎么样?”。这是为了“唤醒”那些很久没聊天的私聊对象。
|
||||||
|
适宜度分数:固定高分(例如 1.0),确保在冷启动时优先执行。
|
||||||
|
MemoryDrivenStrategy (记忆驱动策略)
|
||||||
|
|
||||||
|
触发条件:常规触发 (ProactiveInitiationEvent),且当前聊天流的上下文不为空。
|
||||||
|
核心逻辑:
|
||||||
|
查询 MemoryManager,获取关于当前聊天对象的长期记忆或近期摘要。
|
||||||
|
查询 ContextManager,分析最近的几条对话,寻找可以延续的话题。
|
||||||
|
利用 LLM 生成一个与上下文或记忆相关的话题。例如:“我们上次聊到的那个项目,后来进展如何了?”
|
||||||
|
适宜度分数计算 (借鉴AFC):
|
||||||
|
context_relevance_score (上下文相关性):上下文越丰富、越接近现在,分数越高。
|
||||||
|
relationship_score (关系分):从 RelationshipManager 获取,关系越好,越适合深入聊记忆话题。
|
||||||
|
final_score = (context_relevance_score * 权重) + (relationship_score * 权重)
|
||||||
|
TaskDrivenStrategy (任务/日程驱动策略)
|
||||||
|
|
||||||
|
触发条件:常规触发。
|
||||||
|
核心逻辑:
|
||||||
|
查询 ScheduleManager 或 MonthlyPlanManager,看看今天或最近有没有“待办事项”或“计划”。
|
||||||
|
如果有,可以围绕这个任务发起对话。例如:“我看到日程表上说今天要去图书馆,准备好了吗?”
|
||||||
|
适宜度分数计算:
|
||||||
|
task_urgency_score (任务紧急度):任务越紧急,分数越高。
|
||||||
|
task_relevance_score (任务相关度):如果任务与当前聊天对象有关,分数更高。
|
||||||
|
final_score = (task_urgency_score * 权重) + (task_relevance_score * 权重)
|
||||||
|
GenericTopicStrategy (通用话题策略)
|
||||||
|
|
||||||
|
触发条件:作为所有其他策略都无法执行时的“兜底”策略。
|
||||||
|
核心逻辑:从一个预设的话题库(或者让 LLM 随机生成)中挑选一个通用的话题,比如“今天天气不错,适合出门散步呢”或者“最近有什么有趣的新闻吗?”。
|
||||||
|
适宜度分数:固定低分(例如 0.1),确保它是最后的选择。
|
||||||
Reference in New Issue
Block a user