feat:进一步优化缓存
This commit is contained in:
@@ -108,30 +108,6 @@ class BaseMaisakaReplyGenerator:
|
|||||||
del message
|
del message
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _strip_guided_reply_formatting(content: str) -> str:
|
|
||||||
"""移除 guided_reply 可见文本中的引用包装,仅保留真正回复正文。"""
|
|
||||||
|
|
||||||
normalized_content = content.strip()
|
|
||||||
if not normalized_content:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
reply_body_marker = "[发言内容]"
|
|
||||||
if normalized_content.startswith("[引用]quote_id=") or normalized_content.startswith("[引用消息]"):
|
|
||||||
marker_index = normalized_content.find(reply_body_marker)
|
|
||||||
if marker_index >= 0:
|
|
||||||
return normalized_content[marker_index + len(reply_body_marker) :].strip()
|
|
||||||
|
|
||||||
newline_index = normalized_content.find("\n")
|
|
||||||
if newline_index < 0:
|
|
||||||
return ""
|
|
||||||
normalized_content = normalized_content[newline_index + 1 :].lstrip()
|
|
||||||
|
|
||||||
if normalized_content.startswith(reply_body_marker):
|
|
||||||
normalized_content = normalized_content[len(reply_body_marker) :].lstrip()
|
|
||||||
|
|
||||||
return normalized_content.strip()
|
|
||||||
|
|
||||||
def _extract_guided_bot_reply(self, message: SessionBackedMessage) -> str:
|
def _extract_guided_bot_reply(self, message: SessionBackedMessage) -> str:
|
||||||
# 只能根据结构化来源字段判断是否为 bot 自身写回的历史消息,
|
# 只能根据结构化来源字段判断是否为 bot 自身写回的历史消息,
|
||||||
# 不能依赖昵称/群名片等可控文本,避免误判和提示注入。
|
# 不能依赖昵称/群名片等可控文本,避免误判和提示注入。
|
||||||
@@ -140,9 +116,7 @@ class BaseMaisakaReplyGenerator:
|
|||||||
|
|
||||||
plain_text = message.processed_plain_text.strip()
|
plain_text = message.processed_plain_text.strip()
|
||||||
_, body = parse_speaker_content(plain_text)
|
_, body = parse_speaker_content(plain_text)
|
||||||
normalized_body = self._strip_guided_reply_formatting(body) or self._strip_guided_reply_formatting(
|
normalized_body = body.strip()
|
||||||
plain_text
|
|
||||||
)
|
|
||||||
return self._normalize_content(normalized_body) if normalized_body else ""
|
return self._normalize_content(normalized_body) if normalized_body else ""
|
||||||
|
|
||||||
def _build_target_message_block(self, reply_message: Optional[SessionMessage]) -> str:
|
def _build_target_message_block(self, reply_message: Optional[SessionMessage]) -> str:
|
||||||
|
|||||||
@@ -244,7 +244,10 @@ class BuiltinToolRuntimeContext:
|
|||||||
history_message = SessionBackedMessage.from_session_message(
|
history_message = SessionBackedMessage.from_session_message(
|
||||||
message,
|
message,
|
||||||
raw_message=build_prefixed_message_sequence(message.raw_message, planner_prefix),
|
raw_message=build_prefixed_message_sequence(message.raw_message, planner_prefix),
|
||||||
visible_text=build_session_message_visible_text(message),
|
visible_text=build_session_message_visible_text(
|
||||||
|
message,
|
||||||
|
include_reply_components=source_kind != "guided_reply",
|
||||||
|
),
|
||||||
source_kind=source_kind,
|
source_kind=source_kind,
|
||||||
)
|
)
|
||||||
self.runtime._chat_history.append(history_message)
|
self.runtime._chat_history.append(history_message)
|
||||||
|
|||||||
@@ -650,7 +650,6 @@ class MaisakaChatLoopService:
|
|||||||
|
|
||||||
selected_indices.reverse()
|
selected_indices.reverse()
|
||||||
selected_history = [filtered_history[index] for index in selected_indices]
|
selected_history = [filtered_history[index] for index in selected_indices]
|
||||||
selected_history, _ = MaisakaChatLoopService._hide_early_assistant_messages(selected_history)
|
|
||||||
selected_history, _ = drop_orphan_tool_results(selected_history)
|
selected_history, _ = drop_orphan_tool_results(selected_history)
|
||||||
selected_history, _ = normalize_tool_result_order(selected_history)
|
selected_history, _ = normalize_tool_result_order(selected_history)
|
||||||
tool_message_count = sum(1 for message in selected_history if isinstance(message, ToolResultMessage))
|
tool_message_count = sum(1 for message in selected_history if isinstance(message, ToolResultMessage))
|
||||||
@@ -709,38 +708,3 @@ class MaisakaChatLoopService:
|
|||||||
return resolve_enable_visual_planner()
|
return resolve_enable_visual_planner()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _hide_early_assistant_messages(
|
|
||||||
selected_history: List[LLMContextMessage],
|
|
||||||
) -> tuple[List[LLMContextMessage], int]:
|
|
||||||
"""隐藏上下文中最早 50% 的 assistant 文本消息,但保留工具调用链路。"""
|
|
||||||
|
|
||||||
assistant_indices = [
|
|
||||||
index
|
|
||||||
for index, message in enumerate(selected_history)
|
|
||||||
if isinstance(message, AssistantMessage)
|
|
||||||
]
|
|
||||||
hidden_assistant_count = len(assistant_indices) // 2
|
|
||||||
if hidden_assistant_count <= 0:
|
|
||||||
return selected_history, 0
|
|
||||||
|
|
||||||
removed_assistant_indices = set(assistant_indices[:hidden_assistant_count])
|
|
||||||
|
|
||||||
filtered_history: List[LLMContextMessage] = []
|
|
||||||
for index, message in enumerate(selected_history):
|
|
||||||
if index in removed_assistant_indices:
|
|
||||||
if not message.tool_calls:
|
|
||||||
continue
|
|
||||||
filtered_history.append(
|
|
||||||
AssistantMessage(
|
|
||||||
content="",
|
|
||||||
timestamp=message.timestamp,
|
|
||||||
tool_calls=list(message.tool_calls),
|
|
||||||
source_kind=message.source_kind,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
filtered_history.append(message)
|
|
||||||
|
|
||||||
return filtered_history, hidden_assistant_count
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
"""Maisaka 历史消息轮次结束后处理。"""
|
"""Maisaka 历史消息轮次结束后处理。"""
|
||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
from .context_messages import AssistantMessage, LLMContextMessage
|
from .context_messages import AssistantMessage, LLMContextMessage
|
||||||
from .history_utils import drop_leading_orphan_tool_results, drop_orphan_tool_results, normalize_tool_result_order
|
from .history_utils import drop_leading_orphan_tool_results, drop_orphan_tool_results, normalize_tool_result_order
|
||||||
|
|
||||||
EARLY_TRIM_RATIO = 0.2
|
EARLY_TRIM_RATIO = 0.3
|
||||||
|
TRIM_THRESHOLD_RATIO = 1.2
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
@@ -26,27 +28,32 @@ def process_chat_history_after_cycle(
|
|||||||
"""在每轮结束后统一执行历史裁切与清理。"""
|
"""在每轮结束后统一执行历史裁切与清理。"""
|
||||||
|
|
||||||
processed_history = list(chat_history)
|
processed_history = list(chat_history)
|
||||||
removed_assistant_thought_count = _remove_early_assistant_thoughts(processed_history)
|
processed_history, normalized_removed_count, moved_tool_result_count = _normalize_history_structure(
|
||||||
|
processed_history
|
||||||
processed_history, orphan_removed_count = drop_orphan_tool_results(processed_history)
|
|
||||||
processed_history, moved_tool_result_count = normalize_tool_result_order(processed_history)
|
|
||||||
remaining_context_count = sum(1 for message in processed_history if message.count_in_context)
|
|
||||||
removed_overflow_count = 0
|
|
||||||
|
|
||||||
while remaining_context_count > max_context_size and processed_history:
|
|
||||||
removed_message = processed_history.pop(0)
|
|
||||||
removed_overflow_count += 1
|
|
||||||
if removed_message.count_in_context:
|
|
||||||
remaining_context_count -= 1
|
|
||||||
|
|
||||||
processed_history, leading_orphan_removed_count = drop_leading_orphan_tool_results(processed_history)
|
|
||||||
removed_overflow_count += leading_orphan_removed_count
|
|
||||||
remaining_context_count = sum(1 for message in processed_history if message.count_in_context)
|
|
||||||
removed_count = (
|
|
||||||
removed_assistant_thought_count
|
|
||||||
+ orphan_removed_count
|
|
||||||
+ removed_overflow_count
|
|
||||||
)
|
)
|
||||||
|
remaining_context_count = sum(1 for message in processed_history if message.count_in_context)
|
||||||
|
|
||||||
|
compact_removed_count = 0
|
||||||
|
trim_threshold = ceil(max_context_size * TRIM_THRESHOLD_RATIO)
|
||||||
|
if remaining_context_count > trim_threshold:
|
||||||
|
removed_early_message_count = _remove_early_history_messages(processed_history)
|
||||||
|
processed_history, removed_after_message_trim_count, moved_after_message_trim_count = (
|
||||||
|
_normalize_history_structure(processed_history)
|
||||||
|
)
|
||||||
|
removed_assistant_thought_count = _remove_early_assistant_thoughts(processed_history)
|
||||||
|
processed_history, removed_after_thought_trim_count, moved_after_thought_trim_count = (
|
||||||
|
_normalize_history_structure(processed_history)
|
||||||
|
)
|
||||||
|
compact_removed_count = (
|
||||||
|
removed_early_message_count
|
||||||
|
+ removed_after_message_trim_count
|
||||||
|
+ removed_assistant_thought_count
|
||||||
|
+ removed_after_thought_trim_count
|
||||||
|
)
|
||||||
|
moved_tool_result_count += moved_after_message_trim_count + moved_after_thought_trim_count
|
||||||
|
|
||||||
|
remaining_context_count = sum(1 for message in processed_history if message.count_in_context)
|
||||||
|
removed_count = normalized_removed_count + compact_removed_count
|
||||||
changed_count = removed_count + moved_tool_result_count
|
changed_count = removed_count + moved_tool_result_count
|
||||||
return HistoryPostProcessResult(
|
return HistoryPostProcessResult(
|
||||||
history=processed_history,
|
history=processed_history,
|
||||||
@@ -56,8 +63,34 @@ def process_chat_history_after_cycle(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_history_structure(
|
||||||
|
chat_history: list[LLMContextMessage],
|
||||||
|
) -> tuple[list[LLMContextMessage], int, int]:
|
||||||
|
"""规范化历史消息结构,保证工具调用链符合 LLM 消息协议。"""
|
||||||
|
|
||||||
|
processed_history, orphan_removed_count = drop_orphan_tool_results(chat_history)
|
||||||
|
processed_history, moved_tool_result_count = normalize_tool_result_order(processed_history)
|
||||||
|
processed_history, leading_orphan_removed_count = drop_leading_orphan_tool_results(processed_history)
|
||||||
|
return (
|
||||||
|
processed_history,
|
||||||
|
orphan_removed_count + leading_orphan_removed_count,
|
||||||
|
moved_tool_result_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _remove_early_history_messages(chat_history: list[LLMContextMessage]) -> int:
|
||||||
|
"""移除最早 30% 的全部历史消息。"""
|
||||||
|
|
||||||
|
remove_count = int(len(chat_history) * EARLY_TRIM_RATIO)
|
||||||
|
if remove_count <= 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
del chat_history[:remove_count]
|
||||||
|
return remove_count
|
||||||
|
|
||||||
|
|
||||||
def _remove_early_assistant_thoughts(chat_history: list[LLMContextMessage]) -> int:
|
def _remove_early_assistant_thoughts(chat_history: list[LLMContextMessage]) -> int:
|
||||||
"""移除最早 20% 的非工具 assistant 思考内容。"""
|
"""移除最早 30% 的非工具 assistant 思考内容。"""
|
||||||
|
|
||||||
candidate_indexes = [
|
candidate_indexes = [
|
||||||
index
|
index
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from src.common.data_models.message_component_data_model import MessageSequence, TextComponent
|
from src.common.data_models.message_component_data_model import MessageSequence, ReplyComponent, TextComponent
|
||||||
|
|
||||||
from .context_messages import AssistantMessage, LLMContextMessage, ToolResultMessage
|
from .context_messages import AssistantMessage, LLMContextMessage, ToolResultMessage
|
||||||
from .message_adapter import build_visible_text_from_sequence, clone_message_sequence, format_speaker_content
|
from .message_adapter import build_visible_text_from_sequence, clone_message_sequence, format_speaker_content
|
||||||
@@ -28,6 +28,8 @@ def build_prefixed_message_sequence(
|
|||||||
def build_session_message_visible_text(
|
def build_session_message_visible_text(
|
||||||
message: "SessionMessage",
|
message: "SessionMessage",
|
||||||
source_sequence: MessageSequence | None = None,
|
source_sequence: MessageSequence | None = None,
|
||||||
|
*,
|
||||||
|
include_reply_components: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""将真实会话消息转换为 Maisaka 可见文本。"""
|
"""将真实会话消息转换为 Maisaka 可见文本。"""
|
||||||
|
|
||||||
@@ -46,6 +48,8 @@ def build_session_message_visible_text(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
for component in clone_message_sequence(normalized_sequence).components:
|
for component in clone_message_sequence(normalized_sequence).components:
|
||||||
|
if not include_reply_components and isinstance(component, ReplyComponent):
|
||||||
|
continue
|
||||||
visible_sequence.components.append(component)
|
visible_sequence.components.append(component)
|
||||||
return build_visible_text_from_sequence(visible_sequence).strip()
|
return build_visible_text_from_sequence(visible_sequence).strip()
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,10 @@ class MaisakaHeartFlowChatting:
|
|||||||
history_message = SessionBackedMessage.from_session_message(
|
history_message = SessionBackedMessage.from_session_message(
|
||||||
message,
|
message,
|
||||||
raw_message=build_prefixed_message_sequence(message.raw_message, planner_prefix),
|
raw_message=build_prefixed_message_sequence(message.raw_message, planner_prefix),
|
||||||
visible_text=build_session_message_visible_text(message),
|
visible_text=build_session_message_visible_text(
|
||||||
|
message,
|
||||||
|
include_reply_components=source_kind != "guided_reply",
|
||||||
|
),
|
||||||
source_kind=source_kind,
|
source_kind=source_kind,
|
||||||
)
|
)
|
||||||
self._chat_history.append(history_message)
|
self._chat_history.append(history_message)
|
||||||
|
|||||||
Reference in New Issue
Block a user