此提交完全重写了用户关系和分析系统,创建了一个更强大、详细和响应式的框架。旧系统已被弃用,取而代之的是一个集中式的`UserRelationships`模型。
主要变更:
1. ‌**增强数据库模型(`UserRelationships`):**‌
- 添加`impression_text`用于长期、叙述式印象。
- 引入`key_facts`(JSON)存储结构化数据如生日、工作和位置。
- 添加`relationship_stage`跟踪关系进展(如陌生人、朋友、挚友)。
- 添加`first_met_time`和`last_impression_update`的时间戳。
2. ‌**重设计`UserProfileTool`:**‌
- 工具的用途被限定为仅捕捉重要新信息,防止用于小聊。
- 更新现在在后台异步处理,确保机器人回复不被延迟。
- 引入`key_info_type`和`key_info_value`参数供LLM提交结构化事实。
3. ‌**复杂的印象和情感逻辑:**‌
- 关系追踪LLM现在分析最近聊天历史生成更丰富、更上下文的印象。
- 用渐进的`affection_change`(最大±0.03)取代直接情感分数设置,使关系发展更真实。
4. ‌**数据源整合:**‌
- `RelationshipFetcher`重构为仅依赖`UserRelationships`表作为唯一数据源。
- 简化`get_user_relationship` API并移除其缓存,确保分析的实时数据访问。
破坏性变更:`UserProfileTool`已重设计,新增参数(`key_info_type`、`key_info_value`)并改变用途。移除`affection_score`参数。此外,`get_user_relationship`数据库API签名简化为仅接受`user_id`。
19 KiB
19 KiB
用户画像系统重构规划 v2
决策:统一使用
user_relationships表,废弃person_info表的印象相关功能旧表数据保留但不再调用,作为历史存档。
一、设计理念:像人一样记住别人
1.1 人类是怎么记住一个人的?
当我们认识一个人,我们的记忆不是一堆标签,而是:
- 第一印象 - "这个人看起来挺随和的"
- 具体片段 - "上次他帮我修电脑,折腾了一晚上"
- 情感色彩 - "和他聊天很舒服,不会有压力"
- 关键信息 - "他生日是11月23日,喜欢吃辣"
- 关系定位 - "算是比较聊得来的朋友"
- 印象演变 - "一开始觉得他很高冷,后来发现其实很话痨"
1.2 当前系统的问题
❌ 现状:
"该用户性格开朗,喜欢编程、游戏、音乐,关系分数0.65"
✅ 期望:
"柒柒是个挺有意思的人,说话很有逻辑但偶尔会冒出一些冷笑话。
和他聊天挺舒服的,他不会问让人尴尬的问题。他在游戏公司做后端,
经常加班但从不抱怨。他说以后想开一家咖啡店,感觉是认真的。
生日是11月23日。"
1.3 与记忆系统的分工
| 系统 | 负责内容 | 特点 |
|---|---|---|
| 上下文 | 当前对话内容 | 即时、完整 |
| 短期记忆 | 近期聊天片段 | 几天内、可检索 |
| 长期记忆 | 重要事件、知识 | 持久、语义检索 |
| 用户印象(本系统) | 对人的认知 | 长期、主观、人格化 |
本系统只关注:
- ✅ 对这个人的长期印象(性格、相处感受)
- ✅ 重要的具体信息(生日、职业、理想等)
- ✅ 关系状态(熟悉程度、好感度)
不需要记录:
- ❌ 最近聊了什么(短期记忆负责)
- ❌ 具体对话内容(上下文和长期记忆负责)
- ❌ 临时性的事件(记忆系统负责)
1.4 新系统的设计目标
| 维度 | 要求 |
|---|---|
| 长期 | 只记稳定的、长期的认知,不记临时的 |
| 精炼 | 200-500字的印象 + 关键信息列表 |
| 主观 | 是"我"眼中的TA,不是客观档案 |
| 实时 | 通过工具调用实时更新 |
二、数据结构重设计
2.1 扩展 user_relationships 表
-- 保留现有字段
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 |
其他 | - |
存储格式:
[
{"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 |
三、工具调用设计(实时更新)
沿用之前的两阶段设计:
tool_use模型决定是否更新relationship_tracker模型生成高质量内容
3.1 工具1:update_user_impression - 更新印象
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 - 记住重要信息
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():
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 数据库变更
-- 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_infoRelationshipManager._update_impression()- 停止使用person_info表的以下字段停止更新:impressionshort_impressionpointsforgotten_pointsattitude
六、实施步骤
Phase 1:数据库扩展 ✅ 已完成
- 修改
models.py,添加新字段impression_text: 长期印象key_facts: 关键信息JSONrelationship_stage: 关系阶段first_met_time: 认识时间last_impression_update: 上次更新时间
Phase 2:核心工具重构 ✅ 已完成
- 重构
UserProfileTool- 新增
_get_recent_chat_history()读取聊天记录 - 新增
_generate_impression_with_affection()联动生成印象+好感度 - 新增
_calculate_relationship_stage()自动计算关系阶段
- 新增
- 创建
UserFactTool(remember_user_info 工具) - 编写印象生成提示词(包含好感度变化规则)
Phase 3:读取层适配 ✅ 已完成
- 修改
RelationshipFetcher.build_relation_info()- 读取新字段
impression_text,key_facts,relationship_stage - 新增辅助方法
_get_stage_description(),_format_key_facts()
- 读取新字段
- 优先使用新字段,兼容旧字段
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 |
保留 | 只用于基础信息 |