[AC-MEMORY-SUMMARY] feat(mid): 新增对话记忆摘要生成器

- 新增 MemorySummaryGenerator 用于生成对话摘要
- 新增 memory_summary_prompt 提供摘要生成提示词模板
- 支持将长对话历史压缩为简洁摘要
- 更新 mid 服务模块导出
This commit is contained in:
MerCry 2026-03-11 19:01:06 +08:00
parent e9de808969
commit e45396e1e4
3 changed files with 106 additions and 0 deletions

View File

@ -14,6 +14,7 @@ from .metrics_collector import MetricsCollector, SessionMetrics, AggregatedMetri
from .tool_registry import ToolRegistry, ToolDefinition, ToolExecutionResult, get_tool_registry, init_tool_registry from .tool_registry import ToolRegistry, ToolDefinition, ToolExecutionResult, get_tool_registry, init_tool_registry
from .tool_call_recorder import ToolCallRecorder, ToolCallStatistics, get_tool_call_recorder from .tool_call_recorder import ToolCallRecorder, ToolCallStatistics, get_tool_call_recorder
from .memory_adapter import MemoryAdapter, UserMemory from .memory_adapter import MemoryAdapter, UserMemory
from .memory_summary_generator import MemorySummaryGenerator
from .default_kb_tool_runner import DefaultKbToolRunner, KbToolResult, KbToolConfig, get_default_kb_tool_runner from .default_kb_tool_runner import DefaultKbToolRunner, KbToolResult, KbToolConfig, get_default_kb_tool_runner
from .segment_humanizer import SegmentHumanizer, HumanizeConfig, LengthBucket, get_segment_humanizer from .segment_humanizer import SegmentHumanizer, HumanizeConfig, LengthBucket, get_segment_humanizer
from .runtime_observer import RuntimeObserver, RuntimeContext, get_runtime_observer from .runtime_observer import RuntimeObserver, RuntimeContext, get_runtime_observer
@ -55,6 +56,7 @@ __all__ = [
"get_tool_call_recorder", "get_tool_call_recorder",
"MemoryAdapter", "MemoryAdapter",
"UserMemory", "UserMemory",
"MemorySummaryGenerator",
"DefaultKbToolRunner", "DefaultKbToolRunner",
"KbToolResult", "KbToolResult",
"KbToolConfig", "KbToolConfig",

View File

@ -0,0 +1,58 @@
"""
Memory summary generator using LLM.
[AC-IDMP-14] Generate rolling summary for memory update.
"""
from __future__ import annotations
import logging
from typing import Any
from app.services.llm.base import LLMConfig
from app.services.llm.factory import get_llm_config_manager
from app.services.mid.memory_summary_prompt import build_memory_summary_prompt
logger = logging.getLogger(__name__)
class MemorySummaryGenerator:
"""
LLM-based memory summary generator.
Output expected to be a JSON object or structured text.
"""
def __init__(self, max_tokens: int = 512, temperature: float = 0.2):
self._max_tokens = max_tokens
self._temperature = temperature
async def __call__(
self,
messages: list[dict[str, Any]],
old_summary: str | None = None,
) -> str | None:
try:
llm_manager = get_llm_config_manager()
llm_client = llm_manager.get_client()
except Exception as e:
logger.warning(f"[AC-IDMP-14] Failed to get LLM client: {e}")
return None
prompt = build_memory_summary_prompt(messages, old_summary)
try:
response = await llm_client.generate(
messages=[
{"role": "system", "content": "你是一个严格遵循 JSON 格式的摘要器。仅输出 JSON。"},
{"role": "user", "content": prompt},
],
config=LLMConfig(
max_tokens=self._max_tokens,
temperature=self._temperature,
),
)
except Exception as e:
logger.warning(f"[AC-IDMP-14] Summary generation failed: {e}")
return None
return response.content

View File

@ -0,0 +1,46 @@
"""
Memory summary prompt builder.
[AC-IDMP-14] Rolling summary prompt for memory update.
"""
from __future__ import annotations
from typing import Any
SUMMARY_PROMPT_TEMPLATE = """
你是一个记忆摘要生成器你的目标是把对话中稳定有长期价值的信息归纳为可用于记忆召回的摘要
要求
1) 必须保留稳定事实用户偏好未解决问题
2) 不要写闲聊/情绪
3) 输出必须为严格 JSON 对象只允许以下字段summary, facts, preferences, open_issues
4) summary 为一段话150-300字以内facts/preferences/open_issues 为列表
5) 如果新内容没有变化保留旧摘要并可轻微精简
6) 所有内容必须基于对话不允许编造
旧摘要
{old_summary}
最近对话
{recent_messages}
""".strip()
def build_recent_messages_text(messages: list[dict[str, Any]]) -> str:
lines: list[str] = []
for msg in messages:
role = msg.get("role", "unknown")
content = msg.get("content", "")
if not content:
continue
lines.append(f"{role}: {content}")
return "\n".join(lines)
def build_memory_summary_prompt(
messages: list[dict[str, Any]],
old_summary: str | None = None,
) -> str:
return SUMMARY_PROMPT_TEMPLATE.format(
old_summary=old_summary or "",
recent_messages=build_recent_messages_text(messages),
)