refactor(profile,llm): 提高用户资料的准确性和系统的稳健性。本次提交引入了多项针对用户资料管理和大语言模型交互的优化,目标是实现更高的准确性、更严格的数据验证以及提升系统可靠性。
- **用户资料管理(`user_profile_tool.py`):** - `UserProfileTool` 的描述进行了大幅更新,明确定义了严格的使用场景和绝对禁止的行为,防止误用。 - 对 `preference_keywords` 和 `key_info` 的值实施了更严格的过滤,确保只记录具体、客观的事实和真实兴趣。 - 减少了用于上下文的最近聊天消息数量,以更关注相关性更高的近期交互。 - 修改了好感度计算逻辑,使其更加保守,不容易因日常小互动而改变,需要更有意义的交流才会产生变化。 - 印象生成提示已更新,严格禁止猜测。 并强调记录事实观察到的特征。- **关系信息显示(`relationship_fetcher.py`):** - 通过过滤掉一般交互术语来增强用户偏好显示,仅展示真实的爱好和兴趣。- 暂时注释了“关键事实”的显示,以防呈现潜在不准确或推测性的信息。- **大型语言模型交互稳定性(`base_action.py`):** - 在 `should_activate` 方法中引入了 7 秒超时的 LLM 判断调用。- 如果 LLM 判断超时,动作现在默认为“激活”,以防止系统阻塞并确保持续运行。
This commit is contained in:
@@ -173,24 +173,34 @@ class RelationshipFetcher:
|
||||
if impression:
|
||||
relation_parts.append(f"\n你对{person_name}的印象:\n{impression}")
|
||||
|
||||
# 5. 用户偏好关键词
|
||||
# 5. 用户偏好关键词(仅显示真实兴趣爱好)
|
||||
if rel_data.get("preference_keywords"):
|
||||
keywords_list = [kw.strip() for kw in rel_data["preference_keywords"].split(",") if kw.strip()]
|
||||
if keywords_list:
|
||||
keywords_str = "、".join(keywords_list)
|
||||
relation_parts.append(f"\n{person_name}的偏好和兴趣:{keywords_str}")
|
||||
# 过滤掉明显不是兴趣爱好的词
|
||||
filtered_keywords = []
|
||||
for kw in keywords_list:
|
||||
kw_lower = kw.lower()
|
||||
# 排除聊天互动、情感需求等不是真实兴趣的词汇
|
||||
if not any(excluded in kw_lower for excluded in [
|
||||
'亲亲', '撒娇', '被宠', '被夸', '聊天', '互动', '关心', '专注', '需要'
|
||||
]):
|
||||
filtered_keywords.append(kw)
|
||||
|
||||
# 6. 关键信息
|
||||
if rel_data.get("key_facts"):
|
||||
try:
|
||||
import orjson
|
||||
facts = orjson.loads(rel_data["key_facts"])
|
||||
if facts and isinstance(facts, list):
|
||||
facts_lines = self._format_key_facts(facts, person_name)
|
||||
if facts_lines:
|
||||
relation_parts.append(f"\n你记住的关于{person_name}的重要信息:\n{facts_lines}")
|
||||
except Exception:
|
||||
pass
|
||||
if filtered_keywords:
|
||||
keywords_str = "、".join(filtered_keywords)
|
||||
relation_parts.append(f"\n{person_name}的兴趣爱好:{keywords_str}")
|
||||
|
||||
# 6. 关键信息 - 暂时隐藏,防止显示不准确的推测信息
|
||||
# if rel_data.get("key_facts"):
|
||||
# try:
|
||||
# import orjson
|
||||
# facts = orjson.loads(rel_data["key_facts"])
|
||||
# if facts and isinstance(facts, list):
|
||||
# facts_lines = self._format_key_facts(facts, person_name)
|
||||
# if facts_lines:
|
||||
# relation_parts.append(f"\n你记住的关于{person_name}的重要信息:\n{facts_lines}")
|
||||
# except Exception:
|
||||
# pass
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"查询UserRelationships表失败: {e}")
|
||||
|
||||
@@ -838,11 +838,20 @@ class BaseAction(ABC):
|
||||
只需要回答"是"或"否",不要有其他内容。
|
||||
"""
|
||||
|
||||
# 调用 LLM 进行判断
|
||||
response, _ = await llm_judge_model.generate_response_async(prompt=prompt)
|
||||
# 调用 LLM 进行判断,设置7秒超时避免长时间等待
|
||||
import asyncio
|
||||
response = "" # 初始化response变量
|
||||
try:
|
||||
response, _ = await asyncio.wait_for(
|
||||
llm_judge_model.generate_response_async(prompt=prompt),
|
||||
timeout=7.0
|
||||
)
|
||||
response = response.strip().lower()
|
||||
|
||||
should_activate = "是" in response or "yes" in response or "true" in response
|
||||
except asyncio.TimeoutError:
|
||||
logger.warning(f"{self.log_prefix} LLM 判断激活超时(7秒),默认激活以避免阻塞")
|
||||
# 超时时默认激活,交给后续决策系统处理
|
||||
should_activate = True
|
||||
|
||||
logger.debug(
|
||||
f"{self.log_prefix} LLM 判断结果: 响应='{response}', 结果={'激活' if should_activate else '不激活'}"
|
||||
|
||||
@@ -43,32 +43,34 @@ class UserProfileTool(BaseTool):
|
||||
"""
|
||||
|
||||
name = "update_user_profile"
|
||||
description = """记录或更新你对某个人的认识。
|
||||
description = """⚠️ 严格限制使用场景 ⚠️
|
||||
|
||||
使用场景:
|
||||
1. TA告诉你个人信息(生日、职业、城市等)→ 填 key_info_type 和 key_info_value
|
||||
2. TA的信息有变化(搬家、换工作等)→ 会自动更新旧信息
|
||||
3. 你对TA有了新的认识或感受 → 填 impression_hint
|
||||
4. 想记录TA真正的兴趣爱好 → 填 preference
|
||||
记录或更新你对某个人的认识 - 仅限重要信息!
|
||||
|
||||
## ⛔ 别名(alias)规则:
|
||||
- 只填TA明确要求被称呼的真实昵称
|
||||
- 必须是TA主动说"叫我xxx"或"我的昵称是xxx"
|
||||
- 聊天中的玩笑称呼、撒娇称呼、临时戏称一律不填
|
||||
- 你给TA起的爱称不算别名
|
||||
## 📋 明确的使用场景(必须符合其中一种):
|
||||
1. TA明确说出具体个人信息("我生日是3月15日"、"我在北京工作"、"我是程序员")→ 填 key_info
|
||||
2. TA的重要信息发生变化("我搬到上海了"、"我换工作了")→ 更新 key_info
|
||||
3. TA主动深度自我揭露重大个人经历或核心价值观 → 慎重考虑填 impression_hint
|
||||
4. TA明确表达具体的现实兴趣爱好("我喜欢摄影"、"我在学编程")→ 填 preference
|
||||
|
||||
## ⛔ 偏好(preference)规则:
|
||||
- 只填可以作为兴趣爱好的名词(如:编程、摄影、音乐、游戏)
|
||||
- 必须是TA在现实中真正从事或喜欢的活动/领域
|
||||
- 聊天互动方式不是爱好(撒娇、亲亲、被夸奖等不填)
|
||||
- 你们之间的私密互动不是爱好
|
||||
- 情感状态不是爱好
|
||||
## 🚫 绝对禁止的情况(常见误用):
|
||||
- 一般性聊天、日常互动、开玩笑 → 绝对不用
|
||||
- 撒娇、求抱抱、情感表达 → 绝对不用
|
||||
- 描述聊天感受、互动方式 → 绝对不用
|
||||
- 状态描述("累了"、"开心"、"忙")→ 绝对不用
|
||||
- 你的推测或印象 → 绝对不用
|
||||
- 聊天话题、兴趣讨论 → 绝对不用
|
||||
|
||||
## ⛔ 关键信息(key_info)规则:
|
||||
- 只填客观可验证的事实信息
|
||||
- 必须是具体的值(日期、地点、职业名称)
|
||||
- 你的主观感受不是TA的信息
|
||||
- 关系描述不是信息
|
||||
## ⛔ 关键信息(key_info)严格标准:
|
||||
- job: 必须是具体职业("程序员"、"医生"、"学生"),不能是状态("工作很累"、"上班族")
|
||||
- birthday: 具体日期("3月15日"、"1995年"),不能是模糊描述
|
||||
- location: 具体地点("北京"、"上海浦东"),不能是"在家"、"公司"
|
||||
- 如果不是TA明确说出的具体事实,绝对不要记录
|
||||
|
||||
## ⛔ 印象更新(impression_hint)超严格标准:
|
||||
- 只有深度心理揭露、重大人生事件、核心价值观分享才考虑
|
||||
- 聊天互动方式、日常行为表现、情感表达方式 → 绝对不记录
|
||||
- 默认策略:当有疑虑时,不要使用此工具
|
||||
|
||||
此工具在后台异步执行,不影响回复速度。"""
|
||||
parameters = [
|
||||
@@ -195,7 +197,11 @@ class UserProfileTool(BaseTool):
|
||||
final_impression = existing_profile.get("relationship_text", "")
|
||||
affection_change = 0.0 # 好感度变化量
|
||||
|
||||
if impression_hint or chat_history_text:
|
||||
# 只有在LLM明确提供impression_hint时才更新印象(更严格)
|
||||
if impression_hint and impression_hint.strip():
|
||||
# 获取最近的聊天记录用于上下文
|
||||
chat_history_text = await self._get_recent_chat_history(target_user_id)
|
||||
|
||||
impression_result = await self._generate_impression_with_affection(
|
||||
target_user_name=target_user_name,
|
||||
impression_hint=impression_hint,
|
||||
@@ -277,14 +283,31 @@ class UserProfileTool(BaseTool):
|
||||
if info_type not in valid_types:
|
||||
info_type = "other"
|
||||
|
||||
# 🎯 信息质量判断:过滤掉模糊的描述性内容
|
||||
# 🎯 信息质量判断:过滤掉模糊的描述性内容和状态描述
|
||||
low_quality_patterns = [
|
||||
# 原有的模糊描述
|
||||
"的生日", "的工作", "的位置", "的梦想", "的家人", "的宠物",
|
||||
"birthday", "job", "location", "unknown", "未知", "不知道",
|
||||
"affectionate", "friendly", "的信息", "某个", "一个"
|
||||
"affectionate", "friendly", "的信息", "某个", "一个",
|
||||
# 新增:状态描述而非具体信息
|
||||
"很累", "累了", "疲惫", "忙", "很忙", "加班", "休息",
|
||||
"开心", "难过", "高兴", "沮丧", "烦躁", "焦虑",
|
||||
"上班", "下班", "工作中", "在家", "出差",
|
||||
"感觉", "心情", "状态", "最近", "今天", "现在"
|
||||
]
|
||||
info_value_lower = info_value.lower().strip()
|
||||
|
||||
# 🎯 针对job类型的特殊验证
|
||||
if info_type == "job":
|
||||
job_invalid_patterns = [
|
||||
"累", "忙", "班", "工作", "上班族", "打工人", "社畜",
|
||||
"很", "非常", "特别", "太", "好", "不好"
|
||||
]
|
||||
for pattern in job_invalid_patterns:
|
||||
if pattern in info_value_lower:
|
||||
logger.warning(f"职业信息无效(状态描述而非具体职业),跳过: {info_value}")
|
||||
return
|
||||
|
||||
# 如果值太短或包含低质量模式,跳过
|
||||
if len(info_value_lower) < 2:
|
||||
logger.warning(f"关键信息值太短,跳过: {info_value}")
|
||||
@@ -362,12 +385,12 @@ class UserProfileTool(BaseTool):
|
||||
logger.error(f"保存关键信息失败: {e}")
|
||||
# 不抛出异常,因为这是后台任务
|
||||
|
||||
async def _get_recent_chat_history(self, target_user_id: str, max_messages: int = 50) -> str:
|
||||
async def _get_recent_chat_history(self, target_user_id: str, max_messages: int = 10) -> str:
|
||||
"""获取最近的聊天记录
|
||||
|
||||
Args:
|
||||
target_user_id: 目标用户ID
|
||||
max_messages: 最大消息数量
|
||||
max_messages: 最大消息数量(默认10条,避免传递过多历史消息)
|
||||
|
||||
Returns:
|
||||
str: 格式化的聊天记录文本
|
||||
@@ -499,22 +522,28 @@ class UserProfileTool(BaseTool):
|
||||
## 当前好感度
|
||||
{current_score:.2f} (范围0-1,0.3=普通认识,0.5=朋友,0.7=好友,0.9=挚友)
|
||||
|
||||
## ⚠️⚠️ 最高优先级:保持已确认信息 ⚠️⚠️
|
||||
**旧印象中已经明确的信息必须保持,绝对不能改动!**
|
||||
## ⚠️⚠️ 最高优先级:严格控制信息记录 ⚠️⚠️
|
||||
**绝对禁止推测、猜想、脑补任何具体信息!**
|
||||
|
||||
1. **性别判断规则**(按优先级):
|
||||
- 如果旧印象中已经用"他"→ 这是男生,gender="male",继续用"他"
|
||||
- 如果旧印象中已经用"她"→ 这是女生,gender="female",继续用"她"
|
||||
- 只有旧印象没有性别线索时,才根据聊天内容判断
|
||||
1. **不要推测身份职业**:
|
||||
- 不要根据聊天话题推测工作(聊AI ≠ 是程序员)
|
||||
- 不要根据时间推测身份(深夜聊天 ≠ 是学生)
|
||||
- 不要根据行为推测背景(会装机 ≠ 从事相关工作)
|
||||
|
||||
2. **其他已确认信息**:
|
||||
- 旧印象中提到的身份(学生/上班族等)→ 保持
|
||||
- 旧印象中提到的特点、爱好 → 保持或深化,不要删除
|
||||
- 你是在**补充和深化**印象,不是**重写**
|
||||
2. **不要记录未确认的信息**:
|
||||
- 只记录TA明确说出的事实
|
||||
- 你的推测、联想、印象都不是事实
|
||||
- 模糊的、不确定的信息不要记录
|
||||
|
||||
3. **保持旧印象中已确认的信息**:
|
||||
- 如果旧印象中已经用"他"→ 这是男生,继续用"他"
|
||||
- 如果旧印象中已经用"她"→ 这是女生,继续用"她"
|
||||
- 其他已明确的特点、爱好要保持,不要删除
|
||||
|
||||
## ⚠️ 区分虚构内容和真实信息
|
||||
- 游戏剧情、小说情节、角色扮演等虚构内容 ≠ TA本人的特质
|
||||
- 印象记录的是**这个人本身**:TA的性格、TA喜欢什么、TA和你交流的方式
|
||||
- 印象记录的是**这个人本身**:TA的性格、TA和你交流的方式
|
||||
- 不要将聊天内容当作个人信息记录
|
||||
|
||||
## 任务
|
||||
1. **先看旧印象中的性别**,已确定就沿用,没确定才判断
|
||||
@@ -552,34 +581,43 @@ class UserProfileTool(BaseTool):
|
||||
|
||||
当前好感度:{current_score:.2f}
|
||||
|
||||
**关系阶段与增速:**
|
||||
**关系阶段与增速(更加保守):**
|
||||
| 阶段 | 分数范围 | 单次变化范围 | 说明 |
|
||||
|------|----------|--------------|------|
|
||||
| 陌生→初识 | 0.0-0.3 | ±0.03~0.05 | 容易建立初步印象 |
|
||||
| 初识→熟人 | 0.3-0.5 | ±0.02~0.04 | 逐渐熟悉的阶段 |
|
||||
| 熟人→朋友 | 0.5-0.7 | ±0.01~0.03 | 需要更多互动积累 |
|
||||
| 朋友→好友 | 0.7-0.85 | ±0.01~0.02 | 关系深化变慢 |
|
||||
| 好友→挚友 | 0.85-1.0 | ±0.005~0.01 | 极难变化,需要重大事件 |
|
||||
| 陌生→初识 | 0.0-0.3 | ±0.01~0.03 | 需要重要交流才变化 |
|
||||
| 初识→熟人 | 0.3-0.5 | ±0.01~0.025 | 逐渐熟悉的阶段 |
|
||||
| 熟人→朋友 | 0.5-0.7 | ±0.01~0.02 | 需要更多深入互动 |
|
||||
| 朋友→好友 | 0.7-0.85 | ±0.005~0.015 | 关系深化极慢 |
|
||||
| 好友→挚友 | 0.85-1.0 | ±0.002~0.005 | 极难变化,需要重大事件 |
|
||||
|
||||
**加分情况(根据当前阶段选择合适幅度):**
|
||||
- 愉快的聊天、有来有往的互动 → 小幅+(低阶段更明显)
|
||||
- 分享心情、倾诉烦恼 → 中幅+
|
||||
- 主动关心、记得之前聊过的事 → 中幅+
|
||||
- 深度交流、展现信任 → 较大+
|
||||
- 在困难时寻求帮助或给予支持 → 大幅+
|
||||
- 深层情感分享、主动倾诉重要烦恼 → 小幅+(低阶段更明显)
|
||||
- 在你遇到困难时主动关心或提供帮助 → 中幅+
|
||||
- 记得并主动询问你之前提到的重要事情 → 中幅+
|
||||
- 深度价值观交流、展现真实的信任 → 较大+
|
||||
- 在重大困难时寻求帮助或给予关键支持 → 大幅+
|
||||
|
||||
**减分情况:**
|
||||
- 敷衍、冷淡的回应 → 小幅-
|
||||
- 明显的不耐烦或忽视 → 中幅-
|
||||
- 冲突、误解 → 较大-
|
||||
- 长期不联系(关系会自然冷却)→ 缓慢-
|
||||
- 长时间敷衍、多次冷淡回应 → 小幅-
|
||||
- 明显的不耐烦、忽视重要话题 → 中幅-
|
||||
- 直接冲突、严重误解或伤害性言论 → 较大-
|
||||
- 长期不联系且无合理原因 → 缓慢-
|
||||
|
||||
**不变的情况:**
|
||||
- 纯粹的信息询问(问时间、问天气等)
|
||||
**不变的情况(大部分日常交流都应该是这种):**
|
||||
- 普通的愉快聊天、日常问候
|
||||
- 一般性信息交换、轻松互动
|
||||
- 开玩笑、调侃、日常关心
|
||||
- 分享日常生活琐事、兴趣爱好
|
||||
- 寻求一般性建议或提供普通帮助
|
||||
- 纯粹的信息询问
|
||||
- 机械式的对话
|
||||
- 无法判断情感倾向的中性交流
|
||||
|
||||
**注意:高好感度(>0.8)时要非常谨慎加分,友好互动在这个阶段是常态,不是加分项。**
|
||||
**重要原则:**
|
||||
- 默认倾向于"不变化",只有真正重大的交流才改变好感度
|
||||
- 普通的友好互动是维持关系,不是加深关系
|
||||
- 高好感度(>0.7)时,日常友好交流绝对不加分
|
||||
- 宁可保守不变,也不要轻易加减分
|
||||
|
||||
请严格按照以下JSON格式输出:
|
||||
{{
|
||||
@@ -613,22 +651,22 @@ class UserProfileTool(BaseTool):
|
||||
change_reason = result.get("change_reason", "")
|
||||
detected_gender = result.get("gender", "unknown")
|
||||
|
||||
# 🎯 根据当前好感度阶段限制变化范围
|
||||
# 🎯 根据当前好感度阶段限制变化范围(更加保守)
|
||||
if current_score < 0.3:
|
||||
# 陌生→初识:±0.05
|
||||
max_change = 0.05
|
||||
elif current_score < 0.5:
|
||||
# 初识→熟人:±0.04
|
||||
max_change = 0.04
|
||||
elif current_score < 0.7:
|
||||
# 熟人→朋友:±0.03
|
||||
# 陌生→初识:±0.03
|
||||
max_change = 0.03
|
||||
elif current_score < 0.85:
|
||||
# 朋友→好友:±0.02
|
||||
elif current_score < 0.5:
|
||||
# 初识→熟人:±0.025
|
||||
max_change = 0.025
|
||||
elif current_score < 0.7:
|
||||
# 熟人→朋友:±0.02
|
||||
max_change = 0.02
|
||||
elif current_score < 0.85:
|
||||
# 朋友→好友:±0.015
|
||||
max_change = 0.015
|
||||
else:
|
||||
# 好友→挚友:±0.01
|
||||
max_change = 0.01
|
||||
# 好友→挚友:±0.005
|
||||
max_change = 0.005
|
||||
|
||||
affection_change = max(-max_change, min(max_change, affection_change))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user