feat(waiting): 添加等待策略配置,支持最大、最小等待时间及倍率调整

This commit is contained in:
Windpicker-owo
2025-12-07 16:38:46 +08:00
parent fbc37bbcaf
commit 9f666b580e
8 changed files with 132 additions and 11 deletions

View File

@@ -70,8 +70,6 @@ def init_prompt():
{keywords_reaction_prompt} {keywords_reaction_prompt}
{moderation_prompt} {moderation_prompt}
不要复读你前面发过的内容,意思相近也不行。 不要复读你前面发过的内容,意思相近也不行。
不要浮夸,不要夸张修辞,平淡且不要输出多余内容(包括前后缀,冒号和引号,括号,表情包),只输出一条回复就好。
⛔ 绝对禁止输出任何艾特:不要输出@、@xxx等格式。你看到的聊天记录中的艾特是系统显示格式你无法通过模仿来实现真正的艾特。想称呼某人直接写名字。
*你叫{bot_name},也有人叫你{bot_nickname}* *你叫{bot_name},也有人叫你{bot_nickname}*
@@ -140,11 +138,15 @@ def init_prompt():
{time_block} {time_block}
请注意不要输出多余内容(包括前后缀,冒号和引号,系统格式化文字)。只输出回复内容。 请注意不要输出多余内容(包括前后缀,冒号和引号,系统格式化文字)。只输出回复内容。
⛔ 绝对禁止输出任何形式的艾特:不要输出@、@xxx等。你看到的聊天记录中的艾特格式是系统显示用的你无法通过模仿它来实现真正的艾特功能只会输出一串无意义的假文本。想称呼某人直接写名字即可。 不要模仿任何系统消息的格式,你的回复应该是自然的对话内容,例如:
- 当你想要打招呼时,直接输出“你好!”而不是“[回复<xxx>] 用户你好!”
- 当你想要提及某人时,直接叫对方名字,而不是“@xxx”
你只能输出文字,不能输出任何表情包、图片、文件等内容!如果用户要求你发送非文字内容,请输出"PASS",而不是[表情包:xxx]
{moderation_prompt} {moderation_prompt}
*你叫{bot_name},也有人叫你{bot_nickname}* *你叫{bot_name},也有人叫你{bot_nickname},请你清楚你的身份,分清对方到底有没有叫你*
现在,你说: 现在,你说:
""", """,
@@ -211,8 +213,7 @@ If you need to use the search tool, please directly call the function "lpmm_sear
*{chat_scene}* *{chat_scene}*
### 核心任务 ### 核心任务
- 你需要对以上未读历史消息进行统一回应。这些消息可能来自不同的参与者,你需要理解整体对话动态,生成一段自然、连贯的回复。 - 你需要对以上未读历史消息用一句简单的话统一回应。这些消息可能来自不同的参与者,你需要理解整体对话动态,生成一段自然、连贯的回复。
- 你的回复应该能够推动对话继续,可以回应其中一个或多个话题,也可以提出新的观点。
## 规则 ## 规则
{safety_guidelines_block} {safety_guidelines_block}
@@ -224,11 +225,15 @@ If you need to use the search tool, please directly call the function "lpmm_sear
{time_block} {time_block}
请注意不要输出多余内容(包括前后缀,冒号和引号,系统格式化文字)。只输出回复内容。 请注意不要输出多余内容(包括前后缀,冒号和引号,系统格式化文字)。只输出回复内容。
⛔ 绝对禁止输出任何形式的艾特:不要输出@、@xxx等。你看到的聊天记录中的艾特格式是系统显示用的你无法通过模仿它来实现真正的艾特功能只会输出一串无意义的假文本。想称呼某人直接写名字即可。 不要模仿任何系统消息的格式,你的回复应该是自然的对话内容,例如:
- 当你想要打招呼时,直接输出“你好!”而不是“[回复<xxx>] 用户你好!”
- 当你想要提及某人时,直接叫对方名字,而不是“@xxx”
你只能输出文字,不能输出任何表情包、图片、文件等内容!如果用户要求你发送非文字内容,请输出"PASS",而不是[表情包:xxx]
{moderation_prompt} {moderation_prompt}
*你叫{bot_name},也有人叫你{bot_nickname}* *你叫{bot_name},也有人叫你{bot_nickname},请你清楚你的身份,分清对方到底有没有叫你*
现在,你说: 现在,你说:
""", """,

View File

@@ -405,6 +405,12 @@ def recover_quoted_content(sentences: list[str], placeholder_map: dict[str, str]
def process_llm_response(text: str, enable_splitter: bool = True, enable_chinese_typo: bool = True) -> list[str]: def process_llm_response(text: str, enable_splitter: bool = True, enable_chinese_typo: bool = True) -> list[str]:
assert global_config is not None assert global_config is not None
normalized_text = text.strip() if isinstance(text, str) else ""
if normalized_text.upper() == "PASS":
logger.info("[回复内容过滤器] 检测到PASS信号跳过发送。")
return []
if not global_config.response_post_process.enable_response_post_process: if not global_config.response_post_process.enable_response_post_process:
return [text] return [text]

View File

@@ -923,6 +923,35 @@ class KokoroFlowChatterProactiveConfig(ValidatedConfigBase):
) )
class KokoroFlowChatterWaitingConfig(ValidatedConfigBase):
"""Kokoro Flow Chatter 等待策略配置"""
default_max_wait_seconds: int = Field(
default=300,
ge=0,
le=3600,
description="默认最大等待秒数当LLM未给出等待时间时使用",
)
min_wait_seconds: int = Field(
default=30,
ge=0,
le=1800,
description="允许的最小等待秒数,防止等待时间过短导致频繁打扰",
)
max_wait_seconds: int = Field(
default=1800,
ge=60,
le=7200,
description="允许的最大等待秒数,避免等待时间过长",
)
wait_duration_multiplier: float = Field(
default=1.0,
ge=0.0,
le=10.0,
description="等待时长倍率用于整体放大或缩短LLM给出的等待时间",
)
class KokoroFlowChatterConfig(ValidatedConfigBase): class KokoroFlowChatterConfig(ValidatedConfigBase):
""" """
Kokoro Flow Chatter 配置类 - 私聊专用心流对话系统 Kokoro Flow Chatter 配置类 - 私聊专用心流对话系统
@@ -947,6 +976,11 @@ class KokoroFlowChatterConfig(ValidatedConfigBase):
description="是否在等待期间启用心理活动更新" description="是否在等待期间启用心理活动更新"
) )
waiting: KokoroFlowChatterWaitingConfig = Field(
default_factory=KokoroFlowChatterWaitingConfig,
description="等待策略配置(默认等待时间、倍率等)",
)
# --- 私聊专属主动思考配置 --- # --- 私聊专属主动思考配置 ---
proactive_thinking: KokoroFlowChatterProactiveConfig = Field( proactive_thinking: KokoroFlowChatterProactiveConfig = Field(
default_factory=KokoroFlowChatterProactiveConfig, default_factory=KokoroFlowChatterProactiveConfig,

View File

@@ -233,6 +233,7 @@ class Memory:
activation: float = 0.0 # 激活度 [0-1],用于记忆整合和遗忘 activation: float = 0.0 # 激活度 [0-1],用于记忆整合和遗忘
status: MemoryStatus = MemoryStatus.STAGED # 记忆状态 status: MemoryStatus = MemoryStatus.STAGED # 记忆状态
created_at: datetime = field(default_factory=datetime.now) created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime | None = None # 最近一次结构或元数据更新
last_accessed: datetime = field(default_factory=datetime.now) # 最后访问时间 last_accessed: datetime = field(default_factory=datetime.now) # 最后访问时间
access_count: int = 0 # 访问次数 access_count: int = 0 # 访问次数
decay_factor: float = 1.0 # 衰减因子(随时间变化) decay_factor: float = 1.0 # 衰减因子(随时间变化)
@@ -245,6 +246,8 @@ class Memory:
# 确保重要性和激活度在有效范围内 # 确保重要性和激活度在有效范围内
self.importance = max(0.0, min(1.0, self.importance)) self.importance = max(0.0, min(1.0, self.importance))
self.activation = max(0.0, min(1.0, self.activation)) self.activation = max(0.0, min(1.0, self.activation))
if not self.updated_at:
self.updated_at = self.created_at
def to_dict(self) -> dict[str, Any]: def to_dict(self) -> dict[str, Any]:
"""转换为字典(用于序列化)""" """转换为字典(用于序列化)"""
@@ -258,6 +261,7 @@ class Memory:
"activation": self.activation, "activation": self.activation,
"status": self.status.value, "status": self.status.value,
"created_at": self.created_at.isoformat(), "created_at": self.created_at.isoformat(),
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
"last_accessed": self.last_accessed.isoformat(), "last_accessed": self.last_accessed.isoformat(),
"access_count": self.access_count, "access_count": self.access_count,
"decay_factor": self.decay_factor, "decay_factor": self.decay_factor,
@@ -278,6 +282,13 @@ class Memory:
# 备选:使用直接的 activation 字段 # 备选:使用直接的 activation 字段
activation_level = data.get("activation", 0.0) activation_level = data.get("activation", 0.0)
updated_at_raw = data.get("updated_at")
if updated_at_raw:
updated_at = datetime.fromisoformat(updated_at_raw)
else:
# 旧数据没有 updated_at退化为最后访问时间或创建时间
updated_at = datetime.fromisoformat(data.get("last_accessed", data["created_at"]))
return cls( return cls(
id=data["id"], id=data["id"],
subject_id=data["subject_id"], subject_id=data["subject_id"],
@@ -288,6 +299,7 @@ class Memory:
activation=activation_level, # 使用统一的激活度值 activation=activation_level, # 使用统一的激活度值
status=MemoryStatus(data.get("status", "staged")), status=MemoryStatus(data.get("status", "staged")),
created_at=datetime.fromisoformat(data["created_at"]), created_at=datetime.fromisoformat(data["created_at"]),
updated_at=updated_at,
last_accessed=datetime.fromisoformat(data.get("last_accessed", data["created_at"])), last_accessed=datetime.fromisoformat(data.get("last_accessed", data["created_at"])),
access_count=data.get("access_count", 0), access_count=data.get("access_count", 0),
decay_factor=data.get("decay_factor", 1.0), decay_factor=data.get("decay_factor", 1.0),

View File

@@ -26,7 +26,7 @@ from src.common.logger import get_logger
from src.plugin_system.base.base_chatter import BaseChatter from src.plugin_system.base.base_chatter import BaseChatter
from src.plugin_system.base.component_types import ChatType from src.plugin_system.base.component_types import ChatType
from .config import KFCMode, get_config from .config import KFCMode, apply_wait_duration_rules, get_config
from .models import SessionStatus from .models import SessionStatus
from .session import get_session_manager from .session import get_session_manager
@@ -179,6 +179,15 @@ class KokoroFlowChatter(BaseChatter):
) )
# 10. 执行动作 # 10. 执行动作
adjusted_wait = apply_wait_duration_rules(plan_response.max_wait_seconds)
if adjusted_wait != plan_response.max_wait_seconds:
logger.debug(
"[KFC] 调整等待时长: raw=%ss adjusted=%ss",
plan_response.max_wait_seconds,
adjusted_wait,
)
plan_response.max_wait_seconds = adjusted_wait
exec_results = [] exec_results = []
has_reply = False has_reply = False

View File

@@ -48,6 +48,9 @@ class WaitingDefaults:
# 最大等待时间 # 最大等待时间
max_wait_seconds: int = 1800 max_wait_seconds: int = 1800
# 等待时长倍率(>1 放大等待时间,<1 缩短)
wait_duration_multiplier: float = 1.0
@dataclass @dataclass
class ProactiveConfig: class ProactiveConfig:
@@ -202,6 +205,7 @@ def load_config() -> KokoroFlowChatterConfig:
default_max_wait_seconds=getattr(wait_cfg, 'default_max_wait_seconds', 300), default_max_wait_seconds=getattr(wait_cfg, 'default_max_wait_seconds', 300),
min_wait_seconds=getattr(wait_cfg, 'min_wait_seconds', 30), min_wait_seconds=getattr(wait_cfg, 'min_wait_seconds', 30),
max_wait_seconds=getattr(wait_cfg, 'max_wait_seconds', 1800), max_wait_seconds=getattr(wait_cfg, 'max_wait_seconds', 1800),
wait_duration_multiplier=getattr(wait_cfg, 'wait_duration_multiplier', 1.0),
) )
# 主动思考配置 - 支持 proactive 和 proactive_thinking 两种写法 # 主动思考配置 - 支持 proactive 和 proactive_thinking 两种写法
@@ -262,3 +266,29 @@ def reload_config() -> KokoroFlowChatterConfig:
global _config global _config
_config = load_config() _config = load_config()
return _config return _config
def apply_wait_duration_rules(raw_wait_seconds: int) -> int:
"""根据配置计算最终的等待时间"""
if raw_wait_seconds <= 0:
return 0
waiting_cfg = get_config().waiting
multiplier = max(waiting_cfg.wait_duration_multiplier, 0.0)
if multiplier == 0:
return 0
adjusted = int(round(raw_wait_seconds * multiplier))
min_wait = max(0, waiting_cfg.min_wait_seconds)
max_wait = max(waiting_cfg.max_wait_seconds, 0)
if max_wait > 0 and min_wait > 0 and max_wait < min_wait:
max_wait = min_wait
if max_wait > 0:
adjusted = min(adjusted, max_wait)
if min_wait > 0:
adjusted = max(adjusted, min_wait)
return max(adjusted, 0)

View File

@@ -24,7 +24,7 @@ from src.common.logger import get_logger
from src.config.config import global_config from src.config.config import global_config
from src.plugin_system.apis.unified_scheduler import TriggerType, unified_scheduler from src.plugin_system.apis.unified_scheduler import TriggerType, unified_scheduler
from .config import KFCMode, get_config from .config import KFCMode, apply_wait_duration_rules, get_config
from .models import EventType, SessionStatus from .models import EventType, SessionStatus
from .session import KokoroSession, get_session_manager from .session import KokoroSession, get_session_manager
@@ -460,6 +460,15 @@ class ProactiveThinker:
action.params["thought"] = plan_response.thought action.params["thought"] = plan_response.thought
action.params["situation_type"] = "timeout" action.params["situation_type"] = "timeout"
action.params["extra_context"] = extra_context action.params["extra_context"] = extra_context
adjusted_wait = apply_wait_duration_rules(plan_response.max_wait_seconds)
if adjusted_wait != plan_response.max_wait_seconds:
logger.debug(
"[ProactiveThinker] 调整超时等待: raw=%ss adjusted=%ss",
plan_response.max_wait_seconds,
adjusted_wait,
)
plan_response.max_wait_seconds = adjusted_wait
# ★ 在执行动作前最后一次检查状态,防止与 Chatter 并发 # ★ 在执行动作前最后一次检查状态,防止与 Chatter 并发
if session.status != SessionStatus.WAITING: if session.status != SessionStatus.WAITING:
@@ -683,6 +692,15 @@ class ProactiveThinker:
action.params["thought"] = plan_response.thought action.params["thought"] = plan_response.thought
action.params["situation_type"] = "proactive" action.params["situation_type"] = "proactive"
action.params["extra_context"] = extra_context action.params["extra_context"] = extra_context
adjusted_wait = apply_wait_duration_rules(plan_response.max_wait_seconds)
if adjusted_wait != plan_response.max_wait_seconds:
logger.debug(
"[ProactiveThinker] 调整主动等待: raw=%ss adjusted=%ss",
plan_response.max_wait_seconds,
adjusted_wait,
)
plan_response.max_wait_seconds = adjusted_wait
# 执行动作(回复生成在 Action.execute() 中完成) # 执行动作(回复生成在 Action.execute() 中完成)
for action in plan_response.actions: for action in plan_response.actions:

View File

@@ -1,5 +1,5 @@
[inner] [inner]
version = "7.9.6" version = "7.9.7"
#----以下是给开发人员阅读的如果你只是部署了MoFox-Bot不需要阅读---- #----以下是给开发人员阅读的如果你只是部署了MoFox-Bot不需要阅读----
#如果你想要修改配置文件请递增version的值 #如果你想要修改配置文件请递增version的值
@@ -622,6 +622,13 @@ mode = "split"
max_wait_seconds_default = 300 # 默认的最大等待秒数AI发送消息后愿意等待用户回复的时间 max_wait_seconds_default = 300 # 默认的最大等待秒数AI发送消息后愿意等待用户回复的时间
enable_continuous_thinking = true # 是否在等待期间启用心理活动更新 enable_continuous_thinking = true # 是否在等待期间启用心理活动更新
# --- 等待策略 ---
[kokoro_flow_chatter.waiting]
default_max_wait_seconds = 300 # LLM 未给出等待时间时的默认值
min_wait_seconds = 30 # 允许的最短等待时间,防止太快打扰用户
max_wait_seconds = 1800 # 允许的最长等待时间(秒)
wait_duration_multiplier = 1.0 # 对 LLM 给出的等待时间应用的倍率(>1 放大,<1 缩短)
# --- 私聊专属主动思考配置 --- # --- 私聊专属主动思考配置 ---
# 注意这是KFC专属的主动思考配置只有当KFC启用时才生效。 # 注意这是KFC专属的主动思考配置只有当KFC启用时才生效。
# 它旨在模拟更真实、情感驱动的互动,而非简单的定时任务。 # 它旨在模拟更真实、情感驱动的互动,而非简单的定时任务。