Merge branch 'feature/kfc' of https://github.com/MoFox-Studio/MoFox-Core into feature/kfc
This commit is contained in:
@@ -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,7 +448,8 @@ 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,
|
||||||
|
)
|
||||||
|
|
||||||
# 分离模式下需要注入上下文信息
|
# 分离模式下需要注入上下文信息
|
||||||
for action in plan_response.actions:
|
for action in plan_response.actions:
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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":
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -66,6 +66,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:
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user