fix:用户画像修复

This commit is contained in:
2026-09-14 11:08:52 +08:00
parent 5eedddbc78
commit fed7cd45e4
3 changed files with 85 additions and 44 deletions
+68 -40
View File
@@ -48,13 +48,15 @@ class MemoryService:
self.relations = relations or CustomerRelationMemory()
self.products = products or CustomerProductMemory()
self.holdings = holdings or CustomerHoldingsMemory()
self.long_term = long_term or LongTermMemoryService()
# composed 依赖上面三个已初始化的组件:必须先赋值 self.long_term,
# 否则默认分支会把 None 传进去(历史 bug)。
self.composed = composed or ComposedProfileService(
profile=self.profile,
long_term=self.long_term,
holdings=self.holdings,
redis=self.short_term.redis,
)
self.long_term = long_term or LongTermMemoryService()
self.archiver = archiver or ConversationArchiver(short_term=self.short_term)
self.rank_tool = rank_tool or FinalConfidenceRankTool()
self.interest_tracker = interest_tracker or InterestTopicTracker(self.short_term.redis)
@@ -72,8 +74,24 @@ class MemoryService:
session_id: str,
query: str | None = None,
limit: int = 10,
include_long_term: bool = False,
include_composed: bool = False,
) -> CustomerMemoryContext:
"""召回并组装客服 Agent 当前请求的全部可用记忆。"""
"""召回并组装客服 Agent 当前请求的全部可用记忆。
默认**不含**长期记忆与最终画像:客服 Agent 的职责是知识问答/开户引导/
公司信息,合规上不推荐产品,长期记忆(偏好标签、投资目标)对其没有
合法用途,却要付出 memory_unit 查询 + Embedding + Milvus 检索的代价。
长期记忆与最终画像的两个合法消费方各自显式开启:
- 投顾 Agent:``include_long_term=True``(推荐场景需要偏好与目标);
- 最终画像接口(``/profile/composed``,仅员工):直接走
``ComposedProfileService.compose_response``,不经本方法。
写入路径(``save_memory`` / ``record_interest_signal``)不受影响,
客服会话仍持续沉淀长期记忆,供投顾与画像使用。
"""
if limit < 0:
raise ValueError("limit 必须是非负整数")
warnings: list[str] = []
@@ -81,14 +99,15 @@ class MemoryService:
warnings.extend(self.short_term.last_warnings)
async with self._db() as db:
try:
refresh_confidence = getattr(self.long_term, "refresh_confidence", None)
if refresh_confidence is not None:
await refresh_confidence(
db, customer_id, limit=max(limit, 10)
)
except Exception as exc:
warnings.append(f"confidence_refresh_failed:{type(exc).__name__}")
if include_long_term:
try:
refresh_confidence = getattr(self.long_term, "refresh_confidence", None)
if refresh_confidence is not None:
await refresh_confidence(
db, customer_id, limit=max(limit, 10)
)
except Exception as exc:
warnings.append(f"confidence_refresh_failed:{type(exc).__name__}")
profile, profile_warnings = await self.profile.get(db, customer_id)
warnings.extend(profile_warnings)
try:
@@ -106,14 +125,45 @@ class MemoryService:
except Exception as exc:
customer_products = []
warnings.append(f"customer_product_recall_failed:{type(exc).__name__}")
try:
memories, memory_warnings = await self.long_term.recall(
db, customer_id, limit=max(limit, 10), query=query
)
warnings.extend(memory_warnings)
except Exception as exc:
memories = []
warnings.append(f"long_term_recall_failed:{type(exc).__name__}")
memories: list[MemoryUnitDTO] = []
if include_long_term:
try:
memories, memory_warnings = await self.long_term.recall(
db, customer_id, limit=max(limit, 10), query=query
)
warnings.extend(memory_warnings)
except Exception as exc:
memories = []
warnings.append(f"long_term_recall_failed:{type(exc).__name__}")
holdings_summary = None
composed_payload: dict[str, Any] | None = None
if include_composed:
try:
holdings_summary, holdings_warnings = await self.holdings.summary(
db, customer_id
)
warnings.extend(holdings_warnings)
except Exception as exc:
warnings.append(f"holdings_recall_failed:{type(exc).__name__}")
try:
# ComposedProfileService 期望 list[dict](与 _recall_memories
# 的返回一致),这里传已序列化的记忆,避免在块外再触碰会话。
composed_payload, composed_warnings = await self.composed.compose(
db,
customer_id=customer_id,
query=query,
profile=profile,
memories=[
memory.model_dump(mode="json") for memory in memories
],
holdings_summary=holdings_summary,
)
warnings.extend(composed_warnings)
except Exception as exc:
warnings.append(f"composed_profile_failed:{type(exc).__name__}")
ranked = self.rank_tool.rank(
[memory.model_dump(mode="json") for memory in memories],
@@ -121,28 +171,6 @@ class MemoryService:
)
ranked_memories = [MemoryUnitDTO.model_validate(item) for item in ranked]
holdings_summary = None
try:
holdings_summary, holdings_warnings = await self.holdings.summary(
db, customer_id
)
warnings.extend(holdings_warnings)
except Exception as exc:
warnings.append(f"holdings_recall_failed:{type(exc).__name__}")
composed_payload: dict[str, Any] | None = None
try:
composed_payload, composed_warnings = await self.composed.compose(
db,
customer_id=customer_id,
query=query,
profile=profile,
memories=ranked,
holdings_summary=holdings_summary,
)
warnings.extend(composed_warnings)
except Exception as exc:
warnings.append(f"composed_profile_failed:{type(exc).__name__}")
return build_customer_memory_context(
customer_id=customer_id,
session_id=session_id,