fix:记忆架构优化
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import logging
|
||||
import uuid
|
||||
@@ -72,13 +73,20 @@ class MemoryConversationContext:
|
||||
|
||||
|
||||
class MemoryAwareClientAgent:
|
||||
"""在现有客服 Agent 外包裹记忆召回、候选保存和降级处理。"""
|
||||
"""在现有客服 Agent 外包裹记忆召回、候选保存和降级处理。
|
||||
|
||||
def __init__(self, *, agent, memory_service, context, extractor=None):
|
||||
候选记忆保存默认放入后台任务执行(background_saves=True),不阻塞
|
||||
客服响应;测试或需要确定性顺序的场景可设为 False 改回同步执行。
|
||||
"""
|
||||
|
||||
def __init__(self, *, agent, memory_service, context, extractor=None,
|
||||
background_saves: bool = True):
|
||||
self.agent = agent
|
||||
self.memory_service = memory_service
|
||||
self.context = context
|
||||
self.extractor = extractor
|
||||
self.background_saves = background_saves
|
||||
self._pending_saves: set[asyncio.Task] = set()
|
||||
|
||||
async def handle(self, session_id: str, query: str, *, trace_id: str, customer_id: int) -> dict:
|
||||
"""执行记忆召回、客服回答、消息写入和候选记忆保存。"""
|
||||
@@ -107,7 +115,7 @@ class MemoryAwareClientAgent:
|
||||
result = await self.agent.handle(
|
||||
session_id, query, trace_id=trace_id, customer_id=customer_id
|
||||
)
|
||||
if self.extractor is not None:
|
||||
if self.extractor is not None and not self.background_saves:
|
||||
await self._save_candidates(
|
||||
customer_id,
|
||||
session_id,
|
||||
@@ -117,6 +125,15 @@ class MemoryAwareClientAgent:
|
||||
trace_id=trace_id,
|
||||
)
|
||||
result["memory_warnings"] = list(warnings)
|
||||
if self.extractor is not None and self.background_saves:
|
||||
self._spawn_save(
|
||||
customer_id,
|
||||
session_id,
|
||||
query,
|
||||
memory_context,
|
||||
warnings,
|
||||
trace_id=trace_id,
|
||||
)
|
||||
return result
|
||||
finally:
|
||||
_active_customer.reset(message_token)
|
||||
@@ -124,6 +141,35 @@ class MemoryAwareClientAgent:
|
||||
_active_warnings.reset(warnings_token)
|
||||
_active_memory_context.reset(context_token)
|
||||
|
||||
def _spawn_save(
|
||||
self,
|
||||
customer_id,
|
||||
session_id,
|
||||
query,
|
||||
context,
|
||||
warnings,
|
||||
*,
|
||||
trace_id: str | None = None,
|
||||
) -> None:
|
||||
"""把候选记忆保存放入后台任务;任务异常已自捕获,不击穿响应。"""
|
||||
task = asyncio.create_task(
|
||||
self._save_candidates(
|
||||
customer_id,
|
||||
session_id,
|
||||
query,
|
||||
context,
|
||||
warnings,
|
||||
trace_id=trace_id,
|
||||
)
|
||||
)
|
||||
self._pending_saves.add(task)
|
||||
task.add_done_callback(self._pending_saves.discard)
|
||||
|
||||
async def wait_for_pending_saves(self) -> None:
|
||||
"""等待全部后台保存完成,供测试与优雅退出使用。"""
|
||||
if self._pending_saves:
|
||||
await asyncio.gather(*list(self._pending_saves), return_exceptions=True)
|
||||
|
||||
async def _save_candidates(
|
||||
self,
|
||||
customer_id,
|
||||
@@ -157,9 +203,24 @@ class MemoryAwareClientAgent:
|
||||
try:
|
||||
evidence_count = 1
|
||||
if candidate.get("signal_type") == "interest_query":
|
||||
# 兴趣主题首次出现即保存为候选,后续由长期记忆按精确内容合并证据。
|
||||
# 兴趣主题先计数:达到阈值才固化为长期记忆,避免单次关注污染画像。
|
||||
candidate["memory_type"] = "CUSTOMER_PREFERENCE"
|
||||
candidate["source"] = "dialogue_inferred"
|
||||
try:
|
||||
_, reached = await self.memory_service.record_interest_signal(
|
||||
customer_id=customer_id, tag=candidate["tag"]
|
||||
)
|
||||
except Exception as exc:
|
||||
logger.exception(
|
||||
"client interest signal failed: trace_id=%s customer_id=%s tag=%s",
|
||||
trace_id,
|
||||
customer_id,
|
||||
candidate.get("tag"),
|
||||
)
|
||||
warnings.append(f"interest_signal_failed:{type(exc).__name__}")
|
||||
continue
|
||||
if not reached:
|
||||
continue
|
||||
memory = MemoryUnitDTO(
|
||||
customer_id=customer_id,
|
||||
session_id=session_id,
|
||||
|
||||
Reference in New Issue
Block a user