Merge branch 'feature/kfc' of https://github.com/MoFox-Studio/MoFox-Core into feature/kfc

This commit is contained in:
tt-P607
2025-12-01 14:45:46 +08:00
4 changed files with 84 additions and 6 deletions

View File

@@ -392,7 +392,13 @@ class ProactiveThinker:
) )
return return
logger.info(f"等待超时: user={session.user_id}") # 增加连续超时计数
session.consecutive_timeout_count += 1
logger.info(
f"[ProactiveThinker] 等待超时: user={session.user_id}, "
f"consecutive_timeout={session.consecutive_timeout_count}"
)
try: try:
# 获取用户名 # 获取用户名
@@ -410,6 +416,18 @@ class ProactiveThinker:
action_modifier = ActionModifier(action_manager, session.stream_id) action_modifier = ActionModifier(action_manager, session.stream_id)
await action_modifier.modify_actions(chatter_name="KokoroFlowChatter") await action_modifier.modify_actions(chatter_name="KokoroFlowChatter")
# 计算用户最后回复距今的时间
time_since_user_reply = None
if session.last_user_message_at:
time_since_user_reply = time.time() - session.last_user_message_at
# 构建超时上下文信息
extra_context = {
"consecutive_timeout_count": session.consecutive_timeout_count,
"time_since_user_reply": time_since_user_reply,
"time_since_user_reply_str": self._format_duration(time_since_user_reply) if time_since_user_reply else "未知",
}
# 根据模式选择生成方式 # 根据模式选择生成方式
if self._mode == KFCMode.UNIFIED: if self._mode == KFCMode.UNIFIED:
# 统一模式:单次 LLM 调用 # 统一模式:单次 LLM 调用
@@ -430,6 +448,7 @@ class ProactiveThinker:
situation_type="timeout", situation_type="timeout",
chat_stream=chat_stream, chat_stream=chat_stream,
available_actions=action_manager.get_using_actions(), available_actions=action_manager.get_using_actions(),
extra_context=extra_context,
) )
# 分离模式下需要注入上下文信息 # 分离模式下需要注入上下文信息
@@ -439,6 +458,7 @@ class ProactiveThinker:
action.params["user_name"] = user_name action.params["user_name"] = user_name
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.execute() 中完成) # 执行动作(回复生成在 Action.execute() 中完成)
for action in plan_response.actions: for action in plan_response.actions:
@@ -477,7 +497,8 @@ class ProactiveThinker:
logger.info( logger.info(
f"[ProactiveThinker] 超时决策完成: user={session.user_id}, " f"[ProactiveThinker] 超时决策完成: user={session.user_id}, "
f"actions={[a.type for a in plan_response.actions]}, " f"actions={[a.type for a in plan_response.actions]}, "
f"continue_wait={plan_response.max_wait_seconds > 0}" f"continue_wait={plan_response.max_wait_seconds > 0}, "
f"consecutive_timeout={session.consecutive_timeout_count}"
) )
except Exception as e: except Exception as e:
@@ -716,6 +737,23 @@ class ProactiveThinker:
# 回退到 user_id # 回退到 user_id
return user_id return user_id
def _format_duration(self, seconds: float | None) -> str:
"""格式化时间间隔为人类可读的字符串"""
if seconds is None or seconds < 0:
return "未知"
if seconds < 60:
return f"{int(seconds)}"
elif seconds < 3600:
minutes = seconds / 60
return f"{minutes:.0f} 分钟"
elif seconds < 86400:
hours = seconds / 3600
return f"{hours:.1f} 小时"
else:
days = seconds / 86400
return f"{days:.1f}"
def get_stats(self) -> dict: def get_stats(self) -> dict:
"""获取统计信息""" """获取统计信息"""
return { return {

View File

@@ -531,6 +531,24 @@ class PromptBuilder:
elapsed = session.waiting_config.get_elapsed_seconds() elapsed = session.waiting_config.get_elapsed_seconds()
max_wait = session.waiting_config.max_wait_seconds max_wait = session.waiting_config.max_wait_seconds
expected = session.waiting_config.expected_reaction expected = session.waiting_config.expected_reaction
# 构建连续超时上下文
timeout_context_parts = []
# 添加连续超时次数信息
consecutive_count = extra_context.get("consecutive_timeout_count", 0)
if consecutive_count > 1:
timeout_context_parts.append(f"⚠️ 这已经是你连续第 {consecutive_count} 次等到超时了。")
# 添加距离用户上次回复的时间
time_since_user_reply_str = extra_context.get("time_since_user_reply_str")
if time_since_user_reply_str:
timeout_context_parts.append(f"距离 {user_name} 上一次回复你已经过去了 {time_since_user_reply_str}")
timeout_context = "\n".join(timeout_context_parts)
if timeout_context:
timeout_context = "\n" + timeout_context + "\n"
return await global_prompt_manager.format_prompt( return await global_prompt_manager.format_prompt(
PROMPT_NAMES["situation_timeout"], PROMPT_NAMES["situation_timeout"],
current_time=current_time, current_time=current_time,
@@ -538,6 +556,7 @@ class PromptBuilder:
elapsed_minutes=elapsed / 60, elapsed_minutes=elapsed / 60,
max_wait_minutes=max_wait / 60, max_wait_minutes=max_wait / 60,
expected_reaction=expected or "对方能回复点什么", expected_reaction=expected or "对方能回复点什么",
timeout_context=timeout_context,
) )
elif situation_type == "proactive": elif situation_type == "proactive":

View File

@@ -123,12 +123,15 @@ kfc_SITUATION_TIMEOUT = Prompt(
你之前发了消息后一直在等 {user_name} 的回复。 你之前发了消息后一直在等 {user_name} 的回复。
你原本打算最多等 {max_wait_minutes:.1f} 分钟,现在已经等了 {elapsed_minutes:.1f} 分钟了,对方还是没回。 你原本打算最多等 {max_wait_minutes:.1f} 分钟,现在已经等了 {elapsed_minutes:.1f} 分钟了,对方还是没回。
你当时期待的反应是:"{expected_reaction}" 你当时期待的反应是:"{expected_reaction}"
{timeout_context}
你需要决定: 你需要决定:
1. 继续等待(设置新的 max_wait_seconds 1. 继续等待(设置新的 max_wait_seconds
2. 主动说点什么打破沉默 2. 主动说点什么打破沉默
3. 做点别的事情(执行其他动作) 3. 做点别的事情(执行其他动作)
4. 算了不等了max_wait_seconds = 0""", 4. 算了不等了max_wait_seconds = 0
【注意】如果已经连续多次超时,对方可能暂时不方便回复。频繁主动发消息可能会打扰到对方。
考虑是否应该暂时放下期待,让对方有空间。""",
) )
kfc_SITUATION_PROACTIVE = Prompt( kfc_SITUATION_PROACTIVE = Prompt(

View File

@@ -67,6 +67,12 @@ class KokoroSession:
# 上次主动思考时间 # 上次主动思考时间
self.last_proactive_at: Optional[float] = None self.last_proactive_at: Optional[float] = None
# 连续超时计数(用于避免过度打扰用户)
self.consecutive_timeout_count: int = 0
# 用户最后发消息的时间(用于计算距离用户上次回复的时间)
self.last_user_message_at: Optional[float] = None
@property @property
def status(self) -> SessionStatus: def status(self) -> SessionStatus:
return self._status return self._status
@@ -95,14 +101,20 @@ class KokoroSession:
timestamp: Optional[float] = None, timestamp: Optional[float] = None,
) -> MentalLogEntry: ) -> MentalLogEntry:
"""添加用户消息事件""" """添加用户消息事件"""
msg_time = timestamp or time.time()
entry = MentalLogEntry( entry = MentalLogEntry(
event_type=EventType.USER_MESSAGE, event_type=EventType.USER_MESSAGE,
timestamp=timestamp or time.time(), timestamp=msg_time,
content=content, content=content,
user_name=user_name, user_name=user_name,
user_id=user_id, user_id=user_id,
) )
# 收到用户消息,重置连续超时计数
self.consecutive_timeout_count = 0
self.last_user_message_at = msg_time
# 如果之前在等待,记录收到回复的情况 # 如果之前在等待,记录收到回复的情况
if self.status == SessionStatus.WAITING and self.waiting_config.is_active(): if self.status == SessionStatus.WAITING and self.waiting_config.is_active():
elapsed = self.waiting_config.get_elapsed_seconds() elapsed = self.waiting_config.get_elapsed_seconds()
@@ -213,6 +225,8 @@ class KokoroSession:
"last_activity_at": self.last_activity_at, "last_activity_at": self.last_activity_at,
"total_interactions": self.total_interactions, "total_interactions": self.total_interactions,
"last_proactive_at": self.last_proactive_at, "last_proactive_at": self.last_proactive_at,
"consecutive_timeout_count": self.consecutive_timeout_count,
"last_user_message_at": self.last_user_message_at,
} }
@classmethod @classmethod
@@ -244,6 +258,10 @@ class KokoroSession:
session.total_interactions = data.get("total_interactions", 0) session.total_interactions = data.get("total_interactions", 0)
session.last_proactive_at = data.get("last_proactive_at") session.last_proactive_at = data.get("last_proactive_at")
# 连续超时相关
session.consecutive_timeout_count = data.get("consecutive_timeout_count", 0)
session.last_user_message_at = data.get("last_user_message_at")
return session return session