feat: 增加连续超时计数和用户最后回复时间,优化超时决策上下文信息

This commit is contained in:
Windpicker-owo
2025-12-01 09:20:03 +08:00
parent 35c4937068
commit 0154c8fedd
4 changed files with 83 additions and 5 deletions

View File

@@ -385,7 +385,13 @@ class ProactiveThinker:
)
return
logger.info(f"[ProactiveThinker] 等待超时: 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:
# 获取用户名
@@ -403,6 +409,18 @@ class ProactiveThinker:
action_modifier = ActionModifier(action_manager, session.stream_id)
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 "未知",
}
# 调用 Planner 生成超时决策
plan_response = await generate_plan(
session=session,
@@ -410,6 +428,7 @@ class ProactiveThinker:
situation_type="timeout",
chat_stream=chat_stream,
available_actions=action_manager.get_using_actions(),
extra_context=extra_context,
)
# 为 kfc_reply 动作注入必要的上下文信息
@@ -419,6 +438,7 @@ class ProactiveThinker:
action.params["user_name"] = user_name
action.params["thought"] = plan_response.thought
action.params["situation_type"] = "timeout"
action.params["extra_context"] = extra_context
# 执行动作(回复生成在 Action.execute() 中完成)
for action in plan_response.actions:
@@ -457,7 +477,8 @@ class ProactiveThinker:
logger.info(
f"[ProactiveThinker] 超时决策完成: user={session.user_id}, "
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:
@@ -681,6 +702,23 @@ class ProactiveThinker:
# 回退到 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:
"""获取统计信息"""
return {

View File

@@ -531,6 +531,24 @@ class PromptBuilder:
elapsed = session.waiting_config.get_elapsed_seconds()
max_wait = session.waiting_config.max_wait_seconds
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(
PROMPT_NAMES["situation_timeout"],
current_time=current_time,
@@ -538,6 +556,7 @@ class PromptBuilder:
elapsed_minutes=elapsed / 60,
max_wait_minutes=max_wait / 60,
expected_reaction=expected or "对方能回复点什么",
timeout_context=timeout_context,
)
elif situation_type == "proactive":

View File

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

View File

@@ -67,6 +67,12 @@ class KokoroSession:
# 上次主动思考时间
self.last_proactive_at: Optional[float] = None
# 连续超时计数(用于避免过度打扰用户)
self.consecutive_timeout_count: int = 0
# 用户最后发消息的时间(用于计算距离用户上次回复的时间)
self.last_user_message_at: Optional[float] = None
@property
def status(self) -> SessionStatus:
return self._status
@@ -95,14 +101,20 @@ class KokoroSession:
timestamp: Optional[float] = None,
) -> MentalLogEntry:
"""添加用户消息事件"""
msg_time = timestamp or time.time()
entry = MentalLogEntry(
event_type=EventType.USER_MESSAGE,
timestamp=timestamp or time.time(),
timestamp=msg_time,
content=content,
user_name=user_name,
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():
elapsed = self.waiting_config.get_elapsed_seconds()
@@ -213,6 +225,8 @@ class KokoroSession:
"last_activity_at": self.last_activity_at,
"total_interactions": self.total_interactions,
"last_proactive_at": self.last_proactive_at,
"consecutive_timeout_count": self.consecutive_timeout_count,
"last_user_message_at": self.last_user_message_at,
}
@classmethod
@@ -244,6 +258,10 @@ class KokoroSession:
session.total_interactions = data.get("total_interactions", 0)
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