pull/937/head
114514 2025-05-08 01:35:04 +08:00
parent 920d5bcfc3
commit dab75f5e1f
2 changed files with 143 additions and 96 deletions

View File

@ -98,16 +98,13 @@ class PfcRelationshipUpdater:
current_relationship_value = await self.person_info_mng.get_value(conversation_info.person_id, "relationship_value")
current_relationship_value = self.relationship_mng.ensure_float(current_relationship_value, conversation_info.person_id)
sender_name_for_prompt = getattr(observation_info, 'sender_name', '对方')
if not sender_name_for_prompt: sender_name_for_prompt = '对方'
relationship_prompt = f"""你是{self.bot_name}。你正在与{sender_name_for_prompt}私聊。
relationship_prompt = f"""你是{self.bot_name}。你正在与{self.private_name}私聊。
你们当前的关系值大约是 {current_relationship_value:.0f} (范围通常在-1000到1000越高越代表关系越好)
以下是你们最近的对话内容
---
{readable_history_for_llm}
---
请基于以上对话判断你与{sender_name_for_prompt}的关系值应该如何谨慎地调整
请基于以上对话判断你与{self.private_name}的关系值应该如何谨慎地调整
请输出一个JSON对象包含一个 "adjustment" 字段其值为一个介于 -{self.REL_INCREMENTAL_MAX_CHANGE} +{self.REL_INCREMENTAL_MAX_CHANGE} 之间的整数代表关系值的变化
例如{{ "adjustment": 3 }}如果对话内容不明确或难以判断请倾向于输出较小的调整值如0, 1, -1"""
@ -133,7 +130,7 @@ class PfcRelationshipUpdater:
new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val))
await self.person_info_mng.update_one_field(conversation_info.person_id, "relationship_value", new_relationship_value)
logger.info(f"[私聊][{self.private_name}] 增量关系值更新:与【{sender_name_for_prompt}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},变为 {new_relationship_value:.2f}")
logger.info(f"[私聊][{self.private_name}] 增量关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},变为 {new_relationship_value:.2f}")
if conversation_info.person_id:
conversation_info.relationship_text = await self.relationship_mng.build_relationship_info(conversation_info.person_id, is_id=True)
@ -170,16 +167,13 @@ class PfcRelationshipUpdater:
current_relationship_value = await self.person_info_mng.get_value(conversation_info.person_id, "relationship_value")
current_relationship_value = self.relationship_mng.ensure_float(current_relationship_value, conversation_info.person_id)
sender_name_for_prompt = getattr(observation_info, 'sender_name', '对方')
if not sender_name_for_prompt: sender_name_for_prompt = '对方'
relationship_prompt = f"""你是{self.bot_name}。你与{sender_name_for_prompt}的私聊刚刚结束。
relationship_prompt = f"""你是{self.bot_name}。你与{self.private_name}的私聊刚刚结束。
你们当前的关系值大约是 {current_relationship_value:.0f} (范围通常在-1000到1000越高越好)
以下是你们本次私聊最后部分的对话内容
---
{readable_history_for_llm}
---
请基于以上对话的整体情况判断你与{sender_name_for_prompt}的关系值应该如何进行一次总结性的调整
请基于以上对话的整体情况判断你与{self.private_name}的关系值应该如何进行一次总结性的调整
请输出一个JSON对象包含一个 "final_adjustment" 字段其值为一个整数代表关系值的变化量例如可以是 -{self.REL_FINAL_MAX_CHANGE} +{self.REL_FINAL_MAX_CHANGE} 之间的一个值
请大胆评估但也要合理"""
@ -205,7 +199,7 @@ class PfcRelationshipUpdater:
new_relationship_value = max(-1000.0, min(1000.0, current_relationship_value + adjustment_val))
await self.person_info_mng.update_one_field(conversation_info.person_id, "relationship_value", new_relationship_value)
logger.info(f"[私聊][{self.private_name}] 最终关系值更新:与【{sender_name_for_prompt}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},最终为 {new_relationship_value:.2f}")
logger.info(f"[私聊][{self.private_name}] 最终关系值更新:与【{self.private_name}】的关系值从 {current_relationship_value:.2f} 调整了 {adjustment_val:.2f},最终为 {new_relationship_value:.2f}")
if conversation_info.person_id: # 虽然通常结束了,但更新一下无妨
conversation_info.relationship_text = await self.relationship_mng.build_relationship_info(conversation_info.person_id, is_id=True)

View File

@ -100,104 +100,159 @@ def get_items_from_json(
Returns:
Tuple[bool, Union[Dict[str, Any], List[Dict[str, Any]]]]: (是否成功, 提取的字段字典或字典列表)
"""
content = content.strip()
result = {}
cleaned_content = content.strip()
result: Union[Dict[str, Any], List[Dict[str, Any]]] = {} # 初始化类型
# 匹配 ```json ... ``` 或 ``` ... ```
markdown_match = re.search(r"```(?:json)?\s*([\s\S]*?)\s*```", cleaned_content, re.IGNORECASE)
if markdown_match:
cleaned_content = markdown_match.group(1).strip()
logger.debug(f"[私聊][{private_name}] 已去除 Markdown 标记,剩余内容: {cleaned_content[:100]}...")
# --- 新增结束 ---
# 设置默认值
default_result: Dict[str, Any] = {} # 用于单对象时的默认值
if default_values:
result.update(default_values)
default_result.update(default_values)
result = default_result.copy() # 先用默认值初始化
# 首先尝试解析为JSON数组
if allow_array:
try:
# 尝试找到文本中的JSON数组
array_pattern = r"\[[\s\S]*\]"
array_match = re.search(array_pattern, content)
if array_match:
array_content = array_match.group()
json_array = json.loads(array_content)
# 尝试直接解析清理后的内容为列表
json_array = json.loads(cleaned_content)
# 确认是数组类型
if isinstance(json_array, list):
# 验证数组中的每个项目是否包含所有必需字段
valid_items = []
for item in json_array:
if not isinstance(item, dict):
continue
if isinstance(json_array, list):
valid_items_list: List[Dict[str, Any]] = []
for item in json_array:
if not isinstance(item, dict):
logger.warning(f"[私聊][{private_name}] JSON数组中的元素不是字典: {item}")
continue
# 检查是否有所有必需字段
if all(field in item for field in items):
# 验证字段类型
if required_types:
type_valid = True
for field, expected_type in required_types.items():
if field in item and not isinstance(item[field], expected_type):
type_valid = False
break
current_item_result = default_result.copy() # 每个元素都用默认值初始化
valid_item = True
if not type_valid:
continue
# 提取并验证字段
for field in items:
if field in item:
current_item_result[field] = item[field]
elif field not in default_result: # 如果字段不存在且没有默认值
logger.warning(f"[私聊][{private_name}] JSON数组元素缺少必要字段 '{field}': {item}")
valid_item = False
break # 这个元素无效
# 验证字符串字段不为空
string_valid = True
for field in items:
if isinstance(item[field], str) and not item[field].strip():
string_valid = False
break
if not valid_item: continue
if not string_valid:
continue
# 验证类型
if required_types:
for field, expected_type in required_types.items():
# 检查 current_item_result 中是否存在该字段 (可能来自 item 或 default_values)
if field in current_item_result and not isinstance(current_item_result[field], expected_type):
logger.warning(f"[私聊][{private_name}] JSON数组元素字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(current_item_result[field]).__name__}): {item}")
valid_item = False
break
valid_items.append(item)
if not valid_item: continue
# 验证字符串不为空 (只检查 items 中要求的字段)
for field in items:
if field in current_item_result and isinstance(current_item_result[field], str) and not current_item_result[field].strip():
logger.warning(f"[私聊][{private_name}] JSON数组元素字段 '{field}' 不能为空字符串: {item}")
valid_item = False
break
if valid_item:
valid_items_list.append(current_item_result) # 只添加完全有效的项
if valid_items_list: # 只有当列表不为空时才认为是成功
logger.debug(f"[私聊][{private_name}] 成功解析JSON数组包含 {len(valid_items_list)} 个有效项目。")
return True, valid_items_list
else:
# 如果列表为空(可能所有项都无效),则继续尝试解析为单个对象
logger.debug(f"[私聊][{private_name}] 解析为JSON数组但未找到有效项目尝试解析单个JSON对象。")
# result 重置回单个对象的默认值
result = default_result.copy()
if valid_items:
return True, valid_items
except json.JSONDecodeError:
logger.debug(f"[私聊][{private_name}]JSON数组解析失败尝试解析单个JSON对象")
logger.debug(f"[私聊][{private_name}] JSON数组直接解析失败尝试解析单个JSON对象")
# result 重置回单个对象的默认值
result = default_result.copy()
except Exception as e:
logger.debug(f"[私聊][{private_name}]尝试解析JSON数组时出错: {str(e)}")
logger.error(f"[私聊][{private_name}] 尝试解析JSON数组时发生未知错误: {str(e)}")
# result 重置回单个对象的默认值
result = default_result.copy()
# 尝试解析JSON对象
# 尝试解析为单个JSON对象
try:
json_data = json.loads(content)
# 尝试直接解析清理后的内容
json_data = json.loads(cleaned_content)
if not isinstance(json_data, dict):
logger.error(f"[私聊][{private_name}] 解析为单个对象,但结果不是字典类型: {type(json_data)}")
return False, default_result # 返回失败和默认值
except json.JSONDecodeError:
# 如果直接解析失败尝试查找和提取JSON部分
json_pattern = r"\{[^{}]*\}"
json_match = re.search(json_pattern, content)
# 如果直接解析失败,尝试用正则表达式查找 JSON 对象部分 (作为后备)
# 这个正则比较简单,可能无法处理嵌套或复杂的 JSON
json_pattern = r"\{[\s\S]*?\}" # 使用非贪婪匹配
json_match = re.search(json_pattern, cleaned_content)
if json_match:
try:
json_data = json.loads(json_match.group())
potential_json_str = json_match.group()
json_data = json.loads(potential_json_str)
if not isinstance(json_data, dict):
logger.error(f"[私聊][{private_name}] 正则提取后解析,但结果不是字典类型: {type(json_data)}")
return False, default_result
logger.debug(f"[私聊][{private_name}] 通过正则提取并成功解析JSON对象。")
except json.JSONDecodeError:
logger.error(f"[私聊][{private_name}]提取的JSON内容解析失败")
return False, result
logger.error(f"[私聊][{private_name}] 正则提取的部分 '{potential_json_str[:100]}...' 无法解析为JSON。")
return False, default_result
else:
logger.error(f"[私聊][{private_name}]无法在返回内容中找到有效的JSON")
return False, result
logger.error(f"[私聊][{private_name}] 无法在返回内容中找到有效的JSON对象部分。原始内容: {cleaned_content[:100]}...")
return False, default_result
# 提取字段
# 提取并验证字段 (适用于单个JSON对象)
# 确保 result 是字典类型用于更新
if not isinstance(result, dict):
result = default_result.copy() # 如果之前是列表,重置为字典
valid_single_object = True
for item in items:
if item in json_data:
result[item] = json_data[item]
elif item not in default_result: # 如果字段不存在且没有默认值
logger.error(f"[私聊][{private_name}] JSON对象缺少必要字段 '{item}'。JSON内容: {json_data}")
valid_single_object = False
break # 这个对象无效
# 验证必需字段
if not all(item in result for item in items):
logger.error(f"[私聊][{private_name}]JSON缺少必要字段实际内容: {json_data}")
return False, result
if not valid_single_object:
return False, default_result
# 验证字段类型
# 验证类型
if required_types:
for field, expected_type in required_types.items():
if field in result and not isinstance(result[field], expected_type):
logger.error(f"[私聊][{private_name}]{field} 必须是 {expected_type.__name__} 类型")
return False, result
logger.error(f"[私聊][{private_name}] JSON对象字段 '{field}' 类型错误 (应为 {expected_type.__name__}, 实际为 {type(result[field]).__name__})")
valid_single_object = False
break
# 验证字符串字段不为空
if not valid_single_object:
return False, default_result
# 验证字符串不为空 (只检查 items 中要求的字段)
for field in items:
if isinstance(result[field], str) and not result[field].strip():
logger.error(f"[私聊][{private_name}]{field} 不能为空")
return False, result
if field in result and isinstance(result[field], str) and not result[field].strip():
logger.error(f"[私聊][{private_name}] JSON对象字段 '{field}' 不能为空字符串")
valid_single_object = False
break
if valid_single_object:
logger.debug(f"[私聊][{private_name}] 成功解析并验证了单个JSON对象。")
return True, result # 返回提取并验证后的字典
else:
return False, default_result # 验证失败
return True, result
async def get_person_id(private_name: str, chat_stream: ChatStream):
private_user_id_str: Optional[str] = None
@ -208,35 +263,33 @@ async def get_person_id(private_name: str, chat_stream: ChatStream):
private_user_id_str = str(chat_stream.user_info.user_id)
private_platform_str = chat_stream.user_info.platform
logger.info(f"[私聊][{private_name}] 从 ChatStream 获取到私聊对象信息: ID={private_user_id_str}, Platform={private_platform_str}, Name={private_nickname_str}")
elif chat_stream.group_info is None and private_name: # 私聊场景,且 private_name 可能有用
# 这是一个备选方案,如果 private_name 直接是 user_id
# 你需要确认 private_name 的确切含义和格式 <- private_name 就是对方昵称,格式是 str
# logger.warning(f"[私聊][{self.private_name}] 尝试使用 private_name ('{self.private_name}') 作为 user_id平台默认为 'qq'")
# private_user_id_str = self.private_name
# private_platform_str = "qq" # 假设平台是qq
# private_nickname_str = self.private_name # 昵称也暂时用 private_name
pass # 暂时不启用此逻辑,依赖 observation_info 或 chat_stream.user_info
elif chat_stream.group_info is None and private_name:
pass
if private_user_id_str and private_platform_str:
try:
# 将 user_id 转换为整数类型,因为 person_info_manager.get_person_id 需要 int
private_user_id_int = int(private_user_id_str)
person_id = person_info_manager.get_person_id(
private_platform_str,
private_user_id_int
# 使用转换后的整数ID
)
# 确保用户在数据库中存在,如果不存在则创建
# get_or_create_person 内部处理 person_id 的生成,所以我们直接传 platform 和 user_id
await person_info_manager.get_or_create_person(
# person_id = person_info_manager.get_person_id( # get_person_id 可能只查询,不创建
# private_platform_str,
# private_user_id_int
# )
# 使用 get_or_create_person 确保用户存在
person_id = await person_info_manager.get_or_create_person(
platform=private_platform_str,
user_id=private_user_id_int, # 使用转换后的整数ID
nickname=private_name,
user_id=private_user_id_int,
nickname=private_name, # 使用传入的 private_name 作为昵称
)
return person_id, private_platform_str, private_user_id_str
if person_id is None: # 如果 get_or_create_person 返回 None说明创建失败
logger.error(f"[私聊][{private_name}] get_or_create_person 未能获取或创建 person_id。")
return None # 返回 None 表示失败
return person_id, private_platform_str, private_user_id_str # 返回获取或创建的 person_id
except ValueError:
logger.error(f"[私聊][{private_name}] 无法将 private_user_id_str ('{private_user_id_str}') 转换为整数。")
return None # 返回 None 表示失败
except Exception as e_pid:
logger.error(f"[私聊][{private_name}] 获取或创建 person_id 时出错: {e_pid}")
return None # 返回 None 表示失败
else:
logger.warning(f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform无法获取 person_id。将在收到消息后尝试。")
logger.warning(f"[私聊][{private_name}] 未能确定私聊对象的 user_id 或 platform无法获取 person_id。将在收到消息后尝试。")
return None # 返回 None 表示失败