feat(context_builder): 更新记忆块构建逻辑,添加查询文本获取策略以提升记忆检索效果
feat(prompt_builder): 扩展上下文数据构建,支持会话和情况类型参数以优化提示词生成
This commit is contained in:
@@ -325,6 +325,10 @@ class KokoroFlowChatter(BaseChatter):
|
|||||||
"""
|
"""
|
||||||
if session.status == SessionStatus.WAITING:
|
if session.status == SessionStatus.WAITING:
|
||||||
# 之前在等待
|
# 之前在等待
|
||||||
|
# 如果 max_wait_seconds <= 0,说明不是有效的等待状态,视为新消息
|
||||||
|
if session.waiting_config.max_wait_seconds <= 0:
|
||||||
|
return "new_message"
|
||||||
|
|
||||||
if session.waiting_config.is_timeout():
|
if session.waiting_config.is_timeout():
|
||||||
# 超时了才收到回复
|
# 超时了才收到回复
|
||||||
return "reply_late"
|
return "reply_late"
|
||||||
|
|||||||
@@ -177,7 +177,12 @@ class KFCContextBuilder:
|
|||||||
return f"你与{sender_name}是普通朋友关系。"
|
return f"你与{sender_name}是普通朋友关系。"
|
||||||
|
|
||||||
async def _build_memory_block(self, chat_history: str, target_message: str) -> str:
|
async def _build_memory_block(self, chat_history: str, target_message: str) -> str:
|
||||||
"""构建记忆块(使用三层记忆系统)"""
|
"""构建记忆块(使用三层记忆系统)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_history: 聊天历史文本
|
||||||
|
target_message: 目标消息/查询文本。如果为空,将使用 chat_history 的前 200 字符作为查询
|
||||||
|
"""
|
||||||
config = _get_config()
|
config = _get_config()
|
||||||
|
|
||||||
if not (config.memory and config.memory.enable):
|
if not (config.memory and config.memory.enable):
|
||||||
@@ -192,8 +197,18 @@ class KFCContextBuilder:
|
|||||||
logger.debug("[三层记忆] 管理器未初始化")
|
logger.debug("[三层记忆] 管理器未初始化")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
# 如果 target_message 为空,使用 chat_history 的前 200 字符作为查询
|
||||||
|
query_text = target_message.strip() if target_message else ""
|
||||||
|
if not query_text and chat_history:
|
||||||
|
query_text = chat_history[:200].strip()
|
||||||
|
logger.debug(f"[三层记忆] target_message 为空,使用 chat_history 前 200 字符作为查询")
|
||||||
|
|
||||||
|
if not query_text:
|
||||||
|
logger.debug("[三层记忆] 没有可用的查询文本,跳过记忆搜索")
|
||||||
|
return ""
|
||||||
|
|
||||||
search_result = await unified_manager.search_memories(
|
search_result = await unified_manager.search_memories(
|
||||||
query_text=target_message,
|
query_text=query_text,
|
||||||
use_judge=True,
|
use_judge=True,
|
||||||
recent_chat_history=chat_history,
|
recent_chat_history=chat_history,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -73,7 +73,10 @@ class PromptBuilder:
|
|||||||
safety_guidelines_block = self._build_safety_guidelines_block()
|
safety_guidelines_block = self._build_safety_guidelines_block()
|
||||||
|
|
||||||
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
||||||
context_data = await self._build_context_data(user_name, chat_stream, user_id)
|
context_data = await self._build_context_data(
|
||||||
|
user_name, chat_stream, user_id,
|
||||||
|
session=session, situation_type=situation_type
|
||||||
|
)
|
||||||
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
||||||
memory_block = context_data.get("memory_block", "")
|
memory_block = context_data.get("memory_block", "")
|
||||||
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
||||||
@@ -148,7 +151,10 @@ class PromptBuilder:
|
|||||||
safety_guidelines_block = self._build_safety_guidelines_block()
|
safety_guidelines_block = self._build_safety_guidelines_block()
|
||||||
|
|
||||||
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
||||||
context_data = await self._build_context_data(user_name, chat_stream, user_id)
|
context_data = await self._build_context_data(
|
||||||
|
user_name, chat_stream, user_id,
|
||||||
|
session=session, situation_type=situation_type
|
||||||
|
)
|
||||||
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
||||||
memory_block = context_data.get("memory_block", "")
|
memory_block = context_data.get("memory_block", "")
|
||||||
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
||||||
@@ -259,11 +265,20 @@ class PromptBuilder:
|
|||||||
user_name: str,
|
user_name: str,
|
||||||
chat_stream: Optional["ChatStream"],
|
chat_stream: Optional["ChatStream"],
|
||||||
user_id: Optional[str] = None,
|
user_id: Optional[str] = None,
|
||||||
|
session: Optional[KokoroSession] = None,
|
||||||
|
situation_type: str = "new_message",
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
使用 KFCContextBuilder 构建完整的上下文数据
|
使用 KFCContextBuilder 构建完整的上下文数据
|
||||||
|
|
||||||
包括:关系信息、记忆、表达习惯等
|
包括:关系信息、记忆、表达习惯等
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_name: 用户名称
|
||||||
|
chat_stream: 聊天流对象
|
||||||
|
user_id: 用户ID
|
||||||
|
session: KokoroSession 会话对象(用于超时/主动思考场景)
|
||||||
|
situation_type: 情况类型(用于判断记忆搜索策略)
|
||||||
"""
|
"""
|
||||||
if not chat_stream:
|
if not chat_stream:
|
||||||
return {
|
return {
|
||||||
@@ -280,12 +295,13 @@ class PromptBuilder:
|
|||||||
|
|
||||||
builder = self._context_builder(chat_stream)
|
builder = self._context_builder(chat_stream)
|
||||||
|
|
||||||
# 获取最近的消息作为 target_message(用于记忆检索)
|
# 获取用于记忆检索的查询文本
|
||||||
target_message = ""
|
target_message = await self._get_memory_search_query(
|
||||||
if chat_stream.context:
|
chat_stream=chat_stream,
|
||||||
unread = chat_stream.context.get_unread_messages()
|
session=session,
|
||||||
if unread:
|
situation_type=situation_type,
|
||||||
target_message = unread[-1].processed_plain_text or unread[-1].display_message or ""
|
user_name=user_name,
|
||||||
|
)
|
||||||
|
|
||||||
context_data = await builder.build_all_context(
|
context_data = await builder.build_all_context(
|
||||||
sender_name=user_name,
|
sender_name=user_name,
|
||||||
@@ -304,6 +320,113 @@ class PromptBuilder:
|
|||||||
"expression_habits": "",
|
"expression_habits": "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def _get_memory_search_query(
|
||||||
|
self,
|
||||||
|
chat_stream: Optional["ChatStream"],
|
||||||
|
session: Optional[KokoroSession],
|
||||||
|
situation_type: str,
|
||||||
|
user_name: str,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
根据场景类型获取合适的记忆搜索查询文本
|
||||||
|
|
||||||
|
策略:
|
||||||
|
1. 优先使用未读消息(new_message/reply_in_time/reply_late)
|
||||||
|
2. 如果没有未读消息(timeout/proactive),使用最近的历史消息
|
||||||
|
3. 如果历史消息也为空,从 session 的 mental_log 中提取
|
||||||
|
4. 最后回退到用户名作为查询
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_stream: 聊天流对象
|
||||||
|
session: KokoroSession 会话对象
|
||||||
|
situation_type: 情况类型
|
||||||
|
user_name: 用户名称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
用于记忆搜索的查询文本
|
||||||
|
"""
|
||||||
|
target_message = ""
|
||||||
|
|
||||||
|
# 策略1: 优先从未读消息获取(适用于 new_message/reply_in_time/reply_late)
|
||||||
|
if chat_stream and chat_stream.context:
|
||||||
|
unread = chat_stream.context.get_unread_messages()
|
||||||
|
if unread:
|
||||||
|
target_message = unread[-1].processed_plain_text or unread[-1].display_message or ""
|
||||||
|
if target_message:
|
||||||
|
logger.debug(f"[记忆搜索] 使用未读消息作为查询: {target_message[:50]}...")
|
||||||
|
return target_message
|
||||||
|
|
||||||
|
# 策略2: 从最近的历史消息获取(适用于 timeout/proactive)
|
||||||
|
if chat_stream and chat_stream.context:
|
||||||
|
history_messages = chat_stream.context.history_messages
|
||||||
|
if history_messages:
|
||||||
|
# 获取最近的几条非通知消息,组合成查询
|
||||||
|
recent_texts = []
|
||||||
|
for msg in reversed(history_messages[-5:]):
|
||||||
|
content = getattr(msg, "processed_plain_text", "") or getattr(msg, "display_message", "")
|
||||||
|
if content and not getattr(msg, "is_notify", False):
|
||||||
|
recent_texts.append(content)
|
||||||
|
if len(recent_texts) >= 3:
|
||||||
|
break
|
||||||
|
|
||||||
|
if recent_texts:
|
||||||
|
target_message = " ".join(reversed(recent_texts))
|
||||||
|
logger.debug(f"[记忆搜索] 使用历史消息作为查询 (situation={situation_type}): {target_message[:80]}...")
|
||||||
|
return target_message
|
||||||
|
|
||||||
|
# 策略3: 从 session 的 mental_log 中提取(超时/主动思考场景的最后手段)
|
||||||
|
if session and situation_type in ("timeout", "proactive"):
|
||||||
|
entries = session.get_recent_entries(limit=10)
|
||||||
|
recent_texts = []
|
||||||
|
|
||||||
|
for entry in reversed(entries):
|
||||||
|
# 从用户消息中提取
|
||||||
|
if entry.event_type == EventType.USER_MESSAGE and entry.content:
|
||||||
|
recent_texts.append(entry.content)
|
||||||
|
# 从 bot 的预期反应中提取(可能包含相关话题)
|
||||||
|
elif entry.event_type == EventType.BOT_PLANNING and entry.expected_reaction:
|
||||||
|
recent_texts.append(entry.expected_reaction)
|
||||||
|
|
||||||
|
if len(recent_texts) >= 3:
|
||||||
|
break
|
||||||
|
|
||||||
|
if recent_texts:
|
||||||
|
target_message = " ".join(reversed(recent_texts))
|
||||||
|
logger.debug(f"[记忆搜索] 使用 mental_log 作为查询 (situation={situation_type}): {target_message[:80]}...")
|
||||||
|
return target_message
|
||||||
|
|
||||||
|
# 策略4: 最后回退 - 使用用户名 + 场景描述
|
||||||
|
if situation_type == "timeout":
|
||||||
|
target_message = f"与 {user_name} 的对话 等待回复"
|
||||||
|
elif situation_type == "proactive":
|
||||||
|
target_message = f"与 {user_name} 的对话 主动发起聊天"
|
||||||
|
else:
|
||||||
|
target_message = f"与 {user_name} 的对话"
|
||||||
|
|
||||||
|
logger.debug(f"[记忆搜索] 使用回退查询 (situation={situation_type}): {target_message}")
|
||||||
|
return target_message
|
||||||
|
|
||||||
|
def _get_latest_user_message(self, session: Optional[KokoroSession]) -> str:
|
||||||
|
"""
|
||||||
|
获取最新的用户消息内容
|
||||||
|
|
||||||
|
Args:
|
||||||
|
session: KokoroSession 会话对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
最新用户消息的内容,如果没有则返回提示文本
|
||||||
|
"""
|
||||||
|
if not session:
|
||||||
|
return "(未知消息)"
|
||||||
|
|
||||||
|
# 从 mental_log 中获取最新的用户消息
|
||||||
|
entries = session.get_recent_entries(limit=10)
|
||||||
|
for entry in reversed(entries):
|
||||||
|
if entry.event_type == EventType.USER_MESSAGE and entry.content:
|
||||||
|
return entry.content
|
||||||
|
|
||||||
|
return "(消息内容不可用)"
|
||||||
|
|
||||||
async def _build_chat_history_block(
|
async def _build_chat_history_block(
|
||||||
self,
|
self,
|
||||||
chat_stream: Optional["ChatStream"],
|
chat_stream: Optional["ChatStream"],
|
||||||
@@ -525,32 +648,39 @@ class PromptBuilder:
|
|||||||
situation_type = "new_message"
|
situation_type = "new_message"
|
||||||
|
|
||||||
if situation_type == "new_message":
|
if situation_type == "new_message":
|
||||||
|
# 获取最新消息内容
|
||||||
|
latest_message = self._get_latest_user_message(session)
|
||||||
return await global_prompt_manager.format_prompt(
|
return await global_prompt_manager.format_prompt(
|
||||||
PROMPT_NAMES["situation_new_message"],
|
PROMPT_NAMES["situation_new_message"],
|
||||||
current_time=current_time,
|
current_time=current_time,
|
||||||
user_name=user_name,
|
user_name=user_name,
|
||||||
|
latest_message=latest_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif situation_type == "reply_in_time":
|
elif situation_type == "reply_in_time":
|
||||||
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
|
||||||
|
latest_message = self._get_latest_user_message(session)
|
||||||
return await global_prompt_manager.format_prompt(
|
return await global_prompt_manager.format_prompt(
|
||||||
PROMPT_NAMES["situation_reply_in_time"],
|
PROMPT_NAMES["situation_reply_in_time"],
|
||||||
current_time=current_time,
|
current_time=current_time,
|
||||||
user_name=user_name,
|
user_name=user_name,
|
||||||
elapsed_minutes=elapsed / 60,
|
elapsed_minutes=elapsed / 60,
|
||||||
max_wait_minutes=max_wait / 60,
|
max_wait_minutes=max_wait / 60,
|
||||||
|
latest_message=latest_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif situation_type == "reply_late":
|
elif situation_type == "reply_late":
|
||||||
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
|
||||||
|
latest_message = self._get_latest_user_message(session)
|
||||||
return await global_prompt_manager.format_prompt(
|
return await global_prompt_manager.format_prompt(
|
||||||
PROMPT_NAMES["situation_reply_late"],
|
PROMPT_NAMES["situation_reply_late"],
|
||||||
current_time=current_time,
|
current_time=current_time,
|
||||||
user_name=user_name,
|
user_name=user_name,
|
||||||
elapsed_minutes=elapsed / 60,
|
elapsed_minutes=elapsed / 60,
|
||||||
max_wait_minutes=max_wait / 60,
|
max_wait_minutes=max_wait / 60,
|
||||||
|
latest_message=latest_message,
|
||||||
)
|
)
|
||||||
|
|
||||||
elif situation_type == "timeout":
|
elif situation_type == "timeout":
|
||||||
@@ -911,7 +1041,10 @@ class PromptBuilder:
|
|||||||
safety_guidelines_block = self._build_safety_guidelines_block()
|
safety_guidelines_block = self._build_safety_guidelines_block()
|
||||||
|
|
||||||
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
# 2. 使用 context_builder 获取关系、记忆、表达习惯等
|
||||||
context_data = await self._build_context_data(user_name, chat_stream, user_id)
|
context_data = await self._build_context_data(
|
||||||
|
user_name, chat_stream, user_id,
|
||||||
|
session=session, situation_type=situation_type
|
||||||
|
)
|
||||||
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
relation_block = context_data.get("relation_info", f"你与 {user_name} 还不太熟悉,这是早期的交流阶段。")
|
||||||
memory_block = context_data.get("memory_block", "")
|
memory_block = context_data.get("memory_block", "")
|
||||||
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
expression_habits = self._build_combined_expression_block(context_data.get("expression_habits", ""))
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ kfc_SITUATION_NEW_MESSAGE = Prompt(
|
|||||||
name="kfc_situation_new_message",
|
name="kfc_situation_new_message",
|
||||||
template="""现在是 {current_time}。
|
template="""现在是 {current_time}。
|
||||||
|
|
||||||
{user_name} 刚刚给你发了消息。这是一次新的对话发起(不是对你之前消息的回复)。
|
{user_name} 刚刚给你发了消息:「{latest_message}」
|
||||||
|
|
||||||
|
这是一次新的对话发起(不是对你之前消息的回复)。
|
||||||
|
|
||||||
请决定你要怎么回应。你可以:
|
请决定你要怎么回应。你可以:
|
||||||
- 发送文字消息回复
|
- 发送文字消息回复
|
||||||
@@ -103,7 +105,7 @@ kfc_SITUATION_REPLY_IN_TIME = Prompt(
|
|||||||
|
|
||||||
你之前发了消息后一直在等 {user_name} 的回复。
|
你之前发了消息后一直在等 {user_name} 的回复。
|
||||||
等了大约 {elapsed_minutes:.1f} 分钟(你原本打算最多等 {max_wait_minutes:.1f} 分钟)。
|
等了大约 {elapsed_minutes:.1f} 分钟(你原本打算最多等 {max_wait_minutes:.1f} 分钟)。
|
||||||
现在 {user_name} 回复了!
|
现在 {user_name} 回复了:「{latest_message}」
|
||||||
|
|
||||||
请决定你接下来要怎么回应。""",
|
请决定你接下来要怎么回应。""",
|
||||||
)
|
)
|
||||||
@@ -114,7 +116,7 @@ kfc_SITUATION_REPLY_LATE = Prompt(
|
|||||||
|
|
||||||
你之前发了消息后在等 {user_name} 的回复。
|
你之前发了消息后在等 {user_name} 的回复。
|
||||||
你原本打算最多等 {max_wait_minutes:.1f} 分钟,但实际等了 {elapsed_minutes:.1f} 分钟才收到回复。
|
你原本打算最多等 {max_wait_minutes:.1f} 分钟,但实际等了 {elapsed_minutes:.1f} 分钟才收到回复。
|
||||||
虽然有点迟,但 {user_name} 终于回复了。
|
虽然有点迟,但 {user_name} 终于回复了:「{latest_message}」
|
||||||
|
|
||||||
请决定你接下来要怎么回应。(可以选择轻轻抱怨一下迟到,也可以装作没在意)""",
|
请决定你接下来要怎么回应。(可以选择轻轻抱怨一下迟到,也可以装作没在意)""",
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user