feat(memory): sync user entity aliases and metadata to PostgreSQL

- Add `aliases` and `end_user_id` fields to user entity dicts in
  `collect_user_entities_for_metadata` so downstream tasks can write
  them to PostgreSQL
- Add `update_aliases_and_metadata` method to `EndUserInfoRepository`
  for incremental, case-insensitive dedup merge of aliases and
  structured metadata fields
- Add `_sync_end_user_info_pg` helper in tasks.py that writes aliases
  and extracted metadata to `end_user_info`, and back-fills
  `end_user.other_name` when empty
- Call `_sync_end_user_info_pg` from `extract_metadata_batch_task`
  after Neo4j write, and also when no new metadata but aliases exist
- Filter `meta_data` response in `UserMemoryService.get_end_user_info`
  to expose only four core fields: goals, traits, interests, core_facts
This commit is contained in:
lanceyq
2026-05-07 18:07:32 +08:00
parent d255f33f1f
commit e3ab19dd4f
4 changed files with 159 additions and 16 deletions

View File

@@ -2,7 +2,7 @@
终端用户信息仓储层
"""
import uuid
from typing import List, Optional
from typing import Dict, List, Optional
from sqlalchemy.orm import Session
from app.models.end_user_info_model import EndUserInfo
@@ -69,3 +69,72 @@ class EndUserInfoRepository:
self.db.commit()
logger.info(f"删除用户所有信息记录: end_user_id={end_user_id}, count={count}")
return count
def update_aliases_and_metadata(
self,
end_user_id: uuid.UUID,
new_aliases: Optional[List[str]] = None,
new_metadata: Optional[dict] = None,
) -> Optional[EndUserInfo]:
"""增量更新用户别名列表和元数据。
- aliases将 new_aliases 合并到现有列表(去重,忽略大小写),不覆盖
- meta_data将 new_metadata 的各字段列表合并到现有 meta_data去重不覆盖
- other_name若当前为空且 aliases 非空,则取 aliases[0] 作为 other_name
Args:
end_user_id: 终端用户 ID
new_aliases: 本次新增的别名列表
new_metadata: 本次提取的 extracted_metadata 字典
Returns:
更新后的 EndUserInfo若记录不存在则返回 None
"""
end_user_info = self.get_by_end_user_id(end_user_id)
if not end_user_info:
logger.warning(f"[EndUserInfo] 记录不存在,跳过更新: end_user_id={end_user_id}")
return None
changed = False
# ── 合并 aliases去重忽略大小写──
if new_aliases:
existing = list(end_user_info.aliases or [])
existing_lower = {a.lower() for a in existing}
for alias in new_aliases:
alias = alias.strip()
if alias and alias.lower() not in existing_lower:
existing.append(alias)
existing_lower.add(alias.lower())
end_user_info.aliases = existing
changed = True
# ── 同步 other_name取 aliases[0](若当前为空)──
if end_user_info.aliases and not (end_user_info.other_name or "").strip():
end_user_info.other_name = end_user_info.aliases[0]
changed = True
# ── 合并 meta_data各字段列表去重追加──
if new_metadata:
existing_meta = dict(end_user_info.meta_data or {})
for field, values in new_metadata.items():
if not isinstance(values, list):
continue
existing_list = list(existing_meta.get(field) or [])
existing_set = {str(v).lower() for v in existing_list}
for v in values:
if str(v).lower() not in existing_set:
existing_list.append(v)
existing_set.add(str(v).lower())
existing_meta[field] = existing_list
end_user_info.meta_data = existing_meta
changed = True
if changed:
self.db.commit()
self.db.refresh(end_user_info)
logger.info(
f"[EndUserInfo] 更新完成: end_user_id={end_user_id}, "
f"aliases_count={len(end_user_info.aliases or [])}"
)
return end_user_info