Merge branch 'MaiM-with-u:refactor' into refactor
This commit is contained in:
10
.github/workflows/docker-image.yml
vendored
10
.github/workflows/docker-image.yml
vendored
@@ -13,6 +13,9 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build-and-push:
|
build-and-push:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DATE_TAG: $(date -u +'%Y-%m-%dT%H-%M-%S')
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -40,7 +43,7 @@ jobs:
|
|||||||
elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then
|
elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then
|
||||||
echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT
|
echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT
|
||||||
elif [ "${{ github.ref }}" == "refs/heads/refactor" ]; then # 新增 refactor 分支处理
|
elif [ "${{ github.ref }}" == "refs/heads/refactor" ]; then # 新增 refactor 分支处理
|
||||||
echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor" >> $GITHUB_OUTPUT
|
echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor,${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor$(date -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Build and Push Docker Image
|
- name: Build and Push Docker Image
|
||||||
@@ -52,4 +55,7 @@ jobs:
|
|||||||
tags: ${{ steps.tags.outputs.tags }}
|
tags: ${{ steps.tags.outputs.tags }}
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache
|
cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache
|
||||||
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max
|
cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.created=${{ steps.tags.outputs.date_tag }}
|
||||||
|
org.opencontainers.image.revision=${{ github.sha }}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
FROM python:3.13.2-slim-bookworm
|
FROM python:3.13.2-slim-bookworm
|
||||||
|
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||||
|
|
||||||
# 工作目录
|
# 工作目录
|
||||||
WORKDIR /MaiMBot
|
WORKDIR /MaiMBot
|
||||||
@@ -9,9 +10,9 @@ COPY requirements.txt .
|
|||||||
COPY maim_message /maim_message
|
COPY maim_message /maim_message
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
RUN pip install --upgrade pip
|
RUN uv pip install --system --upgrade pip
|
||||||
RUN pip install -e /maim_message
|
RUN uv pip install --system -e /maim_message
|
||||||
RUN pip install --upgrade -r requirements.txt
|
RUN uv pip install --system -r requirements.txt
|
||||||
|
|
||||||
# 复制项目代码
|
# 复制项目代码
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
这里放置了测试版本的细节更新
|
这里放置了测试版本的细节更新
|
||||||
|
|
||||||
|
## [test-0.6.0-snapshot-8] - 2025-4-3
|
||||||
|
- 修复了表情包的注册,获取和发送逻辑
|
||||||
|
- 表情包增加存储上限
|
||||||
|
- 更改了回复引用的逻辑,从基于时间改为基于新消息
|
||||||
|
- 增加了调试信息
|
||||||
|
- 自动清理缓存图片
|
||||||
|
|
||||||
## [test-0.6.0-snapshot-7] - 2025-4-2
|
## [test-0.6.0-snapshot-7] - 2025-4-2
|
||||||
- 修改版本号命名:test-前缀为测试版,无前缀为正式版
|
- 修改版本号命名:test-前缀为测试版,无前缀为正式版
|
||||||
- 提供私聊的PFC模式
|
- 提供私聊的PFC模式,可以进行有目的,自由多轮对话
|
||||||
|
|
||||||
## [0.6.0-mmc-4] - 2025-4-1
|
## [0.6.0-mmc-4] - 2025-4-1
|
||||||
- 提供两种聊天逻辑,思维流聊天(ThinkFlowChat 和 推理聊天(ReasoningChat)
|
- 提供两种聊天逻辑,思维流聊天(ThinkFlowChat 和 推理聊天(ReasoningChat)
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ class SubHeartflow:
|
|||||||
logger.info(f"麦麦的思考前脑内状态:{self.current_mind}")
|
logger.info(f"麦麦的思考前脑内状态:{self.current_mind}")
|
||||||
|
|
||||||
async def do_thinking_after_reply(self, reply_content, chat_talking_prompt):
|
async def do_thinking_after_reply(self, reply_content, chat_talking_prompt):
|
||||||
print("麦麦回复之后脑袋转起来了")
|
# print("麦麦回复之后脑袋转起来了")
|
||||||
current_thinking_info = self.current_mind
|
current_thinking_info = self.current_mind
|
||||||
mood_info = self.current_state.mood
|
mood_info = self.current_state.mood
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from .plugins.utils.statistic import LLMStatistics
|
|||||||
from .plugins.moods.moods import MoodManager
|
from .plugins.moods.moods import MoodManager
|
||||||
from .plugins.schedule.schedule_generator import bot_schedule
|
from .plugins.schedule.schedule_generator import bot_schedule
|
||||||
from .plugins.chat.emoji_manager import emoji_manager
|
from .plugins.chat.emoji_manager import emoji_manager
|
||||||
from .plugins.relationship.relationship_manager import relationship_manager
|
from .plugins.person_info.person_info import person_info_manager
|
||||||
from .plugins.willing.willing_manager import willing_manager
|
from .plugins.willing.willing_manager import willing_manager
|
||||||
from .plugins.chat.chat_stream import chat_manager
|
from .plugins.chat.chat_stream import chat_manager
|
||||||
from .heart_flow.heartflow import heartflow
|
from .heart_flow.heartflow import heartflow
|
||||||
@@ -50,14 +50,14 @@ class MainSystem:
|
|||||||
|
|
||||||
# 初始化表情管理器
|
# 初始化表情管理器
|
||||||
emoji_manager.initialize()
|
emoji_manager.initialize()
|
||||||
|
logger.success("表情包管理器初始化成功")
|
||||||
|
|
||||||
# 启动情绪管理器
|
# 启动情绪管理器
|
||||||
self.mood_manager.start_mood_update(update_interval=global_config.mood_update_interval)
|
self.mood_manager.start_mood_update(update_interval=global_config.mood_update_interval)
|
||||||
logger.success("情绪管理器启动成功")
|
logger.success("情绪管理器启动成功")
|
||||||
|
|
||||||
# 加载用户关系
|
# 检查并清除person_info冗余字段
|
||||||
await relationship_manager.load_all_relationships()
|
await person_info_manager.del_all_undefined_field()
|
||||||
asyncio.create_task(relationship_manager._start_relationship_manager())
|
|
||||||
|
|
||||||
# 启动愿望管理器
|
# 启动愿望管理器
|
||||||
await willing_manager.ensure_started()
|
await willing_manager.ensure_started()
|
||||||
@@ -107,6 +107,7 @@ class MainSystem:
|
|||||||
self.print_mood_task(),
|
self.print_mood_task(),
|
||||||
self.remove_recalled_message_task(),
|
self.remove_recalled_message_task(),
|
||||||
emoji_manager.start_periodic_check(),
|
emoji_manager.start_periodic_check(),
|
||||||
|
emoji_manager.start_periodic_register(),
|
||||||
self.app.run(),
|
self.app.run(),
|
||||||
]
|
]
|
||||||
await asyncio.gather(*tasks)
|
await asyncio.gather(*tasks)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ MaiMBot插件系统
|
|||||||
|
|
||||||
from .chat.chat_stream import chat_manager
|
from .chat.chat_stream import chat_manager
|
||||||
from .chat.emoji_manager import emoji_manager
|
from .chat.emoji_manager import emoji_manager
|
||||||
from .relationship.relationship_manager import relationship_manager
|
from .person_info.relationship_manager import relationship_manager
|
||||||
from .moods.moods import MoodManager
|
from .moods.moods import MoodManager
|
||||||
from .willing.willing_manager import willing_manager
|
from .willing.willing_manager import willing_manager
|
||||||
from .schedule.schedule_generator import bot_schedule
|
from .schedule.schedule_generator import bot_schedule
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from .emoji_manager import emoji_manager
|
from .emoji_manager import emoji_manager
|
||||||
from ..relationship.relationship_manager import relationship_manager
|
from ..person_info.relationship_manager import relationship_manager
|
||||||
from .chat_stream import chat_manager
|
from .chat_stream import chat_manager
|
||||||
from .message_sender import message_manager
|
from .message_sender import message_manager
|
||||||
from ..storage.storage import MessageStorage
|
from ..storage.storage import MessageStorage
|
||||||
|
|||||||
@@ -75,25 +75,48 @@ class ChatBot:
|
|||||||
- 表情包处理
|
- 表情包处理
|
||||||
- 性能计时
|
- 性能计时
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
message = MessageRecv(message_data)
|
message = MessageRecv(message_data)
|
||||||
groupinfo = message.message_info.group_info
|
groupinfo = message.message_info.group_info
|
||||||
|
logger.debug(f"处理消息:{str(message_data)[:50]}...")
|
||||||
|
|
||||||
if global_config.enable_pfc_chatting:
|
if global_config.enable_pfc_chatting:
|
||||||
try:
|
try:
|
||||||
|
if groupinfo is None and global_config.enable_friend_chat:
|
||||||
|
userinfo = message.message_info.user_info
|
||||||
|
messageinfo = message.message_info
|
||||||
|
# 创建聊天流
|
||||||
|
chat = await chat_manager.get_or_create_stream(
|
||||||
|
platform=messageinfo.platform,
|
||||||
|
user_info=userinfo,
|
||||||
|
group_info=groupinfo,
|
||||||
|
)
|
||||||
|
message.update_chat_stream(chat)
|
||||||
|
await self.only_process_chat.process_message(message)
|
||||||
|
await self._create_PFC_chat(message)
|
||||||
|
else:
|
||||||
|
if groupinfo.group_id in global_config.talk_allowed_groups:
|
||||||
|
logger.debug(f"开始群聊模式{message_data}")
|
||||||
|
if global_config.response_mode == "heart_flow":
|
||||||
|
await self.think_flow_chat.process_message(message_data)
|
||||||
|
elif global_config.response_mode == "reasoning":
|
||||||
|
logger.debug(f"开始推理模式{message_data}")
|
||||||
|
await self.reasoning_chat.process_message(message_data)
|
||||||
|
else:
|
||||||
|
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"处理PFC消息失败: {e}")
|
||||||
|
else:
|
||||||
if groupinfo is None and global_config.enable_friend_chat:
|
if groupinfo is None and global_config.enable_friend_chat:
|
||||||
userinfo = message.message_info.user_info
|
# 私聊处理流程
|
||||||
messageinfo = message.message_info
|
# await self._handle_private_chat(message)
|
||||||
# 创建聊天流
|
if global_config.response_mode == "heart_flow":
|
||||||
chat = await chat_manager.get_or_create_stream(
|
await self.think_flow_chat.process_message(message_data)
|
||||||
platform=messageinfo.platform,
|
elif global_config.response_mode == "reasoning":
|
||||||
user_info=userinfo,
|
await self.reasoning_chat.process_message(message_data)
|
||||||
group_info=groupinfo,
|
else:
|
||||||
)
|
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
||||||
message.update_chat_stream(chat)
|
else: # 群聊处理
|
||||||
await self.only_process_chat.process_message(message)
|
|
||||||
await self._create_PFC_chat(message)
|
|
||||||
else:
|
|
||||||
if groupinfo.group_id in global_config.talk_allowed_groups:
|
if groupinfo.group_id in global_config.talk_allowed_groups:
|
||||||
if global_config.response_mode == "heart_flow":
|
if global_config.response_mode == "heart_flow":
|
||||||
await self.think_flow_chat.process_message(message_data)
|
await self.think_flow_chat.process_message(message_data)
|
||||||
@@ -101,26 +124,8 @@ class ChatBot:
|
|||||||
await self.reasoning_chat.process_message(message_data)
|
await self.reasoning_chat.process_message(message_data)
|
||||||
else:
|
else:
|
||||||
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"处理PFC消息失败: {e}")
|
logger.error(f"预处理消息失败: {e}")
|
||||||
else:
|
|
||||||
if groupinfo is None and global_config.enable_friend_chat:
|
|
||||||
# 私聊处理流程
|
|
||||||
# await self._handle_private_chat(message)
|
|
||||||
if global_config.response_mode == "heart_flow":
|
|
||||||
await self.think_flow_chat.process_message(message_data)
|
|
||||||
elif global_config.response_mode == "reasoning":
|
|
||||||
await self.reasoning_chat.process_message(message_data)
|
|
||||||
else:
|
|
||||||
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
|
||||||
else: # 群聊处理
|
|
||||||
if groupinfo.group_id in global_config.talk_allowed_groups:
|
|
||||||
if global_config.response_mode == "heart_flow":
|
|
||||||
await self.think_flow_chat.process_message(message_data)
|
|
||||||
elif global_config.response_mode == "reasoning":
|
|
||||||
await self.reasoning_chat.process_message(message_data)
|
|
||||||
else:
|
|
||||||
logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}")
|
|
||||||
|
|
||||||
|
|
||||||
# 创建全局ChatBot实例
|
# 创建全局ChatBot实例
|
||||||
|
|||||||
@@ -38,11 +38,29 @@ class EmojiManager:
|
|||||||
self.llm_emotion_judge = LLM_request(
|
self.llm_emotion_judge = LLM_request(
|
||||||
model=global_config.llm_emotion_judge, max_tokens=600, temperature=0.8, request_type="emoji"
|
model=global_config.llm_emotion_judge, max_tokens=600, temperature=0.8, request_type="emoji"
|
||||||
) # 更高的温度,更少的token(后续可以根据情绪来调整温度)
|
) # 更高的温度,更少的token(后续可以根据情绪来调整温度)
|
||||||
|
|
||||||
|
self.emoji_num = 0
|
||||||
|
self.emoji_num_max = global_config.max_emoji_num
|
||||||
|
self.emoji_num_max_reach_deletion = global_config.max_reach_deletion
|
||||||
|
|
||||||
|
logger.info("启动表情包管理器")
|
||||||
|
|
||||||
def _ensure_emoji_dir(self):
|
def _ensure_emoji_dir(self):
|
||||||
"""确保表情存储目录存在"""
|
"""确保表情存储目录存在"""
|
||||||
os.makedirs(self.EMOJI_DIR, exist_ok=True)
|
os.makedirs(self.EMOJI_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
def _update_emoji_count(self):
|
||||||
|
"""更新表情包数量统计
|
||||||
|
|
||||||
|
检查数据库中的表情包数量并更新到 self.emoji_num
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._ensure_db()
|
||||||
|
self.emoji_num = db.emoji.count_documents({})
|
||||||
|
logger.info(f"[统计] 当前表情包数量: {self.emoji_num}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[错误] 更新表情包数量失败: {str(e)}")
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
"""初始化数据库连接和表情目录"""
|
"""初始化数据库连接和表情目录"""
|
||||||
if not self._initialized:
|
if not self._initialized:
|
||||||
@@ -50,6 +68,8 @@ class EmojiManager:
|
|||||||
self._ensure_emoji_collection()
|
self._ensure_emoji_collection()
|
||||||
self._ensure_emoji_dir()
|
self._ensure_emoji_dir()
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
|
# 更新表情包数量
|
||||||
|
self._update_emoji_count()
|
||||||
# 启动时执行一次完整性检查
|
# 启动时执行一次完整性检查
|
||||||
self.check_emoji_file_integrity()
|
self.check_emoji_file_integrity()
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -338,12 +358,23 @@ class EmojiManager:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("[错误] 扫描表情包失败")
|
logger.exception("[错误] 扫描表情包失败")
|
||||||
|
|
||||||
async def _periodic_scan(self):
|
async def start_periodic_register(self):
|
||||||
"""定期扫描新表情包"""
|
"""定期扫描新表情包"""
|
||||||
while True:
|
while True:
|
||||||
logger.info("[扫描] 开始扫描新表情包...")
|
logger.info("[扫描] 开始扫描新表情包...")
|
||||||
await self.scan_new_emojis()
|
if (self.emoji_num > self.emoji_num_max):
|
||||||
await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60)
|
logger.warning(f"[警告] 表情包数量超过最大限制: {self.emoji_num} > {self.emoji_num_max},跳过注册")
|
||||||
|
if not global_config.max_reach_deletion:
|
||||||
|
logger.warning("表情包数量超过最大限制,终止注册")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logger.warning("表情包数量超过最大限制,开始删除表情包")
|
||||||
|
self.check_emoji_file_full()
|
||||||
|
else:
|
||||||
|
await self.scan_new_emojis()
|
||||||
|
await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def check_emoji_file_integrity(self):
|
def check_emoji_file_integrity(self):
|
||||||
"""检查表情包文件完整性
|
"""检查表情包文件完整性
|
||||||
@@ -416,12 +447,124 @@ class EmojiManager:
|
|||||||
logger.error(f"[错误] 检查表情包完整性失败: {str(e)}")
|
logger.error(f"[错误] 检查表情包完整性失败: {str(e)}")
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def check_emoji_file_full(self):
|
||||||
|
"""检查表情包文件是否完整,如果数量超出限制且允许删除,则删除多余的表情包
|
||||||
|
|
||||||
|
删除规则:
|
||||||
|
1. 优先删除创建时间更早的表情包
|
||||||
|
2. 优先删除使用次数少的表情包,但使用次数多的也有小概率被删除
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self._ensure_db()
|
||||||
|
# 更新表情包数量
|
||||||
|
self._update_emoji_count()
|
||||||
|
|
||||||
|
# 检查是否超出限制
|
||||||
|
if self.emoji_num <= self.emoji_num_max:
|
||||||
|
return
|
||||||
|
|
||||||
|
# 如果超出限制但不允许删除,则只记录警告
|
||||||
|
if not global_config.max_reach_deletion:
|
||||||
|
logger.warning(f"[警告] 表情包数量({self.emoji_num})超出限制({self.emoji_num_max}),但未开启自动删除")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 计算需要删除的数量
|
||||||
|
delete_count = self.emoji_num - self.emoji_num_max
|
||||||
|
logger.info(f"[清理] 需要删除 {delete_count} 个表情包")
|
||||||
|
|
||||||
|
# 获取所有表情包,按时间戳升序(旧的在前)排序
|
||||||
|
all_emojis = list(db.emoji.find().sort([("timestamp", 1)]))
|
||||||
|
|
||||||
|
# 计算权重:使用次数越多,被删除的概率越小
|
||||||
|
weights = []
|
||||||
|
max_usage = max((emoji.get("usage_count", 0) for emoji in all_emojis), default=1)
|
||||||
|
for emoji in all_emojis:
|
||||||
|
usage_count = emoji.get("usage_count", 0)
|
||||||
|
# 使用指数衰减函数计算权重,使用次数越多权重越小
|
||||||
|
weight = 1.0 / (1.0 + usage_count / max(1, max_usage))
|
||||||
|
weights.append(weight)
|
||||||
|
|
||||||
|
# 根据权重随机选择要删除的表情包
|
||||||
|
to_delete = []
|
||||||
|
remaining_indices = list(range(len(all_emojis)))
|
||||||
|
|
||||||
|
while len(to_delete) < delete_count and remaining_indices:
|
||||||
|
# 计算当前剩余表情包的权重
|
||||||
|
current_weights = [weights[i] for i in remaining_indices]
|
||||||
|
# 归一化权重
|
||||||
|
total_weight = sum(current_weights)
|
||||||
|
if total_weight == 0:
|
||||||
|
break
|
||||||
|
normalized_weights = [w/total_weight for w in current_weights]
|
||||||
|
|
||||||
|
# 随机选择一个表情包
|
||||||
|
selected_idx = random.choices(remaining_indices, weights=normalized_weights, k=1)[0]
|
||||||
|
to_delete.append(all_emojis[selected_idx])
|
||||||
|
remaining_indices.remove(selected_idx)
|
||||||
|
|
||||||
|
# 删除选中的表情包
|
||||||
|
deleted_count = 0
|
||||||
|
for emoji in to_delete:
|
||||||
|
try:
|
||||||
|
# 删除文件
|
||||||
|
if "path" in emoji and os.path.exists(emoji["path"]):
|
||||||
|
os.remove(emoji["path"])
|
||||||
|
logger.info(f"[删除] 文件: {emoji['path']} (使用次数: {emoji.get('usage_count', 0)})")
|
||||||
|
|
||||||
|
# 删除数据库记录
|
||||||
|
db.emoji.delete_one({"_id": emoji["_id"]})
|
||||||
|
deleted_count += 1
|
||||||
|
|
||||||
|
# 同时从images集合中删除
|
||||||
|
if "hash" in emoji:
|
||||||
|
db.images.delete_one({"hash": emoji["hash"]})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[错误] 删除表情包失败: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 更新表情包数量
|
||||||
|
self._update_emoji_count()
|
||||||
|
logger.success(f"[清理] 已删除 {deleted_count} 个表情包,当前数量: {self.emoji_num}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[错误] 检查表情包数量失败: {str(e)}")
|
||||||
|
|
||||||
async def start_periodic_check(self):
|
async def start_periodic_check(self):
|
||||||
|
"""定期检查表情包完整性和数量"""
|
||||||
while True:
|
while True:
|
||||||
|
self.check_emoji_file_full()
|
||||||
self.check_emoji_file_integrity()
|
self.check_emoji_file_integrity()
|
||||||
|
await self.delete_all_images()
|
||||||
await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60)
|
await asyncio.sleep(global_config.EMOJI_CHECK_INTERVAL * 60)
|
||||||
|
|
||||||
|
async def delete_all_images(self):
|
||||||
|
"""删除 data/image 目录下的所有文件"""
|
||||||
|
try:
|
||||||
|
image_dir = os.path.join("data", "image")
|
||||||
|
if not os.path.exists(image_dir):
|
||||||
|
logger.warning(f"[警告] 目录不存在: {image_dir}")
|
||||||
|
return
|
||||||
|
|
||||||
|
deleted_count = 0
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
# 遍历目录下的所有文件
|
||||||
|
for filename in os.listdir(image_dir):
|
||||||
|
file_path = os.path.join(image_dir, filename)
|
||||||
|
try:
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
os.remove(file_path)
|
||||||
|
deleted_count += 1
|
||||||
|
logger.debug(f"[删除] 文件: {file_path}")
|
||||||
|
except Exception as e:
|
||||||
|
failed_count += 1
|
||||||
|
logger.error(f"[错误] 删除文件失败 {file_path}: {str(e)}")
|
||||||
|
|
||||||
|
logger.success(f"[清理] 已删除 {deleted_count} 个文件,失败 {failed_count} 个")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[错误] 删除图片目录失败: {str(e)}")
|
||||||
|
|
||||||
# 创建全局单例
|
# 创建全局单例
|
||||||
|
|
||||||
emoji_manager = EmojiManager()
|
emoji_manager = EmojiManager()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Message(MessageBase):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
message_id: str,
|
message_id: str,
|
||||||
time: int,
|
time: float,
|
||||||
chat_stream: ChatStream,
|
chat_stream: ChatStream,
|
||||||
user_info: UserInfo,
|
user_info: UserInfo,
|
||||||
message_segment: Optional[Seg] = None,
|
message_segment: Optional[Seg] = None,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from .message import MessageSending, MessageThinking, MessageSet
|
|||||||
|
|
||||||
from ..storage.storage import MessageStorage
|
from ..storage.storage import MessageStorage
|
||||||
from ..config.config import global_config
|
from ..config.config import global_config
|
||||||
from .utils import truncate_message, calculate_typing_time
|
from .utils import truncate_message, calculate_typing_time, count_messages_between
|
||||||
|
|
||||||
from src.common.logger import LogConfig, SENDER_STYLE_CONFIG
|
from src.common.logger import LogConfig, SENDER_STYLE_CONFIG
|
||||||
|
|
||||||
@@ -67,6 +67,8 @@ class Message_Sender:
|
|||||||
try:
|
try:
|
||||||
end_point = global_config.api_urls.get(message.message_info.platform, None)
|
end_point = global_config.api_urls.get(message.message_info.platform, None)
|
||||||
if end_point:
|
if end_point:
|
||||||
|
# logger.info(f"发送消息到{end_point}")
|
||||||
|
# logger.info(message_json)
|
||||||
await global_api.send_message(end_point, message_json)
|
await global_api.send_message(end_point, message_json)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"未找到平台:{message.message_info.platform} 的url配置,请检查配置文件")
|
raise ValueError(f"未找到平台:{message.message_info.platform} 的url配置,请检查配置文件")
|
||||||
@@ -83,16 +85,16 @@ class MessageContainer:
|
|||||||
self.max_size = max_size
|
self.max_size = max_size
|
||||||
self.messages = []
|
self.messages = []
|
||||||
self.last_send_time = 0
|
self.last_send_time = 0
|
||||||
self.thinking_timeout = 10 # 思考等待超时时间(秒)
|
self.thinking_wait_timeout = 20 # 思考等待超时时间(秒)
|
||||||
|
|
||||||
def get_timeout_messages(self) -> List[MessageSending]:
|
def get_timeout_messages(self) -> List[MessageSending]:
|
||||||
"""获取所有超时的Message_Sending对象(思考时间超过30秒),按thinking_start_time排序"""
|
"""获取所有超时的Message_Sending对象(思考时间超过20秒),按thinking_start_time排序"""
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
timeout_messages = []
|
timeout_messages = []
|
||||||
|
|
||||||
for msg in self.messages:
|
for msg in self.messages:
|
||||||
if isinstance(msg, MessageSending):
|
if isinstance(msg, MessageSending):
|
||||||
if current_time - msg.thinking_start_time > self.thinking_timeout:
|
if current_time - msg.thinking_start_time > self.thinking_wait_timeout:
|
||||||
timeout_messages.append(msg)
|
timeout_messages.append(msg)
|
||||||
|
|
||||||
# 按thinking_start_time排序,时间早的在前面
|
# 按thinking_start_time排序,时间早的在前面
|
||||||
@@ -170,6 +172,7 @@ class MessageManager:
|
|||||||
message_earliest = container.get_earliest_message()
|
message_earliest = container.get_earliest_message()
|
||||||
|
|
||||||
if isinstance(message_earliest, MessageThinking):
|
if isinstance(message_earliest, MessageThinking):
|
||||||
|
"""取得了思考消息"""
|
||||||
message_earliest.update_thinking_time()
|
message_earliest.update_thinking_time()
|
||||||
thinking_time = message_earliest.thinking_time
|
thinking_time = message_earliest.thinking_time
|
||||||
# print(thinking_time)
|
# print(thinking_time)
|
||||||
@@ -185,14 +188,18 @@ class MessageManager:
|
|||||||
container.remove_message(message_earliest)
|
container.remove_message(message_earliest)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# print(message_earliest.is_head)
|
"""取得了发送消息"""
|
||||||
# print(message_earliest.update_thinking_time())
|
|
||||||
# print(message_earliest.is_private_message())
|
|
||||||
thinking_time = message_earliest.update_thinking_time()
|
thinking_time = message_earliest.update_thinking_time()
|
||||||
print(thinking_time)
|
thinking_start_time = message_earliest.thinking_start_time
|
||||||
|
now_time = time.time()
|
||||||
|
thinking_messages_count, thinking_messages_length = count_messages_between(start_time=thinking_start_time, end_time=now_time, stream_id=message_earliest.chat_stream.stream_id)
|
||||||
|
# print(thinking_time)
|
||||||
|
# print(thinking_messages_count)
|
||||||
|
# print(thinking_messages_length)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
message_earliest.is_head
|
message_earliest.is_head
|
||||||
and message_earliest.update_thinking_time() > 18
|
and (thinking_messages_count > 4 or thinking_messages_length > 250)
|
||||||
and not message_earliest.is_private_message() # 避免在私聊时插入reply
|
and not message_earliest.is_private_message() # 避免在私聊时插入reply
|
||||||
):
|
):
|
||||||
logger.debug(f"设置回复消息{message_earliest.processed_plain_text}")
|
logger.debug(f"设置回复消息{message_earliest.processed_plain_text}")
|
||||||
@@ -214,12 +221,16 @@ class MessageManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# print(msg.is_head)
|
thinking_time = msg.update_thinking_time()
|
||||||
print(msg.update_thinking_time())
|
thinking_start_time = msg.thinking_start_time
|
||||||
# print(msg.is_private_message())
|
now_time = time.time()
|
||||||
|
thinking_messages_count, thinking_messages_length = count_messages_between(start_time=thinking_start_time, end_time=now_time, stream_id=msg.chat_stream.stream_id)
|
||||||
|
# print(thinking_time)
|
||||||
|
# print(thinking_messages_count)
|
||||||
|
# print(thinking_messages_length)
|
||||||
if (
|
if (
|
||||||
msg.is_head
|
msg.is_head
|
||||||
and msg.update_thinking_time() > 18
|
and (thinking_messages_count > 4 or thinking_messages_length > 250)
|
||||||
and not msg.is_private_message() # 避免在私聊时插入reply
|
and not msg.is_private_message() # 避免在私聊时插入reply
|
||||||
):
|
):
|
||||||
logger.debug(f"设置回复消息{msg.processed_plain_text}")
|
logger.debug(f"设置回复消息{msg.processed_plain_text}")
|
||||||
|
|||||||
@@ -149,7 +149,6 @@ def get_recent_group_speaker(chat_stream_id: int, sender, limit: int = 12) -> li
|
|||||||
db.messages.find(
|
db.messages.find(
|
||||||
{"chat_id": chat_stream_id},
|
{"chat_id": chat_stream_id},
|
||||||
{
|
{
|
||||||
"chat_info": 1,
|
|
||||||
"user_info": 1,
|
"user_info": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -160,20 +159,17 @@ def get_recent_group_speaker(chat_stream_id: int, sender, limit: int = 12) -> li
|
|||||||
if not recent_messages:
|
if not recent_messages:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
who_chat_in_group = [] # ChatStream列表
|
who_chat_in_group = []
|
||||||
|
|
||||||
duplicate_removal = []
|
|
||||||
for msg_db_data in recent_messages:
|
for msg_db_data in recent_messages:
|
||||||
user_info = UserInfo.from_dict(msg_db_data["user_info"])
|
user_info = UserInfo.from_dict(msg_db_data["user_info"])
|
||||||
if (
|
if (
|
||||||
(user_info.user_id, user_info.platform) != sender
|
(user_info.platform, user_info.user_id) != sender
|
||||||
and (user_info.user_id, user_info.platform) != (global_config.BOT_QQ, "qq")
|
and user_info.user_id != global_config.BOT_QQ
|
||||||
and (user_info.user_id, user_info.platform) not in duplicate_removal
|
and (user_info.platform, user_info.user_id, user_info.user_nickname) not in who_chat_in_group
|
||||||
and len(duplicate_removal) < 5
|
and len(who_chat_in_group) < 5
|
||||||
): # 排除重复,排除消息发送者,排除bot(此处bot的平台强制为了qq,可能需要更改),限制加载的关系数目
|
): # 排除重复,排除消息发送者,排除bot,限制加载的关系数目
|
||||||
duplicate_removal.append((user_info.user_id, user_info.platform))
|
who_chat_in_group.append((user_info.platform, user_info.user_id, user_info.user_nickname))
|
||||||
chat_info = msg_db_data.get("chat_info", {})
|
|
||||||
who_chat_in_group.append(ChatStream.from_dict(chat_info))
|
|
||||||
return who_chat_in_group
|
return who_chat_in_group
|
||||||
|
|
||||||
|
|
||||||
@@ -349,6 +345,15 @@ def calculate_typing_time(input_string: str, chinese_time: float = 0.2, english_
|
|||||||
- 如果只有一个中文字符,将使用3倍的中文输入时间
|
- 如果只有一个中文字符,将使用3倍的中文输入时间
|
||||||
- 在所有输入结束后,额外加上回车时间0.3秒
|
- 在所有输入结束后,额外加上回车时间0.3秒
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# 如果输入是列表,将其连接成字符串
|
||||||
|
if isinstance(input_string, list):
|
||||||
|
input_string = ''.join(input_string)
|
||||||
|
|
||||||
|
# 确保现在是字符串类型
|
||||||
|
if not isinstance(input_string, str):
|
||||||
|
input_string = str(input_string)
|
||||||
|
|
||||||
mood_manager = MoodManager.get_instance()
|
mood_manager = MoodManager.get_instance()
|
||||||
# 将0-1的唤醒度映射到-1到1
|
# 将0-1的唤醒度映射到-1到1
|
||||||
mood_arousal = mood_manager.current_mood.arousal
|
mood_arousal = mood_manager.current_mood.arousal
|
||||||
@@ -482,3 +487,108 @@ def is_western_char(char):
|
|||||||
def is_western_paragraph(paragraph):
|
def is_western_paragraph(paragraph):
|
||||||
"""检测是否为西文字符段落"""
|
"""检测是否为西文字符段落"""
|
||||||
return all(is_western_char(char) for char in paragraph if char.isalnum())
|
return all(is_western_char(char) for char in paragraph if char.isalnum())
|
||||||
|
|
||||||
|
|
||||||
|
def count_messages_between(start_time: float, end_time: float, stream_id: str) -> tuple[int, int]:
|
||||||
|
"""计算两个时间点之间的消息数量和文本总长度
|
||||||
|
|
||||||
|
Args:
|
||||||
|
start_time (float): 起始时间戳
|
||||||
|
end_time (float): 结束时间戳
|
||||||
|
stream_id (str): 聊天流ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
tuple[int, int]: (消息数量, 文本总长度)
|
||||||
|
- 消息数量:包含起始时间的消息,不包含结束时间的消息
|
||||||
|
- 文本总长度:所有消息的processed_plain_text长度之和
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取开始时间之前最新的一条消息
|
||||||
|
start_message = db.messages.find_one(
|
||||||
|
{
|
||||||
|
"chat_id": stream_id,
|
||||||
|
"time": {"$lte": start_time}
|
||||||
|
},
|
||||||
|
sort=[("time", -1), ("_id", -1)] # 按时间倒序,_id倒序(最后插入的在前)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 获取结束时间最近的一条消息
|
||||||
|
# 先找到结束时间点的所有消息
|
||||||
|
end_time_messages = list(db.messages.find(
|
||||||
|
{
|
||||||
|
"chat_id": stream_id,
|
||||||
|
"time": {"$lte": end_time}
|
||||||
|
},
|
||||||
|
sort=[("time", -1)] # 先按时间倒序
|
||||||
|
).limit(10)) # 限制查询数量,避免性能问题
|
||||||
|
|
||||||
|
if not end_time_messages:
|
||||||
|
logger.warning(f"未找到结束时间 {end_time} 之前的消息")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# 找到最大时间
|
||||||
|
max_time = end_time_messages[0]["time"]
|
||||||
|
# 在最大时间的消息中找最后插入的(_id最大的)
|
||||||
|
end_message = max(
|
||||||
|
[msg for msg in end_time_messages if msg["time"] == max_time],
|
||||||
|
key=lambda x: x["_id"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not start_message:
|
||||||
|
logger.warning(f"未找到开始时间 {start_time} 之前的消息")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# 调试输出
|
||||||
|
# print("\n=== 消息范围信息 ===")
|
||||||
|
# print("Start message:", {
|
||||||
|
# "message_id": start_message.get("message_id"),
|
||||||
|
# "time": start_message.get("time"),
|
||||||
|
# "text": start_message.get("processed_plain_text", ""),
|
||||||
|
# "_id": str(start_message.get("_id"))
|
||||||
|
# })
|
||||||
|
# print("End message:", {
|
||||||
|
# "message_id": end_message.get("message_id"),
|
||||||
|
# "time": end_message.get("time"),
|
||||||
|
# "text": end_message.get("processed_plain_text", ""),
|
||||||
|
# "_id": str(end_message.get("_id"))
|
||||||
|
# })
|
||||||
|
# print("Stream ID:", stream_id)
|
||||||
|
|
||||||
|
# 如果结束消息的时间等于开始时间,返回0
|
||||||
|
if end_message["time"] == start_message["time"]:
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
# 获取并打印这个时间范围内的所有消息
|
||||||
|
# print("\n=== 时间范围内的所有消息 ===")
|
||||||
|
all_messages = list(db.messages.find(
|
||||||
|
{
|
||||||
|
"chat_id": stream_id,
|
||||||
|
"time": {
|
||||||
|
"$gte": start_message["time"],
|
||||||
|
"$lte": end_message["time"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sort=[("time", 1), ("_id", 1)] # 按时间正序,_id正序
|
||||||
|
))
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
total_length = 0
|
||||||
|
for msg in all_messages:
|
||||||
|
count += 1
|
||||||
|
text_length = len(msg.get("processed_plain_text", ""))
|
||||||
|
total_length += text_length
|
||||||
|
# print(f"\n消息 {count}:")
|
||||||
|
# print({
|
||||||
|
# "message_id": msg.get("message_id"),
|
||||||
|
# "time": msg.get("time"),
|
||||||
|
# "text": msg.get("processed_plain_text", ""),
|
||||||
|
# "text_length": text_length,
|
||||||
|
# "_id": str(msg.get("_id"))
|
||||||
|
# })
|
||||||
|
|
||||||
|
# 如果时间不同,需要把end_message本身也计入
|
||||||
|
return count - 1, total_length
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"计算消息数量时出错: {str(e)}")
|
||||||
|
return 0, 0
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from ...willing.willing_manager import willing_manager
|
|||||||
from ...message import UserInfo, Seg
|
from ...message import UserInfo, Seg
|
||||||
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
|
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
|
||||||
from ...chat.chat_stream import chat_manager
|
from ...chat.chat_stream import chat_manager
|
||||||
|
from ...person_info.relationship_manager import relationship_manager
|
||||||
|
|
||||||
# 定义日志配置
|
# 定义日志配置
|
||||||
chat_config = LogConfig(
|
chat_config = LogConfig(
|
||||||
@@ -123,6 +124,15 @@ class ReasoningChat:
|
|||||||
)
|
)
|
||||||
message_manager.add_message(bot_message)
|
message_manager.add_message(bot_message)
|
||||||
|
|
||||||
|
async def _update_relationship(self, message, response_set):
|
||||||
|
"""更新关系情绪"""
|
||||||
|
ori_response = ",".join(response_set)
|
||||||
|
stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text)
|
||||||
|
await relationship_manager.calculate_update_relationship_value(
|
||||||
|
chat_stream=message.chat_stream, label=emotion, stance=stance
|
||||||
|
)
|
||||||
|
self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor)
|
||||||
|
|
||||||
async def process_message(self, message_data: str) -> None:
|
async def process_message(self, message_data: str) -> None:
|
||||||
"""处理消息并生成回复"""
|
"""处理消息并生成回复"""
|
||||||
timing_results = {}
|
timing_results = {}
|
||||||
@@ -226,6 +236,12 @@ class ReasoningChat:
|
|||||||
timer2 = time.time()
|
timer2 = time.time()
|
||||||
timing_results["处理表情包"] = timer2 - timer1
|
timing_results["处理表情包"] = timer2 - timer1
|
||||||
|
|
||||||
|
# 更新关系情绪
|
||||||
|
timer1 = time.time()
|
||||||
|
await self._update_relationship(message, response_set)
|
||||||
|
timer2 = time.time()
|
||||||
|
timing_results["更新关系情绪"] = timer2 - timer1
|
||||||
|
|
||||||
# 输出性能计时结果
|
# 输出性能计时结果
|
||||||
if do_reply:
|
if do_reply:
|
||||||
timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()])
|
timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()])
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ from ...memory_system.Hippocampus import HippocampusManager
|
|||||||
from ...moods.moods import MoodManager
|
from ...moods.moods import MoodManager
|
||||||
from ...schedule.schedule_generator import bot_schedule
|
from ...schedule.schedule_generator import bot_schedule
|
||||||
from ...config.config import global_config
|
from ...config.config import global_config
|
||||||
from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text
|
from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text, get_recent_group_speaker
|
||||||
from ...chat.chat_stream import chat_manager
|
from ...chat.chat_stream import chat_manager
|
||||||
from src.common.logger import get_module_logger
|
from src.common.logger import get_module_logger
|
||||||
|
from ...person_info.relationship_manager import relationship_manager
|
||||||
|
|
||||||
logger = get_module_logger("prompt")
|
logger = get_module_logger("prompt")
|
||||||
|
|
||||||
@@ -25,6 +26,25 @@ class PromptBuilder:
|
|||||||
|
|
||||||
# 开始构建prompt
|
# 开始构建prompt
|
||||||
|
|
||||||
|
# 关系
|
||||||
|
who_chat_in_group = [(chat_stream.user_info.platform,
|
||||||
|
chat_stream.user_info.user_id,
|
||||||
|
chat_stream.user_info.user_nickname)]
|
||||||
|
who_chat_in_group += get_recent_group_speaker(
|
||||||
|
stream_id,
|
||||||
|
(chat_stream.user_info.platform, chat_stream.user_info.user_id),
|
||||||
|
limit=global_config.MAX_CONTEXT_SIZE,
|
||||||
|
)
|
||||||
|
|
||||||
|
relation_prompt = ""
|
||||||
|
for person in who_chat_in_group:
|
||||||
|
relation_prompt += await relationship_manager.build_relationship_info(person)
|
||||||
|
|
||||||
|
relation_prompt_all = (
|
||||||
|
f"{relation_prompt}关系等级越大,关系越好,请分析聊天记录,"
|
||||||
|
f"根据你和说话者{sender_name}的关系和态度进行回复,明确你的立场和情感。"
|
||||||
|
)
|
||||||
|
|
||||||
# 心情
|
# 心情
|
||||||
mood_manager = MoodManager.get_instance()
|
mood_manager = MoodManager.get_instance()
|
||||||
mood_prompt = mood_manager.get_prompt()
|
mood_prompt = mood_manager.get_prompt()
|
||||||
@@ -127,7 +147,7 @@ class PromptBuilder:
|
|||||||
{schedule_prompt}
|
{schedule_prompt}
|
||||||
{chat_target}
|
{chat_target}
|
||||||
{chat_talking_prompt}
|
{chat_talking_prompt}
|
||||||
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n
|
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。{relation_prompt_all}\n
|
||||||
你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。
|
你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。
|
||||||
你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些,
|
你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些,
|
||||||
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger}
|
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ from ...message import UserInfo, Seg
|
|||||||
from src.heart_flow.heartflow import heartflow
|
from src.heart_flow.heartflow import heartflow
|
||||||
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
|
from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig
|
||||||
from ...chat.chat_stream import chat_manager
|
from ...chat.chat_stream import chat_manager
|
||||||
|
from ...person_info.relationship_manager import relationship_manager
|
||||||
|
|
||||||
# 定义日志配置
|
# 定义日志配置
|
||||||
chat_config = LogConfig(
|
chat_config = LogConfig(
|
||||||
@@ -101,9 +102,13 @@ class ThinkFlowChat:
|
|||||||
"""处理表情包"""
|
"""处理表情包"""
|
||||||
if random() < global_config.emoji_chance:
|
if random() < global_config.emoji_chance:
|
||||||
emoji_raw = await emoji_manager.get_emoji_for_text(response)
|
emoji_raw = await emoji_manager.get_emoji_for_text(response)
|
||||||
|
# print("11111111111111")
|
||||||
|
# logger.info(emoji_raw)
|
||||||
if emoji_raw:
|
if emoji_raw:
|
||||||
emoji_path, description = emoji_raw
|
emoji_path, description = emoji_raw
|
||||||
emoji_cq = image_path_to_base64(emoji_path)
|
emoji_cq = image_path_to_base64(emoji_path)
|
||||||
|
|
||||||
|
# logger.info(emoji_cq)
|
||||||
|
|
||||||
thinking_time_point = round(message.message_info.time, 2)
|
thinking_time_point = round(message.message_info.time, 2)
|
||||||
|
|
||||||
@@ -122,6 +127,8 @@ class ThinkFlowChat:
|
|||||||
is_head=False,
|
is_head=False,
|
||||||
is_emoji=True,
|
is_emoji=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# logger.info("22222222222222")
|
||||||
message_manager.add_message(bot_message)
|
message_manager.add_message(bot_message)
|
||||||
|
|
||||||
async def _update_using_response(self, message, response_set):
|
async def _update_using_response(self, message, response_set):
|
||||||
@@ -135,6 +142,15 @@ class ThinkFlowChat:
|
|||||||
|
|
||||||
await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt)
|
await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt)
|
||||||
|
|
||||||
|
async def _update_relationship(self, message, response_set):
|
||||||
|
"""更新关系情绪"""
|
||||||
|
ori_response = ",".join(response_set)
|
||||||
|
stance, emotion = await self.gpt._get_emotion_tags(ori_response, message.processed_plain_text)
|
||||||
|
await relationship_manager.calculate_update_relationship_value(
|
||||||
|
chat_stream=message.chat_stream, label=emotion, stance=stance
|
||||||
|
)
|
||||||
|
self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor)
|
||||||
|
|
||||||
async def process_message(self, message_data: str) -> None:
|
async def process_message(self, message_data: str) -> None:
|
||||||
"""处理消息并生成回复"""
|
"""处理消息并生成回复"""
|
||||||
timing_results = {}
|
timing_results = {}
|
||||||
@@ -158,15 +174,18 @@ class ThinkFlowChat:
|
|||||||
heartflow.create_subheartflow(chat.stream_id)
|
heartflow.create_subheartflow(chat.stream_id)
|
||||||
|
|
||||||
await message.process()
|
await message.process()
|
||||||
|
logger.debug(f"消息处理成功{message.processed_plain_text}")
|
||||||
|
|
||||||
# 过滤词/正则表达式过滤
|
# 过滤词/正则表达式过滤
|
||||||
if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex(
|
if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex(
|
||||||
message.raw_message, chat, userinfo
|
message.raw_message, chat, userinfo
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
|
logger.debug(f"过滤词/正则表达式过滤成功{message.processed_plain_text}")
|
||||||
|
|
||||||
await self.storage.store_message(message, chat)
|
await self.storage.store_message(message, chat)
|
||||||
|
logger.debug(f"存储成功{message.processed_plain_text}")
|
||||||
|
|
||||||
# 记忆激活
|
# 记忆激活
|
||||||
timer1 = time.time()
|
timer1 = time.time()
|
||||||
interested_rate = await HippocampusManager.get_instance().get_activate_from_text(
|
interested_rate = await HippocampusManager.get_instance().get_activate_from_text(
|
||||||
@@ -180,8 +199,10 @@ class ThinkFlowChat:
|
|||||||
|
|
||||||
# 计算回复意愿
|
# 计算回复意愿
|
||||||
current_willing_old = willing_manager.get_willing(chat_stream=chat)
|
current_willing_old = willing_manager.get_willing(chat_stream=chat)
|
||||||
current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4
|
# current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4
|
||||||
current_willing = (current_willing_old + current_willing_new) / 2
|
# current_willing = (current_willing_old + current_willing_new) / 2
|
||||||
|
# 有点bug
|
||||||
|
current_willing = current_willing_old
|
||||||
|
|
||||||
|
|
||||||
willing_manager.set_willing(chat.stream_id, current_willing)
|
willing_manager.set_willing(chat.stream_id, current_willing)
|
||||||
@@ -263,6 +284,12 @@ class ThinkFlowChat:
|
|||||||
timer2 = time.time()
|
timer2 = time.time()
|
||||||
timing_results["更新心流"] = timer2 - timer1
|
timing_results["更新心流"] = timer2 - timer1
|
||||||
|
|
||||||
|
# 更新关系情绪
|
||||||
|
timer1 = time.time()
|
||||||
|
await self._update_relationship(message, response_set)
|
||||||
|
timer2 = time.time()
|
||||||
|
timing_results["更新关系情绪"] = timer2 - timer1
|
||||||
|
|
||||||
# 输出性能计时结果
|
# 输出性能计时结果
|
||||||
if do_reply:
|
if do_reply:
|
||||||
timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()])
|
timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()])
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ from ...memory_system.Hippocampus import HippocampusManager
|
|||||||
from ...moods.moods import MoodManager
|
from ...moods.moods import MoodManager
|
||||||
from ...schedule.schedule_generator import bot_schedule
|
from ...schedule.schedule_generator import bot_schedule
|
||||||
from ...config.config import global_config
|
from ...config.config import global_config
|
||||||
from ...chat.utils import get_recent_group_detailed_plain_text
|
from ...chat.utils import get_recent_group_detailed_plain_text, get_recent_group_speaker
|
||||||
from ...chat.chat_stream import chat_manager
|
from ...chat.chat_stream import chat_manager
|
||||||
from src.common.logger import get_module_logger
|
from src.common.logger import get_module_logger
|
||||||
|
from ...person_info.relationship_manager import relationship_manager
|
||||||
|
|
||||||
from src.heart_flow.heartflow import heartflow
|
from src.heart_flow.heartflow import heartflow
|
||||||
|
|
||||||
@@ -28,6 +29,25 @@ class PromptBuilder:
|
|||||||
|
|
||||||
# 开始构建prompt
|
# 开始构建prompt
|
||||||
|
|
||||||
|
# 关系
|
||||||
|
who_chat_in_group = [(chat_stream.user_info.platform,
|
||||||
|
chat_stream.user_info.user_id,
|
||||||
|
chat_stream.user_info.user_nickname)]
|
||||||
|
who_chat_in_group += get_recent_group_speaker(
|
||||||
|
stream_id,
|
||||||
|
(chat_stream.user_info.platform, chat_stream.user_info.user_id),
|
||||||
|
limit=global_config.MAX_CONTEXT_SIZE,
|
||||||
|
)
|
||||||
|
|
||||||
|
relation_prompt = ""
|
||||||
|
for person in who_chat_in_group:
|
||||||
|
relation_prompt += await relationship_manager.build_relationship_info(person)
|
||||||
|
|
||||||
|
relation_prompt_all = (
|
||||||
|
f"{relation_prompt}关系等级越大,关系越好,请分析聊天记录,"
|
||||||
|
f"根据你和说话者{sender_name}的关系和态度进行回复,明确你的立场和情感。"
|
||||||
|
)
|
||||||
|
|
||||||
# 心情
|
# 心情
|
||||||
mood_manager = MoodManager.get_instance()
|
mood_manager = MoodManager.get_instance()
|
||||||
mood_prompt = mood_manager.get_prompt()
|
mood_prompt = mood_manager.get_prompt()
|
||||||
@@ -98,18 +118,19 @@ class PromptBuilder:
|
|||||||
logger.info("开始构建prompt")
|
logger.info("开始构建prompt")
|
||||||
|
|
||||||
prompt = f"""
|
prompt = f"""
|
||||||
|
{relation_prompt_all}\n
|
||||||
{chat_target}
|
{chat_target}
|
||||||
{chat_talking_prompt}
|
{chat_talking_prompt}
|
||||||
你刚刚脑子里在想:
|
你刚刚脑子里在想:
|
||||||
{current_mind_info}
|
{current_mind_info}
|
||||||
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n
|
现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。{relation_prompt_all}\n
|
||||||
你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。
|
你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。
|
||||||
你正在{chat_target_2},现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些,
|
你正在{chat_target_2},现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些,
|
||||||
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger}
|
尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger}
|
||||||
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话
|
请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话
|
||||||
请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。
|
请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。
|
||||||
{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。"""
|
{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。"""
|
||||||
|
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def _build_initiative_prompt_select(self, group_id, probability_1=0.8, probability_2=0.1):
|
def _build_initiative_prompt_select(self, group_id, probability_1=0.8, probability_2=0.1):
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ logger = get_module_logger("config", config=config_config)
|
|||||||
|
|
||||||
#考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
#考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
||||||
mai_version_main = "test-0.6.0"
|
mai_version_main = "test-0.6.0"
|
||||||
mai_version_fix = "snapshot-7"
|
mai_version_fix = "snapshot-8"
|
||||||
mai_version = f"{mai_version_main}-{mai_version_fix}"
|
mai_version = f"{mai_version_main}-{mai_version_fix}"
|
||||||
|
|
||||||
def update_config():
|
def update_config():
|
||||||
@@ -184,6 +184,8 @@ class BotConfig:
|
|||||||
# MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率
|
# MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率
|
||||||
|
|
||||||
# emoji
|
# emoji
|
||||||
|
max_emoji_num: int = 200 # 表情包最大数量
|
||||||
|
max_reach_deletion: bool = True # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包
|
||||||
EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟)
|
EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟)
|
||||||
EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟)
|
EMOJI_REGISTER_INTERVAL: int = 10 # 表情包注册间隔(分钟)
|
||||||
EMOJI_SAVE: bool = True # 偷表情包
|
EMOJI_SAVE: bool = True # 偷表情包
|
||||||
@@ -368,6 +370,9 @@ class BotConfig:
|
|||||||
config.EMOJI_CHECK_PROMPT = emoji_config.get("check_prompt", config.EMOJI_CHECK_PROMPT)
|
config.EMOJI_CHECK_PROMPT = emoji_config.get("check_prompt", config.EMOJI_CHECK_PROMPT)
|
||||||
config.EMOJI_SAVE = emoji_config.get("auto_save", config.EMOJI_SAVE)
|
config.EMOJI_SAVE = emoji_config.get("auto_save", config.EMOJI_SAVE)
|
||||||
config.EMOJI_CHECK = emoji_config.get("enable_check", config.EMOJI_CHECK)
|
config.EMOJI_CHECK = emoji_config.get("enable_check", config.EMOJI_CHECK)
|
||||||
|
if config.INNER_VERSION in SpecifierSet(">=1.1.1"):
|
||||||
|
config.max_emoji_num = emoji_config.get("max_emoji_num", config.max_emoji_num)
|
||||||
|
config.max_reach_deletion = emoji_config.get("max_reach_deletion", config.max_reach_deletion)
|
||||||
|
|
||||||
def bot(parent: dict):
|
def bot(parent: dict):
|
||||||
# 机器人基础配置
|
# 机器人基础配置
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ class BaseMessageInfo:
|
|||||||
|
|
||||||
platform: Optional[str] = None
|
platform: Optional[str] = None
|
||||||
message_id: Union[str, int, None] = None
|
message_id: Union[str, int, None] = None
|
||||||
time: Optional[int] = None
|
time: Optional[float] = None
|
||||||
group_info: Optional[GroupInfo] = None
|
group_info: Optional[GroupInfo] = None
|
||||||
user_info: Optional[UserInfo] = None
|
user_info: Optional[UserInfo] = None
|
||||||
format_info: Optional[FormatInfo] = None
|
format_info: Optional[FormatInfo] = None
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from ..config.config import global_config
|
from ..config.config import global_config
|
||||||
from src.common.logger import get_module_logger, LogConfig, MOOD_STYLE_CONFIG
|
from src.common.logger import get_module_logger, LogConfig, MOOD_STYLE_CONFIG
|
||||||
|
from ..person_info.relationship_manager import relationship_manager
|
||||||
|
|
||||||
mood_config = LogConfig(
|
mood_config = LogConfig(
|
||||||
# 使用海马体专用样式
|
# 使用海马体专用样式
|
||||||
@@ -55,15 +56,15 @@ class MoodManager:
|
|||||||
|
|
||||||
# 情绪词映射表 (valence, arousal)
|
# 情绪词映射表 (valence, arousal)
|
||||||
self.emotion_map = {
|
self.emotion_map = {
|
||||||
"开心": (0.8, 0.6), # 高愉悦度,中等唤醒度
|
"开心": (0.21, 0.6),
|
||||||
"愤怒": (-0.7, 0.7), # 负愉悦度,高唤醒度
|
"害羞": (0.15, 0.2),
|
||||||
"悲伤": (-0.6, 0.3), # 负愉悦度,低唤醒度
|
"愤怒": (-0.24, 0.8),
|
||||||
"惊讶": (0.2, 0.8), # 中等愉悦度,高唤醒度
|
"恐惧": (-0.21, 0.7),
|
||||||
"害羞": (0.5, 0.2), # 中等愉悦度,低唤醒度
|
"悲伤": (-0.21, 0.3),
|
||||||
"平静": (0.0, 0.5), # 中性愉悦度,中等唤醒度
|
"厌恶": (-0.12, 0.4),
|
||||||
"恐惧": (-0.7, 0.6), # 负愉悦度,高唤醒度
|
"惊讶": (0.06, 0.7),
|
||||||
"厌恶": (-0.4, 0.4), # 负愉悦度,低唤醒度
|
"困惑": (0.0, 0.6),
|
||||||
"困惑": (0.0, 0.6), # 中性愉悦度,高唤醒度
|
"平静": (0.03, 0.5),
|
||||||
}
|
}
|
||||||
|
|
||||||
# 情绪文本映射表
|
# 情绪文本映射表
|
||||||
@@ -93,7 +94,7 @@ class MoodManager:
|
|||||||
cls._instance = MoodManager()
|
cls._instance = MoodManager()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def start_mood_update(self, update_interval: float = 1.0) -> None:
|
def start_mood_update(self, update_interval: float = 5.0) -> None:
|
||||||
"""
|
"""
|
||||||
启动情绪更新线程
|
启动情绪更新线程
|
||||||
:param update_interval: 更新间隔(秒)
|
:param update_interval: 更新间隔(秒)
|
||||||
@@ -228,9 +229,15 @@ class MoodManager:
|
|||||||
:param intensity: 情绪强度(0.0-1.0)
|
:param intensity: 情绪强度(0.0-1.0)
|
||||||
"""
|
"""
|
||||||
if emotion not in self.emotion_map:
|
if emotion not in self.emotion_map:
|
||||||
|
logger.debug(f"[情绪更新] 未知情绪词: {emotion}")
|
||||||
return
|
return
|
||||||
|
|
||||||
valence_change, arousal_change = self.emotion_map[emotion]
|
valence_change, arousal_change = self.emotion_map[emotion]
|
||||||
|
old_valence = self.current_mood.valence
|
||||||
|
old_arousal = self.current_mood.arousal
|
||||||
|
old_mood = self.current_mood.text
|
||||||
|
|
||||||
|
valence_change *= relationship_manager.gain_coefficient[relationship_manager.positive_feedback_value]
|
||||||
|
|
||||||
# 应用情绪强度
|
# 应用情绪强度
|
||||||
valence_change *= intensity
|
valence_change *= intensity
|
||||||
@@ -243,5 +250,8 @@ class MoodManager:
|
|||||||
# 限制范围
|
# 限制范围
|
||||||
self.current_mood.valence = max(-1.0, min(1.0, self.current_mood.valence))
|
self.current_mood.valence = max(-1.0, min(1.0, self.current_mood.valence))
|
||||||
self.current_mood.arousal = max(0.0, min(1.0, self.current_mood.arousal))
|
self.current_mood.arousal = max(0.0, min(1.0, self.current_mood.arousal))
|
||||||
|
|
||||||
self._update_mood_text()
|
self._update_mood_text()
|
||||||
|
|
||||||
|
logger.info(f"[情绪变化] {emotion}(强度:{intensity:.2f}) | 愉悦度:{old_valence:.2f}->{self.current_mood.valence:.2f}, 唤醒度:{old_arousal:.2f}->{self.current_mood.arousal:.2f} | 心情:{old_mood}->{self.current_mood.text}")
|
||||||
|
|
||||||
|
|||||||
213
src/plugins/person_info/person_info.py
Normal file
213
src/plugins/person_info/person_info.py
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
from src.common.logger import get_module_logger
|
||||||
|
from ...common.database import db
|
||||||
|
import copy
|
||||||
|
import hashlib
|
||||||
|
from typing import Any, Callable, Dict, TypeVar
|
||||||
|
T = TypeVar('T') # 泛型类型
|
||||||
|
|
||||||
|
"""
|
||||||
|
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_module_logger("person_info")
|
||||||
|
|
||||||
|
person_info_default = {
|
||||||
|
"person_id" : None,
|
||||||
|
"platform" : None,
|
||||||
|
"user_id" : None,
|
||||||
|
"nickname" : None,
|
||||||
|
# "age" : 0,
|
||||||
|
"relationship_value" : 0,
|
||||||
|
# "saved" : True,
|
||||||
|
# "impression" : None,
|
||||||
|
# "gender" : Unkown,
|
||||||
|
"konw_time" : 0,
|
||||||
|
} # 个人信息的各项与默认值在此定义,以下处理会自动创建/补全每一项
|
||||||
|
|
||||||
|
class PersonInfoManager:
|
||||||
|
def __init__(self):
|
||||||
|
if "person_info" not in db.list_collection_names():
|
||||||
|
db.create_collection("person_info")
|
||||||
|
db.person_info.create_index("person_id", unique=True)
|
||||||
|
|
||||||
|
def get_person_id(self, platform:str, user_id:int):
|
||||||
|
"""获取唯一id"""
|
||||||
|
components = [platform, str(user_id)]
|
||||||
|
key = "_".join(components)
|
||||||
|
return hashlib.md5(key.encode()).hexdigest()
|
||||||
|
|
||||||
|
async def create_person_info(self, person_id:str, data:dict = None):
|
||||||
|
"""创建一个项"""
|
||||||
|
if not person_id:
|
||||||
|
logger.debug("创建失败,personid不存在")
|
||||||
|
return
|
||||||
|
|
||||||
|
_person_info_default = copy.deepcopy(person_info_default)
|
||||||
|
_person_info_default["person_id"] = person_id
|
||||||
|
|
||||||
|
if data:
|
||||||
|
for key in _person_info_default:
|
||||||
|
if key != "person_id" and key in data:
|
||||||
|
_person_info_default[key] = data[key]
|
||||||
|
|
||||||
|
db.person_info.insert_one(_person_info_default)
|
||||||
|
|
||||||
|
async def update_one_field(self, person_id:str, field_name:str, value, Data:dict = None):
|
||||||
|
"""更新某一个字段,会补全"""
|
||||||
|
if field_name not in person_info_default.keys():
|
||||||
|
logger.debug(f"更新'{field_name}'失败,未定义的字段")
|
||||||
|
return
|
||||||
|
|
||||||
|
document = db.person_info.find_one({"person_id": person_id})
|
||||||
|
|
||||||
|
if document:
|
||||||
|
db.person_info.update_one(
|
||||||
|
{"person_id": person_id},
|
||||||
|
{"$set": {field_name: value}}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
Data[field_name] = value
|
||||||
|
logger.debug(f"更新时{person_id}不存在,已新建")
|
||||||
|
await self.create_person_info(person_id, Data)
|
||||||
|
|
||||||
|
async def del_one_document(self, person_id: str):
|
||||||
|
"""删除指定 person_id 的文档"""
|
||||||
|
if not person_id:
|
||||||
|
logger.debug("删除失败:person_id 不能为空")
|
||||||
|
return
|
||||||
|
|
||||||
|
result = db.person_info.delete_one({"person_id": person_id})
|
||||||
|
if result.deleted_count > 0:
|
||||||
|
logger.debug(f"删除成功:person_id={person_id}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"删除失败:未找到 person_id={person_id}")
|
||||||
|
|
||||||
|
async def get_value(self, person_id: str, field_name: str):
|
||||||
|
"""获取指定person_id文档的字段值,若不存在该字段,则返回该字段的全局默认值"""
|
||||||
|
if not person_id:
|
||||||
|
logger.debug("get_value获取失败:person_id不能为空")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if field_name not in person_info_default:
|
||||||
|
logger.debug(f"get_value获取失败:字段'{field_name}'未定义")
|
||||||
|
return None
|
||||||
|
|
||||||
|
document = db.person_info.find_one(
|
||||||
|
{"person_id": person_id},
|
||||||
|
{field_name: 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
if document and field_name in document:
|
||||||
|
return document[field_name]
|
||||||
|
else:
|
||||||
|
logger.debug(f"获取{person_id}的{field_name}失败,已返回默认值{person_info_default[field_name]}")
|
||||||
|
return person_info_default[field_name]
|
||||||
|
|
||||||
|
async def get_values(self, person_id: str, field_names: list) -> dict:
|
||||||
|
"""获取指定person_id文档的多个字段值,若不存在该字段,则返回该字段的全局默认值"""
|
||||||
|
if not person_id:
|
||||||
|
logger.debug("get_values获取失败:person_id不能为空")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 检查所有字段是否有效
|
||||||
|
for field in field_names:
|
||||||
|
if field not in person_info_default:
|
||||||
|
logger.debug(f"get_values获取失败:字段'{field}'未定义")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# 构建查询投影(所有字段都有效才会执行到这里)
|
||||||
|
projection = {field: 1 for field in field_names}
|
||||||
|
|
||||||
|
document = db.person_info.find_one(
|
||||||
|
{"person_id": person_id},
|
||||||
|
projection
|
||||||
|
)
|
||||||
|
|
||||||
|
result = {}
|
||||||
|
for field in field_names:
|
||||||
|
result[field] = document.get(field, person_info_default[field]) if document else person_info_default[field]
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def del_all_undefined_field(self):
|
||||||
|
"""删除所有项里的未定义字段"""
|
||||||
|
# 获取所有已定义的字段名
|
||||||
|
defined_fields = set(person_info_default.keys())
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 遍历集合中的所有文档
|
||||||
|
for document in db.person_info.find({}):
|
||||||
|
# 找出文档中未定义的字段
|
||||||
|
undefined_fields = set(document.keys()) - defined_fields - {'_id'}
|
||||||
|
|
||||||
|
if undefined_fields:
|
||||||
|
# 构建更新操作,使用$unset删除未定义字段
|
||||||
|
update_result = db.person_info.update_one(
|
||||||
|
{'_id': document['_id']},
|
||||||
|
{'$unset': {field: 1 for field in undefined_fields}}
|
||||||
|
)
|
||||||
|
|
||||||
|
if update_result.modified_count > 0:
|
||||||
|
logger.debug(f"已清理文档 {document['_id']} 的未定义字段: {undefined_fields}")
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"清理未定义字段时出错: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
async def get_specific_value_list(
|
||||||
|
self,
|
||||||
|
field_name: str,
|
||||||
|
way: Callable[[Any], bool], # 接受任意类型值
|
||||||
|
) ->Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取满足条件的字段值字典
|
||||||
|
|
||||||
|
Args:
|
||||||
|
field_name: 目标字段名
|
||||||
|
way: 判断函数 (value: Any) -> bool
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{person_id: value} | {}
|
||||||
|
|
||||||
|
Example:
|
||||||
|
# 查找所有nickname包含"admin"的用户
|
||||||
|
result = manager.specific_value_list(
|
||||||
|
"nickname",
|
||||||
|
lambda x: "admin" in x.lower()
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
if field_name not in person_info_default:
|
||||||
|
logger.error(f"字段检查失败:'{field_name}'未定义")
|
||||||
|
return {}
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = {}
|
||||||
|
for doc in db.person_info.find(
|
||||||
|
{field_name: {"$exists": True}},
|
||||||
|
{"person_id": 1, field_name: 1, "_id": 0}
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
value = doc[field_name]
|
||||||
|
if way(value):
|
||||||
|
result[doc["person_id"]] = value
|
||||||
|
except (KeyError, TypeError, ValueError) as e:
|
||||||
|
logger.debug(f"记录{doc.get('person_id')}处理失败: {str(e)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"数据库查询失败: {str(e)}", exc_info=True)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
person_info_manager = PersonInfoManager()
|
||||||
195
src/plugins/person_info/relationship_manager.py
Normal file
195
src/plugins/person_info/relationship_manager.py
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
from src.common.logger import get_module_logger, LogConfig, RELATION_STYLE_CONFIG
|
||||||
|
from ..chat.chat_stream import ChatStream
|
||||||
|
import math
|
||||||
|
from bson.decimal128 import Decimal128
|
||||||
|
from .person_info import person_info_manager
|
||||||
|
import time
|
||||||
|
|
||||||
|
relationship_config = LogConfig(
|
||||||
|
# 使用关系专用样式
|
||||||
|
console_format=RELATION_STYLE_CONFIG["console_format"],
|
||||||
|
file_format=RELATION_STYLE_CONFIG["file_format"],
|
||||||
|
)
|
||||||
|
logger = get_module_logger("rel_manager", config=relationship_config)
|
||||||
|
|
||||||
|
class RelationshipManager:
|
||||||
|
def __init__(self):
|
||||||
|
self.positive_feedback_value = 0 # 正反馈系统
|
||||||
|
self.gain_coefficient = [1.0, 1.0, 1.1, 1.2, 1.4, 1.7, 1.9, 2.0]
|
||||||
|
self._mood_manager = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mood_manager(self):
|
||||||
|
if self._mood_manager is None:
|
||||||
|
from ..moods.moods import MoodManager # 延迟导入
|
||||||
|
self._mood_manager = MoodManager.get_instance()
|
||||||
|
return self._mood_manager
|
||||||
|
|
||||||
|
def positive_feedback_sys(self, label: str, stance: str):
|
||||||
|
"""正反馈系统,通过正反馈系数增益情绪变化,根据情绪再影响关系变更"""
|
||||||
|
|
||||||
|
positive_list = [
|
||||||
|
"开心",
|
||||||
|
"惊讶",
|
||||||
|
"害羞",
|
||||||
|
]
|
||||||
|
|
||||||
|
negative_list = [
|
||||||
|
"愤怒",
|
||||||
|
"悲伤",
|
||||||
|
"恐惧",
|
||||||
|
"厌恶",
|
||||||
|
]
|
||||||
|
|
||||||
|
if label in positive_list and stance != "反对":
|
||||||
|
if 7 > self.positive_feedback_value >= 0:
|
||||||
|
self.positive_feedback_value += 1
|
||||||
|
elif self.positive_feedback_value < 0:
|
||||||
|
self.positive_feedback_value = 0
|
||||||
|
elif label in negative_list and stance != "支持":
|
||||||
|
if -7 < self.positive_feedback_value <= 0:
|
||||||
|
self.positive_feedback_value -= 1
|
||||||
|
elif self.positive_feedback_value > 0:
|
||||||
|
self.positive_feedback_value = 0
|
||||||
|
|
||||||
|
if abs(self.positive_feedback_value) > 1:
|
||||||
|
logger.info(f"触发mood变更增益,当前增益系数:{self.gain_coefficient[abs(self.positive_feedback_value)]}")
|
||||||
|
|
||||||
|
def mood_feedback(self, value):
|
||||||
|
"""情绪反馈"""
|
||||||
|
mood_manager = self.mood_manager
|
||||||
|
mood_gain = (mood_manager.get_current_mood().valence) ** 2 \
|
||||||
|
* math.copysign(1, value * mood_manager.get_current_mood().valence)
|
||||||
|
value += value * mood_gain
|
||||||
|
logger.info(f"当前relationship增益系数:{mood_gain:.3f}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
async def calculate_update_relationship_value(self, chat_stream: ChatStream, label: str, stance: str) -> None:
|
||||||
|
"""计算并变更关系值
|
||||||
|
新的关系值变更计算方式:
|
||||||
|
将关系值限定在-1000到1000
|
||||||
|
对于关系值的变更,期望:
|
||||||
|
1.向两端逼近时会逐渐减缓
|
||||||
|
2.关系越差,改善越难,关系越好,恶化越容易
|
||||||
|
3.人维护关系的精力往往有限,所以当高关系值用户越多,对于中高关系值用户增长越慢
|
||||||
|
4.连续正面或负面情感会正反馈
|
||||||
|
"""
|
||||||
|
stancedict = {
|
||||||
|
"支持": 0,
|
||||||
|
"中立": 1,
|
||||||
|
"反对": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
valuedict = {
|
||||||
|
"开心": 1.5,
|
||||||
|
"愤怒": -2.0,
|
||||||
|
"悲伤": -0.5,
|
||||||
|
"惊讶": 0.6,
|
||||||
|
"害羞": 2.0,
|
||||||
|
"平静": 0.3,
|
||||||
|
"恐惧": -1.5,
|
||||||
|
"厌恶": -1.0,
|
||||||
|
"困惑": 0.5,
|
||||||
|
}
|
||||||
|
|
||||||
|
person_id = person_info_manager.get_person_id(chat_stream.user_info.platform, chat_stream.user_info.user_id)
|
||||||
|
data = {
|
||||||
|
"platform" : chat_stream.user_info.platform,
|
||||||
|
"user_id" : chat_stream.user_info.user_id,
|
||||||
|
"nickname" : chat_stream.user_info.user_nickname,
|
||||||
|
"konw_time" : int(time.time())
|
||||||
|
}
|
||||||
|
old_value = await person_info_manager.get_value(person_id, "relationship_value")
|
||||||
|
old_value = self.ensure_float(old_value, person_id)
|
||||||
|
|
||||||
|
if old_value > 1000:
|
||||||
|
old_value = 1000
|
||||||
|
elif old_value < -1000:
|
||||||
|
old_value = -1000
|
||||||
|
|
||||||
|
value = valuedict[label]
|
||||||
|
if old_value >= 0:
|
||||||
|
if valuedict[label] >= 0 and stancedict[stance] != 2:
|
||||||
|
value = value * math.cos(math.pi * old_value / 2000)
|
||||||
|
if old_value > 500:
|
||||||
|
rdict = await person_info_manager.get_specific_value_list("relationship_value", lambda x: x > 700)
|
||||||
|
high_value_count = len(rdict)
|
||||||
|
if old_value > 700:
|
||||||
|
value *= 3 / (high_value_count + 2) # 排除自己
|
||||||
|
else:
|
||||||
|
value *= 3 / (high_value_count + 3)
|
||||||
|
elif valuedict[label] < 0 and stancedict[stance] != 0:
|
||||||
|
value = value * math.exp(old_value / 2000)
|
||||||
|
else:
|
||||||
|
value = 0
|
||||||
|
elif old_value < 0:
|
||||||
|
if valuedict[label] >= 0 and stancedict[stance] != 2:
|
||||||
|
value = value * math.exp(old_value / 2000)
|
||||||
|
elif valuedict[label] < 0 and stancedict[stance] != 0:
|
||||||
|
value = value * math.cos(math.pi * old_value / 2000)
|
||||||
|
else:
|
||||||
|
value = 0
|
||||||
|
|
||||||
|
self.positive_feedback_sys(label, stance)
|
||||||
|
value = self.mood_feedback(value)
|
||||||
|
|
||||||
|
level_num = self.calculate_level_num(old_value + value)
|
||||||
|
relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"]
|
||||||
|
logger.info(
|
||||||
|
f"当前关系: {relationship_level[level_num]}, "
|
||||||
|
f"关系值: {old_value:.2f}, "
|
||||||
|
f"当前立场情感: {stance}-{label}, "
|
||||||
|
f"变更: {value:+.5f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
await person_info_manager.update_one_field(person_id, "relationship_value", old_value + value, data)
|
||||||
|
|
||||||
|
async def build_relationship_info(self, person) -> str:
|
||||||
|
person_id = person_info_manager.get_person_id(person[0], person[1])
|
||||||
|
relationship_value = await person_info_manager.get_value(person_id, "relationship_value")
|
||||||
|
level_num = self.calculate_level_num(relationship_value)
|
||||||
|
relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"]
|
||||||
|
relation_prompt2_list = [
|
||||||
|
"厌恶回应",
|
||||||
|
"冷淡回复",
|
||||||
|
"保持理性",
|
||||||
|
"愿意回复",
|
||||||
|
"积极回复",
|
||||||
|
"无条件支持",
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
f"你对昵称为'({person[1]}){person[2]}'的用户的态度为{relationship_level[level_num]},"
|
||||||
|
f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。"
|
||||||
|
)
|
||||||
|
|
||||||
|
def calculate_level_num(self, relationship_value) -> int:
|
||||||
|
"""关系等级计算"""
|
||||||
|
if -1000 <= relationship_value < -227:
|
||||||
|
level_num = 0
|
||||||
|
elif -227 <= relationship_value < -73:
|
||||||
|
level_num = 1
|
||||||
|
elif -73 <= relationship_value < 227:
|
||||||
|
level_num = 2
|
||||||
|
elif 227 <= relationship_value < 587:
|
||||||
|
level_num = 3
|
||||||
|
elif 587 <= relationship_value < 900:
|
||||||
|
level_num = 4
|
||||||
|
elif 900 <= relationship_value <= 1000:
|
||||||
|
level_num = 5
|
||||||
|
else:
|
||||||
|
level_num = 5 if relationship_value > 1000 else 0
|
||||||
|
return level_num
|
||||||
|
|
||||||
|
def ensure_float(self, value, person_id):
|
||||||
|
"""确保返回浮点数,转换失败返回0.0"""
|
||||||
|
if isinstance(value, float):
|
||||||
|
return value
|
||||||
|
try:
|
||||||
|
return float(value.to_decimal() if isinstance(value, Decimal128) else value)
|
||||||
|
except (ValueError, TypeError, AttributeError):
|
||||||
|
logger.warning(f"[关系管理] {person_id}值转换失败(原始值:{value}),已重置为0")
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
relationship_manager = RelationshipManager()
|
||||||
@@ -1,383 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
from typing import Optional
|
|
||||||
from src.common.logger import get_module_logger, LogConfig, RELATION_STYLE_CONFIG
|
|
||||||
|
|
||||||
from ...common.database import db
|
|
||||||
from ..message.message_base import UserInfo
|
|
||||||
from ..chat.chat_stream import ChatStream
|
|
||||||
import math
|
|
||||||
from bson.decimal128 import Decimal128
|
|
||||||
|
|
||||||
relationship_config = LogConfig(
|
|
||||||
# 使用关系专用样式
|
|
||||||
console_format=RELATION_STYLE_CONFIG["console_format"],
|
|
||||||
file_format=RELATION_STYLE_CONFIG["file_format"],
|
|
||||||
)
|
|
||||||
logger = get_module_logger("rel_manager", config=relationship_config)
|
|
||||||
|
|
||||||
|
|
||||||
class Impression:
|
|
||||||
traits: str = None
|
|
||||||
called: str = None
|
|
||||||
know_time: float = None
|
|
||||||
|
|
||||||
relationship_value: float = None
|
|
||||||
|
|
||||||
|
|
||||||
class Relationship:
|
|
||||||
user_id: int = None
|
|
||||||
platform: str = None
|
|
||||||
gender: str = None
|
|
||||||
age: int = None
|
|
||||||
nickname: str = None
|
|
||||||
relationship_value: float = None
|
|
||||||
saved = False
|
|
||||||
|
|
||||||
def __init__(self, chat: ChatStream = None, data: dict = None):
|
|
||||||
self.user_id = chat.user_info.user_id if chat else data.get("user_id", 0)
|
|
||||||
self.platform = chat.platform if chat else data.get("platform", "")
|
|
||||||
self.nickname = chat.user_info.user_nickname if chat else data.get("nickname", "")
|
|
||||||
self.relationship_value = data.get("relationship_value", 0) if data else 0
|
|
||||||
self.age = data.get("age", 0) if data else 0
|
|
||||||
self.gender = data.get("gender", "") if data else ""
|
|
||||||
|
|
||||||
|
|
||||||
class RelationshipManager:
|
|
||||||
def __init__(self):
|
|
||||||
self.relationships: dict[tuple[int, str], Relationship] = {} # 修改为使用(user_id, platform)作为键
|
|
||||||
|
|
||||||
async def update_relationship(self, chat_stream: ChatStream, data: dict = None, **kwargs) -> Optional[Relationship]:
|
|
||||||
"""更新或创建关系
|
|
||||||
Args:
|
|
||||||
chat_stream: 聊天流对象
|
|
||||||
data: 字典格式的数据(可选)
|
|
||||||
**kwargs: 其他参数
|
|
||||||
Returns:
|
|
||||||
Relationship: 关系对象
|
|
||||||
"""
|
|
||||||
# 确定user_id和platform
|
|
||||||
if chat_stream.user_info is not None:
|
|
||||||
user_id = chat_stream.user_info.user_id
|
|
||||||
platform = chat_stream.user_info.platform or "qq"
|
|
||||||
else:
|
|
||||||
platform = platform or "qq"
|
|
||||||
|
|
||||||
if user_id is None:
|
|
||||||
raise ValueError("必须提供user_id或user_info")
|
|
||||||
|
|
||||||
# 使用(user_id, platform)作为键
|
|
||||||
key = (user_id, platform)
|
|
||||||
|
|
||||||
# 检查是否在内存中已存在
|
|
||||||
relationship = self.relationships.get(key)
|
|
||||||
if relationship:
|
|
||||||
# 如果存在,更新现有对象
|
|
||||||
if isinstance(data, dict):
|
|
||||||
for k, value in data.items():
|
|
||||||
if hasattr(relationship, k) and value is not None:
|
|
||||||
setattr(relationship, k, value)
|
|
||||||
else:
|
|
||||||
# 如果不存在,创建新对象
|
|
||||||
if chat_stream.user_info is not None:
|
|
||||||
relationship = Relationship(chat=chat_stream, **kwargs)
|
|
||||||
else:
|
|
||||||
raise ValueError("必须提供user_id或user_info")
|
|
||||||
self.relationships[key] = relationship
|
|
||||||
|
|
||||||
# 保存到数据库
|
|
||||||
await self.storage_relationship(relationship)
|
|
||||||
relationship.saved = True
|
|
||||||
|
|
||||||
return relationship
|
|
||||||
|
|
||||||
async def update_relationship_value(self, chat_stream: ChatStream, **kwargs) -> Optional[Relationship]:
|
|
||||||
"""更新关系值
|
|
||||||
Args:
|
|
||||||
user_id: 用户ID(可选,如果提供user_info则不需要)
|
|
||||||
platform: 平台(可选,如果提供user_info则不需要)
|
|
||||||
user_info: 用户信息对象(可选)
|
|
||||||
**kwargs: 其他参数
|
|
||||||
Returns:
|
|
||||||
Relationship: 关系对象
|
|
||||||
"""
|
|
||||||
# 确定user_id和platform
|
|
||||||
user_info = chat_stream.user_info
|
|
||||||
if user_info is not None:
|
|
||||||
user_id = user_info.user_id
|
|
||||||
platform = user_info.platform or "qq"
|
|
||||||
else:
|
|
||||||
platform = platform or "qq"
|
|
||||||
|
|
||||||
if user_id is None:
|
|
||||||
raise ValueError("必须提供user_id或user_info")
|
|
||||||
|
|
||||||
# 使用(user_id, platform)作为键
|
|
||||||
key = (user_id, platform)
|
|
||||||
|
|
||||||
# 检查是否在内存中已存在
|
|
||||||
relationship = self.relationships.get(key)
|
|
||||||
if relationship:
|
|
||||||
for k, value in kwargs.items():
|
|
||||||
if k == "relationship_value":
|
|
||||||
# 检查relationship.relationship_value是否为double类型
|
|
||||||
if not isinstance(relationship.relationship_value, float):
|
|
||||||
try:
|
|
||||||
# 处理 Decimal128 类型
|
|
||||||
if isinstance(relationship.relationship_value, Decimal128):
|
|
||||||
relationship.relationship_value = float(relationship.relationship_value.to_decimal())
|
|
||||||
else:
|
|
||||||
relationship.relationship_value = float(relationship.relationship_value)
|
|
||||||
logger.info(
|
|
||||||
f"[关系管理] 用户 {user_id}({platform}) 的关系值已转换为double类型: {relationship.relationship_value}"
|
|
||||||
) # noqa: E501
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
# 如果不能解析/强转则将relationship.relationship_value设置为double类型的0
|
|
||||||
relationship.relationship_value = 0.0
|
|
||||||
logger.warning(f"[关系管理] 用户 {user_id}({platform}) 的无法转换为double类型,已设置为0")
|
|
||||||
relationship.relationship_value += value
|
|
||||||
await self.storage_relationship(relationship)
|
|
||||||
relationship.saved = True
|
|
||||||
return relationship
|
|
||||||
else:
|
|
||||||
# 如果不存在且提供了user_info,则创建新的关系
|
|
||||||
if user_info is not None:
|
|
||||||
return await self.update_relationship(chat_stream=chat_stream, **kwargs)
|
|
||||||
logger.warning(f"[关系管理] 用户 {user_id}({platform}) 不存在,无法更新")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_relationship(self, chat_stream: ChatStream) -> Optional[Relationship]:
|
|
||||||
"""获取用户关系对象
|
|
||||||
Args:
|
|
||||||
user_id: 用户ID(可选,如果提供user_info则不需要)
|
|
||||||
platform: 平台(可选,如果提供user_info则不需要)
|
|
||||||
user_info: 用户信息对象(可选)
|
|
||||||
Returns:
|
|
||||||
Relationship: 关系对象
|
|
||||||
"""
|
|
||||||
# 确定user_id和platform
|
|
||||||
user_info = chat_stream.user_info
|
|
||||||
platform = chat_stream.user_info.platform or "qq"
|
|
||||||
if user_info is not None:
|
|
||||||
user_id = user_info.user_id
|
|
||||||
platform = user_info.platform or "qq"
|
|
||||||
else:
|
|
||||||
platform = platform or "qq"
|
|
||||||
|
|
||||||
if user_id is None:
|
|
||||||
raise ValueError("必须提供user_id或user_info")
|
|
||||||
|
|
||||||
key = (user_id, platform)
|
|
||||||
if key in self.relationships:
|
|
||||||
return self.relationships[key]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def load_relationship(self, data: dict) -> Relationship:
|
|
||||||
"""从数据库加载或创建新的关系对象"""
|
|
||||||
# 确保data中有platform字段,如果没有则默认为'qq'
|
|
||||||
if "platform" not in data:
|
|
||||||
data["platform"] = "qq"
|
|
||||||
|
|
||||||
rela = Relationship(data=data)
|
|
||||||
rela.saved = True
|
|
||||||
key = (rela.user_id, rela.platform)
|
|
||||||
self.relationships[key] = rela
|
|
||||||
return rela
|
|
||||||
|
|
||||||
async def load_all_relationships(self):
|
|
||||||
"""加载所有关系对象"""
|
|
||||||
all_relationships = db.relationships.find({})
|
|
||||||
for data in all_relationships:
|
|
||||||
await self.load_relationship(data)
|
|
||||||
|
|
||||||
async def _start_relationship_manager(self):
|
|
||||||
"""每5分钟自动保存一次关系数据"""
|
|
||||||
# 获取所有关系记录
|
|
||||||
all_relationships = db.relationships.find({})
|
|
||||||
# 依次加载每条记录
|
|
||||||
for data in all_relationships:
|
|
||||||
await self.load_relationship(data)
|
|
||||||
logger.debug(f"[关系管理] 已加载 {len(self.relationships)} 条关系记录")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
logger.debug("正在自动保存关系")
|
|
||||||
await asyncio.sleep(300) # 等待300秒(5分钟)
|
|
||||||
await self._save_all_relationships()
|
|
||||||
|
|
||||||
async def _save_all_relationships(self):
|
|
||||||
"""将所有关系数据保存到数据库"""
|
|
||||||
# 保存所有关系数据
|
|
||||||
for _, relationship in self.relationships.items():
|
|
||||||
if not relationship.saved:
|
|
||||||
relationship.saved = True
|
|
||||||
await self.storage_relationship(relationship)
|
|
||||||
|
|
||||||
async def storage_relationship(self, relationship: Relationship):
|
|
||||||
"""将关系记录存储到数据库中"""
|
|
||||||
user_id = relationship.user_id
|
|
||||||
platform = relationship.platform
|
|
||||||
nickname = relationship.nickname
|
|
||||||
relationship_value = relationship.relationship_value
|
|
||||||
gender = relationship.gender
|
|
||||||
age = relationship.age
|
|
||||||
saved = relationship.saved
|
|
||||||
|
|
||||||
db.relationships.update_one(
|
|
||||||
{"user_id": user_id, "platform": platform},
|
|
||||||
{
|
|
||||||
"$set": {
|
|
||||||
"platform": platform,
|
|
||||||
"nickname": nickname,
|
|
||||||
"relationship_value": relationship_value,
|
|
||||||
"gender": gender,
|
|
||||||
"age": age,
|
|
||||||
"saved": saved,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
upsert=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_name(self, user_id: int = None, platform: str = None, user_info: UserInfo = None) -> str:
|
|
||||||
"""获取用户昵称
|
|
||||||
Args:
|
|
||||||
user_id: 用户ID(可选,如果提供user_info则不需要)
|
|
||||||
platform: 平台(可选,如果提供user_info则不需要)
|
|
||||||
user_info: 用户信息对象(可选)
|
|
||||||
Returns:
|
|
||||||
str: 用户昵称
|
|
||||||
"""
|
|
||||||
# 确定user_id和platform
|
|
||||||
if user_info is not None:
|
|
||||||
user_id = user_info.user_id
|
|
||||||
platform = user_info.platform or "qq"
|
|
||||||
else:
|
|
||||||
platform = platform or "qq"
|
|
||||||
|
|
||||||
if user_id is None:
|
|
||||||
raise ValueError("必须提供user_id或user_info")
|
|
||||||
|
|
||||||
# 确保user_id是整数类型
|
|
||||||
user_id = int(user_id)
|
|
||||||
key = (user_id, platform)
|
|
||||||
if key in self.relationships:
|
|
||||||
return self.relationships[key].nickname
|
|
||||||
elif user_info is not None:
|
|
||||||
return user_info.user_nickname or user_info.user_cardname or "某人"
|
|
||||||
else:
|
|
||||||
return "某人"
|
|
||||||
|
|
||||||
async def calculate_update_relationship_value(self, chat_stream: ChatStream, label: str, stance: str) -> None:
|
|
||||||
"""计算变更关系值
|
|
||||||
新的关系值变更计算方式:
|
|
||||||
将关系值限定在-1000到1000
|
|
||||||
对于关系值的变更,期望:
|
|
||||||
1.向两端逼近时会逐渐减缓
|
|
||||||
2.关系越差,改善越难,关系越好,恶化越容易
|
|
||||||
3.人维护关系的精力往往有限,所以当高关系值用户越多,对于中高关系值用户增长越慢
|
|
||||||
"""
|
|
||||||
stancedict = {
|
|
||||||
"支持": 0,
|
|
||||||
"中立": 1,
|
|
||||||
"反对": 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
valuedict = {
|
|
||||||
"开心": 1.5,
|
|
||||||
"愤怒": -3.5,
|
|
||||||
"悲伤": -1.5,
|
|
||||||
"惊讶": 0.6,
|
|
||||||
"害羞": 2.0,
|
|
||||||
"平静": 0.3,
|
|
||||||
"恐惧": -2,
|
|
||||||
"厌恶": -2.5,
|
|
||||||
"困惑": 0.5,
|
|
||||||
}
|
|
||||||
if self.get_relationship(chat_stream):
|
|
||||||
old_value = self.get_relationship(chat_stream).relationship_value
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
if old_value > 1000:
|
|
||||||
old_value = 1000
|
|
||||||
elif old_value < -1000:
|
|
||||||
old_value = -1000
|
|
||||||
|
|
||||||
value = valuedict[label]
|
|
||||||
if old_value >= 0:
|
|
||||||
if valuedict[label] >= 0 and stancedict[stance] != 2:
|
|
||||||
value = value * math.cos(math.pi * old_value / 2000)
|
|
||||||
if old_value > 500:
|
|
||||||
high_value_count = 0
|
|
||||||
for _, relationship in self.relationships.items():
|
|
||||||
if relationship.relationship_value >= 700:
|
|
||||||
high_value_count += 1
|
|
||||||
if old_value >= 700:
|
|
||||||
value *= 3 / (high_value_count + 2) # 排除自己
|
|
||||||
else:
|
|
||||||
value *= 3 / (high_value_count + 3)
|
|
||||||
elif valuedict[label] < 0 and stancedict[stance] != 0:
|
|
||||||
value = value * math.exp(old_value / 1000)
|
|
||||||
else:
|
|
||||||
value = 0
|
|
||||||
elif old_value < 0:
|
|
||||||
if valuedict[label] >= 0 and stancedict[stance] != 2:
|
|
||||||
value = value * math.exp(old_value / 1000)
|
|
||||||
elif valuedict[label] < 0 and stancedict[stance] != 0:
|
|
||||||
value = value * math.cos(math.pi * old_value / 2000)
|
|
||||||
else:
|
|
||||||
value = 0
|
|
||||||
|
|
||||||
level_num = self.calculate_level_num(old_value + value)
|
|
||||||
relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"]
|
|
||||||
logger.info(
|
|
||||||
f"当前关系: {relationship_level[level_num]}, "
|
|
||||||
f"关系值: {old_value:.2f}, "
|
|
||||||
f"当前立场情感: {stance}-{label}, "
|
|
||||||
f"变更: {value:+.5f}"
|
|
||||||
)
|
|
||||||
|
|
||||||
await self.update_relationship_value(chat_stream=chat_stream, relationship_value=value)
|
|
||||||
|
|
||||||
def build_relationship_info(self, person) -> str:
|
|
||||||
relationship_value = relationship_manager.get_relationship(person).relationship_value
|
|
||||||
level_num = self.calculate_level_num(relationship_value)
|
|
||||||
relationship_level = ["厌恶", "冷漠", "一般", "友好", "喜欢", "暧昧"]
|
|
||||||
relation_prompt2_list = [
|
|
||||||
"冷漠回应",
|
|
||||||
"冷淡回复",
|
|
||||||
"保持理性",
|
|
||||||
"愿意回复",
|
|
||||||
"积极回复",
|
|
||||||
"无条件支持",
|
|
||||||
]
|
|
||||||
if person.user_info.user_cardname:
|
|
||||||
return (
|
|
||||||
f"你对昵称为'[({person.user_info.user_id}){person.user_info.user_nickname}]{person.user_info.user_cardname}'的用户的态度为{relationship_level[level_num]},"
|
|
||||||
f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return (
|
|
||||||
f"你对昵称为'({person.user_info.user_id}){person.user_info.user_nickname}'的用户的态度为{relationship_level[level_num]},"
|
|
||||||
f"回复态度为{relation_prompt2_list[level_num]},关系等级为{level_num}。"
|
|
||||||
)
|
|
||||||
|
|
||||||
def calculate_level_num(self, relationship_value) -> int:
|
|
||||||
"""关系等级计算"""
|
|
||||||
if -1000 <= relationship_value < -227:
|
|
||||||
level_num = 0
|
|
||||||
elif -227 <= relationship_value < -73:
|
|
||||||
level_num = 1
|
|
||||||
elif -73 <= relationship_value < 227:
|
|
||||||
level_num = 2
|
|
||||||
elif 227 <= relationship_value < 587:
|
|
||||||
level_num = 3
|
|
||||||
elif 587 <= relationship_value < 900:
|
|
||||||
level_num = 4
|
|
||||||
elif 900 <= relationship_value <= 1000:
|
|
||||||
level_num = 5
|
|
||||||
else:
|
|
||||||
level_num = 5 if relationship_value > 1000 else 0
|
|
||||||
return level_num
|
|
||||||
|
|
||||||
|
|
||||||
relationship_manager = RelationshipManager()
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
[inner]
|
[inner]
|
||||||
version = "1.1.0"
|
version = "1.1.1"
|
||||||
|
|
||||||
|
|
||||||
#以下是给开发人员阅读的,一般用户不需要阅读
|
#以下是给开发人员阅读的,一般用户不需要阅读
|
||||||
@@ -94,8 +94,10 @@ emoji_response_penalty = 0.1 # 表情包回复惩罚系数,设为0为不回复
|
|||||||
|
|
||||||
|
|
||||||
[emoji]
|
[emoji]
|
||||||
check_interval = 15 # 检查破损表情包的时间间隔(分钟)
|
max_emoji_num = 120 # 表情包最大数量
|
||||||
register_interval = 60 # 注册表情包的时间间隔(分钟)
|
max_reach_deletion = true # 开启则在达到最大数量时删除表情包,关闭则不会继续收集表情包
|
||||||
|
check_interval = 30 # 检查破损表情包的时间间隔(分钟)
|
||||||
|
register_interval = 30 # 注册表情包的时间间隔(分钟)
|
||||||
auto_save = true # 是否保存表情包和图片
|
auto_save = true # 是否保存表情包和图片
|
||||||
enable_check = false # 是否启用表情包过滤
|
enable_check = false # 是否启用表情包过滤
|
||||||
check_prompt = "符合公序良俗" # 表情包过滤要求
|
check_prompt = "符合公序良俗" # 表情包过滤要求
|
||||||
|
|||||||
Reference in New Issue
Block a user