feat(waiting): 添加等待策略配置,支持最大、最小等待时间及倍率调整
This commit is contained in:
@@ -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},请你清楚你的身份,分清对方到底有没有叫你*
|
||||||
|
|
||||||
现在,你说:
|
现在,你说:
|
||||||
""",
|
""",
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -461,6 +461,15 @@ class ProactiveThinker:
|
|||||||
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:
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -684,6 +693,15 @@ class ProactiveThinker:
|
|||||||
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:
|
||||||
await action_manager.execute_action(
|
await action_manager.execute_action(
|
||||||
|
|||||||
@@ -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启用时才生效。
|
||||||
# 它旨在模拟更真实、情感驱动的互动,而非简单的定时任务。
|
# 它旨在模拟更真实、情感驱动的互动,而非简单的定时任务。
|
||||||
|
|||||||
Reference in New Issue
Block a user