better:重构personinfo,使用Person类和类属性

This commit is contained in:
SengokuCola
2025-08-12 14:33:13 +08:00
parent 1e7f3a92a6
commit ae254de494
21 changed files with 468 additions and 1202 deletions

View File

@@ -17,7 +17,7 @@ from src.chat.planner_actions.action_manager import ActionManager
from src.chat.chat_loop.hfc_utils import CycleDetail from src.chat.chat_loop.hfc_utils import CycleDetail
from src.person_info.relationship_builder_manager import relationship_builder_manager from src.person_info.relationship_builder_manager import relationship_builder_manager
from src.chat.express.expression_learner import expression_learner_manager from src.chat.express.expression_learner import expression_learner_manager
from src.person_info.person_info import get_person_info_manager from src.person_info.person_info import Person
from src.person_info.group_relationship_manager import get_group_relationship_manager from src.person_info.group_relationship_manager import get_group_relationship_manager
from src.plugin_system.base.component_types import ChatMode, EventType from src.plugin_system.base.component_types import ChatMode, EventType
from src.plugin_system.core import events_manager from src.plugin_system.core import events_manager
@@ -307,19 +307,13 @@ class HeartFChatting:
with Timer("回复发送", cycle_timers): with Timer("回复发送", cycle_timers):
reply_text = await self._send_response(response_set, action_message) reply_text = await self._send_response(response_set, action_message)
# 存储reply action信息
person_info_manager = get_person_info_manager()
# 获取 platform如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值 # 获取 platform如果不存在则从 chat_stream 获取,如果还是 None 则使用默认值
platform = action_message.get("chat_info_platform") platform = action_message.get("chat_info_platform")
if platform is None: if platform is None:
platform = getattr(self.chat_stream, "platform", "unknown") platform = getattr(self.chat_stream, "platform", "unknown")
person_id = person_info_manager.get_person_id( person = Person(platform = platform ,user_id = action_message.get("user_id", ""))
platform, person_name = person.person_name
action_message.get("user_id", ""),
)
person_name = await person_info_manager.get_value(person_id, "person_name")
action_prompt_display = f"你对{person_name}进行了回复:{reply_text}" action_prompt_display = f"你对{person_name}进行了回复:{reply_text}"
await database_api.store_action_info( await database_api.store_action_info(

View File

@@ -281,7 +281,7 @@ class ExpressionLearner:
logger.info(f"{group_name} 学习到表达风格:\n{learnt_expressions_str}") logger.info(f"{group_name} 学习到表达风格:\n{learnt_expressions_str}")
if not learnt_expressions: if not learnt_expressions:
logger.info(f"没有学习到表达风格") logger.info("没有学习到表达风格")
return [] return []
# 按chat_id分组 # 按chat_id分组

View File

@@ -3,7 +3,7 @@ import time
import random import random
import hashlib import hashlib
from typing import List, Dict, Tuple, Optional, Any from typing import List, Dict, Optional, Any
from json_repair import repair_json from json_repair import repair_json
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
@@ -22,16 +22,22 @@ def init_prompt():
你的名字是{bot_name}{target_message} 你的名字是{bot_name}{target_message}
你知道以下这些表达方式,梗和说话方式 以下是可选的表达情境
{all_situations} {all_situations}
现在,请你根据聊天记录从中挑选合适的表达方式,梗和说话方式,组织一条回复风格指导,指导的目的是在组织回复的时候提供一些语言风格和梗上的参考 请你分析聊天内容的语境、情绪、话题类型,从上述情境中选择最适合当前聊天情境的,最多{max_num}个情境
请在reply_style_guide中以平文本输出指导不要浮夸并在selected_expressions中说明在指导中你挑选了哪些表达方式梗和说话方式以json格式输出 考虑因素包括
例子: 1. 聊天的情绪氛围(轻松、严肃、幽默等)
2. 话题类型(日常、技术、游戏、情感等)
3. 情境与当前语境的匹配度
{target_message_extra_block}
请以JSON格式输出只需要输出选中的情境编号
例如:
{{ {{
"reply_style_guide": "...", "selected_situations": [2, 3, 5, 7, 19]
"selected_expressions": [2, 3, 4, 7]
}} }}
请严格按照JSON格式输出不要包含其他内容 请严格按照JSON格式输出不要包含其他内容
""" """
Prompt(expression_evaluation_prompt, "expression_evaluation_prompt") Prompt(expression_evaluation_prompt, "expression_evaluation_prompt")
@@ -190,14 +196,14 @@ class ExpressionSelector:
chat_info: str, chat_info: str,
max_num: int = 10, max_num: int = 10,
target_message: Optional[str] = None, target_message: Optional[str] = None,
) -> Tuple[str, List[Dict[str, Any]]]: ) -> List[Dict[str, Any]]:
# sourcery skip: inline-variable, list-comprehension # sourcery skip: inline-variable, list-comprehension
"""使用LLM选择适合的表达方式""" """使用LLM选择适合的表达方式"""
# 检查是否允许在此聊天流中使用表达 # 检查是否允许在此聊天流中使用表达
if not self.can_use_expression_for_chat(chat_id): if not self.can_use_expression_for_chat(chat_id):
logger.debug(f"聊天流 {chat_id} 不允许使用表达,返回空列表") logger.debug(f"聊天流 {chat_id} 不允许使用表达,返回空列表")
return "", [] return []
# 1. 获取20个随机表达方式现在按权重抽取 # 1. 获取20个随机表达方式现在按权重抽取
style_exprs = self.get_random_expressions(chat_id, 10) style_exprs = self.get_random_expressions(chat_id, 10)
@@ -216,7 +222,7 @@ class ExpressionSelector:
if not all_expressions: if not all_expressions:
logger.warning("没有找到可用的表达方式") logger.warning("没有找到可用的表达方式")
return "", [] return []
all_situations_str = "\n".join(all_situations) all_situations_str = "\n".join(all_situations)
@@ -255,24 +261,23 @@ class ExpressionSelector:
if not content: if not content:
logger.warning("LLM返回空结果") logger.warning("LLM返回空结果")
return "", [] return []
# 5. 解析结果 # 5. 解析结果
result = repair_json(content) result = repair_json(content)
if isinstance(result, str): if isinstance(result, str):
result = json.loads(result) result = json.loads(result)
if not isinstance(result, dict) or "reply_style_guide" not in result or "selected_expressions" not in result: if not isinstance(result, dict) or "selected_situations" not in result:
logger.error("LLM返回格式错误") logger.error("LLM返回格式错误")
logger.info(f"LLM返回结果: \n{content}") logger.info(f"LLM返回结果: \n{content}")
return "", [] return []
reply_style_guide = result["reply_style_guide"] selected_indices = result["selected_situations"]
selected_expressions = result["selected_expressions"]
# 根据索引获取完整的表达方式 # 根据索引获取完整的表达方式
valid_expressions = [] valid_expressions = []
for idx in selected_expressions: for idx in selected_indices:
if isinstance(idx, int) and 1 <= idx <= len(all_expressions): if isinstance(idx, int) and 1 <= idx <= len(all_expressions):
expression = all_expressions[idx - 1] # 索引从1开始 expression = all_expressions[idx - 1] # 索引从1开始
valid_expressions.append(expression) valid_expressions.append(expression)
@@ -282,11 +287,11 @@ class ExpressionSelector:
self.update_expressions_count_batch(valid_expressions, 0.006) self.update_expressions_count_batch(valid_expressions, 0.006)
# logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个") # logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个")
return reply_style_guide, valid_expressions return valid_expressions
except Exception as e: except Exception as e:
logger.error(f"LLM处理表达方式选择时出错: {e}") logger.error(f"LLM处理表达方式选择时出错: {e}")
return "", [] return []

View File

@@ -22,22 +22,16 @@ def init_prompt():
你的名字是{bot_name}{target_message} 你的名字是{bot_name}{target_message}
以下是可选的表达情境 你知道以下这些表达方式梗和说话方式
{all_situations} {all_situations}
请你分析聊天内容的语境情绪话题类型从上述情境中选择最适合当前聊天情境的最多{max_num}个情境 现在请你根据聊天记录从中挑选合适的表达方式梗和说话方式组织一条回复风格指导指导的目的是在组织回复的时候提供一些语言风格和梗上的参考
考虑因素包括 请在reply_style_guide中以平文本输出指导不要浮夸并在selected_expressions中说明在指导中你挑选了哪些表达方式梗和说话方式以json格式输出
1. 聊天的情绪氛围轻松严肃幽默等 例子
2. 话题类型日常技术游戏情感等
3. 情境与当前语境的匹配度
{target_message_extra_block}
请以JSON格式输出只需要输出选中的情境编号
例如
{{ {{
"selected_situations": [2, 3, 5, 7, 19] "reply_style_guide": "...",
"selected_expressions": [2, 3, 4, 7]
}} }}
请严格按照JSON格式输出不要包含其他内容 请严格按照JSON格式输出不要包含其他内容
""" """
Prompt(expression_evaluation_prompt, "expression_evaluation_prompt") Prompt(expression_evaluation_prompt, "expression_evaluation_prompt")
@@ -196,14 +190,14 @@ class ExpressionSelector:
chat_info: str, chat_info: str,
max_num: int = 10, max_num: int = 10,
target_message: Optional[str] = None, target_message: Optional[str] = None,
) -> List[Dict[str, Any]]: ) -> Tuple[str, List[Dict[str, Any]]]:
# sourcery skip: inline-variable, list-comprehension # sourcery skip: inline-variable, list-comprehension
"""使用LLM选择适合的表达方式""" """使用LLM选择适合的表达方式"""
# 检查是否允许在此聊天流中使用表达 # 检查是否允许在此聊天流中使用表达
if not self.can_use_expression_for_chat(chat_id): if not self.can_use_expression_for_chat(chat_id):
logger.debug(f"聊天流 {chat_id} 不允许使用表达,返回空列表") logger.debug(f"聊天流 {chat_id} 不允许使用表达,返回空列表")
return [] return "", []
# 1. 获取20个随机表达方式现在按权重抽取 # 1. 获取20个随机表达方式现在按权重抽取
style_exprs = self.get_random_expressions(chat_id, 10) style_exprs = self.get_random_expressions(chat_id, 10)
@@ -222,7 +216,7 @@ class ExpressionSelector:
if not all_expressions: if not all_expressions:
logger.warning("没有找到可用的表达方式") logger.warning("没有找到可用的表达方式")
return [] return "", []
all_situations_str = "\n".join(all_situations) all_situations_str = "\n".join(all_situations)
@@ -261,23 +255,24 @@ class ExpressionSelector:
if not content: if not content:
logger.warning("LLM返回空结果") logger.warning("LLM返回空结果")
return [] return "", []
# 5. 解析结果 # 5. 解析结果
result = repair_json(content) result = repair_json(content)
if isinstance(result, str): if isinstance(result, str):
result = json.loads(result) result = json.loads(result)
if not isinstance(result, dict) or "selected_situations" not in result: if not isinstance(result, dict) or "reply_style_guide" not in result or "selected_expressions" not in result:
logger.error("LLM返回格式错误") logger.error("LLM返回格式错误")
logger.info(f"LLM返回结果: \n{content}") logger.info(f"LLM返回结果: \n{content}")
return [] return "", []
selected_indices = result["selected_situations"] reply_style_guide = result["reply_style_guide"]
selected_expressions = result["selected_expressions"]
# 根据索引获取完整的表达方式 # 根据索引获取完整的表达方式
valid_expressions = [] valid_expressions = []
for idx in selected_indices: for idx in selected_expressions:
if isinstance(idx, int) and 1 <= idx <= len(all_expressions): if isinstance(idx, int) and 1 <= idx <= len(all_expressions):
expression = all_expressions[idx - 1] # 索引从1开始 expression = all_expressions[idx - 1] # 索引从1开始
valid_expressions.append(expression) valid_expressions.append(expression)
@@ -287,11 +282,11 @@ class ExpressionSelector:
self.update_expressions_count_batch(valid_expressions, 0.006) self.update_expressions_count_batch(valid_expressions, 0.006)
# logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个") # logger.info(f"LLM从{len(all_expressions)}个情境中选择了{len(valid_expressions)}个")
return valid_expressions return reply_style_guide, valid_expressions
except Exception as e: except Exception as e:
logger.error(f"LLM处理表达方式选择时出错: {e}") logger.error(f"LLM处理表达方式选择时出错: {e}")
return [] return "", []

View File

@@ -14,34 +14,14 @@ from src.chat.utils.utils import is_mentioned_bot_in_message
from src.chat.utils.timer_calculator import Timer from src.chat.utils.timer_calculator import Timer
from src.chat.utils.chat_message_builder import replace_user_references_sync from src.chat.utils.chat_message_builder import replace_user_references_sync
from src.common.logger import get_logger from src.common.logger import get_logger
from src.person_info.relationship_manager import get_relationship_manager
from src.mood.mood_manager import mood_manager from src.mood.mood_manager import mood_manager
from src.person_info.person_info import Person
if TYPE_CHECKING: if TYPE_CHECKING:
from src.chat.heart_flow.sub_heartflow import SubHeartflow from src.chat.heart_flow.sub_heartflow import SubHeartflow
logger = get_logger("chat") logger = get_logger("chat")
async def _process_relationship(message: MessageRecv) -> None:
"""处理用户关系逻辑
Args:
message: 消息对象,包含用户信息
"""
platform = message.message_info.platform
user_id = message.message_info.user_info.user_id # type: ignore
nickname = message.message_info.user_info.user_nickname # type: ignore
cardname = message.message_info.user_info.user_cardname or nickname # type: ignore
relationship_manager = get_relationship_manager()
is_known = await relationship_manager.is_known_some_one(platform, user_id)
if not is_known:
logger.info(f"首次认识用户: {nickname}")
await relationship_manager.first_knowing_some_one(platform, user_id, nickname, cardname) # type: ignore
async def _calculate_interest(message: MessageRecv) -> Tuple[float, bool, list[str]]: async def _calculate_interest(message: MessageRecv) -> Tuple[float, bool, list[str]]:
"""计算消息的兴趣度 """计算消息的兴趣度
@@ -165,7 +145,7 @@ class HeartFCMessageReceiver:
# 4. 关系处理 # 4. 关系处理
if global_config.relationship.enable_relationship: if global_config.relationship.enable_relationship:
await _process_relationship(message) person = Person(platform=message.message_info.platform, user_id=message.message_info.user_info.user_id,nickname=userinfo.user_nickname)
except Exception as e: except Exception as e:
logger.error(f"消息处理失败: {e}") logger.error(f"消息处理失败: {e}")

View File

@@ -9,7 +9,6 @@ from datetime import datetime
from src.mais4u.mai_think import mai_thinking_manager from src.mais4u.mai_think import mai_thinking_manager
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
from src.config.api_ada_configs import TaskConfig
from src.individuality.individuality import get_individuality from src.individuality.individuality import get_individuality
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.chat.message_receive.message import UserInfo, Seg, MessageRecv, MessageSending from src.chat.message_receive.message import UserInfo, Seg, MessageRecv, MessageSending
@@ -27,8 +26,7 @@ from src.chat.express.expression_selector import expression_selector
from src.chat.memory_system.memory_activator import MemoryActivator from src.chat.memory_system.memory_activator import MemoryActivator
from src.chat.memory_system.instant_memory import InstantMemory from src.chat.memory_system.instant_memory import InstantMemory
from src.mood.mood_manager import mood_manager from src.mood.mood_manager import mood_manager
from src.person_info.relationship_fetcher import relationship_fetcher_manager from src.person_info.person_info import Person
from src.person_info.person_info import get_person_info_manager
from src.plugin_system.base.component_types import ActionInfo, EventType from src.plugin_system.base.component_types import ActionInfo, EventType
from src.plugin_system.apis import llm_api from src.plugin_system.apis import llm_api
@@ -302,16 +300,14 @@ class DefaultReplyer:
if not global_config.relationship.enable_relationship: if not global_config.relationship.enable_relationship:
return "" return ""
relationship_fetcher = relationship_fetcher_manager.get_fetcher(self.chat_stream.stream_id)
# 获取用户ID # 获取用户ID
person_info_manager = get_person_info_manager() person = Person(platform=self.chat_stream.platform, user_id=sender)
person_id = person_info_manager.get_person_id_by_person_name(sender) person_id = person.person_id
if not person_id: if not person_id:
logger.warning(f"未找到用户 {sender} 的ID跳过信息提取") logger.warning(f"未找到用户 {sender} 的ID跳过信息提取")
return f"你完全不认识{sender}不理解ta的相关信息。" return f"你完全不认识{sender}不理解ta的相关信息。"
return await relationship_fetcher.build_relation_info(person_id, points_num=5) return person.build_relationship(points_num=5)
async def build_expression_habits(self, chat_history: str, target: str) -> Tuple[str, str]: async def build_expression_habits(self, chat_history: str, target: str) -> Tuple[str, str]:
"""构建表达习惯块 """构建表达习惯块
@@ -330,7 +326,7 @@ class DefaultReplyer:
style_habits = [] style_habits = []
# 使用从处理器传来的选中表达方式 # 使用从处理器传来的选中表达方式
# LLM模式调用LLM选择5-10个然后随机选5个 # LLM模式调用LLM选择5-10个然后随机选5个
reply_style_guide, selected_expressions = await expression_selector.select_suitable_expressions_llm( selected_expressions = await expression_selector.select_suitable_expressions_llm(
self.chat_stream.stream_id, chat_history, max_num=8, target_message=target self.chat_stream.stream_id, chat_history, max_num=8, target_message=target
) )
@@ -354,7 +350,7 @@ class DefaultReplyer:
) )
expression_habits_block += f"{style_habits_str}\n" expression_habits_block += f"{style_habits_str}\n"
return (f"{expression_habits_title}\n{expression_habits_block}", reply_style_guide) return f"{expression_habits_title}\n{expression_habits_block}"
async def build_memory_block(self, chat_history: str, target: str) -> str: async def build_memory_block(self, chat_history: str, target: str) -> str:
"""构建记忆块 """构建记忆块
@@ -659,18 +655,16 @@ class DefaultReplyer:
available_actions = {} available_actions = {}
chat_stream = self.chat_stream chat_stream = self.chat_stream
chat_id = chat_stream.stream_id chat_id = chat_stream.stream_id
person_info_manager = get_person_info_manager()
is_group_chat = bool(chat_stream.group_info) is_group_chat = bool(chat_stream.group_info)
platform = chat_stream.platform platform = chat_stream.platform
user_id = reply_message.get("user_id","") user_id = reply_message.get("user_id","")
if user_id: if user_id:
person_id = person_info_manager.get_person_id(platform,user_id) person = Person(platform=platform, user_id=user_id)
person_name = await person_info_manager.get_value(person_id, "person_name") person_name = person.person_name or user_id
sender = person_name sender = person_name
target = reply_message.get('processed_plain_text') target = reply_message.get('processed_plain_text')
else: else:
person_id = ""
person_name = "用户" person_name = "用户"
sender = "用户" sender = "用户"
target = "消息" target = "消息"
@@ -746,7 +740,7 @@ class DefaultReplyer:
logger.warning(f"回复生成前信息获取耗时过长: {chinese_name} 耗时: {duration:.1f}s请使用更快的模型") logger.warning(f"回复生成前信息获取耗时过长: {chinese_name} 耗时: {duration:.1f}s请使用更快的模型")
logger.info(f"在回复前的步骤耗时: {'; '.join(timing_logs)}") logger.info(f"在回复前的步骤耗时: {'; '.join(timing_logs)}")
(expression_habits_block, reply_style_guide) = results_dict["expression_habits"] expression_habits_block = results_dict["expression_habits"]
relation_info = results_dict["relation_info"] relation_info = results_dict["relation_info"]
memory_block = results_dict["memory_block"] memory_block = results_dict["memory_block"]
tool_info = results_dict["tool_info"] tool_info = results_dict["tool_info"]
@@ -802,7 +796,7 @@ class DefaultReplyer:
if global_config.bot.qq_account == user_id and platform == global_config.bot.platform: if global_config.bot.qq_account == user_id and platform == global_config.bot.platform:
return await global_prompt_manager.format_prompt( return await global_prompt_manager.format_prompt(
"replyer_self_prompt", "replyer_self_prompt",
expression_habits_block=reply_style_guide, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
memory_block=memory_block, memory_block=memory_block,
@@ -822,7 +816,7 @@ class DefaultReplyer:
else: else:
return await global_prompt_manager.format_prompt( return await global_prompt_manager.format_prompt(
"replyer_prompt", "replyer_prompt",
expression_habits_block=reply_style_guide, expression_habits_block=expression_habits_block,
tool_info_block=tool_info, tool_info_block=tool_info,
knowledge_prompt=prompt_info, knowledge_prompt=prompt_info,
memory_block=memory_block, memory_block=memory_block,
@@ -885,7 +879,6 @@ class DefaultReplyer:
self.build_relation_info(sender, target), self.build_relation_info(sender, target),
) )
expression_habits_block, reply_style_guide = expression_habits_block
keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target) keywords_reaction_prompt = await self.build_keywords_reaction_prompt(target)

View File

@@ -1,7 +1,6 @@
from typing import Dict, Optional, List, Tuple from typing import Dict, Optional
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.api_ada_configs import TaskConfig
from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager from src.chat.message_receive.chat_stream import ChatStream, get_chat_manager
from src.chat.replyer.default_generator import DefaultReplyer from src.chat.replyer.default_generator import DefaultReplyer

View File

@@ -9,7 +9,7 @@ from src.config.config import global_config
from src.common.message_repository import find_messages, count_messages from src.common.message_repository import find_messages, count_messages
from src.common.database.database_model import ActionRecords from src.common.database.database_model import ActionRecords
from src.common.database.database_model import Images from src.common.database.database_model import Images
from src.person_info.person_info import PersonInfoManager, get_person_info_manager from src.person_info.person_info import Person,get_person_id
from src.chat.utils.utils import translate_timestamp_to_human_readable, assign_message_ids from src.chat.utils.utils import translate_timestamp_to_human_readable, assign_message_ids
install(extra_lines=3) install(extra_lines=3)
@@ -35,14 +35,12 @@ def replace_user_references_sync(
str: 处理后的内容字符串 str: 处理后的内容字符串
""" """
if name_resolver is None: if name_resolver is None:
person_info_manager = get_person_info_manager()
def default_resolver(platform: str, user_id: str) -> str: def default_resolver(platform: str, user_id: str) -> str:
# 检查是否是机器人自己 # 检查是否是机器人自己
if replace_bot_name and user_id == global_config.bot.qq_account: if replace_bot_name and user_id == global_config.bot.qq_account:
return f"{global_config.bot.nickname}(你)" return f"{global_config.bot.nickname}(你)"
person_id = PersonInfoManager.get_person_id(platform, user_id) person = Person(platform=platform, user_id=user_id)
return person_info_manager.get_value_sync(person_id, "person_name") or user_id # type: ignore return person.person_name or user_id # type: ignore
name_resolver = default_resolver name_resolver = default_resolver
@@ -110,14 +108,12 @@ async def replace_user_references_async(
str: 处理后的内容字符串 str: 处理后的内容字符串
""" """
if name_resolver is None: if name_resolver is None:
person_info_manager = get_person_info_manager()
async def default_resolver(platform: str, user_id: str) -> str: async def default_resolver(platform: str, user_id: str) -> str:
# 检查是否是机器人自己 # 检查是否是机器人自己
if replace_bot_name and user_id == global_config.bot.qq_account: if replace_bot_name and user_id == global_config.bot.qq_account:
return f"{global_config.bot.nickname}(你)" return f"{global_config.bot.nickname}(你)"
person_id = PersonInfoManager.get_person_id(platform, user_id) person = Person(platform=platform, user_id=user_id)
return await person_info_manager.get_value(person_id, "person_name") or user_id # type: ignore return person.person_name or user_id # type: ignore
name_resolver = default_resolver name_resolver = default_resolver
@@ -506,14 +502,13 @@ def _build_readable_messages_internal(
if not all([platform, user_id, timestamp is not None]): if not all([platform, user_id, timestamp is not None]):
continue continue
person_id = PersonInfoManager.get_person_id(platform, user_id) person = Person(platform=platform, user_id=user_id)
person_info_manager = get_person_info_manager()
# 根据 replace_bot_name 参数决定是否替换机器人名称 # 根据 replace_bot_name 参数决定是否替换机器人名称
person_name: str person_name: str
if replace_bot_name and user_id == global_config.bot.qq_account: if replace_bot_name and user_id == global_config.bot.qq_account:
person_name = f"{global_config.bot.nickname}(你)" person_name = f"{global_config.bot.nickname}(你)"
else: else:
person_name = person_info_manager.get_value_sync(person_id, "person_name") # type: ignore person_name = person.person_name or user_id # type: ignore
# 如果 person_name 未设置,则使用消息中的 nickname 或默认名称 # 如果 person_name 未设置,则使用消息中的 nickname 或默认名称
if not person_name: if not person_name:
@@ -1009,7 +1004,7 @@ async def build_anonymous_messages(messages: List[Dict[str, Any]]) -> str:
# print("SELF11111111111111") # print("SELF11111111111111")
return "SELF" return "SELF"
try: try:
person_id = PersonInfoManager.get_person_id(platform, user_id) person_id = get_person_id(platform, user_id)
except Exception as _e: except Exception as _e:
person_id = None person_id = None
if not person_id: if not person_id:
@@ -1102,7 +1097,7 @@ async def get_person_id_list(messages: List[Dict[str, Any]]) -> List[str]:
if platform is None: if platform is None:
platform = "unknown" platform = "unknown"
if person_id := PersonInfoManager.get_person_id(platform, user_id): if person_id := get_person_id(platform, user_id):
person_ids_set.add(person_id) person_ids_set.add(person_id)
return list(person_ids_set) # 将集合转换为列表返回 return list(person_ids_set) # 将集合转换为列表返回

View File

@@ -15,7 +15,7 @@ from src.config.config import global_config, model_config
from src.chat.message_receive.message import MessageRecv from src.chat.message_receive.message import MessageRecv
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.person_info.person_info import PersonInfoManager, get_person_info_manager from src.person_info.person_info import Person
from .typo_generator import ChineseTypoGenerator from .typo_generator import ChineseTypoGenerator
logger = get_logger("chat_utils") logger = get_logger("chat_utils")
@@ -639,12 +639,12 @@ def get_chat_type_and_target_info(chat_id: str) -> Tuple[bool, Optional[Dict]]:
# Try to fetch person info # Try to fetch person info
try: try:
# Assume get_person_id is sync (as per original code), keep using to_thread # Assume get_person_id is sync (as per original code), keep using to_thread
person_id = PersonInfoManager.get_person_id(platform, user_id) person = Person(platform=platform, user_id=user_id)
person_id = person.person_id
person_name = None person_name = None
if person_id: if person_id:
# get_value is async, so await it directly # get_value is async, so await it directly
person_info_manager = get_person_info_manager() person_name = person.person_name
person_name = person_info_manager.get_value_sync(person_id, "person_name")
target_info["person_id"] = person_id target_info["person_id"] = person_id
target_info["person_name"] = person_name target_info["person_name"] = person_name

View File

@@ -254,6 +254,7 @@ class PersonInfo(BaseModel):
用于存储个人信息数据的模型。 用于存储个人信息数据的模型。
""" """
is_known = BooleanField(default=False) # 是否已认识
person_id = TextField(unique=True, index=True) # 个人唯一ID person_id = TextField(unique=True, index=True) # 个人唯一ID
person_name = TextField(null=True) # 个人名称 (允许为空) person_name = TextField(null=True) # 个人名称 (允许为空)
name_reason = TextField(null=True) # 名称设定的原因 name_reason = TextField(null=True) # 名称设定的原因
@@ -261,16 +262,26 @@ class PersonInfo(BaseModel):
user_id = TextField(index=True) # 用户ID user_id = TextField(index=True) # 用户ID
nickname = TextField(null=True) # 用户昵称 nickname = TextField(null=True) # 用户昵称
points = TextField(null=True) # 个人印象的点 points = TextField(null=True) # 个人印象的点
attitude_to_me = TextField(null=True) # 对bot的态度
rudeness = TextField(null=True) # 对bot的冒犯程度
neuroticism = TextField(null=True) # 对bot的神经质程度
conscientiousness = TextField(null=True) # 对bot的尽责程度
likeness = TextField(null=True) # 对bot的相似程度
know_times = FloatField(null=True) # 认识时间 (时间戳) know_times = FloatField(null=True) # 认识时间 (时间戳)
know_since = FloatField(null=True) # 首次印象总结时间 know_since = FloatField(null=True) # 首次印象总结时间
last_know = FloatField(null=True) # 最后一次印象总结时间 last_know = FloatField(null=True) # 最后一次印象总结时间
attitude_to_me = TextField(null=True) # 对bot的态度
attitude_to_me_confidence = FloatField(null=True) # 对bot的态度置信度
friendly_value = FloatField(null=True) # 对bot的友好程度
friendly_value_confidence = FloatField(null=True) # 对bot的友好程度置信度
rudeness = TextField(null=True) # 对bot的冒犯程度
rudeness_confidence = FloatField(null=True) # 对bot的冒犯程度置信度
neuroticism = TextField(null=True) # 对bot的神经质程度
neuroticism_confidence = FloatField(null=True) # 对bot的神经质程度置信度
conscientiousness = TextField(null=True) # 对bot的尽责程度
conscientiousness_confidence = FloatField(null=True) # 对bot的尽责程度置信度
likeness = TextField(null=True) # 对bot的相似程度
likeness_confidence = FloatField(null=True) # 对bot的相似程度置信度
class Meta: class Meta:
# database = db # 继承自 BaseModel # database = db # 继承自 BaseModel
table_name = "person_info" table_name = "person_info"

View File

@@ -6,7 +6,6 @@ import time
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.person_info.person_info import get_person_info_manager
from rich.traceback import install from rich.traceback import install
install(extra_lines=3) install(extra_lines=3)
@@ -19,7 +18,6 @@ class Individuality:
def __init__(self): def __init__(self):
self.name = "" self.name = ""
self.bot_person_id = ""
self.meta_info_file_path = "data/personality/meta.json" self.meta_info_file_path = "data/personality/meta.json"
self.personality_data_file_path = "data/personality/personality_data.json" self.personality_data_file_path = "data/personality/personality_data.json"
@@ -32,8 +30,6 @@ class Individuality:
personality_side = global_config.personality.personality_side personality_side = global_config.personality.personality_side
identity = global_config.personality.identity identity = global_config.personality.identity
person_info_manager = get_person_info_manager()
self.bot_person_id = person_info_manager.get_person_id("system", "bot_id")
self.name = bot_nickname self.name = bot_nickname
# 检查配置变化,如果变化则清空 # 检查配置变化,如果变化则清空
@@ -64,16 +60,6 @@ class Individuality:
else: else:
logger.error("人设构建失败") logger.error("人设构建失败")
# 如果任何一个发生变化都需要清空数据库中的info_list因为这影响整体人设
if personality_changed or identity_changed:
logger.info("将清空数据库中原有的关键词缓存")
update_data = {
"platform": "system",
"user_id": "bot_id",
"person_name": self.name,
"nickname": self.name,
}
await person_info_manager.update_one_field(self.bot_person_id, "info_list", [], data=update_data)
async def get_personality_block(self) -> str: async def get_personality_block(self) -> str:
bot_name = global_config.bot.nickname bot_name = global_config.bot.nickname
@@ -130,7 +116,6 @@ class Individuality:
Returns: Returns:
tuple: (personality_changed, identity_changed) tuple: (personality_changed, identity_changed)
""" """
person_info_manager = get_person_info_manager()
current_personality_hash, current_identity_hash = self._get_config_hash( current_personality_hash, current_identity_hash = self._get_config_hash(
bot_nickname, personality_core, personality_side, identity bot_nickname, personality_core, personality_side, identity
) )
@@ -148,17 +133,6 @@ class Individuality:
if identity_changed: if identity_changed:
logger.info("检测到身份配置发生变化") logger.info("检测到身份配置发生变化")
# 如果任何一个发生变化都需要清空info_list因为这影响整体人设
if personality_changed or identity_changed:
logger.info("将清空原有的关键词缓存")
update_data = {
"platform": "system",
"user_id": "bot_id",
"person_name": self.name,
"nickname": self.name,
}
await person_info_manager.update_one_field(self.bot_person_id, "info_list", [], data=update_data)
# 更新元信息文件 # 更新元信息文件
new_meta_info = { new_meta_info = {
"personality_hash": current_personality_hash, "personality_hash": current_personality_hash,

View File

@@ -16,7 +16,7 @@ import json
from .s4u_mood_manager import mood_manager from .s4u_mood_manager import mood_manager
from src.person_info.relationship_builder_manager import relationship_builder_manager from src.person_info.relationship_builder_manager import relationship_builder_manager
from src.mais4u.s4u_config import s4u_config from src.mais4u.s4u_config import s4u_config
from src.person_info.person_info import PersonInfoManager from src.person_info.person_info import get_person_id
from .super_chat_manager import get_super_chat_manager from .super_chat_manager import get_super_chat_manager
from .yes_or_no import yes_or_no_head from .yes_or_no import yes_or_no_head
from src.mais4u.constant_s4u import ENABLE_S4U from src.mais4u.constant_s4u import ENABLE_S4U
@@ -262,7 +262,7 @@ class S4UChat:
"""根据VIP状态和中断逻辑将消息放入相应队列。""" """根据VIP状态和中断逻辑将消息放入相应队列。"""
user_id = message.message_info.user_info.user_id user_id = message.message_info.user_info.user_id
platform = message.message_info.platform platform = message.message_info.platform
person_id = PersonInfoManager.get_person_id(platform, user_id) person_id = get_person_id(platform, user_id)
try: try:
is_gift = message.is_gift is_gift = message.is_gift

View File

@@ -10,8 +10,7 @@ from datetime import datetime
import asyncio import asyncio
from src.mais4u.s4u_config import s4u_config from src.mais4u.s4u_config import s4u_config
from src.chat.message_receive.message import MessageRecvS4U from src.chat.message_receive.message import MessageRecvS4U
from src.person_info.relationship_fetcher import relationship_fetcher_manager from src.person_info.person_info import Person, get_person_id
from src.person_info.person_info import PersonInfoManager, get_person_info_manager
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.mais4u.mais4u_chat.super_chat_manager import get_super_chat_manager from src.mais4u.mais4u_chat.super_chat_manager import get_super_chat_manager
from src.mais4u.mais4u_chat.screen_manager import screen_manager from src.mais4u.mais4u_chat.screen_manager import screen_manager
@@ -103,7 +102,7 @@ class PromptBuilder:
# 使用从处理器传来的选中表达方式 # 使用从处理器传来的选中表达方式
# LLM模式调用LLM选择5-10个然后随机选5个 # LLM模式调用LLM选择5-10个然后随机选5个
_,selected_expressions = await expression_selector.select_suitable_expressions_llm( selected_expressions = await expression_selector.select_suitable_expressions_llm(
chat_stream.stream_id, chat_history, max_num=12, target_message=target chat_stream.stream_id, chat_history, max_num=12, target_message=target
) )
@@ -142,18 +141,16 @@ class PromptBuilder:
relation_prompt = "" relation_prompt = ""
if global_config.relationship.enable_relationship and who_chat_in_group: if global_config.relationship.enable_relationship and who_chat_in_group:
relationship_fetcher = relationship_fetcher_manager.get_fetcher(chat_stream.stream_id)
# 将 (platform, user_id, nickname) 转换为 person_id # 将 (platform, user_id, nickname) 转换为 person_id
person_ids = [] person_ids = []
for person in who_chat_in_group: for person in who_chat_in_group:
person_id = PersonInfoManager.get_person_id(person[0], person[1]) person_id = get_person_id(person[0], person[1])
person_ids.append(person_id) person_ids.append(person_id)
# 使用 RelationshipFetcher 的 build_relation_info 方法,设置 points_num=3 保持与原来相同的行为 # 使用 Person 的 build_relationship 方法,设置 points_num=3 保持与原来相同的行为
relation_info_list = await asyncio.gather( relation_info_list = [
*[relationship_fetcher.build_relation_info(person_id, points_num=3) for person_id in person_ids] Person(person_id=person_id).build_relationship(points_num=3) for person_id in person_ids
) ]
if relation_info := "".join(relation_info_list): if relation_info := "".join(relation_info_list):
relation_prompt = await global_prompt_manager.format_prompt( relation_prompt = await global_prompt_manager.format_prompt(
"relation_prompt", relation_info=relation_info "relation_prompt", relation_info=relation_info
@@ -288,11 +285,8 @@ class PromptBuilder:
chat_stream = message.chat_stream chat_stream = message.chat_stream
person_id = PersonInfoManager.get_person_id( person = Person(platform=message.chat_stream.user_info.platform, user_id=message.chat_stream.user_info.user_id)
message.chat_stream.user_info.platform, message.chat_stream.user_info.user_id person_name = person.person_name
)
person_info_manager = get_person_info_manager()
person_name = await person_info_manager.get_value(person_id, "person_name")
if message.chat_stream.user_info.user_nickname: if message.chat_stream.user_info.user_nickname:
if person_name: if person_name:

View File

@@ -5,7 +5,6 @@ from src.config.config import model_config
from src.chat.message_receive.message import MessageRecvS4U from src.chat.message_receive.message import MessageRecvS4U
from src.mais4u.mais4u_chat.s4u_prompt import prompt_builder from src.mais4u.mais4u_chat.s4u_prompt import prompt_builder
from src.common.logger import get_logger from src.common.logger import get_logger
import asyncio
import re import re

View File

@@ -1,4 +0,0 @@
from .person_info import get_person_info_manager
from .group_info import get_group_info_manager
__all__ = ["get_person_info_manager", "get_group_info_manager"]

View File

@@ -1,11 +1,11 @@
import copy
import hashlib import hashlib
import datetime
import asyncio import asyncio
import json import json
import time
import random
from json_repair import repair_json from json_repair import repair_json
from typing import Any, Callable, Dict, Union, Optional from typing import Union
from src.common.logger import get_logger from src.common.logger import get_logger
from src.common.database.database import db from src.common.database.database import db
@@ -14,45 +14,276 @@ from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
"""
PersonInfoManager 类方法功能摘要:
1. get_person_id - 根据平台和用户ID生成MD5哈希的唯一person_id
2. create_person_info - 创建新个人信息文档(自动合并默认值)
3. update_one_field - 更新单个字段值(若文档不存在则创建)
4. del_one_document - 删除指定person_id的文档
5. get_value - 获取单个字段值(返回实际值或默认值)
6. get_values - 批量获取字段值(任一字段无效则返回空字典)
7. del_all_undefined_field - 清理全集合中未定义的字段
8. get_specific_value_list - 根据指定条件返回person_id,value字典
"""
logger = get_logger("person_info") logger = get_logger("person_info")
JSON_SERIALIZED_FIELDS = ["points"] def get_person_id(platform: str, user_id: Union[int, str]) -> str:
"""获取唯一id"""
if "-" in platform:
platform = platform.split("-")[1]
components = [platform, str(user_id)]
key = "_".join(components)
return hashlib.md5(key.encode()).hexdigest()
person_info_default = { def get_person_id_by_person_name(person_name: str) -> str:
"person_id": None, """根据用户名获取用户ID"""
"person_name": None, try:
"name_reason": None, # Corrected from person_name_reason to match common usage if intended record = PersonInfo.get_or_none(PersonInfo.person_name == person_name)
"platform": "unknown", return record.person_id if record else ""
"user_id": "unknown", except Exception as e:
"nickname": "Unknown", logger.error(f"根据用户名 {person_name} 获取用户ID时出错 (Peewee): {e}")
"know_times": 0, return ""
"know_since": None,
"last_know": None, class Person:
"attitude_to_me": "0,1", def __init__(self, platform: str = "", user_id: str = "",person_id: str = "",person_name: str = "",nickname: str = ""):
"friendly_value": 50, if person_id:
"rudeness":50, self.person_id = person_id
"neuroticism":"5,1", elif person_name:
"conscientiousness": 50, self.person_id = get_person_id_by_person_name(person_name)
"likeness": 50, if not self.person_id:
"points": None, logger.error(f"根据用户名 {person_name} 获取用户ID时出错不存在用户{person_name}")
} return ""
elif platform and user_id:
self.person_id = get_person_id(platform, user_id)
else:
logger.error("Person 初始化失败,缺少必要参数")
return ""
self.is_known = False
self.platform = platform
self.user_id = user_id
# 初始化默认值
self.nickname = nickname
self.person_name = None
self.name_reason = None
self.know_times = 0
self.know_since = None
self.last_know = None
self.points = []
# 初始化性格特征相关字段
self.attitude_to_me:float = 0
self.attitude_to_me_confidence:float = 1
self.neuroticism:float = 5
self.neuroticism_confidence:float = 1
self.friendly_value:float = 50
self.friendly_value_confidence:float = 1
self.rudeness:float = 50
self.rudeness_confidence:float = 1
self.conscientiousness:float = 50
self.conscientiousness_confidence:float = 1
self.likeness:float = 50
self.likeness_confidence:float = 1
# 从数据库加载数据
self.load_from_database()
def load_from_database(self):
"""从数据库加载个人信息数据"""
try:
# 查询数据库中的记录
record = PersonInfo.get_or_none(PersonInfo.person_id == self.person_id)
if record:
self.is_known = record.is_known if record.is_known else False
self.nickname = record.nickname if record.nickname else self.nickname
if not self.is_known:
if self.nickname:
self.is_known = True
self.person_name = self.nickname
logger.info(f"用户 {self.person_id} 已认识,昵称:{self.nickname}")
else:
logger.warning(f"用户 {self.person_id} 尚未认识,昵称为空")
else:
self.person_name = record.person_name if record.person_name else self.nickname
self.name_reason = record.name_reason if record.name_reason else None
self.know_times = record.know_times if record.know_times else 0
self.know_since = record.know_since if record.know_since else time.time()
self.last_know = record.last_know if record.last_know else time.time()
# 处理points字段JSON格式的列表
if record.points:
try:
self.points = json.loads(record.points)
except (json.JSONDecodeError, TypeError):
logger.warning(f"解析用户 {self.person_id} 的points字段失败使用默认值")
self.points = []
else:
self.points = []
# 加载性格特征相关字段
if record.attitude_to_me and not isinstance(record.attitude_to_me, str):
self.attitude_to_me = record.attitude_to_me
if record.attitude_to_me_confidence is not None:
self.attitude_to_me_confidence = float(record.attitude_to_me_confidence)
if record.friendly_value is not None:
self.friendly_value = float(record.friendly_value)
if record.friendly_value_confidence is not None:
self.friendly_value_confidence = float(record.friendly_value_confidence)
if record.rudeness is not None:
self.rudeness = float(record.rudeness)
if record.rudeness_confidence is not None:
self.rudeness_confidence = float(record.rudeness_confidence)
if record.neuroticism and not isinstance(record.neuroticism, str):
self.neuroticism = float(record.neuroticism)
if record.neuroticism_confidence is not None:
self.neuroticism_confidence = float(record.neuroticism_confidence)
if record.conscientiousness is not None:
self.conscientiousness = float(record.conscientiousness)
if record.conscientiousness_confidence is not None:
self.conscientiousness_confidence = float(record.conscientiousness_confidence)
if record.likeness is not None:
self.likeness = float(record.likeness)
if record.likeness_confidence is not None:
self.likeness_confidence = float(record.likeness_confidence)
logger.info(f"已从数据库加载用户 {self.person_id} 的信息")
else:
self.sync_to_database()
logger.info(f"用户 {self.person_id} 在数据库中不存在,使用默认值并创建")
except Exception as e:
logger.error(f"从数据库加载用户 {self.person_id} 信息时出错: {e}")
# 出错时保持默认值
def sync_to_database(self):
"""将所有属性同步回数据库"""
try:
# 准备数据
data = {
'person_id': self.person_id,
'is_known': self.is_known,
'platform': self.platform,
'user_id': self.user_id,
'nickname': self.nickname,
'person_name': self.person_name,
'name_reason': self.name_reason,
'know_times': self.know_times,
'know_since': self.know_since,
'last_know': self.last_know,
'points': json.dumps(self.points, ensure_ascii=False) if self.points else json.dumps([], ensure_ascii=False),
'attitude_to_me': self.attitude_to_me,
'attitude_to_me_confidence': self.attitude_to_me_confidence,
'friendly_value': self.friendly_value,
'friendly_value_confidence': self.friendly_value_confidence,
'rudeness': self.rudeness,
'rudeness_confidence': self.rudeness_confidence,
'neuroticism': self.neuroticism,
'neuroticism_confidence': self.neuroticism_confidence,
'conscientiousness': self.conscientiousness,
'conscientiousness_confidence': self.conscientiousness_confidence,
'likeness': self.likeness,
'likeness_confidence': self.likeness_confidence,
}
# 检查记录是否存在
record = PersonInfo.get_or_none(PersonInfo.person_id == self.person_id)
if record:
# 更新现有记录
for field, value in data.items():
if hasattr(record, field):
setattr(record, field, value)
record.save()
logger.debug(f"已同步用户 {self.person_id} 的信息到数据库")
else:
# 创建新记录
PersonInfo.create(**data)
logger.debug(f"已创建用户 {self.person_id} 的信息到数据库")
except Exception as e:
logger.error(f"同步用户 {self.person_id} 信息到数据库时出错: {e}")
def build_relationship(self,points_num=3):
if not self.is_known:
return ""
# 按时间排序forgotten_points
current_points = self.points
current_points.sort(key=lambda x: x[2])
# 按权重加权随机抽取最多3个不重复的pointspoint[1]的值在1-10之间权重越高被抽到概率越大
if len(current_points) > points_num:
# point[1] 取值范围1-10直接作为权重
weights = [max(1, min(10, int(point[1]))) for point in current_points]
# 使用加权采样不放回,保证不重复
indices = list(range(len(current_points)))
points = []
for _ in range(points_num):
if not indices:
break
sub_weights = [weights[i] for i in indices]
chosen_idx = random.choices(indices, weights=sub_weights, k=1)[0]
points.append(current_points[chosen_idx])
indices.remove(chosen_idx)
else:
points = current_points
# 构建points文本
points_text = "\n".join([f"{point[2]}{point[0]}" for point in points])
nickname_str = ""
if self.person_name != nickname_str:
nickname_str = f"(ta在{self.platform}上的昵称是{nickname_str})"
relation_info = ""
attitude_info = ""
if self.attitude_to_me:
if self.attitude_to_me > 8:
attitude_info = f"{self.person_name}对你的态度十分好,"
elif self.attitude_to_me > 5:
attitude_info = f"{self.person_name}对你的态度较好,"
if self.attitude_to_me < -8:
attitude_info = f"{self.person_name}对你的态度十分恶劣,"
elif self.attitude_to_me < -4:
attitude_info = f"{self.person_name}对你的态度不好,"
elif self.attitude_to_me < 0:
attitude_info = f"{self.person_name}对你的态度一般,"
neuroticism_info = ""
if self.neuroticism:
if self.neuroticism > 8:
neuroticism_info = f"{self.person_name}的情绪十分活跃,容易情绪化,"
elif self.neuroticism > 6:
neuroticism_info = f"{self.person_name}的情绪比较活跃,"
elif self.neuroticism > 4:
neuroticism_info = ""
elif self.neuroticism > 2:
neuroticism_info = f"{self.person_name}的情绪比较稳定,"
else:
neuroticism_info = f"{self.person_name}的情绪非常稳定,毫无波动"
points_info = ""
if points_text:
points_info = f"你还记得ta最近做的事{points_text}"
relation_info = f"{self.person_name}:{nickname_str}{attitude_info}{neuroticism_info}{points_info}"
return relation_info
class PersonInfoManager: class PersonInfoManager:
def __init__(self): def __init__(self):
self.person_name_list = {} self.person_name_list = {}
self.qv_name_llm = LLMRequest(model_set=model_config.model_task_config.utils, request_type="relation.qv_name") self.qv_name_llm = LLMRequest(model_set=model_config.model_task_config.utils, request_type="relation.qv_name")
try: try:
@@ -78,157 +309,10 @@ class PersonInfoManager:
except Exception as e: except Exception as e:
logger.error(f"从 Peewee 加载 person_name_list 失败: {e}") logger.error(f"从 Peewee 加载 person_name_list 失败: {e}")
@staticmethod def get_person(self, platform: str, user_id: Union[int, str]) -> Person:
def get_person_id(platform: str, user_id: Union[int, str]) -> str: person = Person(platform, user_id)
"""获取唯一id""" return person
# 添加空值检查,防止 platform 为 None 时出错
if platform is None:
platform = "unknown"
elif "-" in platform:
platform = platform.split("-")[1]
components = [platform, str(user_id)]
key = "_".join(components)
return hashlib.md5(key.encode()).hexdigest()
async def is_person_known(self, platform: str, user_id: int):
"""判断是否认识某人"""
person_id = self.get_person_id(platform, user_id)
def _db_check_known_sync(p_id: str):
return PersonInfo.get_or_none(PersonInfo.person_id == p_id) is not None
try:
return await asyncio.to_thread(_db_check_known_sync, person_id)
except Exception as e:
logger.error(f"检查用户 {person_id} 是否已知时出错 (Peewee): {e}")
return False
def get_person_id_by_person_name(self, person_name: str) -> str:
"""根据用户名获取用户ID"""
try:
record = PersonInfo.get_or_none(PersonInfo.person_name == person_name)
return record.person_id if record else ""
except Exception as e:
logger.error(f"根据用户名 {person_name} 获取用户ID时出错 (Peewee): {e}")
return ""
async def _safe_create_person_info(self, person_id: str, data: Optional[dict] = None):
"""安全地创建用户信息,处理竞态条件"""
if not person_id:
logger.debug("创建失败person_id不存在")
return
_person_info_default = copy.deepcopy(person_info_default)
model_fields = PersonInfo._meta.fields.keys() # type: ignore
final_data = {"person_id": person_id}
# Start with defaults for all model fields
for key, default_value in _person_info_default.items():
if key in model_fields:
final_data[key] = default_value
# Override with provided data
if data:
for key, value in data.items():
if key in model_fields:
final_data[key] = value
# Ensure person_id is correctly set from the argument
final_data["person_id"] = person_id
# Serialize JSON fields
for key in JSON_SERIALIZED_FIELDS:
if key in final_data:
if isinstance(final_data[key], (list, dict)):
final_data[key] = json.dumps(final_data[key], ensure_ascii=False)
elif final_data[key] is None: # Default for lists is [], store as "[]"
final_data[key] = json.dumps([], ensure_ascii=False)
def _db_safe_create_sync(p_data: dict):
try:
# 首先检查是否已存在
existing = PersonInfo.get_or_none(PersonInfo.person_id == p_data["person_id"])
if existing:
logger.debug(f"用户 {p_data['person_id']} 已存在,跳过创建")
return True
# 尝试创建
PersonInfo.create(**p_data)
return True
except Exception as e:
if "UNIQUE constraint failed" in str(e):
logger.debug(f"检测到并发创建用户 {p_data.get('person_id')},跳过错误")
return True # 其他协程已创建,视为成功
else:
logger.error(f"创建 PersonInfo 记录 {p_data.get('person_id')} 失败 (Peewee): {e}")
return False
await asyncio.to_thread(_db_safe_create_sync, final_data)
async def update_one_field(self, person_id: str, field_name: str, value, data: Optional[Dict] = None):
"""更新某一个字段,会补全"""
if field_name not in PersonInfo._meta.fields: # type: ignore
logger.debug(f"更新'{field_name}'失败,未在 PersonInfo Peewee 模型中定义的字段。")
return
processed_value = value
if field_name in JSON_SERIALIZED_FIELDS:
if isinstance(value, (list, dict)):
processed_value = json.dumps(value, ensure_ascii=False, indent=None)
elif value is None: # Store None as "[]" for JSON list fields
processed_value = json.dumps([], ensure_ascii=False, indent=None)
def _db_update_sync(p_id: str, f_name: str, val_to_set):
import time
start_time = time.time()
try:
record = PersonInfo.get_or_none(PersonInfo.person_id == p_id)
query_time = time.time()
if record:
setattr(record, f_name, val_to_set)
record.save()
save_time = time.time()
total_time = save_time - start_time
if total_time > 0.5: # 如果超过500ms就记录日志
logger.warning(
f"数据库更新操作耗时 {total_time:.3f}秒 (查询: {query_time - start_time:.3f}s, 保存: {save_time - query_time:.3f}s) person_id={p_id}, field={f_name}"
)
return True, False # Found and updated, no creation needed
else:
total_time = time.time() - start_time
if total_time > 0.5:
logger.warning(f"数据库查询操作耗时 {total_time:.3f}秒 person_id={p_id}, field={f_name}")
return False, True # Not found, needs creation
except Exception as e:
total_time = time.time() - start_time
logger.error(f"数据库操作异常,耗时 {total_time:.3f}秒: {e}")
raise
found, needs_creation = await asyncio.to_thread(_db_update_sync, person_id, field_name, processed_value)
if needs_creation:
logger.info(f"{person_id} 不存在,将新建。")
creation_data = data if data is not None else {}
# Ensure platform and user_id are present for context if available from 'data'
# but primarily, set the field that triggered the update.
# The create_person_info will handle defaults and serialization.
creation_data[field_name] = value # Pass original value to create_person_info
# Ensure platform and user_id are in creation_data if available,
# otherwise create_person_info will use defaults.
if data and "platform" in data:
creation_data["platform"] = data["platform"]
if data and "user_id" in data:
creation_data["user_id"] = data["user_id"]
# 使用安全的创建方法,处理竞态条件
await self._safe_create_person_info(person_id, creation_data)
@staticmethod @staticmethod
def _extract_json_from_text(text: str) -> dict: def _extract_json_from_text(text: str) -> dict:
@@ -279,8 +363,9 @@ class PersonInfoManager:
logger.debug("取名失败person_id不能为空") logger.debug("取名失败person_id不能为空")
return None return None
old_name = await self.get_value(person_id, "person_name") person = Person(person_id=person_id)
old_reason = await self.get_value(person_id, "name_reason") old_name = person.person_name
old_reason = person.name_reason
max_retries = 8 max_retries = 8
current_try = 0 current_try = 0
@@ -338,8 +423,9 @@ class PersonInfoManager:
current_name_set.add(generated_nickname) current_name_set.add(generated_nickname)
if not is_duplicate: if not is_duplicate:
await self.update_one_field(person_id, "person_name", generated_nickname) person.person_name = generated_nickname
await self.update_one_field(person_id, "name_reason", result.get("reason", "未提供理由")) person.name_reason = result.get("reason", "未提供理由")
person.sync_to_database()
logger.info( logger.info(
f"成功给用户{user_nickname} {person_id} 取名 {generated_nickname},理由:{result.get('reason', '未提供理由')}" f"成功给用户{user_nickname} {person_id} 取名 {generated_nickname},理由:{result.get('reason', '未提供理由')}"
@@ -357,186 +443,11 @@ class PersonInfoManager:
# 如果多次尝试后仍未成功,使用唯一的 user_nickname 作为默认值 # 如果多次尝试后仍未成功,使用唯一的 user_nickname 作为默认值
unique_nickname = await self._generate_unique_person_name(user_nickname) unique_nickname = await self._generate_unique_person_name(user_nickname)
logger.warning(f"{max_retries}次尝试后未能生成唯一昵称,使用默认昵称 {unique_nickname}") logger.warning(f"{max_retries}次尝试后未能生成唯一昵称,使用默认昵称 {unique_nickname}")
await self.update_one_field(person_id, "person_name", unique_nickname) person.person_name = unique_nickname
await self.update_one_field(person_id, "name_reason", "使用用户原始昵称作为默认值") person.name_reason = "使用用户原始昵称作为默认值"
person.sync_to_database()
self.person_name_list[person_id] = unique_nickname self.person_name_list[person_id] = unique_nickname
return {"nickname": unique_nickname, "reason": "使用用户原始昵称作为默认值"} return {"nickname": unique_nickname, "reason": "使用用户原始昵称作为默认值"}
@staticmethod person_info_manager = PersonInfoManager()
async def get_value(person_id: str, field_name: str):
"""获取指定用户指定字段的值"""
default_value_for_field = person_info_default.get(field_name)
if field_name in JSON_SERIALIZED_FIELDS and default_value_for_field is None:
default_value_for_field = [] # Ensure JSON fields default to [] if not in DB
def _db_get_value_sync(p_id: str, f_name: str):
record = PersonInfo.get_or_none(PersonInfo.person_id == p_id)
if record:
val = getattr(record, f_name, None)
if f_name in JSON_SERIALIZED_FIELDS:
if isinstance(val, str):
try:
return json.loads(val)
except json.JSONDecodeError:
logger.warning(f"字段 {f_name} for {p_id} 包含无效JSON: {val}. 返回默认值.")
return [] # Default for JSON fields on error
elif val is None: # Field exists in DB but is None
return [] # Default for JSON fields
# If val is already a list/dict (e.g. if somehow set without serialization)
return val # Should ideally not happen if update_one_field is always used
return val
return None # Record not found
try:
value_from_db = await asyncio.to_thread(_db_get_value_sync, person_id, field_name)
if value_from_db is not None:
return value_from_db
if field_name in person_info_default:
return default_value_for_field
logger.warning(f"字段 {field_name} 在 person_info_default 中未定义,且在数据库中未找到。")
return None # Ultimate fallback
except Exception as e:
logger.error(f"获取字段 {field_name} for {person_id} 时出错 (Peewee): {e}")
# Fallback to default in case of any error during DB access
return default_value_for_field if field_name in person_info_default else None
@staticmethod
def get_value_sync(person_id: str, field_name: str):
"""同步获取指定用户指定字段的值"""
default_value_for_field = person_info_default.get(field_name)
if field_name in JSON_SERIALIZED_FIELDS and default_value_for_field is None:
default_value_for_field = []
record = PersonInfo.get_or_none(PersonInfo.person_id == person_id)
if record:
val = getattr(record, field_name, None)
if field_name in JSON_SERIALIZED_FIELDS:
if isinstance(val, str):
try:
return json.loads(val)
except json.JSONDecodeError:
logger.warning(f"字段 {field_name} for {person_id} 包含无效JSON: {val}. 返回默认值.")
return []
elif val is None:
return []
return val
return val
if field_name in person_info_default:
return default_value_for_field
logger.warning(f"字段 {field_name} 在 person_info_default 中未定义,且在数据库中未找到。")
return None
@staticmethod
async def get_values(person_id: str, field_names: list) -> dict:
"""获取指定person_id文档的多个字段值若不存在该字段则返回该字段的全局默认值"""
if not person_id:
logger.debug("get_values获取失败person_id不能为空")
return {}
result = {}
def _db_get_record_sync(p_id: str):
return PersonInfo.get_or_none(PersonInfo.person_id == p_id)
record = await asyncio.to_thread(_db_get_record_sync, person_id)
for field_name in field_names:
if field_name not in PersonInfo._meta.fields: # type: ignore
if field_name in person_info_default:
result[field_name] = copy.deepcopy(person_info_default[field_name])
logger.debug(f"字段'{field_name}'不在Peewee模型中使用默认配置值。")
else:
logger.debug(f"get_values查询失败字段'{field_name}'未在Peewee模型和默认配置中定义。")
result[field_name] = None
continue
if record:
value = getattr(record, field_name)
if value is not None:
result[field_name] = value
else:
result[field_name] = copy.deepcopy(person_info_default.get(field_name))
else:
result[field_name] = copy.deepcopy(person_info_default.get(field_name))
return result
async def get_or_create_person(
self, platform: str, user_id: int, nickname: str, user_cardname: str, user_avatar: Optional[str] = None
) -> str:
"""
根据 platform 和 user_id 获取 person_id。
如果对应的用户不存在,则使用提供的可选信息创建新用户。
使用try-except处理竞态条件避免重复创建错误。
"""
person_id = self.get_person_id(platform, user_id)
def _db_get_or_create_sync(p_id: str, init_data: dict):
"""原子性的获取或创建操作"""
# 首先尝试获取现有记录
record = PersonInfo.get_or_none(PersonInfo.person_id == p_id)
if record:
return record, False # 记录存在,未创建
# 记录不存在,尝试创建
try:
PersonInfo.create(**init_data)
return PersonInfo.get(PersonInfo.person_id == p_id), True # 创建成功
except Exception as e:
# 如果创建失败(可能是因为竞态条件),再次尝试获取
if "UNIQUE constraint failed" in str(e):
logger.debug(f"检测到并发创建用户 {p_id},获取现有记录")
record = PersonInfo.get_or_none(PersonInfo.person_id == p_id)
if record:
return record, False # 其他协程已创建,返回现有记录
# 如果仍然失败,重新抛出异常
raise e
unique_nickname = await self._generate_unique_person_name(nickname)
initial_data = {
"person_id": person_id,
"platform": platform,
"user_id": str(user_id),
"nickname": nickname,
"person_name": unique_nickname, # 使用群昵称作为person_name
"name_reason": "从群昵称获取",
"know_times": 0,
"know_since": int(datetime.datetime.now().timestamp()),
"last_know": int(datetime.datetime.now().timestamp()),
"impression": None,
"points": [],
"forgotten_points": [],
}
# 序列化JSON字段
for key in JSON_SERIALIZED_FIELDS:
if key in initial_data:
if isinstance(initial_data[key], (list, dict)):
initial_data[key] = json.dumps(initial_data[key], ensure_ascii=False)
elif initial_data[key] is None:
initial_data[key] = json.dumps([], ensure_ascii=False)
model_fields = PersonInfo._meta.fields.keys() # type: ignore
filtered_initial_data = {k: v for k, v in initial_data.items() if v is not None and k in model_fields}
record, was_created = await asyncio.to_thread(_db_get_or_create_sync, person_id, filtered_initial_data)
if was_created:
logger.info(f"用户 {platform}:{user_id} (person_id: {person_id}) 不存在,将创建新记录 (Peewee)。")
logger.info(f"已为 {person_id} 创建新记录,初始数据 (filtered for model): {filtered_initial_data}")
else:
logger.debug(f"用户 {platform}:{user_id} (person_id: {person_id}) 已存在,返回现有记录。")
return person_id
person_info_manager = None
def get_person_info_manager():
global person_info_manager
if person_info_manager is None:
person_info_manager = PersonInfoManager()
return person_info_manager

View File

@@ -7,7 +7,7 @@ from typing import List, Dict, Any
from src.config.config import global_config from src.config.config import global_config
from src.common.logger import get_logger from src.common.logger import get_logger
from src.person_info.relationship_manager import get_relationship_manager from src.person_info.relationship_manager import get_relationship_manager
from src.person_info.person_info import get_person_info_manager, PersonInfoManager from src.person_info.person_info import Person,get_person_id
from src.chat.message_receive.chat_stream import get_chat_manager from src.chat.message_receive.chat_stream import get_chat_manager
from src.chat.utils.chat_message_builder import ( from src.chat.utils.chat_message_builder import (
get_raw_msg_by_timestamp_with_chat, get_raw_msg_by_timestamp_with_chat,
@@ -15,6 +15,7 @@ from src.chat.utils.chat_message_builder import (
get_raw_msg_before_timestamp_with_chat, get_raw_msg_before_timestamp_with_chat,
num_new_messages_since, num_new_messages_since,
) )
import asyncio
logger = get_logger("relationship_builder") logger = get_logger("relationship_builder")
@@ -142,7 +143,8 @@ class RelationshipBuilder:
} }
segments.append(new_segment) segments.append(new_segment)
person_name = get_person_info_manager().get_value_sync(person_id, "person_name") or person_id person = Person(person_id=person_id)
person_name = person.person_name or person_id
logger.debug( logger.debug(
f"{self.log_prefix} 眼熟用户 {person_name}{time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))} 之间有 {new_segment['message_count']} 条消息" f"{self.log_prefix} 眼熟用户 {person_name}{time.strftime('%H:%M:%S', time.localtime(potential_start_time))} - {time.strftime('%H:%M:%S', time.localtime(message_time))} 之间有 {new_segment['message_count']} 条消息"
) )
@@ -188,8 +190,8 @@ class RelationshipBuilder:
"message_count": self._count_messages_in_timerange(potential_start_time, message_time), "message_count": self._count_messages_in_timerange(potential_start_time, message_time),
} }
segments.append(new_segment) segments.append(new_segment)
person_info_manager = get_person_info_manager() person = Person(person_id=person_id)
person_name = person_info_manager.get_value_sync(person_id, "person_name") or person_id person_name = person.person_name or person_id
logger.debug( logger.debug(
f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段超过10条消息间隔: {new_segment}" f"{self.log_prefix} 重新眼熟用户 {person_name} 创建新消息段超过10条消息间隔: {new_segment}"
) )
@@ -375,7 +377,7 @@ class RelationshipBuilder:
and user_id != global_config.bot.qq_account and user_id != global_config.bot.qq_account
and msg_time > self.last_processed_message_time and msg_time > self.last_processed_message_time
): ):
person_id = PersonInfoManager.get_person_id(platform, user_id) person_id = get_person_id(platform, user_id)
self._update_message_segments(person_id, msg_time) self._update_message_segments(person_id, msg_time)
logger.debug( logger.debug(
f"{self.log_prefix} 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}" f"{self.log_prefix} 更新用户 {person_id} 的消息段,消息时间:{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(msg_time))}"
@@ -386,7 +388,8 @@ class RelationshipBuilder:
users_to_build_relationship = [] users_to_build_relationship = []
for person_id, segments in self.person_engaged_cache.items(): for person_id, segments in self.person_engaged_cache.items():
total_message_count = self._get_total_message_count(person_id) total_message_count = self._get_total_message_count(person_id)
person_name = get_person_info_manager().get_value_sync(person_id, "person_name") or person_id person = Person(person_id=person_id)
person_name = person.person_name or person_id
if total_message_count >= max_build_threshold or (total_message_count >= 5 and (immediate_build == person_id or immediate_build == "all")): if total_message_count >= max_build_threshold or (total_message_count >= 5 and (immediate_build == person_id or immediate_build == "all")):
users_to_build_relationship.append(person_id) users_to_build_relationship.append(person_id)
@@ -403,9 +406,9 @@ class RelationshipBuilder:
for person_id in users_to_build_relationship: for person_id in users_to_build_relationship:
segments = self.person_engaged_cache[person_id] segments = self.person_engaged_cache[person_id]
# 异步执行关系构建 # 异步执行关系构建
import asyncio person = Person(person_id=person_id)
if person.is_known:
asyncio.create_task(self.update_impression_on_segments(person_id, self.chat_id, segments)) asyncio.create_task(self.update_impression_on_segments(person_id, self.chat_id, segments))
# 移除已处理的用户缓存 # 移除已处理的用户缓存
del self.person_engaged_cache[person_id] del self.person_engaged_cache[person_id]
self._save_cache() self._save_cache()

View File

@@ -1,468 +0,0 @@
import time
import traceback
import json
import random
from typing import List, Dict, Any
from json_repair import repair_json
from src.common.logger import get_logger
from src.config.config import global_config, model_config
from src.llm_models.utils_model import LLMRequest
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
from src.chat.message_receive.chat_stream import get_chat_manager
from src.person_info.person_info import get_person_info_manager
logger = get_logger("relationship_fetcher")
def init_real_time_info_prompts():
"""初始化实时信息提取相关的提示词"""
relationship_prompt = """
<聊天记录>
{chat_observe_info}
</聊天记录>
{name_block}
现在,你想要回复{person_name}的消息,消息内容是:{target_message}。请根据聊天记录和你要回复的消息,从你对{person_name}的了解中提取有关的信息:
1.你需要提供你想要提取的信息具体是哪方面的信息,例如:年龄,性别,你们之间的交流方式,最近发生的事等等。
2.请注意,请不要重复调取相同的信息,已经调取的信息如下:
{info_cache_block}
3.如果当前聊天记录中没有需要查询的信息,或者现有信息已经足够回复,请返回{{"none": "不需要查询"}}
请以json格式输出例如
{{
"info_type": "信息类型",
}}
请严格按照json输出格式不要输出多余内容
"""
Prompt(relationship_prompt, "real_time_info_identify_prompt")
fetch_info_prompt = """
{name_block}
以下是你在之前与{person_name}的交流中,产生的对{person_name}的了解:
{person_impression_block}
{points_text_block}
请从中提取用户"{person_name}"的有关"{info_type}"信息
请以json格式输出例如
{{
{info_json_str}
}}
请严格按照json输出格式不要输出多余内容
"""
Prompt(fetch_info_prompt, "real_time_fetch_person_info_prompt")
class RelationshipFetcher:
def __init__(self, chat_id):
self.chat_id = chat_id
# 信息获取缓存:记录正在获取的信息请求
self.info_fetching_cache: List[Dict[str, Any]] = []
# 信息结果缓存存储已获取的信息结果带TTL
self.info_fetched_cache: Dict[str, Dict[str, Any]] = {}
# 结构:{person_id: {info_type: {"info": str, "ttl": int, "start_time": float, "person_name": str, "unknown": bool}}}
# LLM模型配置
self.llm_model = LLMRequest(
model_set=model_config.model_task_config.utils_small, request_type="relation.fetcher"
)
# 小模型用于即时信息提取
self.instant_llm_model = LLMRequest(
model_set=model_config.model_task_config.utils_small, request_type="relation.fetch"
)
name = get_chat_manager().get_stream_name(self.chat_id)
self.log_prefix = f"[{name}] 实时信息"
def _cleanup_expired_cache(self):
"""清理过期的信息缓存"""
for person_id in list(self.info_fetched_cache.keys()):
for info_type in list(self.info_fetched_cache[person_id].keys()):
self.info_fetched_cache[person_id][info_type]["ttl"] -= 1
if self.info_fetched_cache[person_id][info_type]["ttl"] <= 0:
del self.info_fetched_cache[person_id][info_type]
if not self.info_fetched_cache[person_id]:
del self.info_fetched_cache[person_id]
async def build_relation_info(self, person_id, points_num=3):
# 清理过期的信息缓存
self._cleanup_expired_cache()
person_info_manager = get_person_info_manager()
person_name = await person_info_manager.get_value(person_id, "person_name")
attitude_to_me = await person_info_manager.get_value(person_id, "attitude_to_me")
neuroticism = await person_info_manager.get_value(person_id, "neuroticism")
conscientiousness = await person_info_manager.get_value(person_id, "conscientiousness")
likeness = await person_info_manager.get_value(person_id, "likeness")
nickname_str = await person_info_manager.get_value(person_id, "nickname")
platform = await person_info_manager.get_value(person_id, "platform")
current_points = await person_info_manager.get_value(person_id, "points") or []
# 按时间排序forgotten_points
current_points.sort(key=lambda x: x[2])
# 按权重加权随机抽取最多3个不重复的pointspoint[1]的值在1-10之间权重越高被抽到概率越大
if len(current_points) > points_num:
# point[1] 取值范围1-10直接作为权重
weights = [max(1, min(10, int(point[1]))) for point in current_points]
# 使用加权采样不放回,保证不重复
indices = list(range(len(current_points)))
points = []
for _ in range(points_num):
if not indices:
break
sub_weights = [weights[i] for i in indices]
chosen_idx = random.choices(indices, weights=sub_weights, k=1)[0]
points.append(current_points[chosen_idx])
indices.remove(chosen_idx)
else:
points = current_points
# 构建points文本
points_text = "\n".join([f"{point[2]}{point[0]}" for point in points])
nickname_str = ""
if person_name != nickname_str:
nickname_str = f"(ta在{platform}上的昵称是{nickname_str})"
relation_info = ""
attitude_info = ""
attitude_parts = attitude_to_me.split(',')
current_attitude_score = float(attitude_parts[0]) if len(attitude_parts) > 0 else 0.0
total_confidence = float(attitude_parts[1]) if len(attitude_parts) > 1 else 1.0
if attitude_to_me:
if current_attitude_score > 8:
attitude_info = f"{person_name}对你的态度十分好,"
elif current_attitude_score > 5:
attitude_info = f"{person_name}对你的态度较好,"
if current_attitude_score < -8:
attitude_info = f"{person_name}对你的态度十分恶劣,"
elif current_attitude_score < -4:
attitude_info = f"{person_name}对你的态度不好,"
elif current_attitude_score < 0:
attitude_info = f"{person_name}对你的态度一般,"
neuroticism_info = ""
neuroticism_parts = neuroticism.split(',')
current_neuroticism_score = float(neuroticism_parts[0]) if len(neuroticism_parts) > 0 else 0.0
total_confidence = float(neuroticism_parts[1]) if len(neuroticism_parts) > 1 else 1.0
if neuroticism:
if current_neuroticism_score > 8:
neuroticism_info = f"{person_name}的情绪十分活跃,容易情绪化,"
elif current_neuroticism_score > 6:
neuroticism_info = f"{person_name}的情绪比较活跃,"
elif current_neuroticism_score > 4:
neuroticism_info = ""
elif current_neuroticism_score > 2:
neuroticism_info = f"{person_name}的情绪比较稳定,"
else:
neuroticism_info = f"{person_name}的情绪非常稳定,毫无波动"
points_info = ""
if points_text:
points_info = f"你还记得ta最近做的事{points_text}"
relation_info = f"{person_name}:{nickname_str}{attitude_info}{neuroticism_info}{points_info}"
return relation_info
async def _build_fetch_query(self, person_id, target_message, chat_history):
nickname_str = ",".join(global_config.bot.alias_names)
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
person_info_manager = get_person_info_manager()
person_name: str = await person_info_manager.get_value(person_id, "person_name") # type: ignore
info_cache_block = self._build_info_cache_block()
prompt = (await global_prompt_manager.get_prompt_async("real_time_info_identify_prompt")).format(
chat_observe_info=chat_history,
name_block=name_block,
info_cache_block=info_cache_block,
person_name=person_name,
target_message=target_message,
)
try:
logger.debug(f"{self.log_prefix} 信息识别prompt: \n{prompt}\n")
content, _ = await self.llm_model.generate_response_async(prompt=prompt)
if content:
content_json = json.loads(repair_json(content))
# 检查是否返回了不需要查询的标志
if "none" in content_json:
logger.debug(f"{self.log_prefix} LLM判断当前不需要查询任何信息{content_json.get('none', '')}")
return None
if info_type := content_json.get("info_type"):
# 记录信息获取请求
self.info_fetching_cache.append(
{
"person_id": get_person_info_manager().get_person_id_by_person_name(person_name),
"person_name": person_name,
"info_type": info_type,
"start_time": time.time(),
"forget": False,
}
)
# 限制缓存大小
if len(self.info_fetching_cache) > 10:
self.info_fetching_cache.pop(0)
logger.info(f"{self.log_prefix} 识别到需要调取用户 {person_name} 的[{info_type}]信息")
return info_type
else:
logger.warning(f"{self.log_prefix} LLM未返回有效的info_type。响应: {content}")
except Exception as e:
logger.error(f"{self.log_prefix} 执行信息识别LLM请求时出错: {e}")
logger.error(traceback.format_exc())
return None
def _build_info_cache_block(self) -> str:
"""构建已获取信息的缓存块"""
info_cache_block = ""
if self.info_fetching_cache:
# 对于每个(person_id, info_type)组合,只保留最新的记录
latest_records = {}
for info_fetching in self.info_fetching_cache:
key = (info_fetching["person_id"], info_fetching["info_type"])
if key not in latest_records or info_fetching["start_time"] > latest_records[key]["start_time"]:
latest_records[key] = info_fetching
# 按时间排序并生成显示文本
sorted_records = sorted(latest_records.values(), key=lambda x: x["start_time"])
for info_fetching in sorted_records:
info_cache_block += (
f"你已经调取了[{info_fetching['person_name']}]的[{info_fetching['info_type']}]信息\n"
)
return info_cache_block
async def _extract_single_info(self, person_id: str, info_type: str, person_name: str):
"""提取单个信息类型
Args:
person_id: 用户ID
info_type: 信息类型
person_name: 用户名
"""
start_time = time.time()
person_info_manager = get_person_info_manager()
# 首先检查 info_list 缓存
info_list = await person_info_manager.get_value(person_id, "info_list") or []
cached_info = None
# 查找对应的 info_type
for info_item in info_list:
if info_item.get("info_type") == info_type:
cached_info = info_item.get("info_content")
logger.debug(f"{self.log_prefix} 在info_list中找到 {person_name}{info_type} 信息: {cached_info}")
break
# 如果缓存中有信息,直接使用
if cached_info:
if person_id not in self.info_fetched_cache:
self.info_fetched_cache[person_id] = {}
self.info_fetched_cache[person_id][info_type] = {
"info": cached_info,
"ttl": 2,
"start_time": start_time,
"person_name": person_name,
"unknown": cached_info == "none",
}
logger.info(f"{self.log_prefix} 记得 {person_name}{info_type}: {cached_info}")
return
# 如果缓存中没有,尝试从用户档案中提取
try:
person_impression = await person_info_manager.get_value(person_id, "impression")
points = await person_info_manager.get_value(person_id, "points")
# 构建印象信息块
if person_impression:
person_impression_block = (
f"<对{person_name}的总体了解>\n{person_impression}\n</对{person_name}的总体了解>"
)
else:
person_impression_block = ""
# 构建要点信息块
if points:
points_text = "\n".join([f"{point[2]}:{point[0]}" for point in points])
points_text_block = f"<对{person_name}的近期了解>\n{points_text}\n</对{person_name}的近期了解>"
else:
points_text_block = ""
# 如果完全没有用户信息
if not points_text_block and not person_impression_block:
if person_id not in self.info_fetched_cache:
self.info_fetched_cache[person_id] = {}
self.info_fetched_cache[person_id][info_type] = {
"info": "none",
"ttl": 2,
"start_time": start_time,
"person_name": person_name,
"unknown": True,
}
logger.info(f"{self.log_prefix} 完全不认识 {person_name}")
await self._save_info_to_cache(person_id, info_type, "none")
return
# 使用LLM提取信息
nickname_str = ",".join(global_config.bot.alias_names)
name_block = f"你的名字是{global_config.bot.nickname},你的昵称有{nickname_str},有人也会用这些昵称称呼你。"
prompt = (await global_prompt_manager.get_prompt_async("real_time_fetch_person_info_prompt")).format(
name_block=name_block,
info_type=info_type,
person_impression_block=person_impression_block,
person_name=person_name,
info_json_str=f'"{info_type}": "有关{info_type}的信息内容"',
points_text_block=points_text_block,
)
# 使用小模型进行即时提取
content, _ = await self.instant_llm_model.generate_response_async(prompt=prompt)
if content:
content_json = json.loads(repair_json(content))
if info_type in content_json:
info_content = content_json[info_type]
is_unknown = info_content == "none" or not info_content
# 保存到运行时缓存
if person_id not in self.info_fetched_cache:
self.info_fetched_cache[person_id] = {}
self.info_fetched_cache[person_id][info_type] = {
"info": "unknown" if is_unknown else info_content,
"ttl": 3,
"start_time": start_time,
"person_name": person_name,
"unknown": is_unknown,
}
# 保存到持久化缓存 (info_list)
await self._save_info_to_cache(person_id, info_type, "none" if is_unknown else info_content)
if not is_unknown:
logger.info(f"{self.log_prefix} 思考得到,{person_name}{info_type}: {info_content}")
else:
logger.info(f"{self.log_prefix} 思考了也不知道{person_name}{info_type} 信息")
else:
logger.warning(f"{self.log_prefix} 小模型返回空结果,获取 {person_name}{info_type} 信息失败。")
except Exception as e:
logger.error(f"{self.log_prefix} 执行信息提取时出错: {e}")
logger.error(traceback.format_exc())
async def _save_info_to_cache(self, person_id: str, info_type: str, info_content: str):
# sourcery skip: use-next
"""将提取到的信息保存到 person_info 的 info_list 字段中
Args:
person_id: 用户ID
info_type: 信息类型
info_content: 信息内容
"""
try:
person_info_manager = get_person_info_manager()
# 获取现有的 info_list
info_list = await person_info_manager.get_value(person_id, "info_list") or []
# 查找是否已存在相同 info_type 的记录
found_index = -1
for i, info_item in enumerate(info_list):
if isinstance(info_item, dict) and info_item.get("info_type") == info_type:
found_index = i
break
# 创建新的信息记录
new_info_item = {
"info_type": info_type,
"info_content": info_content,
}
if found_index >= 0:
# 更新现有记录
info_list[found_index] = new_info_item
logger.info(f"{self.log_prefix} [缓存更新] 更新 {person_id}{info_type} 信息缓存")
else:
# 添加新记录
info_list.append(new_info_item)
logger.info(f"{self.log_prefix} [缓存保存] 新增 {person_id}{info_type} 信息缓存")
# 保存更新后的 info_list
await person_info_manager.update_one_field(person_id, "info_list", info_list)
except Exception as e:
logger.error(f"{self.log_prefix} [缓存保存] 保存信息到缓存失败: {e}")
logger.error(traceback.format_exc())
class RelationshipFetcherManager:
"""关系提取器管理器
管理不同 chat_id 的 RelationshipFetcher 实例
"""
def __init__(self):
self._fetchers: Dict[str, RelationshipFetcher] = {}
def get_fetcher(self, chat_id: str) -> RelationshipFetcher:
"""获取或创建指定 chat_id 的 RelationshipFetcher
Args:
chat_id: 聊天ID
Returns:
RelationshipFetcher: 关系提取器实例
"""
if chat_id not in self._fetchers:
self._fetchers[chat_id] = RelationshipFetcher(chat_id)
return self._fetchers[chat_id]
def remove_fetcher(self, chat_id: str):
"""移除指定 chat_id 的 RelationshipFetcher
Args:
chat_id: 聊天ID
"""
if chat_id in self._fetchers:
del self._fetchers[chat_id]
def clear_all(self):
"""清空所有 RelationshipFetcher"""
self._fetchers.clear()
def get_active_chat_ids(self) -> List[str]:
"""获取所有活跃的 chat_id 列表"""
return list(self._fetchers.keys())
# 全局管理器实例
relationship_fetcher_manager = RelationshipFetcherManager()
init_real_time_info_prompts()

View File

@@ -1,6 +1,5 @@
from src.common.logger import get_logger from src.common.logger import get_logger
from .person_info import PersonInfoManager, get_person_info_manager from .person_info import Person
import time
import random import random
from src.llm_models.utils_model import LLMRequest from src.llm_models.utils_model import LLMRequest
from src.config.config import global_config, model_config from src.config.config import global_config, model_config
@@ -8,11 +7,7 @@ from src.chat.utils.chat_message_builder import build_readable_messages
import json import json
from json_repair import repair_json from json_repair import repair_json
from datetime import datetime from datetime import datetime
from difflib import SequenceMatcher from typing import List, Dict, Any
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from typing import List, Dict, Any, Tuple
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
import traceback import traceback
@@ -52,8 +47,7 @@ def init_prompt():
}} }}
] ]
如果没有,就输出none,或返回空数组: 如果没有,就输出空数组:[]
[]
""", """,
"relation_points", "relation_points",
) )
@@ -83,7 +77,9 @@ def init_prompt():
"attitude": 0, "attitude": 0,
"confidence": 0.5 "confidence": 0.5
}} }}
现在请你输出json: 如果无法看出对方对你的态度,就只输出空数组:[]
现在,请你输出:
""", """,
"attitude_to_me_prompt", "attitude_to_me_prompt",
) )
@@ -115,7 +111,9 @@ def init_prompt():
"neuroticism": 0, "neuroticism": 0,
"confidence": 0.5 "confidence": 0.5
}} }}
现在请你输出json: 如果无法看出对方的神经质程度,就只输出空数组:[]
现在,请你输出:
""", """,
"neuroticism_prompt", "neuroticism_prompt",
) )
@@ -124,46 +122,15 @@ class RelationshipManager:
def __init__(self): def __init__(self):
self.relationship_llm = LLMRequest( self.relationship_llm = LLMRequest(
model_set=model_config.model_task_config.utils, request_type="relationship.person" model_set=model_config.model_task_config.utils, request_type="relationship.person"
) # 用于动作规划
@staticmethod
async def is_known_some_one(platform, user_id):
"""判断是否认识某人"""
person_info_manager = get_person_info_manager()
return await person_info_manager.is_person_known(platform, user_id)
@staticmethod
async def first_knowing_some_one(platform: str, user_id: str, user_nickname: str, user_cardname: str):
"""判断是否认识某人"""
person_id = PersonInfoManager.get_person_id(platform, user_id)
# 生成唯一的 person_name
person_info_manager = get_person_info_manager()
unique_nickname = await person_info_manager._generate_unique_person_name(user_nickname)
data = {
"platform": platform,
"user_id": user_id,
"nickname": user_nickname,
"konw_time": int(time.time()),
"person_name": unique_nickname, # 使用唯一的 person_name
}
# 先创建用户基本信息,使用安全创建方法避免竞态条件
await person_info_manager._safe_create_person_info(person_id=person_id, data=data)
# 更新昵称
await person_info_manager.update_one_field(
person_id=person_id, field_name="nickname", value=user_nickname, data=data
) )
# 尝试生成更好的名字
# await person_info_manager.qv_person_name(
# person_id=person_id, user_nickname=user_nickname, user_cardname=user_cardname, user_avatar=user_avatar
# )
async def get_points(self, async def get_points(self,
person_name: str, person_name: str,
nickname: str, nickname: str,
readable_messages: str, readable_messages: str,
name_mapping: Dict[str, str], name_mapping: Dict[str, str],
timestamp: float, timestamp: float,
current_points: List[Tuple[str, float, str]]): person: Person):
alias_str = ", ".join(global_config.bot.alias_names) alias_str = ", ".join(global_config.bot.alias_names)
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
@@ -198,9 +165,7 @@ class RelationshipManager:
points_data = json.loads(points) points_data = json.loads(points)
# 只处理正确的格式,错误格式直接跳过 # 只处理正确的格式,错误格式直接跳过
if points_data == "none" or not points_data: if points_data == "none" or not points_data or (isinstance(points_data, str) and points_data.lower() == "none") or (isinstance(points_data, list) and len(points_data) == 0):
points_list = []
elif isinstance(points_data, str) and points_data.lower() == "none":
points_list = [] points_list = []
elif isinstance(points_data, list): elif isinstance(points_data, list):
points_list = [(item["point"], float(item["weight"]), current_time) for item in points_data] points_list = [(item["point"], float(item["weight"]), current_time) for item in points_data]
@@ -238,15 +203,15 @@ class RelationshipManager:
return return
current_points.extend(points_list) person.points.extend(points_list)
# 如果points超过10条按权重随机选择多余的条目移动到forgotten_points # 如果points超过10条按权重随机选择多余的条目移动到forgotten_points
if len(current_points) > 20: if len(person.points) > 20:
# 计算当前时间 # 计算当前时间
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
# 计算每个点的最终权重(原始权重 * 时间权重) # 计算每个点的最终权重(原始权重 * 时间权重)
weighted_points = [] weighted_points = []
for point in current_points: for point in person.points:
time_weight = self.calculate_time_weight(point[2], current_time) time_weight = self.calculate_time_weight(point[2], current_time)
final_weight = point[1] * time_weight final_weight = point[1] * time_weight
weighted_points.append((point, final_weight)) weighted_points.append((point, final_weight))
@@ -270,16 +235,15 @@ class RelationshipManager:
idx_to_remove = random.randrange(len(remaining_points)) idx_to_remove = random.randrange(len(remaining_points))
remaining_points[idx_to_remove] = point remaining_points[idx_to_remove] = point
return remaining_points person.points = remaining_points
return current_points return person
async def get_attitude_to_me(self, person_name, nickname, readable_messages, timestamp, current_attitude): async def get_attitude_to_me(self, person_name, nickname, readable_messages, timestamp, person: Person):
alias_str = ", ".join(global_config.bot.alias_names) alias_str = ", ".join(global_config.bot.alias_names)
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
# 解析当前态度值 # 解析当前态度值
attitude_parts = current_attitude.split(',') current_attitude_score = person.attitude_to_me
current_attitude_score = float(attitude_parts[0]) if len(attitude_parts) > 0 else 0.0 total_confidence = person.attitude_to_me_confidence
total_confidence = float(attitude_parts[1]) if len(attitude_parts) > 1 else 1.0
prompt = await global_prompt_manager.format_prompt( prompt = await global_prompt_manager.format_prompt(
"attitude_to_me_prompt", "attitude_to_me_prompt",
@@ -301,23 +265,31 @@ class RelationshipManager:
attitude = repair_json(attitude) attitude = repair_json(attitude)
attitude_data = json.loads(attitude) attitude_data = json.loads(attitude)
if attitude_data == "none" or not attitude_data or (isinstance(attitude_data, str) and attitude_data.lower() == "none") or (isinstance(attitude_data, list) and len(attitude_data) == 0):
return ""
# 确保 attitude_data 是字典格式
if not isinstance(attitude_data, dict):
logger.warning(f"LLM返回了错误的JSON格式跳过解析: {type(attitude_data)}, 内容: {attitude_data}")
return ""
attitude_score = attitude_data["attitude"] attitude_score = attitude_data["attitude"]
confidence = attitude_data["confidence"] confidence = attitude_data["confidence"]
new_confidence = total_confidence + confidence new_confidence = total_confidence + confidence
new_attitude_score = (current_attitude_score * total_confidence + attitude_score * confidence)/new_confidence new_attitude_score = (current_attitude_score * total_confidence + attitude_score * confidence)/new_confidence
person.attitude_to_me = new_attitude_score
person.attitude_to_me_confidence = new_confidence
return f"{new_attitude_score:.3f},{new_confidence:.3f}" return person
async def get_neuroticism(self, person_name, nickname, readable_messages, timestamp, current_neuroticism): async def get_neuroticism(self, person_name, nickname, readable_messages, timestamp, person: Person):
alias_str = ", ".join(global_config.bot.alias_names) alias_str = ", ".join(global_config.bot.alias_names)
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S") current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
# 解析当前态度值 # 解析当前态度值
neuroticism_parts = current_neuroticism.split(',') current_neuroticism_score = person.neuroticism
current_neuroticism_score = float(neuroticism_parts[0]) if len(neuroticism_parts) > 0 else 0.0 total_confidence = person.neuroticism_confidence
total_confidence = float(neuroticism_parts[1]) if len(neuroticism_parts) > 1 else 1.0
prompt = await global_prompt_manager.format_prompt( prompt = await global_prompt_manager.format_prompt(
"neuroticism_prompt", "neuroticism_prompt",
@@ -339,6 +311,14 @@ class RelationshipManager:
neuroticism = repair_json(neuroticism) neuroticism = repair_json(neuroticism)
neuroticism_data = json.loads(neuroticism) neuroticism_data = json.loads(neuroticism)
if neuroticism_data == "none" or not neuroticism_data or (isinstance(neuroticism_data, str) and neuroticism_data.lower() == "none") or (isinstance(neuroticism_data, list) and len(neuroticism_data) == 0):
return ""
# 确保 neuroticism_data 是字典格式
if not isinstance(neuroticism_data, dict):
logger.warning(f"LLM返回了错误的JSON格式跳过解析: {type(neuroticism_data)}, 内容: {neuroticism_data}")
return ""
neuroticism_score = neuroticism_data["neuroticism"] neuroticism_score = neuroticism_data["neuroticism"]
confidence = neuroticism_data["confidence"] confidence = neuroticism_data["confidence"]
@@ -346,8 +326,10 @@ class RelationshipManager:
new_neuroticism_score = (current_neuroticism_score * total_confidence + neuroticism_score * confidence)/new_confidence new_neuroticism_score = (current_neuroticism_score * total_confidence + neuroticism_score * confidence)/new_confidence
person.neuroticism = new_neuroticism_score
person.neuroticism_confidence = new_confidence
return f"{new_neuroticism_score:.3f},{new_confidence:.3f}" return person
async def update_person_impression(self, person_id, timestamp, bot_engaged_messages: List[Dict[str, Any]]): async def update_person_impression(self, person_id, timestamp, bot_engaged_messages: List[Dict[str, Any]]):
@@ -360,21 +342,13 @@ class RelationshipManager:
timestamp: 时间戳 (用于记录交互时间) timestamp: 时间戳 (用于记录交互时间)
bot_engaged_messages: bot参与的消息列表 bot_engaged_messages: bot参与的消息列表
""" """
person_info_manager = get_person_info_manager() person = Person(person_id=person_id)
person_name = await person_info_manager.get_value(person_id, "person_name") person_name = person.person_name
nickname = await person_info_manager.get_value(person_id, "nickname") nickname = person.nickname
know_times: float = await person_info_manager.get_value(person_id, "know_times") or 0 # type: ignore know_times: float = person.know_times
current_points = await person_info_manager.get_value(person_id, "points") or []
attitude_to_me = await person_info_manager.get_value(person_id, "attitude_to_me") or "0,1"
neuroticism = await person_info_manager.get_value(person_id, "neuroticism") or "5,1"
# personality_block =get_individuality().get_personality_prompt(x_person=2, level=2)
# identity_block =get_individuality().get_identity_prompt(x_person=2, level=2)
user_messages = bot_engaged_messages user_messages = bot_engaged_messages
current_time = datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")
# 匿名化消息 # 匿名化消息
# 创建用户名称映射 # 创建用户名称映射
name_mapping = {} name_mapping = {}
@@ -383,33 +357,23 @@ class RelationshipManager:
# 遍历消息,构建映射 # 遍历消息,构建映射
for msg in user_messages: for msg in user_messages:
await person_info_manager.get_or_create_person( msg_person = Person(user_id=msg.get("user_id"), platform=msg.get("chat_info_platform"))
platform=msg.get("chat_info_platform"), # type: ignore
user_id=msg.get("user_id"), # type: ignore
nickname=msg.get("user_nickname"), # type: ignore
user_cardname=msg.get("user_cardname"), # type: ignore
)
replace_user_id: str = msg.get("user_id") # type: ignore
replace_platform: str = msg.get("chat_info_platform") # type: ignore
replace_person_id = PersonInfoManager.get_person_id(replace_platform, replace_user_id)
replace_person_name = await person_info_manager.get_value(replace_person_id, "person_name")
# 跳过机器人自己 # 跳过机器人自己
if replace_user_id == global_config.bot.qq_account: if msg_person.user_id == global_config.bot.qq_account:
name_mapping[f"{global_config.bot.nickname}"] = f"{global_config.bot.nickname}" name_mapping[f"{global_config.bot.nickname}"] = f"{global_config.bot.nickname}"
continue continue
# 跳过目标用户 # 跳过目标用户
if replace_person_name == person_name: if msg_person.person_name == person_name:
name_mapping[replace_person_name] = f"{person_name}" name_mapping[msg_person.person_name] = f"{person_name}"
continue continue
# 其他用户映射 # 其他用户映射
if replace_person_name not in name_mapping: if msg_person.person_name not in name_mapping:
if current_user > "Z": if current_user > "Z":
current_user = "A" current_user = "A"
user_count += 1 user_count += 1
name_mapping[replace_person_name] = f"用户{current_user}{user_count if user_count > 1 else ''}" name_mapping[msg_person.person_name] = f"用户{current_user}{user_count if user_count > 1 else ''}"
current_user = chr(ord(current_user) + 1) current_user = chr(ord(current_user) + 1)
readable_messages = build_readable_messages( readable_messages = build_readable_messages(
@@ -420,23 +384,16 @@ class RelationshipManager:
# print(f"original_name: {original_name}, mapped_name: {mapped_name}") # print(f"original_name: {original_name}, mapped_name: {mapped_name}")
readable_messages = readable_messages.replace(f"{original_name}", f"{mapped_name}") readable_messages = readable_messages.replace(f"{original_name}", f"{mapped_name}")
print(name_mapping)
person = await self.get_points(person_name, nickname, readable_messages, name_mapping, timestamp, person)
person = await self.get_attitude_to_me(person_name, nickname, readable_messages, timestamp, person)
person = await self.get_neuroticism(person_name, nickname, readable_messages, timestamp, person)
remaining_points = await self.get_points(person_name, nickname, readable_messages, name_mapping, timestamp, current_points) person.know_times = know_times + 1
attitude_to_me = await self.get_attitude_to_me(person_name, nickname, readable_messages, timestamp, attitude_to_me) person.last_know = timestamp
neuroticism = await self.get_neuroticism(person_name, nickname, readable_messages, timestamp, neuroticism)
# 更新数据库 person.sync_to_database()
await person_info_manager.update_one_field(
person_id, "points", json.dumps(remaining_points, ensure_ascii=False, indent=None)
)
await person_info_manager.update_one_field(person_id, "neuroticism", neuroticism)
await person_info_manager.update_one_field(person_id, "attitude_to_me", attitude_to_me)
await person_info_manager.update_one_field(person_id, "know_times", know_times + 1)
await person_info_manager.update_one_field(person_id, "last_know", timestamp)
know_since = await person_info_manager.get_value(person_id, "know_since") or 0
if know_since == 0:
await person_info_manager.update_one_field(person_id, "know_since", timestamp)

View File

@@ -12,7 +12,6 @@ import traceback
from typing import Tuple, Any, Dict, List, Optional from typing import Tuple, Any, Dict, List, Optional
from rich.traceback import install from rich.traceback import install
from src.common.logger import get_logger from src.common.logger import get_logger
from src.config.api_ada_configs import TaskConfig
from src.chat.replyer.default_generator import DefaultReplyer from src.chat.replyer.default_generator import DefaultReplyer
from src.chat.message_receive.chat_stream import ChatStream from src.chat.message_receive.chat_stream import ChatStream
from src.chat.utils.utils import process_llm_response from src.chat.utils.utils import process_llm_response

View File

@@ -7,9 +7,9 @@
value = await person_api.get_person_value(person_id, "nickname") value = await person_api.get_person_value(person_id, "nickname")
""" """
from typing import Any, Optional from typing import Any
from src.common.logger import get_logger from src.common.logger import get_logger
from src.person_info.person_info import get_person_info_manager, PersonInfoManager from src.person_info.person_info import Person
logger = get_logger("person_api") logger = get_logger("person_api")
@@ -33,7 +33,7 @@ def get_person_id(platform: str, user_id: int) -> str:
person_id = person_api.get_person_id("qq", 123456) person_id = person_api.get_person_id("qq", 123456)
""" """
try: try:
return PersonInfoManager.get_person_id(platform, user_id) return Person(platform=platform, user_id=user_id).person_id
except Exception as e: except Exception as e:
logger.error(f"[PersonAPI] 获取person_id失败: platform={platform}, user_id={user_id}, error={e}") logger.error(f"[PersonAPI] 获取person_id失败: platform={platform}, user_id={user_id}, error={e}")
return "" return ""
@@ -55,85 +55,14 @@ async def get_person_value(person_id: str, field_name: str, default: Any = None)
impression = await person_api.get_person_value(person_id, "impression") impression = await person_api.get_person_value(person_id, "impression")
""" """
try: try:
person_info_manager = get_person_info_manager() person = Person(person_id=person_id)
value = await person_info_manager.get_value(person_id, field_name) value = getattr(person, field_name)
return value if value is not None else default return value if value is not None else default
except Exception as e: except Exception as e:
logger.error(f"[PersonAPI] 获取用户信息失败: person_id={person_id}, field={field_name}, error={e}") logger.error(f"[PersonAPI] 获取用户信息失败: person_id={person_id}, field={field_name}, error={e}")
return default return default
async def get_person_values(person_id: str, field_names: list, default_dict: Optional[dict] = None) -> dict:
"""批量获取用户信息字段值
Args:
person_id: 用户的唯一标识ID
field_names: 要获取的字段名列表
default_dict: 默认值字典,键为字段名,值为默认值
Returns:
dict: 字段名到值的映射字典
示例:
values = await person_api.get_person_values(
person_id,
["nickname", "impression", "know_times"],
{"nickname": "未知用户", "know_times": 0}
)
"""
try:
person_info_manager = get_person_info_manager()
values = await person_info_manager.get_values(person_id, field_names)
# 如果获取成功,返回结果
if values:
return values
# 如果获取失败,构建默认值字典
result = {}
if default_dict:
for field in field_names:
result[field] = default_dict.get(field, None)
else:
for field in field_names:
result[field] = None
return result
except Exception as e:
logger.error(f"[PersonAPI] 批量获取用户信息失败: person_id={person_id}, fields={field_names}, error={e}")
# 返回默认值字典
result = {}
if default_dict:
for field in field_names:
result[field] = default_dict.get(field, None)
else:
for field in field_names:
result[field] = None
return result
async def is_person_known(platform: str, user_id: int) -> bool:
"""判断是否认识某个用户
Args:
platform: 平台名称
user_id: 用户ID
Returns:
bool: 是否认识该用户
示例:
known = await person_api.is_person_known("qq", 123456)
"""
try:
person_info_manager = get_person_info_manager()
return await person_info_manager.is_person_known(platform, user_id)
except Exception as e:
logger.error(f"[PersonAPI] 检查用户是否已知失败: platform={platform}, user_id={user_id}, error={e}")
return False
def get_person_id_by_name(person_name: str) -> str: def get_person_id_by_name(person_name: str) -> str:
"""根据用户名获取person_id """根据用户名获取person_id
@@ -147,8 +76,8 @@ def get_person_id_by_name(person_name: str) -> str:
person_id = person_api.get_person_id_by_name("张三") person_id = person_api.get_person_id_by_name("张三")
""" """
try: try:
person_info_manager = get_person_info_manager() person = Person(person_name=person_name)
return person_info_manager.get_person_id_by_person_name(person_name) return person.person_id
except Exception as e: except Exception as e:
logger.error(f"[PersonAPI] 根据用户名获取person_id失败: person_name={person_name}, error={e}") logger.error(f"[PersonAPI] 根据用户名获取person_id失败: person_name={person_name}, error={e}")
return "" return ""