feat: A_Memorix:加强严格模式、错误与删除语义

在 A_Memorix 中强制更严格的检索语义,并改进错误传播与删除结果报告。

强制/校验受支持的搜索模式(search/time/hybrid/episode/aggregate);移除 semantic 模式,并对不支持的模式返回明确错误。将 kernel 和 plugin 构造函数中的默认值从 hybrid 改为 search。(plugins/A_memorix/core/runtime/sdk_memory_kernel.py, plugins/A_memorix/plugin.py)
对 time/hybrid 模式要求必须提供 time_start/time_end,并在文档、快速开始和 README 中体现该语义。(plugins/A_memorix/QUICK_START.md, plugins/A_memorix/README.md)
改进删除预览/执行语义:跟踪“请求的来源”与“匹配的来源”,基于匹配/删除项计算成功状态,并返回详细计数(requested_source_count、matched_source_count、deleted_paragraph_count、error)。修复来源删除逻辑,使其基于匹配到的来源执行删除。(plugins/A_memorix/core/runtime/sdk_memory_kernel.py)
在搜索执行中移除遗留的 semantic 映射,并规范化 query_type 处理。(plugins/A_memorix/core/utils/search_execution_service.py)
向调用方传播后端搜索错误:为 MemorySearchResult 增加 success/error 字段,兼容多种运行时响应封装,并在异常时返回失败结果。更新调用方以处理并报告搜索失败。(src/services/memory_service.py, src/plugin_runtime/capabilities/data.py, src/chat/brain_chat/PFC/pfc_KnowledgeFetcher.py, src/memory_system/retrieval_tools/query_long_term_memory.py)
This commit is contained in:
A-Dawn
2026-03-19 15:38:36 +08:00
parent 71b3a828c6
commit a1540d7e17
10 changed files with 135 additions and 37 deletions

View File

@@ -41,6 +41,8 @@ class MemorySearchResult:
summary: str = ""
hits: List[MemoryHit] = field(default_factory=list)
filtered: bool = False
success: bool = True
error: str = ""
def to_text(self, limit: int = 5) -> str:
if not self.hits:
@@ -55,6 +57,8 @@ class MemorySearchResult:
def to_dict(self) -> Dict[str, Any]:
return {
"success": self.success,
"error": self.error,
"summary": self.summary,
"hits": [item.to_dict() for item in self.hits],
"filtered": self.filtered,
@@ -92,13 +96,33 @@ class MemoryService:
runtime = get_plugin_runtime_manager()
if not runtime.is_running:
raise RuntimeError("plugin_runtime 未启动")
return await runtime.invoke_plugin(
response = await runtime.invoke_plugin(
method="plugin.invoke_tool",
plugin_id=PLUGIN_ID,
component_name=component_name,
args=args or {},
timeout_ms=max(1000, int(timeout_ms or 30000)),
)
# 兼容新旧运行时返回:
# - 旧版: 直接返回工具结果(dict)
# - 新版: 返回 Envelope工具结果在 payload.result 中
if isinstance(response, dict):
return response
payload = getattr(response, "payload", None)
if isinstance(payload, dict):
if isinstance(payload.get("result"), dict):
return payload["result"]
return payload
model_dump = getattr(response, "model_dump", None)
if callable(model_dump):
dumped = model_dump()
if isinstance(dumped, dict):
inner_payload = dumped.get("payload")
if isinstance(inner_payload, dict):
if isinstance(inner_payload.get("result"), dict):
return inner_payload["result"]
return inner_payload
return response
async def _invoke_admin(
self,
@@ -134,7 +158,7 @@ class MemoryService:
@staticmethod
def _coerce_search_result(payload: Any) -> MemorySearchResult:
if not isinstance(payload, dict):
return MemorySearchResult()
return MemorySearchResult(success=False, error="invalid_payload")
hits: List[MemoryHit] = []
for item in payload.get("hits", []) or []:
if not isinstance(item, dict):
@@ -158,10 +182,15 @@ class MemoryService:
title=str(item.get("title", "") or ""),
)
)
success_raw = payload.get("success")
error = str(payload.get("error", "") or "")
success = (not bool(error)) if success_raw is None else bool(success_raw)
return MemorySearchResult(
summary=str(payload.get("summary", "") or ""),
hits=hits,
filtered=bool(payload.get("filtered", False)),
success=success,
error=error,
)
@staticmethod
@@ -179,7 +208,7 @@ class MemoryService:
query: str,
*,
limit: int = 5,
mode: str = "hybrid",
mode: str = "search",
chat_id: str = "",
person_id: str = "",
time_start: str | float | None = None,
@@ -212,7 +241,7 @@ class MemoryService:
return self._coerce_search_result(payload)
except Exception as exc:
logger.warning("长期记忆搜索失败: %s", exc)
return MemorySearchResult()
return MemorySearchResult(success=False, error=str(exc))
async def ingest_summary(
self,