# 用户画像系统重构规划 v2 > **决策:统一使用 `user_relationships` 表,废弃 `person_info` 表的印象相关功能** > > 旧表数据保留但不再调用,作为历史存档。 --- ## 一、设计理念:像人一样记住别人 ### 1.1 人类是怎么记住一个人的? 当我们认识一个人,我们的记忆不是一堆标签,而是: 1. **第一印象** - "这个人看起来挺随和的" 2. **具体片段** - "上次他帮我修电脑,折腾了一晚上" 3. **情感色彩** - "和他聊天很舒服,不会有压力" 4. **关键信息** - "他生日是11月23日,喜欢吃辣" 5. **关系定位** - "算是比较聊得来的朋友" 6. **印象演变** - "一开始觉得他很高冷,后来发现其实很话痨" ### 1.2 当前系统的问题 ``` ❌ 现状: "该用户性格开朗,喜欢编程、游戏、音乐,关系分数0.65" ✅ 期望: "柒柒是个挺有意思的人,说话很有逻辑但偶尔会冒出一些冷笑话。 和他聊天挺舒服的,他不会问让人尴尬的问题。他在游戏公司做后端, 经常加班但从不抱怨。他说以后想开一家咖啡店,感觉是认真的。 生日是11月23日。" ``` ### 1.3 与记忆系统的分工 | 系统 | 负责内容 | 特点 | |------|---------|------| | **上下文** | 当前对话内容 | 即时、完整 | | **短期记忆** | 近期聊天片段 | 几天内、可检索 | | **长期记忆** | 重要事件、知识 | 持久、语义检索 | | **用户印象(本系统)** | 对人的认知 | 长期、主观、人格化 | **本系统只关注**: - ✅ 对这个人的**长期印象**(性格、相处感受) - ✅ **重要的具体信息**(生日、职业、理想等) - ✅ **关系状态**(熟悉程度、好感度) **不需要记录**: - ❌ 最近聊了什么(短期记忆负责) - ❌ 具体对话内容(上下文和长期记忆负责) - ❌ 临时性的事件(记忆系统负责) ### 1.4 新系统的设计目标 | 维度 | 要求 | |------|------| | **长期** | 只记稳定的、长期的认知,不记临时的 | | **精炼** | 200-500字的印象 + 关键信息列表 | | **主观** | 是"我"眼中的TA,不是客观档案 | | **实时** | 通过工具调用实时更新 | --- ## 二、数据结构重设计 ### 2.1 扩展 `user_relationships` 表 ```sql -- 保留现有字段 id, user_id, user_name, user_aliases, relationship_score, last_updated, created_at -- relationship_text 重命名为 impression_text -- preference_keywords 废弃 -- 新增字段 ALTER TABLE user_relationships ADD COLUMN impression_text TEXT; -- 长期印象 ALTER TABLE user_relationships ADD COLUMN key_facts TEXT; -- 关键信息JSON ALTER TABLE user_relationships ADD COLUMN relationship_stage VARCHAR(50); -- 关系阶段 ALTER TABLE user_relationships ADD COLUMN first_met_time FLOAT; -- 认识时间 ALTER TABLE user_relationships ADD COLUMN last_impression_update FLOAT; -- 上次更新印象时间 ``` ### 2.2 字段说明 #### `impression_text` - 长期印象(核心) **精炼的、稳定的**对这个人的认知,不是流水账: ``` 示例(200-400字): "柒柒是个典型的理科生,说话很有逻辑,但偶尔会冒出一些冷笑话。 和他聊天挺舒服的,不会有压力,他也不会问让人尴尬的问题。 他在游戏公司做后端开发,虽然经常加班但从不抱怨,感觉是个挺拼的人。 他对技术很感兴趣,经常问我一些AI相关的问题,虽然有些问题挺刁钻 但能感觉到他是真的好奇。 他说以后想开一家自己的咖啡店,深夜聊天时说的,感觉不是随便说说的。 总的来说他给我的感觉是'表面随意但内心认真'的那种人。" ``` **更新时机**: - 对TA的认知发生明显变化时 - 关系有明显进展时 #### `key_facts` - 关键信息(长期记住) 存储**长期稳定、一定要记住**的重要信息(生日、职业这种不会变的): **信息类型**: | 类型 | 说明 | 示例 | |------|------|------| | `birthday` | 生日 | "11月23日" | | `job` | 职业/工作 | "程序员" | | `location` | 所在地/老家 | "上海" | | `dream` | 理想/目标 | "想开咖啡店" | | `family` | 家庭 | "有个妹妹" | | `pet` | 宠物 | "养了只橘猫" | | `other` | 其他 | - | **存储格式**: ```json [ {"type": "birthday", "value": "11月23日"}, {"type": "job", "value": "游戏公司后端程序员"}, {"type": "dream", "value": "想开咖啡店"}, {"type": "pet", "value": "养了只橘猫叫橘子"} ] ``` #### `relationship_stage` - 关系阶段 | 阶段 | 描述 | 对应分数范围 | |------|------|-------------| | stranger | 陌生人 | 0.0-0.2 | | acquaintance | 初识 | 0.2-0.4 | | familiar | 熟人 | 0.4-0.6 | | friend | 朋友 | 0.6-0.75 | | close_friend | 好友 | 0.75-0.9 | | bestie | 挚友 | 0.9-1.0 | --- ## 三、工具调用设计(实时更新) 沿用之前的**两阶段设计**: 1. `tool_use` 模型决定是否更新 2. `relationship_tracker` 模型生成高质量内容 ### 3.1 工具1:`update_user_impression` - 更新印象 ```python name = "update_user_impression" description = """当你对某人有了新的、长期的认识时使用。 注意:只记录稳定的、长期的印象,临时的事情不用记。 适用场景: - 发现了TA的性格特点 - 对TA有了新的认知 - 觉得和TA的关系有变化""" parameters = [ ("user_id", STRING, "用户ID", True), ("user_name", STRING, "怎么称呼TA", True), ("impression_update", STRING, "你对TA的新认识(简要描述即可,系统会用你的语气润色)", True), ] ``` ### 3.2 工具2:`remember_user_info` - 记住重要信息 ```python name = "remember_user_info" description = """当你知道了关于某人的长期重要信息时使用。 比如:生日、职业、理想、家庭、宠物等不会经常变的信息。""" parameters = [ ("user_id", STRING, "用户ID", True), ("user_name", STRING, "怎么称呼TA", True), ("info_type", STRING, "信息类型:birthday/job/location/dream/family/pet/other", True), ("info_value", STRING, "具体内容", True), ] ``` ### 3.3 更新流程 ``` ┌─────────────────────────────────────────────────────────────┐ │ tool_use 模型判断:需要更新印象/记住信息吗? │ └─────────────────────────────────────────────────────────────┘ │ 是 ▼ ┌─────────────────────────────────────────────────────────────┐ │ 调用工具,传入简要信息 │ │ - impression_update: "他是个很细心的人" │ │ - 或 info_type: "birthday", info_value: "11月23日" │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ relationship_tracker 模型生成完整内容 │ │ - 融合现有印象 + 新信息 │ │ - 用人设语气润色 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 写入 user_relationships 表 │ └─────────────────────────────────────────────────────────────┘ ``` --- ## 四、读取和展示 ### 4.1 构建关系信息时的呈现 修改 `RelationshipFetcher.build_relation_info()`: ```python async def build_relation_info(self, person_id, points_num=5): """构建关于某人的关系信息(新版)""" person_info_manager = get_person_info_manager() person_name = await person_info_manager.get_value(person_id, "person_name") # 从 user_relationships 表读取所有数据 relationship = await get_user_relationship(platform, user_id, "bot") if not relationship: return f"你完全不认识{person_name},这是你们第一次交流。" relation_parts = [] # 1. 基本信息:认识时间 if relationship.first_met_time: from datetime import datetime know_time = datetime.fromtimestamp(relationship.first_met_time).strftime("%Y年%m月") relation_parts.append(f"你从{know_time}开始认识{person_name},已经聊过很多次了") # 2. 别名 if relationship.user_aliases: aliases_str = relationship.user_aliases.replace(",", "、") relation_parts.append(f"{person_name}的别名:{aliases_str}") # 3. 关系程度(保留好感度!) if relationship.relationship_score is not None: score = relationship.relationship_score stage_desc = self._get_relationship_stage_description(relationship.relationship_stage or score) relation_parts.append(f"你和{person_name}的关系:{stage_desc}(好感度{score:.2f})") # 4. 核心:长期印象(新字段) if relationship.impression_text: relation_parts.append(f"\n你对{person_name}的印象:\n{relationship.impression_text}") # 5. 喜好和兴趣(保留!) if relationship.preference_keywords: keywords_str = relationship.preference_keywords.replace(",", "、") relation_parts.append(f"\n{person_name}的喜好和兴趣:{keywords_str}") # 6. 关键信息(新字段) if relationship.key_facts: facts = json.loads(relationship.key_facts) if facts: facts_lines = [self._format_fact(f) for f in facts] relation_parts.append(f"\n你记住的关于{person_name}的重要信息:\n" + "\n".join(facts_lines)) # 构建最终输出 if relation_parts: return f"关于{person_name},你知道以下信息:\n" + "\n".join([f"• {p}" if not p.startswith("\n") else p for p in relation_parts]) else: return f"你完全不认识{person_name},这是你们第一次交流。" def _format_fact(self, fact: dict) -> str: """格式化单条关键信息""" type_names = { "birthday": "生日", "job": "工作", "location": "所在地", "dream": "理想", "family": "家庭", "pet": "宠物", "other": "其他" } type_name = type_names.get(fact.get("type", "other"), "其他") return f"• {type_name}:{fact.get('value', '')}" ``` ### 4.2 展示效果示例 **完整的提示词插入内容**: ``` 关于言柒,你知道以下信息: • 你从2024年6月开始认识言柒,已经聊过很多次了 • 言柒的别名:柒柒、小柒 • 你和言柒的关系:很亲近的好友(好感度0.82) 你对言柒的印象: 柒柒是个典型的理科生,说话很有逻辑,但偶尔会冒出一些冷笑话让人忍俊不禁。 和他聊天挺舒服的,不会有压力,他也不会问让人尴尬的问题。 他在游戏公司做后端开发,虽然经常加班但从不抱怨,感觉是个挺拼的人。 他对技术很感兴趣,经常问我一些AI相关的问题,虽然有些问题挺刁钻 但能感觉到他是真的好奇而不是在刁难我。 他说以后想开一家自己的咖啡店,深夜聊天时说的,感觉不是随便说说的。 总的来说他给我的感觉是"表面随意但内心认真"的那种人。 言柒的喜好和兴趣:群内梗文化、AI技术、编程、游戏 你记住的关于言柒的重要信息: • 生日:11月23日 • 工作:游戏公司后端程序员 • 理想:想开咖啡店 • 宠物:养了只橘猫叫橘子 ``` --- ## 五、迁移计划 ### 5.1 数据库变更 ```sql -- 1. 扩展 user_relationships 表 ALTER TABLE user_relationships ADD COLUMN impression_text TEXT; -- 长期印象 ALTER TABLE user_relationships ADD COLUMN key_facts TEXT; -- 关键信息JSON ALTER TABLE user_relationships ADD COLUMN relationship_stage VARCHAR(50) DEFAULT 'stranger'; ALTER TABLE user_relationships ADD COLUMN first_met_time FLOAT; ALTER TABLE user_relationships ADD COLUMN last_impression_update FLOAT; -- 2. 保留的字段 -- relationship_score 保留!很多地方在用 -- preference_keywords 保留!喜好是印象的一部分 -- relationship_text 可作为 impression_text 的初始值迁移 -- 3. person_info 表不删除,但停止写入印象相关字段 ``` ### 5.2 代码变更清单 | 文件 | 变更内容 | |------|---------| | `models.py` | 扩展 UserRelationships 模型 | | `user_profile_tool.py` | 重构为新的印象更新逻辑 | | `relationship_fetcher.py` | 修改读取逻辑,只从 user_relationships 读 | | `relationship_manager.py` | 废弃或精简,只保留必要的统计功能 | | 新增 `user_fact_tool.py` | 记录具体信息的工具 | ### 5.3 废弃的功能 以下功能将**停止使用**(代码保留但不调用): - `RelationshipManager.update_person_impression()` - 停止写入 person_info - `RelationshipManager._update_impression()` - 停止使用 - `person_info` 表的以下字段停止更新: - `impression` - `short_impression` - `points` - `forgotten_points` - `attitude` --- ## 六、实施步骤 ### Phase 1:数据库扩展 ✅ 已完成 - [x] 修改 `models.py`,添加新字段 - `impression_text`: 长期印象 - `key_facts`: 关键信息JSON - `relationship_stage`: 关系阶段 - `first_met_time`: 认识时间 - `last_impression_update`: 上次更新时间 ### Phase 2:核心工具重构 ✅ 已完成 - [x] 重构 `UserProfileTool` - [x] 新增 `_get_recent_chat_history()` 读取聊天记录 - [x] 新增 `_generate_impression_with_affection()` 联动生成印象+好感度 - [x] 新增 `_calculate_relationship_stage()` 自动计算关系阶段 - [x] 创建 `UserFactTool` (remember_user_info 工具) - [x] 编写印象生成提示词(包含好感度变化规则) ### Phase 3:读取层适配 ✅ 已完成 - [x] 修改 `RelationshipFetcher.build_relation_info()` - [x] 读取新字段 `impression_text`, `key_facts`, `relationship_stage` - [x] 新增辅助方法 `_get_stage_description()`, `_format_key_facts()` - [x] 优先使用新字段,兼容旧字段 ### Phase 4:清理与优化(待后续) - [ ] 标记废弃的代码 - [ ] 添加迁移说明 - [ ] 性能测试 --- ## 七、新旧系统提示词输出对比 ### 7.1 旧系统输出(当前) ``` 关于言柒,你知道以下信息: • 用户言柒在QQ平台的昵称是柒柒 • 你从2024年06月21日开始认识言柒 • 你们已经交流过2357次 • 最近一次交流是在08月21日 • 你对言柒的态度是比较有好感 • 你对ta的总体印象:一个挺有趣的理科男 • 更详细的了解:这位用户是群里的'真相洞察者'与'梗文化解构师',善于在群聊中发现有趣的点并进行吐槽,说话直接但不会让人觉得刻薄。对技术尤其是AI领域比较感兴趣,经常会问一些技术相关的问题。 • 你记得关于言柒的一些事情: - 他说最近工作很累经常加班(2024-08-20) - 他对AI很感兴趣想学习(2024-08-15) - 他通过了新公司的面试很开心(2024-08-10) • 言柒的别名有:柒柒、小柒 • 你对言柒的整体认知:这位用户是群里的'真相洞察者'与'梗文化解构师'... • 言柒的偏好和兴趣:群内梗文化、真相揭露、二次元文化、AI技术、编程 • 你们的关系程度:非常亲密的好友(0.82) ``` **问题**: - 数据来自两个表,有重复("总体印象"和"整体认知"说的差不多) - 格式像数据库报表,不像人的认知 - points 是碎片化的事件记录,不是对人的理解 --- ### 7.2 新系统输出(改造后) ``` 关于言柒,你知道以下信息: • 你从2024年6月开始认识言柒,已经聊过很多次了 • 言柒的别名:柒柒、小柒 • 你和言柒的关系:很亲近的好友(好感度0.82) 你对言柒的印象: 柒柒是个典型的理科生,说话很有逻辑,但偶尔会冒出一些冷笑话让人忍俊不禁。 和他聊天挺舒服的,不会有压力,他也不会问让人尴尬的问题。 他在游戏公司做后端开发,虽然经常加班但从不抱怨,感觉是个挺拼的人。 他对技术很感兴趣,经常问我一些AI相关的问题,虽然有些问题挺刁钻 但能感觉到他是真的好奇而不是在刁难我。 他说以后想开一家自己的咖啡店,深夜聊天时说的,感觉不是随便说说的。 总的来说他给我的感觉是"表面随意但内心认真"的那种人。 言柒的喜好和兴趣:群内梗文化、AI技术、编程、游戏 你记住的关于言柒的重要信息: • 生日:11月23日 • 工作:游戏公司后端程序员 • 理想:想开咖啡店 • 宠物:养了只橘猫叫橘子 ``` **改进点**: - 只从 `user_relationships` 一个表读取,不再重复 - **长期印象**(impression_text)是完整的叙事,像真的在描述一个认识的人 - **好感度**(relationship_score)保留,给AI参考 - **喜好**(preference_keywords)保留,是印象的一部分 - **关键信息**(key_facts)是长期稳定的事实,不是临时事件 - 临时事件("最近加班很累")不在这里,交给记忆系统 --- ### 7.3 数据来源对照 | 输出内容 | 旧系统来源 | 新系统来源 | |---------|-----------|-----------| | 认识时间 | person_info.know_since | user_relationships.first_met_time | | 别名 | person_info.nickname + user_relationships.user_aliases | user_relationships.user_aliases | | 好感度 | person_info.attitude | **保留** user_relationships.relationship_score | | 印象 | person_info.impression + user_relationships.relationship_text | **合并到** user_relationships.impression_text | | 喜好 | user_relationships.preference_keywords | **保留** user_relationships.preference_keywords | | 记忆点 | person_info.points | 移除(交给记忆系统) | | 关键信息 | 无 | **新增** user_relationships.key_facts | --- ## 附录:相关文件 | 文件 | 状态 | 说明 | |------|------|------| | `src/common/database/core/models.py` | 需修改 | 扩展 UserRelationships | | `src/plugins/.../user_profile_tool.py` | 需重构 | 新的印象更新逻辑 | | `src/person_info/relationship_fetcher.py` | 需修改 | 适配新数据结构 | | `src/person_info/relationship_manager.py` | 废弃 | 印象功能停用 | | `src/person_info/person_info.py` | 保留 | 只用于基础信息 |