Merge branch 'SengokuCola:debug' into debug

pull/112/head
enKl03B 2025-03-08 22:46:56 +08:00 committed by GitHub
commit d941475bec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 362 additions and 273 deletions

1
.gitignore vendored
View File

@ -21,6 +21,7 @@ __pycache__/
llm_statistics.txt llm_statistics.txt
mongodb mongodb
napcat napcat
run_dev.bat
# C extensions # C extensions
*.so *.so

View File

@ -36,7 +36,8 @@
> - 由于持续迭代可能存在一些已知或未知的bug > - 由于持续迭代可能存在一些已知或未知的bug
> - 由于开发中可能消耗较多token > - 由于开发中可能消耗较多token
**交流群**: 766798517仅用于开发和建议相关讨论不一定有空回复但大家可以自行交流部署问题我会优先写文档和代码 **交流群**: 766798517 一群人较多,建议加下面的(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
**交流群**: 571780722 另一个群(开发和建议相关讨论)不一定有空回复,会优先写文档和代码
## ##
<div align="left"> <div align="left">
@ -148,4 +149,4 @@ SengokuCola纯编程外行面向cursor编程很多代码史一样多多包
## Stargazers over time ## Stargazers over time
[![Stargazers over time](https://starchart.cc/SengokuCola/MaiMBot.svg?variant=adaptive)](https://starchart.cc/SengokuCola/MaiMBot) [![Stargazers over time](https://starchart.cc/SengokuCola/MaiMBot.svg?variant=adaptive)](https://starchart.cc/SengokuCola/MaiMBot)

5
bot.py
View File

@ -1,11 +1,12 @@
import os import os
import nonebot import nonebot
from nonebot.adapters.onebot.v11 import Adapter
from dotenv import load_dotenv from dotenv import load_dotenv
from loguru import logger from loguru import logger
from nonebot.adapters.onebot.v11 import Adapter
'''彩蛋''' '''彩蛋'''
from colorama import init, Fore from colorama import Fore, init
init() init()
text = "多年以后面对AI行刑队张三将会回想起他2023年在会议上讨论人工智能的那个下午" text = "多年以后面对AI行刑队张三将会回想起他2023年在会议上讨论人工智能的那个下午"

View File

@ -5,4 +5,19 @@ description = "New Bot Project"
[tool.nonebot] [tool.nonebot]
plugins = ["src.plugins.chat"] plugins = ["src.plugins.chat"]
plugin_dirs = ["src/plugins"] plugin_dirs = ["src/plugins"]
[tool.ruff]
# 设置 Python 版本
target-version = "py39"
# 启用的规则
select = [
"E", # pycodestyle 错误
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
]
# 行长度设置
line-length = 88

4
run.py
View File

@ -1,7 +1,7 @@
import subprocess
import os import os
import shutil import subprocess
import zipfile import zipfile
import requests import requests
from tqdm import tqdm from tqdm import tqdm

View File

@ -1,4 +1,4 @@
from setuptools import setup, find_packages from setuptools import find_packages, setup
setup( setup(
name="maimai-bot", name="maimai-bot",

View File

@ -1,6 +1,8 @@
from pymongo import MongoClient
from typing import Optional from typing import Optional
from pymongo import MongoClient
class Database: class Database:
_instance: Optional["Database"] = None _instance: Optional["Database"] = None

View File

@ -1,12 +1,12 @@
import customtkinter as ctk import os
from typing import Dict, List
import json
from datetime import datetime
import time
import threading
import queue import queue
import sys import sys
import os import threading
import time
from datetime import datetime
from typing import Dict, List
import customtkinter as ctk
from dotenv import load_dotenv from dotenv import load_dotenv
# 获取当前文件的目录 # 获取当前文件的目录
@ -25,9 +25,11 @@ else:
print("未找到环境配置文件") print("未找到环境配置文件")
sys.exit(1) sys.exit(1)
from pymongo import MongoClient
from typing import Optional from typing import Optional
from pymongo import MongoClient
class Database: class Database:
_instance: Optional["Database"] = None _instance: Optional["Database"] = None

View File

@ -1,6 +1,5 @@
from typing import Dict, List, Union, Optional, Any
import base64 import base64
import os from typing import Any, Dict, List, Union
""" """
OneBot v11 Message Segment Builder OneBot v11 Message Segment Builder

View File

@ -1,21 +1,23 @@
from loguru import logger
from nonebot import on_message, on_command, require, get_driver
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment
from nonebot.typing import T_State
from ...common.database import Database
from .config import global_config
import os
import asyncio import asyncio
import os
import random import random
from .relationship_manager import relationship_manager
from ..schedule.schedule_generator import bot_schedule
from .willing_manager import willing_manager
from nonebot.rule import to_me
from .bot import chat_bot
from .emoji_manager import emoji_manager
from ..moods.moods import MoodManager # 导入情绪管理器
import time import time
from loguru import logger
from nonebot import get_driver, on_command, on_message, require
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, Message, MessageSegment
from nonebot.rule import to_me
from nonebot.typing import T_State
from ...common.database import Database
from ..moods.moods import MoodManager # 导入情绪管理器
from ..schedule.schedule_generator import bot_schedule
from ..utils.statistic import LLMStatistics from ..utils.statistic import LLMStatistics
from .bot import chat_bot
from .config import global_config
from .emoji_manager import emoji_manager
from .relationship_manager import relationship_manager
from .willing_manager import willing_manager
# 创建LLM统计实例 # 创建LLM统计实例
llm_stats = LLMStatistics("llm_statistics.txt") llm_stats = LLMStatistics("llm_statistics.txt")
@ -39,12 +41,11 @@ print("\033[1;32m[初始化数据库完成]\033[0m")
# 导入其他模块 # 导入其他模块
from ..memory_system.memory import hippocampus, memory_graph
from .bot import ChatBot from .bot import ChatBot
from .emoji_manager import emoji_manager
# from .message_send_control import message_sender # from .message_send_control import message_sender
from .relationship_manager import relationship_manager from .message_sender import message_manager, message_sender
from .message_sender import message_manager,message_sender
from ..memory_system.memory import memory_graph,hippocampus
# 初始化表情管理器 # 初始化表情管理器
emoji_manager.initialize() emoji_manager.initialize()

View File

@ -1,23 +1,27 @@
from nonebot.adapters.onebot.v11 import GroupMessageEvent, Message as EventMessage, Bot
from .message import Message, MessageSet, Message_Sending
from .config import BotConfig, global_config
from .storage import MessageStorage
from .llm_generator import ResponseGenerator
# from .message_stream import MessageStream, MessageStreamContainer
from .topic_identifier import topic_identifier
from random import random, choice
from .emoji_manager import emoji_manager # 导入表情包管理器
from ..moods.moods import MoodManager # 导入情绪管理器
import time import time
import os from random import random
from .cq_code import CQCode # 导入CQCode模块
from .message_sender import message_manager # 导入新的消息管理器
from .message import Message_Thinking # 导入 Message_Thinking 类
from .relationship_manager import relationship_manager
from .willing_manager import willing_manager # 导入意愿管理器
from .utils import is_mentioned_bot_in_txt, calculate_typing_time
from ..memory_system.memory import memory_graph,hippocampus
from loguru import logger from loguru import logger
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent
from ..memory_system.memory import hippocampus
from ..moods.moods import MoodManager # 导入情绪管理器
from .config import global_config
from .cq_code import CQCode # 导入CQCode模块
from .emoji_manager import emoji_manager # 导入表情包管理器
from .llm_generator import ResponseGenerator
from .message import (
Message,
Message_Sending,
Message_Thinking, # 导入 Message_Thinking 类
MessageSet,
)
from .message_sender import message_manager # 导入新的消息管理器
from .relationship_manager import relationship_manager
from .storage import MessageStorage
from .utils import calculate_typing_time, is_mentioned_bot_in_txt
from .willing_manager import willing_manager # 导入意愿管理器
class ChatBot: class ChatBot:
def __init__(self): def __init__(self):
@ -123,6 +127,11 @@ class ChatBot:
container.messages.remove(msg) container.messages.remove(msg)
# print(f"\033[1;32m[思考消息删除]\033[0m 已找到思考消息对象,开始删除") # print(f"\033[1;32m[思考消息删除]\033[0m 已找到思考消息对象,开始删除")
break break
# 如果找不到思考消息,直接返回
if not thinking_message:
print(f"\033[1;33m[警告]\033[0m 未找到对应的思考消息,可能已超时被移除")
return
#记录开始思考的时间,避免从思考到回复的时间太久 #记录开始思考的时间,避免从思考到回复的时间太久
thinking_start_time = thinking_message.thinking_start_time thinking_start_time = thinking_message.thinking_start_time

View File

@ -1,12 +1,9 @@
from dataclasses import dataclass, field
from typing import Dict, Any, Optional, Set
import os import os
import configparser from dataclasses import dataclass, field
import tomli from typing import Dict, Optional
import sys
from loguru import logger
from nonebot import get_driver
import tomli
from loguru import logger
@dataclass @dataclass
@ -24,6 +21,12 @@ class BotConfig:
talk_allowed_groups = set() talk_allowed_groups = set()
talk_frequency_down_groups = set() talk_frequency_down_groups = set()
thinking_timeout: int = 100 # 思考时间
response_willing_amplifier: float = 1.0 # 回复意愿放大系数
response_interested_rate_amplifier: float = 1.0 # 回复兴趣度放大系数
down_frequency_rate: float = 3.5 # 降低回复频率的群组回复意愿降低系数
ban_user_id = set() ban_user_id = set()
build_memory_interval: int = 30 # 记忆构建间隔(秒) build_memory_interval: int = 30 # 记忆构建间隔(秒)
@ -61,6 +64,8 @@ class BotConfig:
mood_decay_rate: float = 0.95 # 情绪衰减率 mood_decay_rate: float = 0.95 # 情绪衰减率
mood_intensity_factor: float = 0.7 # 情绪强度因子 mood_intensity_factor: float = 0.7 # 情绪强度因子
keywords_reaction_rules = [] # 关键词回复规则
# 默认人设 # 默认人设
PROMPT_PERSONALITY=[ PROMPT_PERSONALITY=[
"曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧", "曾经是一个学习地质的女大学生,现在学习心理学和脑科学,你会刷贴吧",
@ -175,6 +180,10 @@ class BotConfig:
config.MAX_CONTEXT_SIZE = msg_config.get("max_context_size", config.MAX_CONTEXT_SIZE) config.MAX_CONTEXT_SIZE = msg_config.get("max_context_size", config.MAX_CONTEXT_SIZE)
config.emoji_chance = msg_config.get("emoji_chance", config.emoji_chance) config.emoji_chance = msg_config.get("emoji_chance", config.emoji_chance)
config.ban_words=msg_config.get("ban_words",config.ban_words) config.ban_words=msg_config.get("ban_words",config.ban_words)
config.thinking_timeout = msg_config.get("thinking_timeout", config.thinking_timeout)
config.response_willing_amplifier = msg_config.get("response_willing_amplifier", config.response_willing_amplifier)
config.response_interested_rate_amplifier = msg_config.get("response_interested_rate_amplifier", config.response_interested_rate_amplifier)
config.down_frequency_rate = msg_config.get("down_frequency_rate", config.down_frequency_rate)
if "memory" in toml_dict: if "memory" in toml_dict:
memory_config = toml_dict["memory"] memory_config = toml_dict["memory"]
@ -187,6 +196,13 @@ class BotConfig:
config.mood_decay_rate = mood_config.get("mood_decay_rate", config.mood_decay_rate) config.mood_decay_rate = mood_config.get("mood_decay_rate", config.mood_decay_rate)
config.mood_intensity_factor = mood_config.get("mood_intensity_factor", config.mood_intensity_factor) config.mood_intensity_factor = mood_config.get("mood_intensity_factor", config.mood_intensity_factor)
# print(toml_dict)
if "keywords_reaction" in toml_dict:
# 读取关键词回复配置
keywords_reaction_config = toml_dict["keywords_reaction"]
if keywords_reaction_config.get("enable", False):
config.keywords_reaction_rules = keywords_reaction_config.get("rules", config.keywords_reaction_rules)
# 群组配置 # 群组配置
if "groups" in toml_dict: if "groups" in toml_dict:
groups_config = toml_dict["groups"] groups_config = toml_dict["groups"]

View File

@ -1,24 +1,23 @@
from dataclasses import dataclass
from typing import Dict, Optional, List, Union
import html
import requests
import base64 import base64
from PIL import Image import html
import os import os
from random import random
from nonebot.adapters.onebot.v11 import Bot
from .config import global_config
import time import time
import asyncio from dataclasses import dataclass
from .utils_image import storage_image, storage_emoji from typing import Dict, Optional
from .utils_user import get_user_nickname
from ..models.utils_model import LLM_request import requests
from .mapper import emojimapper
# 解析各种CQ码 # 解析各种CQ码
# 包含CQ码类 # 包含CQ码类
import urllib3 import urllib3
from urllib3.util import create_urllib3_context
from nonebot import get_driver from nonebot import get_driver
from urllib3.util import create_urllib3_context
from ..models.utils_model import LLM_request
from .config import global_config
from .mapper import emojimapper
from .utils_image import storage_emoji, storage_image
from .utils_user import get_user_nickname
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
@ -81,7 +80,7 @@ class CQCode:
if user_nickname: if user_nickname:
self.translated_plain_text = f"[@{user_nickname}]" self.translated_plain_text = f"[@{user_nickname}]"
else: else:
self.translated_plain_text = f"@某人" self.translated_plain_text = "@某人"
elif self.type == 'reply': elif self.type == 'reply':
self.translated_plain_text = await self.translate_reply() self.translated_plain_text = await self.translate_reply()
elif self.type == 'face': elif self.type == 'face':
@ -308,7 +307,7 @@ class CQCode:
return f"[回复 {self.reply_message.sender.nickname} 的消息: {message_obj.processed_plain_text}]" return f"[回复 {self.reply_message.sender.nickname} 的消息: {message_obj.processed_plain_text}]"
else: else:
print(f"\033[1;31m[错误]\033[0m 回复消息的sender.user_id为空") print("\033[1;31m[错误]\033[0m 回复消息的sender.user_id为空")
return '[回复某人消息]' return '[回复某人消息]'
@staticmethod @staticmethod

View File

@ -1,27 +1,18 @@
from typing import List, Dict, Optional
import random
from ...common.database import Database
import os
import json
from dataclasses import dataclass
import jieba.analyse as jieba_analyse
import aiohttp
import hashlib
from datetime import datetime
import base64
import shutil
import asyncio import asyncio
import os
import random
import time import time
from PIL import Image
import io
from loguru import logger
import traceback import traceback
from typing import Optional
from loguru import logger
from nonebot import get_driver from nonebot import get_driver
from ...common.database import Database
from ..chat.config import global_config from ..chat.config import global_config
from ..models.utils_model import LLM_request
from ..chat.utils_image import image_path_to_base64
from ..chat.utils import get_embedding from ..chat.utils import get_embedding
from ..chat.utils_image import image_path_to_base64
from ..models.utils_model import LLM_request
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
@ -275,7 +266,7 @@ class EmojiManager:
async def _periodic_scan(self, interval_MINS: int = 10): async def _periodic_scan(self, interval_MINS: int = 10):
"""定期扫描新表情包""" """定期扫描新表情包"""
while True: while True:
print(f"\033[1;36m[表情包]\033[0m 开始扫描新表情包...") print("\033[1;36m[表情包]\033[0m 开始扫描新表情包...")
await self.scan_new_emojis() await self.scan_new_emojis()
await asyncio.sleep(interval_MINS * 60) # 每600秒扫描一次 await asyncio.sleep(interval_MINS * 60) # 每600秒扫描一次

View File

@ -1,19 +1,16 @@
from typing import Dict, Any, List, Optional, Union, Tuple
from openai import OpenAI
import asyncio
from functools import partial
from .message import Message
from .config import global_config
from ...common.database import Database
import random import random
import time import time
import numpy as np from typing import List, Optional, Tuple, Union
from .relationship_manager import relationship_manager
from .prompt_builder import prompt_builder
from .config import global_config
from .utils import process_llm_response
from nonebot import get_driver from nonebot import get_driver
from ...common.database import Database
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from .config import global_config
from .message import Message
from .prompt_builder import prompt_builder
from .relationship_manager import relationship_manager
from .utils import process_llm_response
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@ -1,16 +1,12 @@
from dataclasses import dataclass
from typing import List, Optional, Dict, Tuple, ForwardRef
import time import time
import jieba.analyse as jieba_analyse from dataclasses import dataclass
import os from typing import Dict, ForwardRef, List, Optional
from datetime import datetime
from ...common.database import Database
from PIL import Image
from .config import global_config
import urllib3 import urllib3
from .utils_user import get_user_nickname,get_user_cardname,get_groupname
from .cq_code import CQCode, cq_code_tool
from .utils_cq import parse_cq_code from .utils_cq import parse_cq_code
from .cq_code import cq_code_tool,CQCode from .utils_user import get_groupname, get_user_cardname, get_user_nickname
Message = ForwardRef('Message') # 添加这行 Message = ForwardRef('Message') # 添加这行
# 禁用SSL警告 # 禁用SSL警告

View File

@ -1,14 +1,15 @@
from typing import Union, List, Optional, Dict
from collections import deque
from .message import Message, Message_Thinking, MessageSet, Message_Sending
import time
import asyncio import asyncio
import time
from typing import Dict, List, Optional, Union
from nonebot.adapters.onebot.v11 import Bot from nonebot.adapters.onebot.v11 import Bot
from .config import global_config
from .storage import MessageStorage
from .cq_code import cq_code_tool from .cq_code import cq_code_tool
import random from .message import Message, Message_Sending, Message_Thinking, MessageSet
from .storage import MessageStorage
from .utils import calculate_typing_time from .utils import calculate_typing_time
from .config import global_config
class Message_Sender: class Message_Sender:
"""发送器""" """发送器"""
@ -162,6 +163,11 @@ class MessageManager:
message_earliest.update_thinking_time() message_earliest.update_thinking_time()
thinking_time = message_earliest.thinking_time thinking_time = message_earliest.thinking_time
print(f"\033[1;34m[调试]\033[0m 消息正在思考中,已思考{int(thinking_time)}\033[K\r", end='', flush=True) print(f"\033[1;34m[调试]\033[0m 消息正在思考中,已思考{int(thinking_time)}\033[K\r", end='', flush=True)
# 检查是否超时
if thinking_time > global_config.thinking_timeout:
print(f"\033[1;33m[警告]\033[0m 消息思考超时({thinking_time}秒),移除该消息")
container.remove_message(message_earliest)
else:# 如果不是message_thinking就只能是message_sending else:# 如果不是message_thinking就只能是message_sending
print(f"\033[1;34m[调试]\033[0m 消息'{message_earliest.processed_plain_text}'正在发送中") print(f"\033[1;34m[调试]\033[0m 消息'{message_earliest.processed_plain_text}'正在发送中")
#直接发,等什么呢 #直接发,等什么呢
@ -199,7 +205,7 @@ class MessageManager:
# 安全地移除消息 # 安全地移除消息
if not container.remove_message(msg): if not container.remove_message(msg):
print(f"\033[1;33m[警告]\033[0m 尝试删除不存在的消息") print("\033[1;33m[警告]\033[0m 尝试删除不存在的消息")
except Exception as e: except Exception as e:
print(f"\033[1;31m[错误]\033[0m 处理超时消息时发生错误: {e}") print(f"\033[1;31m[错误]\033[0m 处理超时消息时发生错误: {e}")
continue continue

View File

@ -1,17 +1,14 @@
import time
import random import random
from ..schedule.schedule_generator import bot_schedule import time
import os from typing import Optional
from .utils import get_embedding, combine_messages, get_recent_group_detailed_plain_text
from ...common.database import Database from ...common.database import Database
from .config import global_config from ..memory_system.memory import hippocampus, memory_graph
from .topic_identifier import topic_identifier
from ..memory_system.memory import memory_graph,hippocampus
from random import choice
import numpy as np
import jieba
from collections import Counter
from ..moods.moods import MoodManager from ..moods.moods import MoodManager
from ..schedule.schedule_generator import bot_schedule
from .config import global_config
from .utils import get_embedding, get_recent_group_detailed_plain_text
class PromptBuilder: class PromptBuilder:
def __init__(self): def __init__(self):
@ -25,7 +22,7 @@ class PromptBuilder:
message_txt: str, message_txt: str,
sender_name: str = "某人", sender_name: str = "某人",
relationship_value: float = 0.0, relationship_value: float = 0.0,
group_id: int = None) -> str: group_id: Optional[int] = None) -> tuple[str, str]:
"""构建prompt """构建prompt
Args: Args:
@ -101,7 +98,7 @@ class PromptBuilder:
for memory in relevant_memories: for memory in relevant_memories:
memory_items.append(f"关于「{memory['topic']}」的记忆:{memory['content']}") memory_items.append(f"关于「{memory['topic']}」的记忆:{memory['content']}")
memory_prompt = f"看到这些聊天,你想起来:\n" + "\n".join(memory_items) + "\n" memory_prompt = "看到这些聊天,你想起来:\n" + "\n".join(memory_items) + "\n"
# 打印调试信息 # 打印调试信息
print("\n\033[1;32m[记忆检索]\033[0m 找到以下相关记忆:") print("\n\033[1;32m[记忆检索]\033[0m 找到以下相关记忆:")
@ -117,13 +114,22 @@ class PromptBuilder:
activate_prompt = '' activate_prompt = ''
activate_prompt = f"以上是群里正在进行的聊天,{memory_prompt} 现在昵称为 '{sender_name}' 的用户说的:{message_txt}。引起了你的注意,你和他{relation_prompt},{mood_prompt},你想要{relation_prompt_2}" activate_prompt = f"以上是群里正在进行的聊天,{memory_prompt} 现在昵称为 '{sender_name}' 的用户说的:{message_txt}。引起了你的注意,你和他{relation_prompt},{mood_prompt},你想要{relation_prompt_2}"
#检测机器人相关词汇 #检测机器人相关词汇,改为关键词检测与反应功能了,提取到全局配置中
bot_keywords = ['人机', 'bot', '机器', '入机', 'robot', '机器人'] # bot_keywords = ['人机', 'bot', '机器', '入机', 'robot', '机器人']
is_bot = any(keyword in message_txt.lower() for keyword in bot_keywords) # is_bot = any(keyword in message_txt.lower() for keyword in bot_keywords)
if is_bot: # if is_bot:
is_bot_prompt = '有人说你是人机或者机器人,否定这一事实,攻击或反问他。注意,只有认为你是机器人才回复,否则不要否认' # is_bot_prompt = '有人说你是人机或者机器人,否定这一事实,攻击或反问他。注意,只有认为你是机器人才回复,否则不要否认'
else: # else:
is_bot_prompt = '' # is_bot_prompt = ''
# 关键词检测与反应
keywords_reaction_prompt = ''
for rule in global_config.keywords_reaction_rules:
if rule.get("enable", False):
if any(keyword in message_txt.lower() for keyword in rule.get("keywords", [])):
print(f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}")
keywords_reaction_prompt += rule.get("reaction", "") + ''
#人格选择 #人格选择
personality=global_config.PROMPT_PERSONALITY personality=global_config.PROMPT_PERSONALITY
@ -134,15 +140,15 @@ class PromptBuilder:
personality_choice = random.random() personality_choice = random.random()
if personality_choice < probability_1: # 第一种人格 if personality_choice < probability_1: # 第一种人格
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[0]}, 你正在浏览qq群,{promt_info_prompt}, prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[0]}, 你正在浏览qq群,{promt_info_prompt},
现在请你给出日常且口语化的回复平淡一些尽量简短一些{is_bot_prompt} 现在请你给出日常且口语化的回复平淡一些尽量简短一些{keywords_reaction_prompt}
请注意把握群里的聊天内容不要刻意突出自身学科背景不要回复的太有条理可以有个性''' 请注意把握群里的聊天内容不要刻意突出自身学科背景不要回复的太有条理可以有个性'''
elif personality_choice < probability_1 + probability_2: # 第二种人格 elif personality_choice < probability_1 + probability_2: # 第二种人格
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[1]}, 你正在浏览qq群{promt_info_prompt}, prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[1]}, 你正在浏览qq群{promt_info_prompt},
现在请你给出日常且口语化的回复请表现你自己的见解不要一昧迎合尽量简短一些{is_bot_prompt} 现在请你给出日常且口语化的回复请表现你自己的见解不要一昧迎合尽量简短一些{keywords_reaction_prompt}
请你表达自己的见解和观点可以有个性''' 请你表达自己的见解和观点可以有个性'''
else: # 第三种人格 else: # 第三种人格
prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[2]}, 你正在浏览qq群{promt_info_prompt}, prompt_personality = f'''{activate_prompt}你的网名叫{global_config.BOT_NICKNAME}{personality[2]}, 你正在浏览qq群{promt_info_prompt},
现在请你给出日常且口语化的回复请表现你自己的见解不要一昧迎合尽量简短一些{is_bot_prompt} 现在请你给出日常且口语化的回复请表现你自己的见解不要一昧迎合尽量简短一些{keywords_reaction_prompt}
请你表达自己的见解和观点可以有个性''' 请你表达自己的见解和观点可以有个性'''
#中文高手(新加的好玩功能) #中文高手(新加的好玩功能)
@ -203,7 +209,7 @@ class PromptBuilder:
#激活prompt构建 #激活prompt构建
activate_prompt = '' activate_prompt = ''
activate_prompt = f"以上是群里正在进行的聊天。" activate_prompt = "以上是群里正在进行的聊天。"
personality=global_config.PROMPT_PERSONALITY personality=global_config.PROMPT_PERSONALITY
prompt_personality = '' prompt_personality = ''
personality_choice = random.random() personality_choice = random.random()

View File

@ -1,8 +1,8 @@
import time
from ...common.database import Database
from nonebot.adapters.onebot.v11 import Bot
from typing import Optional, Tuple
import asyncio import asyncio
from typing import Optional
from ...common.database import Database
class Impression: class Impression:
traits: str = None traits: str = None
@ -123,7 +123,7 @@ class RelationshipManager:
print(f"\033[1;32m[关系管理]\033[0m 已加载 {len(self.relationships)} 条关系记录") print(f"\033[1;32m[关系管理]\033[0m 已加载 {len(self.relationships)} 条关系记录")
while True: while True:
print(f"\033[1;32m[关系管理]\033[0m 正在自动保存关系") print("\033[1;32m[关系管理]\033[0m 正在自动保存关系")
await asyncio.sleep(300) # 等待300秒(5分钟) await asyncio.sleep(300) # 等待300秒(5分钟)
await self._save_all_relationships() await self._save_all_relationships()

View File

@ -1,10 +1,8 @@
from typing import Dict, List, Any, Optional from typing import Optional
import time
import threading
from collections import defaultdict
import asyncio
from .message import Message
from ...common.database import Database from ...common.database import Database
from .message import Message
class MessageStorage: class MessageStorage:
def __init__(self): def __init__(self):

View File

@ -1,10 +1,9 @@
from typing import Optional, Dict, List from typing import List, Optional
from openai import OpenAI
from .message import Message
import jieba
from nonebot import get_driver from nonebot import get_driver
from .config import global_config
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
from .config import global_config
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
@ -26,7 +25,7 @@ class TopicIdentifier:
topic, _ = await self.llm_topic_judge.generate_response(prompt) topic, _ = await self.llm_topic_judge.generate_response(prompt)
if not topic: if not topic:
print(f"\033[1;31m[错误]\033[0m LLM API 返回为空") print("\033[1;31m[错误]\033[0m LLM API 返回为空")
return None return None
# 直接在这里处理主题解析 # 直接在这里处理主题解析

View File

@ -1,19 +1,17 @@
import time
import random
from typing import List
from .message import Message
import requests
import numpy as np
from .config import global_config
import re
from typing import Dict
from collections import Counter
import math import math
from nonebot import get_driver import random
from ..models.utils_model import LLM_request import time
import aiohttp from collections import Counter
from typing import Dict, List
import jieba import jieba
import numpy as np
from nonebot import get_driver
from ..models.utils_model import LLM_request
from ..utils.typo_generator import ChineseTypoGenerator from ..utils.typo_generator import ChineseTypoGenerator
from .config import global_config
from .message import Message
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@ -1,14 +1,15 @@
import base64
import io import io
from PIL import Image
import hashlib
import time
import os import os
import time
import zlib # 用于 CRC32
from loguru import logger
from nonebot import get_driver
from PIL import Image
from ...common.database import Database from ...common.database import Database
from ..chat.config import global_config from ..chat.config import global_config
import zlib # 用于 CRC32
import base64
from nonebot import get_driver
from loguru import logger
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
@ -119,7 +120,7 @@ def storage_compress_image(base64_data: str, max_size: int = 200) -> str:
# 保存记录 # 保存记录
collection.insert_one(image_record) collection.insert_one(image_record)
print(f"\033[1;32m[成功]\033[0m 保存图片记录到数据库") print("\033[1;32m[成功]\033[0m 保存图片记录到数据库")
except Exception as db_error: except Exception as db_error:
print(f"\033[1;31m[错误]\033[0m 数据库操作失败: {str(db_error)}") print(f"\033[1;31m[错误]\033[0m 数据库操作失败: {str(db_error)}")

View File

@ -1,5 +1,6 @@
from .relationship_manager import relationship_manager
from .config import global_config from .config import global_config
from .relationship_manager import relationship_manager
def get_user_nickname(user_id: int) -> str: def get_user_nickname(user_id: int) -> str:
if int(user_id) == int(global_config.BOT_QQ): if int(user_id) == int(global_config.BOT_QQ):

View File

@ -1,4 +1,6 @@
import asyncio import asyncio
from .config import global_config
class WillingManager: class WillingManager:
def __init__(self): def __init__(self):
@ -37,11 +39,14 @@ class WillingManager:
current_willing *= 0.1 current_willing *= 0.1
print(f"表情包, 当前意愿: {current_willing}") print(f"表情包, 当前意愿: {current_willing}")
print(f"放大系数_interested_rate: {global_config.response_interested_rate_amplifier}")
interested_rate *= global_config.response_interested_rate_amplifier #放大回复兴趣度
if interested_rate > 0.4: if interested_rate > 0.4:
print(f"兴趣度: {interested_rate}, 当前意愿: {current_willing}") # print(f"兴趣度: {interested_rate}, 当前意愿: {current_willing}")
current_willing += interested_rate-0.1 current_willing += interested_rate-0.4
self.group_reply_willing[group_id] = min(current_willing, 3.0) current_willing *= global_config.response_willing_amplifier #放大回复意愿
# print(f"放大系数_willing: {global_config.response_willing_amplifier}, 当前意愿: {current_willing}")
reply_probability = max((current_willing - 0.45) * 2, 0) reply_probability = max((current_willing - 0.45) * 2, 0)
if group_id not in config.talk_allowed_groups: if group_id not in config.talk_allowed_groups:
@ -49,11 +54,14 @@ class WillingManager:
reply_probability = 0 reply_probability = 0
if group_id in config.talk_frequency_down_groups: if group_id in config.talk_frequency_down_groups:
reply_probability = reply_probability / 3.5 reply_probability = reply_probability / global_config.down_frequency_rate
reply_probability = min(reply_probability, 1) reply_probability = min(reply_probability, 1)
if reply_probability < 0: if reply_probability < 0:
reply_probability = 0 reply_probability = 0
self.group_reply_willing[group_id] = min(current_willing, 3.0)
return reply_probability return reply_probability
def change_reply_willing_sent(self, group_id: int): def change_reply_willing_sent(self, group_id: int):

View File

@ -1,8 +1,8 @@
import os import os
import sys import sys
import numpy as np
import requests
import time import time
import requests
from dotenv import load_dotenv from dotenv import load_dotenv
# 添加项目根目录到 Python 路径 # 添加项目根目录到 Python 路径

View File

@ -1,19 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os import os
import sys import sys
import jieba
import networkx as nx
import matplotlib.pyplot as plt
import math
from collections import Counter
import datetime
import random
import time import time
import jieba
import matplotlib.pyplot as plt
import networkx as nx
from dotenv import load_dotenv from dotenv import load_dotenv
import sys
import asyncio
import aiohttp
from typing import Tuple
sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径 sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径
from src.common.database import Database # 使用正确的导入语法 from src.common.database import Database # 使用正确的导入语法

View File

@ -1,20 +1,21 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import os
import jieba
import networkx as nx
import matplotlib.pyplot as plt
from collections import Counter
import datetime import datetime
import math
import random import random
import time import time
import jieba
import networkx as nx
from ...common.database import Database # 使用正确的导入语法
from ..chat.config import global_config from ..chat.config import global_config
from ...common.database import Database # 使用正确的导入语法 from ..chat.utils import (
calculate_information_content,
cosine_similarity,
get_cloest_chat_from_db,
text_to_vector,
)
from ..models.utils_model import LLM_request from ..models.utils_model import LLM_request
import math
from ..chat.utils import calculate_information_content, get_cloest_chat_from_db ,text_to_vector,cosine_similarity
class Memory_graph: class Memory_graph:
@ -530,7 +531,8 @@ class Hippocampus:
# 计算每个识别出的主题与记忆主题的相似度 # 计算每个识别出的主题与记忆主题的相似度
for topic in topics: for topic in topics:
if debug_info: if debug_info:
print(f"\033[1;32m[{debug_info}]\033[0m 正在思考有没有见过: {topic}") # print(f"\033[1;32m[{debug_info}]\033[0m 正在思考有没有见过: {topic}")
pass
topic_vector = text_to_vector(topic) topic_vector = text_to_vector(topic)
has_similar_topic = False has_similar_topic = False
@ -548,11 +550,13 @@ class Hippocampus:
if similarity >= similarity_threshold: if similarity >= similarity_threshold:
has_similar_topic = True has_similar_topic = True
if debug_info: if debug_info:
print(f"\033[1;32m[{debug_info}]\033[0m 找到相似主题: {topic} -> {memory_topic} (相似度: {similarity:.2f})") # print(f"\033[1;32m[{debug_info}]\033[0m 找到相似主题: {topic} -> {memory_topic} (相似度: {similarity:.2f})")
pass
all_similar_topics.append((memory_topic, similarity)) all_similar_topics.append((memory_topic, similarity))
if not has_similar_topic and debug_info: if not has_similar_topic and debug_info:
print(f"\033[1;31m[{debug_info}]\033[0m 没有见过: {topic} ,呃呃") # print(f"\033[1;31m[{debug_info}]\033[0m 没有见过: {topic} ,呃呃")
pass
return all_similar_topics return all_similar_topics
@ -696,6 +700,7 @@ def segment_text(text):
from nonebot import get_driver from nonebot import get_driver
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@ -1,21 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import sys
import jieba
import networkx as nx
import matplotlib.pyplot as plt
import math
from collections import Counter
import datetime import datetime
import random import math
import time
import os import os
from dotenv import load_dotenv import random
import pymongo import sys
from loguru import logger import time
from collections import Counter
from pathlib import Path from pathlib import Path
import matplotlib.pyplot as plt
import networkx as nx
import pymongo
from dotenv import load_dotenv
from loguru import logger
# from chat.config import global_config # from chat.config import global_config
sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径 sys.path.append("C:/GitHub/MaiMBot") # 添加项目根目录到 Python 路径
from src.common.database import Database from src.common.database import Database
from src.plugins.memory_system.offline_llm import LLMModel from src.plugins.memory_system.offline_llm import LLMModel
# 获取当前文件的目录 # 获取当前文件的目录
@ -102,7 +103,7 @@ def get_cloest_chat_from_db(db, length: int, timestamp: str):
# 检查当前记录的memorized值 # 检查当前记录的memorized值
current_memorized = record.get('memorized', 0) current_memorized = record.get('memorized', 0)
if current_memorized > 3: if current_memorized > 3:
print(f"消息已读取3次跳过") print("消息已读取3次跳过")
return '' return ''
# 更新memorized值 # 更新memorized值
@ -114,7 +115,7 @@ def get_cloest_chat_from_db(db, length: int, timestamp: str):
chat_text += record["detailed_plain_text"] chat_text += record["detailed_plain_text"]
return chat_text return chat_text
print(f"消息已读取3次跳过") print("消息已读取3次跳过")
return '' return ''
class Memory_graph: class Memory_graph:

View File

@ -1,11 +1,13 @@
import os
import requests
from typing import Tuple, Union
import time
import aiohttp
import asyncio import asyncio
import os
import time
from typing import Tuple, Union
import aiohttp
import requests
from loguru import logger from loguru import logger
class LLMModel: class LLMModel:
def __init__(self, model_name="deepseek-ai/DeepSeek-V3", **kwargs): def __init__(self, model_name="deepseek-ai/DeepSeek-V3", **kwargs):
self.model_name = model_name self.model_name = model_name

View File

@ -1,16 +1,16 @@
import aiohttp
import asyncio import asyncio
import json import json
import requests
import time
import re import re
from datetime import datetime
from typing import Tuple, Union from typing import Tuple, Union
from nonebot import get_driver
import aiohttp
from loguru import logger from loguru import logger
from nonebot import get_driver
from ...common.database import Database
from ..chat.config import global_config from ..chat.config import global_config
from ..chat.utils_image import compress_base64_image_by_scale from ..chat.utils_image import compress_base64_image_by_scale
from datetime import datetime
from ...common.database import Database
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config
@ -181,6 +181,13 @@ class LLM_request:
continue continue
elif response.status in policy["abort_codes"]: elif response.status in policy["abort_codes"]:
logger.error(f"错误码: {response.status} - {error_code_mapping.get(response.status)}") logger.error(f"错误码: {response.status} - {error_code_mapping.get(response.status)}")
if response.status == 403 :
if global_config.llm_normal == "Pro/deepseek-ai/DeepSeek-V3":
logger.error("可能是没有给硅基流动充钱普通模型自动退化至非Pro模型反应速度可能会变慢")
global_config.llm_normal = "deepseek-ai/DeepSeek-V3"
if global_config.llm_reasoning == "Pro/deepseek-ai/DeepSeek-R1":
logger.error("可能是没有给硅基流动充钱推理模型自动退化至非Pro模型反应速度可能会变慢")
global_config.llm_reasoning = "deepseek-ai/DeepSeek-R1"
raise RuntimeError(f"请求被拒绝: {error_code_mapping.get(response.status)}") raise RuntimeError(f"请求被拒绝: {error_code_mapping.get(response.status)}")
response.raise_for_status() response.raise_for_status()
@ -226,7 +233,7 @@ class LLM_request:
await asyncio.sleep(wait_time) await asyncio.sleep(wait_time)
else: else:
logger.critical(f"请求失败: {str(e)}") logger.critical(f"请求失败: {str(e)}")
logger.critical(f"请求头: {await self._build_headers()} 请求体: {payload}") logger.critical(f"请求头: {await self._build_headers(no_key=True)} 请求体: {payload}")
raise RuntimeError(f"API请求失败: {str(e)}") raise RuntimeError(f"API请求失败: {str(e)}")
logger.error("达到最大重试次数,请求仍然失败") logger.error("达到最大重试次数,请求仍然失败")
@ -324,12 +331,19 @@ class LLM_request:
reasoning = "" reasoning = ""
return content, reasoning return content, reasoning
async def _build_headers(self) -> dict: async def _build_headers(self, no_key: bool = False) -> dict:
"""构建请求头""" """构建请求头"""
return { if no_key:
"Authorization": f"Bearer {self.api_key}", return {
"Content-Type": "application/json" "Authorization": f"Bearer **********",
} "Content-Type": "application/json"
}
else:
return {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
# 防止小朋友们截图自己的key
async def generate_response(self, prompt: str) -> Tuple[str, str]: async def generate_response(self, prompt: str) -> Tuple[str, str]:
"""根据输入的提示生成模型的异步响应""" """根据输入的提示生成模型的异步响应"""

View File

@ -1,10 +1,11 @@
import math import math
import time
import threading import threading
from typing import Dict, Tuple, Optional import time
from dataclasses import dataclass from dataclasses import dataclass
from ..chat.config import global_config from ..chat.config import global_config
@dataclass @dataclass
class MoodState: class MoodState:
valence: float # 愉悦度 (-1 到 1) valence: float # 愉悦度 (-1 到 1)

View File

@ -1,12 +1,15 @@
import datetime import datetime
import os
from typing import List, Dict, Union
from ...common.database import Database # 使用正确的导入语法
from src.plugins.chat.config import global_config
from nonebot import get_driver
from ..models.utils_model import LLM_request
from loguru import logger
import json import json
from typing import Dict, Union
from loguru import logger
from nonebot import get_driver
from src.plugins.chat.config import global_config
from ...common.database import Database # 使用正确的导入语法
from ..models.utils_model import LLM_request
driver = get_driver() driver = get_driver()
config = driver.config config = driver.config

View File

@ -1,11 +1,12 @@
from typing import Dict, List, Any
import time
import threading import threading
import json import time
from datetime import datetime, timedelta
from collections import defaultdict from collections import defaultdict
from datetime import datetime, timedelta
from typing import Any, Dict
from ...common.database import Database from ...common.database import Database
class LLMStatistics: class LLMStatistics:
def __init__(self, output_file: str = "llm_statistics.txt"): def __init__(self, output_file: str = "llm_statistics.txt"):
"""初始化LLM统计类 """初始化LLM统计类

View File

@ -2,15 +2,17 @@
错别字生成器 - 基于拼音和字频的中文错别字生成工具 错别字生成器 - 基于拼音和字频的中文错别字生成工具
""" """
from pypinyin import pinyin, Style
from collections import defaultdict
import json import json
import os
import jieba
from pathlib import Path
import random
import math import math
import os
import random
import time import time
from collections import defaultdict
from pathlib import Path
import jieba
from pypinyin import Style, pinyin
class ChineseTypoGenerator: class ChineseTypoGenerator:
def __init__(self, def __init__(self,

View File

@ -1,8 +1,10 @@
import tomli import os
import tomli_w
import sys import sys
from pathlib import Path from pathlib import Path
import os
import tomli
import tomli_w
def sync_configs(): def sync_configs():
# 读取两个配置文件 # 读取两个配置文件

View File

@ -17,6 +17,11 @@ prompt_schedule = "一个曾经学习地质,现在学习心理学和脑科学的
min_text_length = 2 # 与麦麦聊天时麦麦只会回答文本大于等于此数的消息 min_text_length = 2 # 与麦麦聊天时麦麦只会回答文本大于等于此数的消息
max_context_size = 15 # 麦麦获得的上文数量 max_context_size = 15 # 麦麦获得的上文数量
emoji_chance = 0.2 # 麦麦使用表情包的概率 emoji_chance = 0.2 # 麦麦使用表情包的概率
thinking_timeout = 120 # 麦麦思考时间
response_willing_amplifier = 1 # 麦麦回复意愿放大系数一般为1
response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听到记忆里的内容时放大系数
down_frequency_rate = 3.5 # 降低回复频率的群组回复意愿降低系数
ban_words = [ ban_words = [
# "403","张三" # "403","张三"
] ]
@ -46,6 +51,19 @@ mood_update_interval = 1.0 # 情绪更新间隔 单位秒
mood_decay_rate = 0.95 # 情绪衰减率 mood_decay_rate = 0.95 # 情绪衰减率
mood_intensity_factor = 1.0 # 情绪强度因子 mood_intensity_factor = 1.0 # 情绪强度因子
[keywords_reaction] # 针对某个关键词作出反应
enable = true # 关键词反应功能的总开关
[[keywords_reaction.rules]] # 如果想要新增多个关键词直接复制本条修改keywords和reaction即可
enable = true # 是否启用此条为了人类在未来AI战争能更好地识别AIbushi默认开启
keywords = ["人机", "bot", "机器", "入机", "robot", "机器人"] # 会触发反应的关键词
reaction = "有人说你是人机或者机器人,否定这一事实,攻击或反问他。注意,只有认为你是机器人才回复,否则不要否认" # 触发之后添加的提示词
[[keywords_reaction.rules]] # 就像这样复制
enable = false # 仅作示例,不会触发
keywords = ["测试关键词回复","test",""]
reaction = "回答“测试成功”"
[others] [others]
enable_advance_output = true # 是否启用高级输出 enable_advance_output = true # 是否启用高级输出
enable_kuuki_read = true # 是否启用读空气功能 enable_kuuki_read = true # 是否启用读空气功能