Merge branch 'dev' into patch-2
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -323,6 +323,8 @@ run_pet.bat
|
|||||||
!/plugins/hello_world_plugin
|
!/plugins/hello_world_plugin
|
||||||
!/plugins/emoji_manage_plugin
|
!/plugins/emoji_manage_plugin
|
||||||
!/plugins/take_picture_plugin
|
!/plugins/take_picture_plugin
|
||||||
|
!/plugins/deep_think
|
||||||
|
!/plugins/__init__.py
|
||||||
|
|
||||||
config.toml
|
config.toml
|
||||||
|
|
||||||
|
|||||||
0
plugins/__init__.py
Normal file
0
plugins/__init__.py
Normal file
34
plugins/deep_think/_manifest.json
Normal file
34
plugins/deep_think/_manifest.json
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 1,
|
||||||
|
"name": "Deep Think插件 (Deep Think Actions)",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "可以深度思考",
|
||||||
|
"author": {
|
||||||
|
"name": "SengokuCola",
|
||||||
|
"url": "https://github.com/MaiM-with-u"
|
||||||
|
},
|
||||||
|
"license": "GPL-v3.0-or-later",
|
||||||
|
|
||||||
|
"host_application": {
|
||||||
|
"min_version": "0.11.0"
|
||||||
|
},
|
||||||
|
"homepage_url": "https://github.com/MaiM-with-u/maibot",
|
||||||
|
"repository_url": "https://github.com/MaiM-with-u/maibot",
|
||||||
|
"keywords": ["deep", "think", "action", "built-in"],
|
||||||
|
"categories": ["Deep Think"],
|
||||||
|
|
||||||
|
"default_locale": "zh-CN",
|
||||||
|
"locales_path": "_locales",
|
||||||
|
|
||||||
|
"plugin_info": {
|
||||||
|
"is_built_in": true,
|
||||||
|
"plugin_type": "action_provider",
|
||||||
|
"components": [
|
||||||
|
{
|
||||||
|
"type": "action",
|
||||||
|
"name": "deep_think",
|
||||||
|
"description": "发送深度思考"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
102
plugins/deep_think/plugin.py
Normal file
102
plugins/deep_think/plugin.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from typing import List, Tuple, Type, Any
|
||||||
|
|
||||||
|
# 导入新插件系统
|
||||||
|
from src.plugin_system import BasePlugin, register_plugin, ComponentInfo
|
||||||
|
from src.plugin_system.base.config_types import ConfigField
|
||||||
|
from src.person_info.person_info import Person
|
||||||
|
from src.plugin_system.base.base_tool import BaseTool, ToolParamType
|
||||||
|
|
||||||
|
# 导入依赖的系统组件
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
|
from src.plugins.built_in.relation.relation import BuildRelationAction
|
||||||
|
from src.plugin_system.apis import llm_api
|
||||||
|
|
||||||
|
logger = get_logger("relation_actions")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DeepThinkTool(BaseTool):
|
||||||
|
"""获取用户信息"""
|
||||||
|
|
||||||
|
name = "deep_think"
|
||||||
|
description = "深度思考,对某个问题进行全面且深入的思考,当面临复杂环境或重要问题时,使用此获得更好的解决方案"
|
||||||
|
parameters = [
|
||||||
|
("question", ToolParamType.STRING, "需要思考的问题,越具体越好", True, None),
|
||||||
|
]
|
||||||
|
|
||||||
|
available_for_llm = True
|
||||||
|
|
||||||
|
async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""执行比较两个数的大小
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function_args: 工具参数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 工具执行结果
|
||||||
|
"""
|
||||||
|
question: str = function_args.get("question") # type: ignore
|
||||||
|
|
||||||
|
print(f"question: {question}")
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
请你思考以下问题,以简洁的一段话回答:
|
||||||
|
{question}
|
||||||
|
"""
|
||||||
|
|
||||||
|
models = llm_api.get_available_models()
|
||||||
|
chat_model_config = models.get("replyer") # 使用字典访问方式
|
||||||
|
|
||||||
|
success, thinking_result, _, _ = await llm_api.generate_with_model(
|
||||||
|
prompt, model_config=chat_model_config, request_type="deep_think"
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"thinking_result: {thinking_result}")
|
||||||
|
|
||||||
|
thinking_result =f"思考结果:{thinking_result}\n**注意** 因为你进行了深度思考,最后的回复内容可以回复的长一些,更加详细一些,不用太简洁。\n"
|
||||||
|
|
||||||
|
return {"content": thinking_result}
|
||||||
|
|
||||||
|
|
||||||
|
@register_plugin
|
||||||
|
class DeepThinkPlugin(BasePlugin):
|
||||||
|
"""关系动作插件
|
||||||
|
|
||||||
|
系统内置插件,提供基础的聊天交互功能:
|
||||||
|
- Reply: 回复动作
|
||||||
|
- NoReply: 不回复动作
|
||||||
|
- Emoji: 表情动作
|
||||||
|
|
||||||
|
注意:插件基本信息优先从_manifest.json文件中读取
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 插件基本信息
|
||||||
|
plugin_name: str = "deep_think" # 内部标识符
|
||||||
|
enable_plugin: bool = True
|
||||||
|
dependencies: list[str] = [] # 插件依赖列表
|
||||||
|
python_dependencies: list[str] = [] # Python包依赖列表
|
||||||
|
config_file_name: str = "config.toml"
|
||||||
|
|
||||||
|
# 配置节描述
|
||||||
|
config_section_descriptions = {
|
||||||
|
"plugin": "插件启用配置",
|
||||||
|
"components": "核心组件启用配置",
|
||||||
|
}
|
||||||
|
|
||||||
|
# 配置Schema定义
|
||||||
|
config_schema: dict = {
|
||||||
|
"plugin": {
|
||||||
|
"enabled": ConfigField(type=bool, default=False, description="是否启用插件"),
|
||||||
|
"config_version": ConfigField(type=str, default="2.0.0", description="配置文件版本"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||||
|
"""返回插件包含的组件列表"""
|
||||||
|
|
||||||
|
# --- 根据配置注册组件 ---
|
||||||
|
components = []
|
||||||
|
components.append((DeepThinkTool.get_tool_info(), DeepThinkTool))
|
||||||
|
|
||||||
|
return components
|
||||||
@@ -1,26 +1,19 @@
|
|||||||
import random
|
from typing import List, Tuple, Type
|
||||||
from typing import List, Tuple, Type, Any
|
|
||||||
from src.plugin_system import (
|
from src.plugin_system import (
|
||||||
BasePlugin,
|
BasePlugin,
|
||||||
register_plugin,
|
register_plugin,
|
||||||
BaseAction,
|
|
||||||
BaseCommand,
|
BaseCommand,
|
||||||
BaseTool,
|
|
||||||
ComponentInfo,
|
ComponentInfo,
|
||||||
ActionActivationType,
|
|
||||||
ConfigField,
|
ConfigField,
|
||||||
BaseEventHandler,
|
|
||||||
EventType,
|
|
||||||
MaiMessages,
|
|
||||||
ToolParamType,
|
|
||||||
ReplyContentType,
|
ReplyContentType,
|
||||||
emoji_api,
|
emoji_api,
|
||||||
)
|
)
|
||||||
from maim_message import Seg
|
from maim_message import Seg
|
||||||
from src.config.config import global_config
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger("emoji_manage_plugin")
|
logger = get_logger("emoji_manage_plugin")
|
||||||
|
|
||||||
|
|
||||||
class AddEmojiCommand(BaseCommand):
|
class AddEmojiCommand(BaseCommand):
|
||||||
command_name = "add_emoji"
|
command_name = "add_emoji"
|
||||||
command_description = "添加表情包"
|
command_description = "添加表情包"
|
||||||
@@ -51,7 +44,7 @@ class AddEmojiCommand(BaseCommand):
|
|||||||
emotions = result.get("emotions", [])
|
emotions = result.get("emotions", [])
|
||||||
replaced = result.get("replaced", False)
|
replaced = result.get("replaced", False)
|
||||||
|
|
||||||
result_msg = f"表情包 {i+1} 注册成功{'(替换旧表情包)' if replaced else '(新增表情包)'}"
|
result_msg = f"表情包 {i + 1} 注册成功{'(替换旧表情包)' if replaced else '(新增表情包)'}"
|
||||||
if description:
|
if description:
|
||||||
result_msg += f"\n描述: {description}"
|
result_msg += f"\n描述: {description}"
|
||||||
if emotions:
|
if emotions:
|
||||||
@@ -61,11 +54,11 @@ class AddEmojiCommand(BaseCommand):
|
|||||||
else:
|
else:
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
error_msg = result.get("message", "注册失败")
|
error_msg = result.get("message", "注册失败")
|
||||||
results.append(f"表情包 {i+1} 注册失败: {error_msg}")
|
results.append(f"表情包 {i + 1} 注册失败: {error_msg}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
results.append(f"表情包 {i+1} 注册时发生错误: {str(e)}")
|
results.append(f"表情包 {i + 1} 注册时发生错误: {str(e)}")
|
||||||
|
|
||||||
# 构建返回消息
|
# 构建返回消息
|
||||||
total_count = success_count + fail_count
|
total_count = success_count + fail_count
|
||||||
@@ -140,6 +133,7 @@ class AddEmojiCommand(BaseCommand):
|
|||||||
emoji_base64_list.extend(self.find_and_return_emoji_in_message(seg.data))
|
emoji_base64_list.extend(self.find_and_return_emoji_in_message(seg.data))
|
||||||
return emoji_base64_list
|
return emoji_base64_list
|
||||||
|
|
||||||
|
|
||||||
class ListEmojiCommand(BaseCommand):
|
class ListEmojiCommand(BaseCommand):
|
||||||
"""列表表情包Command - 响应/emoji list命令"""
|
"""列表表情包Command - 响应/emoji list命令"""
|
||||||
|
|
||||||
@@ -156,6 +150,7 @@ class ListEmojiCommand(BaseCommand):
|
|||||||
|
|
||||||
# 解析命令参数
|
# 解析命令参数
|
||||||
import re
|
import re
|
||||||
|
|
||||||
match = re.match(r"^/emoji list(?:\s+(\d+))?$", self.message.raw_message)
|
match = re.match(r"^/emoji list(?:\s+(\d+))?$", self.message.raw_message)
|
||||||
max_count = 10 # 默认显示10个
|
max_count = 10 # 默认显示10个
|
||||||
if match and match.group(1):
|
if match and match.group(1):
|
||||||
@@ -195,7 +190,7 @@ class ListEmojiCommand(BaseCommand):
|
|||||||
display_emojis = all_emojis[:max_count]
|
display_emojis = all_emojis[:max_count]
|
||||||
message_lines.append(f"\n📋 显示前 {len(display_emojis)} 个表情包:")
|
message_lines.append(f"\n📋 显示前 {len(display_emojis)} 个表情包:")
|
||||||
|
|
||||||
for i, (emoji_base64, description, emotion) in enumerate(display_emojis, 1):
|
for i, (_, description, emotion) in enumerate(display_emojis, 1):
|
||||||
# 截断过长的描述
|
# 截断过长的描述
|
||||||
short_desc = description[:50] + "..." if len(description) > 50 else description
|
short_desc = description[:50] + "..." if len(description) > 50 else description
|
||||||
message_lines.append(f"{i}. {short_desc} [{emotion}]")
|
message_lines.append(f"{i}. {short_desc} [{emotion}]")
|
||||||
@@ -257,7 +252,7 @@ class DeleteEmojiCommand(BaseCommand):
|
|||||||
count_after = result.get("count_after", 0)
|
count_after = result.get("count_after", 0)
|
||||||
emotions = result.get("emotions", [])
|
emotions = result.get("emotions", [])
|
||||||
|
|
||||||
result_msg = f"表情包 {i+1} 删除成功"
|
result_msg = f"表情包 {i + 1} 删除成功"
|
||||||
if description:
|
if description:
|
||||||
result_msg += f"\n描述: {description}"
|
result_msg += f"\n描述: {description}"
|
||||||
if emotions:
|
if emotions:
|
||||||
@@ -268,11 +263,11 @@ class DeleteEmojiCommand(BaseCommand):
|
|||||||
else:
|
else:
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
error_msg = result.get("message", "删除失败")
|
error_msg = result.get("message", "删除失败")
|
||||||
results.append(f"表情包 {i+1} 删除失败: {error_msg}")
|
results.append(f"表情包 {i + 1} 删除失败: {error_msg}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
fail_count += 1
|
fail_count += 1
|
||||||
results.append(f"表情包 {i+1} 删除时发生错误: {str(e)}")
|
results.append(f"表情包 {i + 1} 删除时发生错误: {str(e)}")
|
||||||
|
|
||||||
# 构建返回消息
|
# 构建返回消息
|
||||||
total_count = success_count + fail_count
|
total_count = success_count + fail_count
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ from src.chat.brain_chat.brain_planner import BrainPlanner
|
|||||||
from src.chat.planner_actions.action_modifier import ActionModifier
|
from src.chat.planner_actions.action_modifier import ActionModifier
|
||||||
from src.chat.planner_actions.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from src.chat.heart_flow.hfc_utils import CycleDetail
|
from src.chat.heart_flow.hfc_utils import CycleDetail
|
||||||
from src.chat.heart_flow.hfc_utils import send_typing, stop_typing
|
|
||||||
from src.chat.express.expression_learner import expression_learner_manager
|
from src.chat.express.expression_learner import expression_learner_manager
|
||||||
from src.person_info.person_info import Person
|
from src.person_info.person_info import Person
|
||||||
from src.plugin_system.base.component_types import EventType, ActionInfo
|
from src.plugin_system.base.component_types import EventType, ActionInfo
|
||||||
@@ -97,7 +96,6 @@ class BrainChatting:
|
|||||||
|
|
||||||
self.more_plan = False
|
self.more_plan = False
|
||||||
|
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""检查是否需要启动主循环,如果未激活则启动。"""
|
"""检查是否需要启动主循环,如果未激活则启动。"""
|
||||||
|
|
||||||
@@ -171,9 +169,7 @@ class BrainChatting:
|
|||||||
|
|
||||||
if len(recent_messages_list) >= 1:
|
if len(recent_messages_list) >= 1:
|
||||||
self.last_read_time = time.time()
|
self.last_read_time = time.time()
|
||||||
await self._observe(
|
await self._observe(recent_messages_list=recent_messages_list)
|
||||||
recent_messages_list=recent_messages_list
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Normal模式:消息数量不足,等待
|
# Normal模式:消息数量不足,等待
|
||||||
@@ -233,11 +229,11 @@ class BrainChatting:
|
|||||||
|
|
||||||
async def _observe(
|
async def _observe(
|
||||||
self, # interest_value: float = 0.0,
|
self, # interest_value: float = 0.0,
|
||||||
recent_messages_list: Optional[List["DatabaseMessages"]] = None
|
recent_messages_list: Optional[List["DatabaseMessages"]] = None,
|
||||||
) -> bool: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
|
) -> bool: # sourcery skip: merge-else-if-into-elif, remove-redundant-if
|
||||||
if recent_messages_list is None:
|
if recent_messages_list is None:
|
||||||
recent_messages_list = []
|
recent_messages_list = []
|
||||||
reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
_reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
||||||
|
|
||||||
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
||||||
await self.expression_learner.trigger_learning_for_chat()
|
await self.expression_learner.trigger_learning_for_chat()
|
||||||
@@ -334,7 +330,7 @@ class BrainChatting:
|
|||||||
"taken_time": time.time(),
|
"taken_time": time.time(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
reply_text = reply_text_from_reply
|
_reply_text = reply_text_from_reply
|
||||||
else:
|
else:
|
||||||
# 没有回复信息,构建纯动作的loop_info
|
# 没有回复信息,构建纯动作的loop_info
|
||||||
loop_info = {
|
loop_info = {
|
||||||
@@ -347,7 +343,7 @@ class BrainChatting:
|
|||||||
"taken_time": time.time(),
|
"taken_time": time.time(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
reply_text = action_reply_text
|
_reply_text = action_reply_text
|
||||||
|
|
||||||
self.end_cycle(loop_info, cycle_timers)
|
self.end_cycle(loop_info, cycle_timers)
|
||||||
self.print_cycle_info(cycle_timers)
|
self.print_cycle_info(cycle_timers)
|
||||||
@@ -484,7 +480,6 @@ class BrainChatting:
|
|||||||
"""执行单个动作的通用函数"""
|
"""执行单个动作的通用函数"""
|
||||||
try:
|
try:
|
||||||
with Timer(f"动作{action_planner_info.action_type}", cycle_timers):
|
with Timer(f"动作{action_planner_info.action_type}", cycle_timers):
|
||||||
|
|
||||||
if action_planner_info.action_type == "no_reply":
|
if action_planner_info.action_type == "no_reply":
|
||||||
# 直接处理no_action逻辑,不再通过动作系统
|
# 直接处理no_action逻辑,不再通过动作系统
|
||||||
reason = action_planner_info.reasoning or "选择不回复"
|
reason = action_planner_info.reasoning or "选择不回复"
|
||||||
@@ -517,7 +512,9 @@ class BrainChatting:
|
|||||||
|
|
||||||
if not success or not llm_response or not llm_response.reply_set:
|
if not success or not llm_response or not llm_response.reply_set:
|
||||||
if action_planner_info.action_message:
|
if action_planner_info.action_message:
|
||||||
logger.info(f"对 {action_planner_info.action_message.processed_plain_text} 的回复生成失败")
|
logger.info(
|
||||||
|
f"对 {action_planner_info.action_message.processed_plain_text} 的回复生成失败"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.info("回复生成失败")
|
logger.info("回复生成失败")
|
||||||
return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
|
return {"action_type": "reply", "success": False, "reply_text": "", "loop_info": None}
|
||||||
|
|||||||
@@ -307,7 +307,9 @@ class BrainPlanner:
|
|||||||
|
|
||||||
if chat_target_info:
|
if chat_target_info:
|
||||||
# 构建聊天上下文描述
|
# 构建聊天上下文描述
|
||||||
chat_context_description = f"你正在和 {chat_target_info.person_name or chat_target_info.user_nickname or '对方'} 聊天中"
|
chat_context_description = (
|
||||||
|
f"你正在和 {chat_target_info.person_name or chat_target_info.user_nickname or '对方'} 聊天中"
|
||||||
|
)
|
||||||
|
|
||||||
# 构建动作选项块
|
# 构建动作选项块
|
||||||
action_options_block = await self._build_action_options_block(current_available_actions)
|
action_options_block = await self._build_action_options_block(current_available_actions)
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ from src.common.logger import get_logger
|
|||||||
from src.common.database.database_model import Expression
|
from src.common.database.database_model import Expression
|
||||||
from src.llm_models.utils_model import LLMRequest
|
from src.llm_models.utils_model import LLMRequest
|
||||||
from src.config.config import model_config, global_config
|
from src.config.config import model_config, global_config
|
||||||
from src.chat.utils.chat_message_builder import get_raw_msg_by_timestamp_with_chat_inclusive, build_anonymous_messages, build_bare_messages
|
from src.chat.utils.chat_message_builder import (
|
||||||
|
get_raw_msg_by_timestamp_with_chat_inclusive,
|
||||||
|
build_anonymous_messages,
|
||||||
|
build_bare_messages,
|
||||||
|
)
|
||||||
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from json_repair import repair_json
|
from json_repair import repair_json
|
||||||
from src.chat.utils.utils import get_embedding
|
|
||||||
|
|
||||||
|
|
||||||
MAX_EXPRESSION_COUNT = 300
|
MAX_EXPRESSION_COUNT = 300
|
||||||
@@ -99,7 +102,9 @@ class ExpressionLearner:
|
|||||||
self.last_learning_time: float = time.time()
|
self.last_learning_time: float = time.time()
|
||||||
|
|
||||||
# 学习参数
|
# 学习参数
|
||||||
_, self.enable_learning, self.learning_intensity = global_config.expression.get_expression_config_for_chat(self.chat_id)
|
_, self.enable_learning, self.learning_intensity = global_config.expression.get_expression_config_for_chat(
|
||||||
|
self.chat_id
|
||||||
|
)
|
||||||
self.min_messages_for_learning = 15 / self.learning_intensity # 触发学习所需的最少消息数
|
self.min_messages_for_learning = 15 / self.learning_intensity # 触发学习所需的最少消息数
|
||||||
self.min_learning_interval = 150 / self.learning_intensity
|
self.min_learning_interval = 150 / self.learning_intensity
|
||||||
|
|
||||||
@@ -237,17 +242,42 @@ class ExpressionLearner:
|
|||||||
return []
|
return []
|
||||||
learnt_expressions = res
|
learnt_expressions = res
|
||||||
learnt_expressions_str = ""
|
learnt_expressions_str = ""
|
||||||
for _chat_id, situation, style, context, context_words, full_context, full_context_embedding in learnt_expressions:
|
for (
|
||||||
|
_chat_id,
|
||||||
|
situation,
|
||||||
|
style,
|
||||||
|
_context,
|
||||||
|
_context_words,
|
||||||
|
_full_context,
|
||||||
|
_full_context_embedding,
|
||||||
|
) in learnt_expressions:
|
||||||
learnt_expressions_str += f"{situation}->{style}\n"
|
learnt_expressions_str += f"{situation}->{style}\n"
|
||||||
|
|
||||||
logger.info(f"在 {self.chat_name} 学习到表达风格:\n{learnt_expressions_str}")
|
logger.info(f"在 {self.chat_name} 学习到表达风格:\n{learnt_expressions_str}")
|
||||||
|
|
||||||
# 按chat_id分组
|
# 按chat_id分组
|
||||||
chat_dict: Dict[str, List[Dict[str, Any]]] = {}
|
chat_dict: Dict[str, List[Dict[str, Any]]] = {}
|
||||||
for chat_id, situation, style, context, context_words, full_context, full_context_embedding in learnt_expressions:
|
for (
|
||||||
|
chat_id,
|
||||||
|
situation,
|
||||||
|
style,
|
||||||
|
context,
|
||||||
|
context_words,
|
||||||
|
full_context,
|
||||||
|
full_context_embedding,
|
||||||
|
) in learnt_expressions:
|
||||||
if chat_id not in chat_dict:
|
if chat_id not in chat_dict:
|
||||||
chat_dict[chat_id] = []
|
chat_dict[chat_id] = []
|
||||||
chat_dict[chat_id].append({"situation": situation, "style": style, "context": context, "context_words": context_words, "full_context": full_context, "full_context_embedding": full_context_embedding})
|
chat_dict[chat_id].append(
|
||||||
|
{
|
||||||
|
"situation": situation,
|
||||||
|
"style": style,
|
||||||
|
"context": context,
|
||||||
|
"context_words": context_words,
|
||||||
|
"full_context": full_context,
|
||||||
|
"full_context_embedding": full_context_embedding,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
current_time = time.time()
|
current_time = time.time()
|
||||||
|
|
||||||
@@ -300,11 +330,13 @@ class ExpressionLearner:
|
|||||||
expr.delete_instance()
|
expr.delete_instance()
|
||||||
return learnt_expressions
|
return learnt_expressions
|
||||||
|
|
||||||
async def match_expression_context(self, expression_pairs: List[Tuple[str, str]], random_msg_match_str: str) -> List[Tuple[str, str, str]]:
|
async def match_expression_context(
|
||||||
|
self, expression_pairs: List[Tuple[str, str]], random_msg_match_str: str
|
||||||
|
) -> List[Tuple[str, str, str]]:
|
||||||
# 为expression_pairs逐个条目赋予编号,并构建成字符串
|
# 为expression_pairs逐个条目赋予编号,并构建成字符串
|
||||||
numbered_pairs = []
|
numbered_pairs = []
|
||||||
for i, (situation, style) in enumerate(expression_pairs, 1):
|
for i, (situation, style) in enumerate(expression_pairs, 1):
|
||||||
numbered_pairs.append(f"{i}. 当\"{situation}\"时,使用\"{style}\"")
|
numbered_pairs.append(f'{i}. 当"{situation}"时,使用"{style}"')
|
||||||
|
|
||||||
expression_pairs_str = "\n".join(numbered_pairs)
|
expression_pairs_str = "\n".join(numbered_pairs)
|
||||||
|
|
||||||
@@ -325,14 +357,14 @@ class ExpressionLearner:
|
|||||||
try:
|
try:
|
||||||
response = response.strip()
|
response = response.strip()
|
||||||
# 检查是否已经是标准JSON数组格式
|
# 检查是否已经是标准JSON数组格式
|
||||||
if response.startswith('[') and response.endswith(']'):
|
if response.startswith("[") and response.endswith("]"):
|
||||||
match_responses = json.loads(response)
|
match_responses = json.loads(response)
|
||||||
else:
|
else:
|
||||||
# 尝试直接解析多个JSON对象
|
# 尝试直接解析多个JSON对象
|
||||||
try:
|
try:
|
||||||
# 如果是多个JSON对象用逗号分隔,包装成数组
|
# 如果是多个JSON对象用逗号分隔,包装成数组
|
||||||
if response.startswith('{') and not response.startswith('['):
|
if response.startswith("{") and not response.startswith("["):
|
||||||
response = '[' + response + ']'
|
response = "[" + response + "]"
|
||||||
match_responses = json.loads(response)
|
match_responses = json.loads(response)
|
||||||
else:
|
else:
|
||||||
# 使用repair_json处理响应
|
# 使用repair_json处理响应
|
||||||
@@ -394,7 +426,9 @@ class ExpressionLearner:
|
|||||||
|
|
||||||
return matched_expressions
|
return matched_expressions
|
||||||
|
|
||||||
async def learn_expression(self, num: int = 10) -> Optional[List[Tuple[str, str, str, List[str], str, List[float]]]]:
|
async def learn_expression(
|
||||||
|
self, num: int = 10
|
||||||
|
) -> Optional[List[Tuple[str, str, str, List[str], str, List[float]]]]:
|
||||||
"""从指定聊天流学习表达方式
|
"""从指定聊天流学习表达方式
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -416,18 +450,17 @@ class ExpressionLearner:
|
|||||||
if not random_msg or random_msg == []:
|
if not random_msg or random_msg == []:
|
||||||
return None
|
return None
|
||||||
# 转化成str
|
# 转化成str
|
||||||
chat_id: str = random_msg[0].chat_id
|
_chat_id: str = random_msg[0].chat_id
|
||||||
# random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal")
|
# random_msg_str: str = build_readable_messages(random_msg, timestamp_mode="normal")
|
||||||
random_msg_str: str = await build_anonymous_messages(random_msg)
|
random_msg_str: str = await build_anonymous_messages(random_msg)
|
||||||
random_msg_match_str: str = await build_bare_messages(random_msg)
|
random_msg_match_str: str = await build_bare_messages(random_msg)
|
||||||
|
|
||||||
|
|
||||||
prompt: str = await global_prompt_manager.format_prompt(
|
prompt: str = await global_prompt_manager.format_prompt(
|
||||||
prompt,
|
prompt,
|
||||||
chat_str=random_msg_str,
|
chat_str=random_msg_str,
|
||||||
)
|
)
|
||||||
|
|
||||||
print(f"random_msg_str:{random_msg_str}")
|
# print(f"random_msg_str:{random_msg_str}")
|
||||||
logger.info(f"学习{type_str}的prompt: {prompt}")
|
logger.info(f"学习{type_str}的prompt: {prompt}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -440,16 +473,21 @@ class ExpressionLearner:
|
|||||||
|
|
||||||
expressions: List[Tuple[str, str]] = self.parse_expression_response(response)
|
expressions: List[Tuple[str, str]] = self.parse_expression_response(response)
|
||||||
|
|
||||||
matched_expressions: List[Tuple[str, str, str]] = await self.match_expression_context(expressions, random_msg_match_str)
|
matched_expressions: List[Tuple[str, str, str]] = await self.match_expression_context(
|
||||||
|
expressions, random_msg_match_str
|
||||||
|
)
|
||||||
|
|
||||||
split_matched_expressions: List[Tuple[str, str, str, List[str]]] = self.split_expression_context(matched_expressions)
|
split_matched_expressions: List[Tuple[str, str, str, List[str]]] = self.split_expression_context(
|
||||||
|
matched_expressions
|
||||||
|
)
|
||||||
|
|
||||||
split_matched_expressions_w_emb = []
|
split_matched_expressions_w_emb = []
|
||||||
full_context_embedding: List[float] = await self.get_full_context_embedding(random_msg_match_str)
|
full_context_embedding: List[float] = await self.get_full_context_embedding(random_msg_match_str)
|
||||||
|
|
||||||
for situation, style, context, context_words in split_matched_expressions:
|
for situation, style, context, context_words in split_matched_expressions:
|
||||||
split_matched_expressions_w_emb.append((self.chat_id, situation, style, context, context_words, random_msg_match_str,full_context_embedding))
|
split_matched_expressions_w_emb.append(
|
||||||
|
(self.chat_id, situation, style, context, context_words, random_msg_match_str, full_context_embedding)
|
||||||
|
)
|
||||||
|
|
||||||
return split_matched_expressions_w_emb
|
return split_matched_expressions_w_emb
|
||||||
|
|
||||||
@@ -457,7 +495,9 @@ class ExpressionLearner:
|
|||||||
embedding, _ = await self.embedding_model.get_embedding(context)
|
embedding, _ = await self.embedding_model.get_embedding(context)
|
||||||
return embedding
|
return embedding
|
||||||
|
|
||||||
def split_expression_context(self, matched_expressions: List[Tuple[str, str, str]]) -> List[Tuple[str, str, str, List[str]]]:
|
def split_expression_context(
|
||||||
|
self, matched_expressions: List[Tuple[str, str, str]]
|
||||||
|
) -> List[Tuple[str, str, str, List[str]]]:
|
||||||
"""
|
"""
|
||||||
对matched_expressions中的context部分进行jieba分词
|
对matched_expressions中的context部分进行jieba分词
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from src.plugin_system.core import events_manager
|
|||||||
from src.plugin_system.apis import generator_api, send_api, message_api, database_api
|
from src.plugin_system.apis import generator_api, send_api, message_api, database_api
|
||||||
from src.mais4u.mai_think import mai_thinking_manager
|
from src.mais4u.mai_think import mai_thinking_manager
|
||||||
from src.mais4u.s4u_config import s4u_config
|
from src.mais4u.s4u_config import s4u_config
|
||||||
|
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||||
from src.chat.utils.chat_message_builder import (
|
from src.chat.utils.chat_message_builder import (
|
||||||
build_readable_messages_with_id,
|
build_readable_messages_with_id,
|
||||||
get_raw_msg_before_timestamp_with_chat,
|
get_raw_msg_before_timestamp_with_chat,
|
||||||
@@ -103,6 +104,7 @@ class HeartFChatting:
|
|||||||
|
|
||||||
self.no_reply_until_call = False
|
self.no_reply_until_call = False
|
||||||
|
|
||||||
|
|
||||||
async def start(self):
|
async def start(self):
|
||||||
"""检查是否需要启动主循环,如果未激活则启动。"""
|
"""检查是否需要启动主循环,如果未激活则启动。"""
|
||||||
|
|
||||||
@@ -206,7 +208,11 @@ class HeartFChatting:
|
|||||||
# *控制频率用
|
# *控制频率用
|
||||||
if mentioned_message:
|
if mentioned_message:
|
||||||
await self._observe(recent_messages_list=recent_messages_list, force_reply_message=mentioned_message)
|
await self._observe(recent_messages_list=recent_messages_list, force_reply_message=mentioned_message)
|
||||||
elif random.random() < global_config.chat.talk_value * frequency_control_manager.get_or_create_frequency_control(self.stream_id).get_talk_frequency_adjust():
|
elif (
|
||||||
|
random.random()
|
||||||
|
< global_config.chat.talk_value
|
||||||
|
* frequency_control_manager.get_or_create_frequency_control(self.stream_id).get_talk_frequency_adjust()
|
||||||
|
):
|
||||||
await self._observe(recent_messages_list=recent_messages_list)
|
await self._observe(recent_messages_list=recent_messages_list)
|
||||||
else:
|
else:
|
||||||
# 没有提到,继续保持沉默,等待5秒防止频繁触发
|
# 没有提到,继续保持沉默,等待5秒防止频繁触发
|
||||||
@@ -276,7 +282,6 @@ class HeartFChatting:
|
|||||||
recent_messages_list = []
|
recent_messages_list = []
|
||||||
reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
reply_text = "" # 初始化reply_text变量,避免UnboundLocalError
|
||||||
|
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
if s4u_config.enable_s4u:
|
if s4u_config.enable_s4u:
|
||||||
@@ -285,6 +290,10 @@ class HeartFChatting:
|
|||||||
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
async with global_prompt_manager.async_message_scope(self.chat_stream.context.get_template_name()):
|
||||||
await self.expression_learner.trigger_learning_for_chat()
|
await self.expression_learner.trigger_learning_for_chat()
|
||||||
|
|
||||||
|
await global_memory_chest.build_running_content(chat_id=self.stream_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cycle_timers, thinking_id = self.start_cycle()
|
cycle_timers, thinking_id = self.start_cycle()
|
||||||
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考")
|
logger.info(f"{self.log_prefix} 开始第{self._cycle_counter}次思考")
|
||||||
|
|
||||||
@@ -423,11 +432,6 @@ class HeartFChatting:
|
|||||||
else:
|
else:
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""S4U内容,暂时保留"""
|
"""S4U内容,暂时保留"""
|
||||||
if s4u_config.enable_s4u:
|
if s4u_config.enable_s4u:
|
||||||
await stop_typing()
|
await stop_typing()
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import asyncio
|
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from typing import Tuple, TYPE_CHECKING
|
from typing import Tuple, TYPE_CHECKING
|
||||||
|
|
||||||
from src.config.config import global_config
|
|
||||||
from src.chat.message_receive.message import MessageRecv
|
from src.chat.message_receive.message import MessageRecv
|
||||||
from src.chat.message_receive.storage import MessageStorage
|
from src.chat.message_receive.storage import MessageStorage
|
||||||
from src.chat.heart_flow.heartflow import heartflow
|
from src.chat.heart_flow.heartflow import heartflow
|
||||||
@@ -74,7 +72,7 @@ class HeartFCMessageReceiver:
|
|||||||
|
|
||||||
await self.storage.store_message(message, chat)
|
await self.storage.store_message(message, chat)
|
||||||
|
|
||||||
heartflow_chat: HeartFChatting = await heartflow.get_or_create_heartflow_chat(chat.stream_id) # type: ignore
|
_heartflow_chat: HeartFChatting = await heartflow.get_or_create_heartflow_chat(chat.stream_id) # type: ignore
|
||||||
|
|
||||||
# 3. 日志记录
|
# 3. 日志记录
|
||||||
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
mes_name = chat.group_info.group_name if chat.group_info else "私聊"
|
||||||
|
|||||||
321
src/chat/memory_system/Memory_chest.py
Normal file
321
src/chat/memory_system/Memory_chest.py
Normal file
@@ -0,0 +1,321 @@
|
|||||||
|
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.config.config import model_config
|
||||||
|
from src.common.database.database_model import MemoryChest as MemoryChestModel
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
from src.config.config import global_config
|
||||||
|
from src.plugin_system.apis.message_api import build_readable_messages
|
||||||
|
import time
|
||||||
|
from src.plugin_system.apis.message_api import get_raw_msg_by_timestamp_with_chat
|
||||||
|
|
||||||
|
logger = get_logger("memory_chest")
|
||||||
|
|
||||||
|
class MemoryChest:
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.LLMRequest = LLMRequest(
|
||||||
|
model_set=model_config.model_task_config.utils_small,
|
||||||
|
request_type="memory_chest",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.memory_build_threshold = 20
|
||||||
|
self.memory_size_limit = 300
|
||||||
|
|
||||||
|
self.running_content_list = {} # {chat_id: {"content": running_content, "last_update_time": timestamp}}
|
||||||
|
self.fetched_memory_list = [] # [(chat_id, (question, answer, timestamp)), ...]
|
||||||
|
|
||||||
|
async def build_running_content(self, chat_id: str = None) -> str:
|
||||||
|
"""
|
||||||
|
构建记忆仓库的运行内容
|
||||||
|
|
||||||
|
Args:
|
||||||
|
message_str: 消息内容
|
||||||
|
chat_id: 聊天ID,用于提取对应的运行内容
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 构建后的运行内容
|
||||||
|
"""
|
||||||
|
# 检查是否需要更新:上次更新时间和现在时间的消息数量大于30
|
||||||
|
if chat_id not in self.running_content_list:
|
||||||
|
self.running_content_list[chat_id] = {
|
||||||
|
"content": "",
|
||||||
|
"last_update_time": time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
should_update = True
|
||||||
|
if chat_id and chat_id in self.running_content_list:
|
||||||
|
last_update_time = self.running_content_list[chat_id]["last_update_time"]
|
||||||
|
current_time = time.time()
|
||||||
|
# 使用message_api获取消息数量
|
||||||
|
message_list = get_raw_msg_by_timestamp_with_chat(
|
||||||
|
timestamp_start=last_update_time,
|
||||||
|
timestamp_end=current_time,
|
||||||
|
chat_id=chat_id,
|
||||||
|
limit=global_config.chat.max_context_size * 2,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_messages_count = len(message_list)
|
||||||
|
should_update = new_messages_count > self.memory_build_threshold
|
||||||
|
logger.info(f"chat_id {chat_id} 自上次更新后有 {new_messages_count} 条新消息,{'需要' if should_update else '不需要'}更新")
|
||||||
|
|
||||||
|
|
||||||
|
if should_update:
|
||||||
|
# 如果有chat_id,先提取对应的running_content
|
||||||
|
message_str = build_readable_messages(
|
||||||
|
message_list,
|
||||||
|
replace_bot_name=True,
|
||||||
|
timestamp_mode="relative",
|
||||||
|
read_mark=0.0,
|
||||||
|
show_actions=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
current_running_content = ""
|
||||||
|
if chat_id and chat_id in self.running_content_list:
|
||||||
|
current_running_content = self.running_content_list[chat_id]["content"]
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
以下是你的记忆内容:
|
||||||
|
{current_running_content}
|
||||||
|
|
||||||
|
请将下面的新聊天记录内的有用的信息,添加到你的记忆中
|
||||||
|
请主要关注概念和知识,而不是聊天的琐事
|
||||||
|
记忆为一段纯文本,逻辑清晰,指出事件,概念的含义,并说明关系
|
||||||
|
请输出添加后的记忆内容,不要输出其他内容:
|
||||||
|
{message_str}
|
||||||
|
"""
|
||||||
|
|
||||||
|
if global_config.debug.show_prompt:
|
||||||
|
logger.info(f"记忆仓库构建运行内容 prompt: {prompt}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"记忆仓库构建运行内容 prompt: {prompt}")
|
||||||
|
|
||||||
|
running_content, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
|
||||||
|
|
||||||
|
print(f"记忆仓库构建运行内容: {running_content}")
|
||||||
|
|
||||||
|
# 如果有chat_id,更新对应的running_content
|
||||||
|
if chat_id and running_content:
|
||||||
|
self.running_content_list[chat_id] = {
|
||||||
|
"content": running_content,
|
||||||
|
"last_update_time": time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查running_content长度是否大于500
|
||||||
|
if len(running_content) > self.memory_size_limit:
|
||||||
|
await self._save_to_database_and_clear(chat_id, running_content)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return running_content
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_titles(self) -> list[str]:
|
||||||
|
"""
|
||||||
|
获取记忆仓库中的所有标题
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list: 包含所有标题的列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 查询所有记忆记录的标题
|
||||||
|
titles = []
|
||||||
|
for memory in MemoryChestModel.select():
|
||||||
|
if memory.title:
|
||||||
|
titles.append(memory.title)
|
||||||
|
return titles
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取记忆标题时出错: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
async def get_answer_by_question(self, chat_id: str = "", question: str = "") -> str:
|
||||||
|
"""
|
||||||
|
根据问题获取答案
|
||||||
|
"""
|
||||||
|
title = await self.select_title_by_question(question)
|
||||||
|
|
||||||
|
if not title:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
for memory in MemoryChestModel.select():
|
||||||
|
if memory.title == title:
|
||||||
|
content = memory.content
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
{content}
|
||||||
|
|
||||||
|
请根据问题:{question}
|
||||||
|
在上方内容中,提取相关信息的原文并输出,请务必提取上面原文,不要输出其他内容:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if global_config.debug.show_prompt:
|
||||||
|
logger.info(f"记忆仓库获取答案 prompt: {prompt}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"记忆仓库获取答案 prompt: {prompt}")
|
||||||
|
|
||||||
|
answer, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
|
||||||
|
|
||||||
|
|
||||||
|
logger.info(f"记忆仓库获取答案: {answer}")
|
||||||
|
|
||||||
|
# 将问题和答案存到fetched_memory_list
|
||||||
|
if chat_id and answer:
|
||||||
|
self.fetched_memory_list.append((chat_id, (question, answer, time.time())))
|
||||||
|
|
||||||
|
# 清理fetched_memory_list
|
||||||
|
self._cleanup_fetched_memory_list()
|
||||||
|
|
||||||
|
return answer
|
||||||
|
|
||||||
|
def get_chat_memories_as_string(self, chat_id: str) -> str:
|
||||||
|
"""
|
||||||
|
获取某个chat_id的所有记忆,并构建成字符串
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_id: 聊天ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 格式化的记忆字符串,格式:问题:xxx,答案:xxxxx\n问题:xxx,答案:xxxxx\n...
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
memories = []
|
||||||
|
|
||||||
|
# 从fetched_memory_list中获取该chat_id的所有记忆
|
||||||
|
for cid, (question, answer, timestamp) in self.fetched_memory_list:
|
||||||
|
if cid == chat_id:
|
||||||
|
memories.append(f"问题:{question},答案:{answer}")
|
||||||
|
|
||||||
|
# 按时间戳排序(最新的在后面)
|
||||||
|
memories.sort()
|
||||||
|
|
||||||
|
# 用换行符连接所有记忆
|
||||||
|
result = "\n".join(memories)
|
||||||
|
|
||||||
|
logger.info(f"chat_id {chat_id} 共有 {len(memories)} 条记忆")
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取chat_id {chat_id} 的记忆时出错: {e}")
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
async def select_title_by_question(self, question: str) -> str:
|
||||||
|
"""
|
||||||
|
根据消息内容选择最匹配的标题
|
||||||
|
|
||||||
|
Args:
|
||||||
|
question: 问题
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 选择的标题
|
||||||
|
"""
|
||||||
|
# 获取所有标题并构建格式化字符串
|
||||||
|
titles = self.get_all_titles()
|
||||||
|
formatted_titles = ""
|
||||||
|
for title in titles:
|
||||||
|
formatted_titles += f"{title}\n"
|
||||||
|
|
||||||
|
prompt = f"""
|
||||||
|
所有主题:
|
||||||
|
{formatted_titles}
|
||||||
|
|
||||||
|
请根据以下问题,选择一个能够回答问题的主题:
|
||||||
|
问题:{question}
|
||||||
|
请你输出主题,不要输出其他内容,完整输出主题名:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if global_config.debug.show_prompt:
|
||||||
|
logger.info(f"记忆仓库选择标题 prompt: {prompt}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"记忆仓库选择标题 prompt: {prompt}")
|
||||||
|
|
||||||
|
|
||||||
|
title, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
|
||||||
|
|
||||||
|
# 根据 title 获取 titles 里的对应项
|
||||||
|
titles = self.get_all_titles()
|
||||||
|
selected_title = None
|
||||||
|
|
||||||
|
# 查找完全匹配的标题
|
||||||
|
for t in titles:
|
||||||
|
if t == title:
|
||||||
|
selected_title = t
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
logger.info(f"记忆仓库选择标题: {selected_title}")
|
||||||
|
|
||||||
|
return selected_title
|
||||||
|
|
||||||
|
def _cleanup_fetched_memory_list(self):
|
||||||
|
"""
|
||||||
|
清理fetched_memory_list,移除超过10分钟的记忆和超过10条的最旧记忆
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
current_time = time.time()
|
||||||
|
ten_minutes_ago = current_time - 600 # 10分钟 = 600秒
|
||||||
|
|
||||||
|
# 移除超过10分钟的记忆
|
||||||
|
self.fetched_memory_list = [
|
||||||
|
(chat_id, (question, answer, timestamp))
|
||||||
|
for chat_id, (question, answer, timestamp) in self.fetched_memory_list
|
||||||
|
if timestamp > ten_minutes_ago
|
||||||
|
]
|
||||||
|
|
||||||
|
# 如果记忆条数超过10条,移除最旧的5条
|
||||||
|
if len(self.fetched_memory_list) > 10:
|
||||||
|
# 按时间戳排序,移除最旧的5条
|
||||||
|
self.fetched_memory_list.sort(key=lambda x: x[1][2]) # 按timestamp排序
|
||||||
|
self.fetched_memory_list = self.fetched_memory_list[5:] # 保留最新的5条
|
||||||
|
|
||||||
|
logger.debug(f"fetched_memory_list清理后,当前有 {len(self.fetched_memory_list)} 条记忆")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"清理fetched_memory_list时出错: {e}")
|
||||||
|
|
||||||
|
async def _save_to_database_and_clear(self, chat_id: str, content: str):
|
||||||
|
"""
|
||||||
|
生成标题,保存到数据库,并清空对应chat_id的running_content
|
||||||
|
|
||||||
|
Args:
|
||||||
|
chat_id: 聊天ID
|
||||||
|
content: 要保存的内容
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 生成标题
|
||||||
|
title_prompt = f"""
|
||||||
|
请为以下内容生成一个描述全面的标题,要求描述内容的主要概念和事件:
|
||||||
|
{content}
|
||||||
|
|
||||||
|
请只输出标题,不要输出其他内容:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if global_config.debug.show_prompt:
|
||||||
|
logger.info(f"记忆仓库生成标题 prompt: {title_prompt}")
|
||||||
|
else:
|
||||||
|
logger.debug(f"记忆仓库生成标题 prompt: {title_prompt}")
|
||||||
|
|
||||||
|
title, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(title_prompt)
|
||||||
|
|
||||||
|
if title:
|
||||||
|
# 保存到数据库
|
||||||
|
MemoryChestModel.create(
|
||||||
|
title=title.strip(),
|
||||||
|
content=content
|
||||||
|
)
|
||||||
|
logger.info(f"已保存记忆仓库内容,标题: {title.strip()}, chat_id: {chat_id}")
|
||||||
|
|
||||||
|
# 清空对应chat_id的running_content
|
||||||
|
if chat_id in self.running_content_list:
|
||||||
|
del self.running_content_list[chat_id]
|
||||||
|
logger.info(f"已清空chat_id {chat_id} 的running_content")
|
||||||
|
else:
|
||||||
|
logger.warning(f"生成标题失败,chat_id: {chat_id}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"保存记忆仓库内容时出错: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
global_memory_chest = MemoryChest()
|
||||||
@@ -8,7 +8,7 @@ from maim_message import UserInfo, Seg, GroupInfo
|
|||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.mood.mood_manager import mood_manager # 导入情绪管理器
|
from src.mood.mood_manager import mood_manager # 导入情绪管理器
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager, ChatStream
|
from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
from src.chat.message_receive.message import MessageRecv, MessageRecvS4U
|
from src.chat.message_receive.message import MessageRecv, MessageRecvS4U
|
||||||
from src.chat.message_receive.storage import MessageStorage
|
from src.chat.message_receive.storage import MessageStorage
|
||||||
from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver
|
from src.chat.heart_flow.heartflow_message_processor import HeartFCMessageReceiver
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ no_reply_until_call
|
|||||||
"""
|
"""
|
||||||
{action_name}
|
{action_name}
|
||||||
动作描述:{action_description}
|
动作描述:{action_description}
|
||||||
使用条件:
|
使用条件{parallel_text}:
|
||||||
{action_require}
|
{action_require}
|
||||||
{{
|
{{
|
||||||
"action": "{action_name}",{action_parameters},
|
"action": "{action_name}",{action_parameters},
|
||||||
@@ -344,7 +344,6 @@ class ActionPlanner:
|
|||||||
plan_style=global_config.personality.plan_style,
|
plan_style=global_config.personality.plan_style,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
return prompt, message_id_list
|
return prompt, message_id_list
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"构建 Planner 提示词时出错: {e}")
|
logger.error(f"构建 Planner 提示词时出错: {e}")
|
||||||
@@ -422,6 +421,11 @@ class ActionPlanner:
|
|||||||
require_text += f"- {require_item}\n"
|
require_text += f"- {require_item}\n"
|
||||||
require_text = require_text.rstrip("\n")
|
require_text = require_text.rstrip("\n")
|
||||||
|
|
||||||
|
if not action_info.parallel_action:
|
||||||
|
parallel_text = "(当选择这个动作时,请不要选择其他动作)"
|
||||||
|
else:
|
||||||
|
parallel_text = ""
|
||||||
|
|
||||||
# 获取动作提示模板并填充
|
# 获取动作提示模板并填充
|
||||||
using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
|
using_action_prompt = await global_prompt_manager.get_prompt_async("action_prompt")
|
||||||
using_action_prompt = using_action_prompt.format(
|
using_action_prompt = using_action_prompt.format(
|
||||||
@@ -429,6 +433,7 @@ class ActionPlanner:
|
|||||||
action_description=action_info.description,
|
action_description=action_info.description,
|
||||||
action_parameters=param_text,
|
action_parameters=param_text,
|
||||||
action_require=require_text,
|
action_require=require_text,
|
||||||
|
parallel_text=parallel_text,
|
||||||
)
|
)
|
||||||
|
|
||||||
action_options_block += using_action_prompt
|
action_options_block += using_action_prompt
|
||||||
@@ -502,9 +507,7 @@ class ActionPlanner:
|
|||||||
action.action_data = action.action_data or {}
|
action.action_data = action.action_data or {}
|
||||||
action.action_data["loop_start_time"] = loop_start_time
|
action.action_data["loop_start_time"] = loop_start_time
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"{self.log_prefix}规划器选择了{len(actions)}个动作: {' '.join([a.action_type for a in actions])}")
|
||||||
f"{self.log_prefix}规划器选择了{len(actions)}个动作: {' '.join([a.action_type for a in actions])}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import re
|
|||||||
|
|
||||||
from typing import List, Optional, Dict, Any, Tuple
|
from typing import List, Optional, Dict, Any, Tuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||||
from src.mais4u.mai_think import mai_thinking_manager
|
from src.mais4u.mai_think import mai_thinking_manager
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.common.data_models.database_data_model import DatabaseMessages
|
from src.common.data_models.database_data_model import DatabaseMessages
|
||||||
@@ -27,7 +28,7 @@ from src.chat.utils.chat_message_builder import (
|
|||||||
from src.chat.express.expression_selector import expression_selector
|
from src.chat.express.expression_selector import expression_selector
|
||||||
|
|
||||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||||
from src.person_info.person_info import Person, is_person_known
|
from src.person_info.person_info import Person
|
||||||
from src.plugin_system.base.component_types import ActionInfo, EventType
|
from src.plugin_system.base.component_types import ActionInfo, EventType
|
||||||
from src.plugin_system.apis import llm_api
|
from src.plugin_system.apis import llm_api
|
||||||
|
|
||||||
@@ -42,6 +43,7 @@ init_rewrite_prompt()
|
|||||||
|
|
||||||
logger = get_logger("replyer")
|
logger = get_logger("replyer")
|
||||||
|
|
||||||
|
|
||||||
class DefaultReplyer:
|
class DefaultReplyer:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -215,7 +217,7 @@ class DefaultReplyer:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return False, llm_response
|
return False, llm_response
|
||||||
|
|
||||||
#移动到 relation插件中构建
|
# 移动到 relation插件中构建
|
||||||
# async def build_relation_info(self, chat_content: str, sender: str, person_list: List[Person]):
|
# async def build_relation_info(self, chat_content: str, sender: str, person_list: List[Person]):
|
||||||
# if not global_config.relationship.enable_relationship:
|
# if not global_config.relationship.enable_relationship:
|
||||||
# return ""
|
# return ""
|
||||||
@@ -277,9 +279,7 @@ class DefaultReplyer:
|
|||||||
expression_habits_block = ""
|
expression_habits_block = ""
|
||||||
expression_habits_title = ""
|
expression_habits_title = ""
|
||||||
if style_habits_str.strip():
|
if style_habits_str.strip():
|
||||||
expression_habits_title = (
|
expression_habits_title = "在回复时,你可以参考以下的语言习惯,不要生硬使用:"
|
||||||
"在回复时,你可以参考以下的语言习惯,不要生硬使用:"
|
|
||||||
)
|
|
||||||
expression_habits_block += f"{style_habits_str}\n"
|
expression_habits_block += f"{style_habits_str}\n"
|
||||||
|
|
||||||
return f"{expression_habits_title}\n{expression_habits_block}", selected_ids
|
return f"{expression_habits_title}\n{expression_habits_block}", selected_ids
|
||||||
@@ -316,6 +316,17 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
# return memory_str
|
# return memory_str
|
||||||
|
|
||||||
|
async def build_memory_block(self) -> str:
|
||||||
|
"""构建记忆块
|
||||||
|
"""
|
||||||
|
# if not global_config.memory.enable_memory:
|
||||||
|
# return ""
|
||||||
|
|
||||||
|
if global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id):
|
||||||
|
return f"你有以下记忆:\n{global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id)}"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
|
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
|
||||||
"""构建工具信息块
|
"""构建工具信息块
|
||||||
|
|
||||||
@@ -498,7 +509,6 @@ class DefaultReplyer:
|
|||||||
--------------------------------
|
--------------------------------
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
# 构建背景对话 prompt
|
# 构建背景对话 prompt
|
||||||
all_dialogue_prompt = ""
|
all_dialogue_prompt = ""
|
||||||
if message_list_before_now:
|
if message_list_before_now:
|
||||||
@@ -524,7 +534,6 @@ class DefaultReplyer:
|
|||||||
time_block: str,
|
time_block: str,
|
||||||
chat_target_1: str,
|
chat_target_1: str,
|
||||||
chat_target_2: str,
|
chat_target_2: str,
|
||||||
|
|
||||||
identity_block: str,
|
identity_block: str,
|
||||||
sender: str,
|
sender: str,
|
||||||
target: str,
|
target: str,
|
||||||
@@ -701,6 +710,7 @@ class DefaultReplyer:
|
|||||||
# self.build_relation_info(chat_talking_prompt_short, sender, person_list_short), "relation_info"
|
# self.build_relation_info(chat_talking_prompt_short, sender, person_list_short), "relation_info"
|
||||||
# ),
|
# ),
|
||||||
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
|
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
|
||||||
|
self._time_and_run_task(self.build_memory_block(), "memory_block"),
|
||||||
self._time_and_run_task(
|
self._time_and_run_task(
|
||||||
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
|
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
|
||||||
),
|
),
|
||||||
@@ -714,6 +724,7 @@ class DefaultReplyer:
|
|||||||
"expression_habits": "选取表达方式",
|
"expression_habits": "选取表达方式",
|
||||||
"relation_info": "感受关系",
|
"relation_info": "感受关系",
|
||||||
# "memory_block": "回忆",
|
# "memory_block": "回忆",
|
||||||
|
"memory_block": "记忆",
|
||||||
"tool_info": "使用工具",
|
"tool_info": "使用工具",
|
||||||
"prompt_info": "获取知识",
|
"prompt_info": "获取知识",
|
||||||
"actions_info": "动作信息",
|
"actions_info": "动作信息",
|
||||||
@@ -742,6 +753,7 @@ class DefaultReplyer:
|
|||||||
selected_expressions: List[int]
|
selected_expressions: List[int]
|
||||||
# relation_info: str = results_dict["relation_info"]
|
# relation_info: str = results_dict["relation_info"]
|
||||||
# memory_block: str = results_dict["memory_block"]
|
# memory_block: str = results_dict["memory_block"]
|
||||||
|
memory_block: str = results_dict["memory_block"]
|
||||||
tool_info: str = results_dict["tool_info"]
|
tool_info: str = results_dict["tool_info"]
|
||||||
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
||||||
actions_info: str = results_dict["actions_info"]
|
actions_info: str = results_dict["actions_info"]
|
||||||
@@ -759,13 +771,9 @@ class DefaultReplyer:
|
|||||||
|
|
||||||
if sender:
|
if sender:
|
||||||
if is_group_chat:
|
if is_group_chat:
|
||||||
reply_target_block = (
|
reply_target_block = f"现在{sender}说的:{target}。引起了你的注意"
|
||||||
f"现在{sender}说的:{target}。引起了你的注意"
|
|
||||||
)
|
|
||||||
else: # private chat
|
else: # private chat
|
||||||
reply_target_block = (
|
reply_target_block = f"现在{sender}说的:{target}。引起了你的注意"
|
||||||
f"现在{sender}说的:{target}。引起了你的注意"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
reply_target_block = ""
|
reply_target_block = ""
|
||||||
|
|
||||||
@@ -779,6 +787,7 @@ class DefaultReplyer:
|
|||||||
"replyer_self_prompt",
|
"replyer_self_prompt",
|
||||||
expression_habits_block=expression_habits_block,
|
expression_habits_block=expression_habits_block,
|
||||||
tool_info_block=tool_info,
|
tool_info_block=tool_info,
|
||||||
|
memory_block=memory_block,
|
||||||
knowledge_prompt=prompt_info,
|
knowledge_prompt=prompt_info,
|
||||||
# memory_block=memory_block,
|
# memory_block=memory_block,
|
||||||
# relation_info_block=relation_info,
|
# relation_info_block=relation_info,
|
||||||
@@ -798,6 +807,7 @@ class DefaultReplyer:
|
|||||||
"replyer_prompt",
|
"replyer_prompt",
|
||||||
expression_habits_block=expression_habits_block,
|
expression_habits_block=expression_habits_block,
|
||||||
tool_info_block=tool_info,
|
tool_info_block=tool_info,
|
||||||
|
memory_block=memory_block,
|
||||||
knowledge_prompt=prompt_info,
|
knowledge_prompt=prompt_info,
|
||||||
# memory_block=memory_block,
|
# memory_block=memory_block,
|
||||||
# relation_info_block=relation_info,
|
# relation_info_block=relation_info,
|
||||||
@@ -946,7 +956,7 @@ class DefaultReplyer:
|
|||||||
async def llm_generate_content(self, prompt: str):
|
async def llm_generate_content(self, prompt: str):
|
||||||
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
with Timer("LLM生成", {}): # 内部计时器,可选保留
|
||||||
# 直接使用已初始化的模型实例
|
# 直接使用已初始化的模型实例
|
||||||
# logger.info(f"\n{prompt}\n")
|
logger.info(f"\n{prompt}\n")
|
||||||
|
|
||||||
if global_config.debug.show_prompt:
|
if global_config.debug.show_prompt:
|
||||||
logger.info(f"\n{prompt}\n")
|
logger.info(f"\n{prompt}\n")
|
||||||
@@ -1044,6 +1054,3 @@ def weighted_sample_no_replacement(items, weights, k) -> list:
|
|||||||
pool.pop(idx)
|
pool.pop(idx)
|
||||||
break
|
break
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import re
|
|||||||
|
|
||||||
from typing import List, Optional, Dict, Any, Tuple
|
from typing import List, Optional, Dict, Any, Tuple
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||||
from src.mais4u.mai_think import mai_thinking_manager
|
from src.mais4u.mai_think import mai_thinking_manager
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.common.data_models.database_data_model import DatabaseMessages
|
from src.common.data_models.database_data_model import DatabaseMessages
|
||||||
@@ -312,6 +313,15 @@ class PrivateReplyer:
|
|||||||
|
|
||||||
# return memory_str
|
# return memory_str
|
||||||
|
|
||||||
|
|
||||||
|
async def build_memory_block(self) -> str:
|
||||||
|
"""构建记忆块
|
||||||
|
"""
|
||||||
|
if global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id):
|
||||||
|
return f"你有以下记忆:\n{global_memory_chest.get_chat_memories_as_string(self.chat_stream.stream_id)}"
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
|
async def build_tool_info(self, chat_history: str, sender: str, target: str, enable_tool: bool = True) -> str:
|
||||||
"""构建工具信息块
|
"""构建工具信息块
|
||||||
|
|
||||||
@@ -582,6 +592,7 @@ class PrivateReplyer:
|
|||||||
self._time_and_run_task(
|
self._time_and_run_task(
|
||||||
self.build_relation_info(chat_talking_prompt_short, sender), "relation_info"
|
self.build_relation_info(chat_talking_prompt_short, sender), "relation_info"
|
||||||
),
|
),
|
||||||
|
self._time_and_run_task(self.build_memory_block(), "memory_block"),
|
||||||
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
|
# self._time_and_run_task(self.build_memory_block(message_list_before_short, target), "memory_block"),
|
||||||
self._time_and_run_task(
|
self._time_and_run_task(
|
||||||
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
|
self.build_tool_info(chat_talking_prompt_short, sender, target, enable_tool=enable_tool), "tool_info"
|
||||||
@@ -595,7 +606,7 @@ class PrivateReplyer:
|
|||||||
task_name_mapping = {
|
task_name_mapping = {
|
||||||
"expression_habits": "选取表达方式",
|
"expression_habits": "选取表达方式",
|
||||||
"relation_info": "感受关系",
|
"relation_info": "感受关系",
|
||||||
# "memory_block": "回忆",
|
"memory_block": "回忆",
|
||||||
"tool_info": "使用工具",
|
"tool_info": "使用工具",
|
||||||
"prompt_info": "获取知识",
|
"prompt_info": "获取知识",
|
||||||
"actions_info": "动作信息",
|
"actions_info": "动作信息",
|
||||||
@@ -623,7 +634,7 @@ class PrivateReplyer:
|
|||||||
expression_habits_block: str
|
expression_habits_block: str
|
||||||
selected_expressions: List[int]
|
selected_expressions: List[int]
|
||||||
relation_info: str = results_dict["relation_info"]
|
relation_info: str = results_dict["relation_info"]
|
||||||
# memory_block: str = results_dict["memory_block"]
|
memory_block: str = results_dict["memory_block"]
|
||||||
tool_info: str = results_dict["tool_info"]
|
tool_info: str = results_dict["tool_info"]
|
||||||
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
||||||
actions_info: str = results_dict["actions_info"]
|
actions_info: str = results_dict["actions_info"]
|
||||||
@@ -649,7 +660,7 @@ class PrivateReplyer:
|
|||||||
expression_habits_block=expression_habits_block,
|
expression_habits_block=expression_habits_block,
|
||||||
tool_info_block=tool_info,
|
tool_info_block=tool_info,
|
||||||
knowledge_prompt=prompt_info,
|
knowledge_prompt=prompt_info,
|
||||||
# memory_block=memory_block,
|
memory_block=memory_block,
|
||||||
relation_info_block=relation_info,
|
relation_info_block=relation_info,
|
||||||
extra_info_block=extra_info_block,
|
extra_info_block=extra_info_block,
|
||||||
identity=personality_prompt,
|
identity=personality_prompt,
|
||||||
@@ -670,7 +681,7 @@ class PrivateReplyer:
|
|||||||
expression_habits_block=expression_habits_block,
|
expression_habits_block=expression_habits_block,
|
||||||
tool_info_block=tool_info,
|
tool_info_block=tool_info,
|
||||||
knowledge_prompt=prompt_info,
|
knowledge_prompt=prompt_info,
|
||||||
# memory_block=memory_block,
|
memory_block=memory_block,
|
||||||
relation_info_block=relation_info,
|
relation_info_block=relation_info,
|
||||||
extra_info_block=extra_info_block,
|
extra_info_block=extra_info_block,
|
||||||
identity=personality_prompt,
|
identity=personality_prompt,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
|
|
||||||
from src.chat.utils.prompt_builder import Prompt
|
from src.chat.utils.prompt_builder import Prompt
|
||||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def init_lpmm_prompt():
|
def init_lpmm_prompt():
|
||||||
Prompt(
|
Prompt(
|
||||||
"""
|
"""
|
||||||
@@ -20,5 +18,3 @@ If you need to use the search tool, please directly call the function "lpmm_sear
|
|||||||
""",
|
""",
|
||||||
name="lpmm_get_knowledge_prompt",
|
name="lpmm_get_knowledge_prompt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ def init_replyer_prompt():
|
|||||||
|
|
||||||
Prompt(
|
Prompt(
|
||||||
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
||||||
{expression_habits_block}
|
{expression_habits_block}{memory_block}
|
||||||
|
|
||||||
你正在qq群里聊天,下面是群里正在聊的内容:
|
你正在qq群里聊天,下面是群里正在聊的内容:
|
||||||
{time_block}
|
{time_block}
|
||||||
@@ -34,7 +34,7 @@ def init_replyer_prompt():
|
|||||||
|
|
||||||
Prompt(
|
Prompt(
|
||||||
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
||||||
{expression_habits_block}
|
{expression_habits_block}{memory_block}
|
||||||
|
|
||||||
你正在qq群里聊天,下面是群里正在聊的内容:
|
你正在qq群里聊天,下面是群里正在聊的内容:
|
||||||
{time_block}
|
{time_block}
|
||||||
@@ -55,7 +55,7 @@ def init_replyer_prompt():
|
|||||||
|
|
||||||
Prompt(
|
Prompt(
|
||||||
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
||||||
{expression_habits_block}
|
{expression_habits_block}{memory_block}
|
||||||
|
|
||||||
你正在和{sender_name}聊天,这是你们之前聊的内容:
|
你正在和{sender_name}聊天,这是你们之前聊的内容:
|
||||||
{time_block}
|
{time_block}
|
||||||
@@ -74,7 +74,7 @@ def init_replyer_prompt():
|
|||||||
|
|
||||||
Prompt(
|
Prompt(
|
||||||
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
"""{knowledge_prompt}{tool_info_block}{extra_info_block}
|
||||||
{expression_habits_block}
|
{expression_habits_block}{memory_block}
|
||||||
|
|
||||||
你正在和{sender_name}聊天,这是你们之前聊的内容:
|
你正在和{sender_name}聊天,这是你们之前聊的内容:
|
||||||
{time_block}
|
{time_block}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
|
|
||||||
from src.chat.utils.prompt_builder import Prompt
|
from src.chat.utils.prompt_builder import Prompt
|
||||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def init_rewrite_prompt():
|
def init_rewrite_prompt():
|
||||||
Prompt("你正在qq群里聊天,下面是群里正在聊的内容:", "chat_target_group1")
|
Prompt("你正在qq群里聊天,下面是群里正在聊的内容:", "chat_target_group1")
|
||||||
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
|
Prompt("你正在和{sender_name}聊天,这是你们之前聊的内容:", "chat_target_private1")
|
||||||
|
|||||||
@@ -859,7 +859,6 @@ async def build_anonymous_messages(messages: List[DatabaseMessages]) -> str:
|
|||||||
# 处理图片ID
|
# 处理图片ID
|
||||||
content = process_pic_ids(content)
|
content = process_pic_ids(content)
|
||||||
|
|
||||||
|
|
||||||
anon_name = get_anon_name(platform, user_id)
|
anon_name = get_anon_name(platform, user_id)
|
||||||
# print(f"anon_name:{anon_name}")
|
# print(f"anon_name:{anon_name}")
|
||||||
|
|
||||||
@@ -945,11 +944,12 @@ async def build_bare_messages(messages: List[DatabaseMessages]) -> str:
|
|||||||
# 获取纯文本内容
|
# 获取纯文本内容
|
||||||
content = msg.processed_plain_text or ""
|
content = msg.processed_plain_text or ""
|
||||||
|
|
||||||
|
|
||||||
# 处理图片ID
|
# 处理图片ID
|
||||||
pic_pattern = r"\[picid:[^\]]+\]"
|
pic_pattern = r"\[picid:[^\]]+\]"
|
||||||
|
|
||||||
def replace_pic_id(match):
|
def replace_pic_id(match):
|
||||||
return "[图片]"
|
return "[图片]"
|
||||||
|
|
||||||
content = re.sub(pic_pattern, replace_pic_id, content)
|
content = re.sub(pic_pattern, replace_pic_id, content)
|
||||||
|
|
||||||
# 处理用户引用格式,移除回复和@标记
|
# 处理用户引用格式,移除回复和@标记
|
||||||
|
|||||||
@@ -317,6 +317,19 @@ class Expression(BaseModel):
|
|||||||
class Meta:
|
class Meta:
|
||||||
table_name = "expression"
|
table_name = "expression"
|
||||||
|
|
||||||
|
class MemoryChest(BaseModel):
|
||||||
|
"""
|
||||||
|
用于存储记忆仓库的模型
|
||||||
|
"""
|
||||||
|
|
||||||
|
title = TextField() # 标题
|
||||||
|
content = TextField() # 内容
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
table_name = "memory_chest"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GraphNodes(BaseModel):
|
class GraphNodes(BaseModel):
|
||||||
"""
|
"""
|
||||||
@@ -369,6 +382,7 @@ def create_tables():
|
|||||||
GraphNodes, # 添加图节点表
|
GraphNodes, # 添加图节点表
|
||||||
GraphEdges, # 添加图边表
|
GraphEdges, # 添加图边表
|
||||||
ActionRecords, # 添加 ActionRecords 到初始化列表
|
ActionRecords, # 添加 ActionRecords 到初始化列表
|
||||||
|
MemoryChest,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -396,6 +410,7 @@ def initialize_database(sync_constraints=False):
|
|||||||
GraphNodes,
|
GraphNodes,
|
||||||
GraphEdges,
|
GraphEdges,
|
||||||
ActionRecords, # 添加 ActionRecords 到初始化列表
|
ActionRecords, # 添加 ActionRecords 到初始化列表
|
||||||
|
MemoryChest,
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -493,6 +508,7 @@ def sync_field_constraints():
|
|||||||
GraphNodes,
|
GraphNodes,
|
||||||
GraphEdges,
|
GraphEdges,
|
||||||
ActionRecords,
|
ActionRecords,
|
||||||
|
MemoryChest,
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -732,11 +748,14 @@ def check_field_constraints():
|
|||||||
logger.exception(f"检查字段约束时出错: {e}")
|
logger.exception(f"检查字段约束时出错: {e}")
|
||||||
|
|
||||||
return inconsistencies
|
return inconsistencies
|
||||||
|
|
||||||
|
|
||||||
def fix_image_id():
|
def fix_image_id():
|
||||||
"""
|
"""
|
||||||
修复表情包的 image_id 字段
|
修复表情包的 image_id 字段
|
||||||
"""
|
"""
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with db:
|
with db:
|
||||||
for img in Images.select():
|
for img in Images.select():
|
||||||
@@ -747,6 +766,7 @@ def fix_image_id():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"修复 image_id 时出错: {e}")
|
logger.exception(f"修复 image_id 时出错: {e}")
|
||||||
|
|
||||||
|
|
||||||
# 模块加载时调用初始化函数
|
# 模块加载时调用初始化函数
|
||||||
initialize_database(sync_constraints=True)
|
initialize_database(sync_constraints=True)
|
||||||
fix_image_id()
|
fix_image_id()
|
||||||
@@ -53,7 +53,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template")
|
|||||||
|
|
||||||
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
||||||
# 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/
|
# 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/
|
||||||
MMC_VERSION = "0.10.4-snapshot.1"
|
MMC_VERSION = "0.11.0-snapshot.1"
|
||||||
|
|
||||||
|
|
||||||
def get_key_comment(toml_table, key):
|
def get_key_comment(toml_table, key):
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ class EmojiConfig(ConfigBase):
|
|||||||
filtration_prompt: str = "符合公序良俗"
|
filtration_prompt: str = "符合公序良俗"
|
||||||
"""表情包过滤要求"""
|
"""表情包过滤要求"""
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class KeywordRuleConfig(ConfigBase):
|
class KeywordRuleConfig(ConfigBase):
|
||||||
"""关键词规则配置类"""
|
"""关键词规则配置类"""
|
||||||
|
|||||||
@@ -396,10 +396,7 @@ def _default_normal_response_parser(
|
|||||||
" 可能会对回复内容造成影响,建议修改模型 max_tokens 配置!"
|
" 可能会对回复内容造成影响,建议修改模型 max_tokens 配置!"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
logger.warning("⚠ Gemini 响应因达到 max_tokens 限制被截断,\n 请修改模型 max_tokens 配置!")
|
||||||
"⚠ Gemini 响应因达到 max_tokens 限制被截断,\n"
|
|
||||||
" 请修改模型 max_tokens 配置!"
|
|
||||||
)
|
|
||||||
|
|
||||||
return api_response, _usage_record
|
return api_response, _usage_record
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -103,9 +103,7 @@ class ChatMood:
|
|||||||
if random.random() > update_probability:
|
if random.random() > update_probability:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(f"{self.log_prefix} 更新情绪状态,更新概率: {update_probability:.2f}")
|
||||||
f"{self.log_prefix} 更新情绪状态,更新概率: {update_probability:.2f}"
|
|
||||||
)
|
|
||||||
|
|
||||||
message_time: float = message.message_info.time # type: ignore
|
message_time: float = message.message_info.time # type: ignore
|
||||||
message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive(
|
message_list_before_now = get_raw_msg_by_timestamp_with_chat_inclusive(
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ from src.config.config import global_config, model_config
|
|||||||
|
|
||||||
logger = get_logger("person_info")
|
logger = get_logger("person_info")
|
||||||
|
|
||||||
relation_selection_model = LLMRequest(model_set=model_config.model_task_config.utils_small, request_type="relation_selection")
|
relation_selection_model = LLMRequest(
|
||||||
|
model_set=model_config.model_task_config.utils_small, request_type="relation_selection"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_person_id(platform: str, user_id: Union[int, str]) -> str:
|
def get_person_id(platform: str, user_id: Union[int, str]) -> str:
|
||||||
@@ -93,7 +95,8 @@ def extract_categories_from_response(response: str) -> list[str]:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
import re
|
import re
|
||||||
pattern = r'<([^<>]+)>'
|
|
||||||
|
pattern = r"<([^<>]+)>"
|
||||||
matches = re.findall(pattern, response)
|
matches = re.findall(pattern, response)
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
@@ -420,7 +423,7 @@ class Person:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"同步用户 {self.person_id} 信息到数据库时出错: {e}")
|
logger.error(f"同步用户 {self.person_id} 信息到数据库时出错: {e}")
|
||||||
|
|
||||||
async def build_relationship(self,chat_content:str = "",info_type = ""):
|
async def build_relationship(self, chat_content: str = "", info_type=""):
|
||||||
if not self.is_known:
|
if not self.is_known:
|
||||||
return ""
|
return ""
|
||||||
# 构建points文本
|
# 构建points文本
|
||||||
@@ -453,7 +456,9 @@ class Person:
|
|||||||
for category in category_list:
|
for category in category_list:
|
||||||
random_memory = self.get_random_memory_by_category(category, 2)
|
random_memory = self.get_random_memory_by_category(category, 2)
|
||||||
if random_memory:
|
if random_memory:
|
||||||
random_memory_str = "\n".join([get_memory_content_from_memory(memory) for memory in random_memory])
|
random_memory_str = "\n".join(
|
||||||
|
[get_memory_content_from_memory(memory) for memory in random_memory]
|
||||||
|
)
|
||||||
points_text = f"有关 {category} 的内容:{random_memory_str}"
|
points_text = f"有关 {category} 的内容:{random_memory_str}"
|
||||||
break
|
break
|
||||||
elif info_type:
|
elif info_type:
|
||||||
@@ -473,11 +478,12 @@ class Person:
|
|||||||
for category in category_list:
|
for category in category_list:
|
||||||
random_memory = self.get_random_memory_by_category(category, 3)
|
random_memory = self.get_random_memory_by_category(category, 3)
|
||||||
if random_memory:
|
if random_memory:
|
||||||
random_memory_str = "\n".join([get_memory_content_from_memory(memory) for memory in random_memory])
|
random_memory_str = "\n".join(
|
||||||
|
[get_memory_content_from_memory(memory) for memory in random_memory]
|
||||||
|
)
|
||||||
points_text = f"有关 {category} 的内容:{random_memory_str}"
|
points_text = f"有关 {category} 的内容:{random_memory_str}"
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|
||||||
for category in category_list:
|
for category in category_list:
|
||||||
random_memory = self.get_random_memory_by_category(category, 1)[0]
|
random_memory = self.get_random_memory_by_category(category, 1)[0]
|
||||||
if random_memory:
|
if random_memory:
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import random
|
|||||||
import base64
|
import base64
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
import time
|
|
||||||
|
|
||||||
from typing import Optional, Tuple, List, Dict, Any
|
from typing import Optional, Tuple, List, Dict, Any
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
@@ -358,7 +357,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 3. 确保emoji目录存在
|
# 3. 确保emoji目录存在
|
||||||
@@ -368,19 +367,21 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
if not filename:
|
if not filename:
|
||||||
# 基于时间戳、微秒和短base64生成唯一文件名
|
# 基于时间戳、微秒和短base64生成唯一文件名
|
||||||
import time
|
import time
|
||||||
|
|
||||||
timestamp = int(time.time())
|
timestamp = int(time.time())
|
||||||
microseconds = int(time.time() * 1000000) % 1000000 # 添加微秒级精度
|
microseconds = int(time.time() * 1000000) % 1000000 # 添加微秒级精度
|
||||||
|
|
||||||
# 生成12位随机标识符,使用base64编码(增加随机性)
|
# 生成12位随机标识符,使用base64编码(增加随机性)
|
||||||
import random
|
import random
|
||||||
random_bytes = random.getrandbits(72).to_bytes(9, 'big') # 72位 = 9字节 = 12位base64
|
|
||||||
short_id = base64.b64encode(random_bytes).decode('ascii')[:12].rstrip('=')
|
random_bytes = random.getrandbits(72).to_bytes(9, "big") # 72位 = 9字节 = 12位base64
|
||||||
|
short_id = base64.b64encode(random_bytes).decode("ascii")[:12].rstrip("=")
|
||||||
# 确保base64编码适合文件名(替换/和-)
|
# 确保base64编码适合文件名(替换/和-)
|
||||||
short_id = short_id.replace('/', '_').replace('+', '-')
|
short_id = short_id.replace("/", "_").replace("+", "-")
|
||||||
filename = f"emoji_{timestamp}_{microseconds}_{short_id}"
|
filename = f"emoji_{timestamp}_{microseconds}_{short_id}"
|
||||||
|
|
||||||
# 确保文件名有扩展名
|
# 确保文件名有扩展名
|
||||||
if not filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')):
|
if not filename.lower().endswith((".jpg", ".jpeg", ".png", ".gif")):
|
||||||
filename = f"{filename}.png" # 默认使用png格式
|
filename = f"{filename}.png" # 默认使用png格式
|
||||||
|
|
||||||
# 检查文件名是否已存在,如果存在则重新生成短标识符
|
# 检查文件名是否已存在,如果存在则重新生成短标识符
|
||||||
@@ -390,14 +391,15 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
while os.path.exists(temp_file_path) and attempts < max_attempts:
|
while os.path.exists(temp_file_path) and attempts < max_attempts:
|
||||||
# 重新生成短标识符
|
# 重新生成短标识符
|
||||||
import random
|
import random
|
||||||
random_bytes = random.getrandbits(48).to_bytes(6, 'big')
|
|
||||||
short_id = base64.b64encode(random_bytes).decode('ascii')[:8].rstrip('=')
|
random_bytes = random.getrandbits(48).to_bytes(6, "big")
|
||||||
short_id = short_id.replace('/', '_').replace('+', '-')
|
short_id = base64.b64encode(random_bytes).decode("ascii")[:8].rstrip("=")
|
||||||
|
short_id = short_id.replace("/", "_").replace("+", "-")
|
||||||
|
|
||||||
# 分离文件名和扩展名,重新生成文件名
|
# 分离文件名和扩展名,重新生成文件名
|
||||||
name_part, ext = os.path.splitext(filename)
|
name_part, ext = os.path.splitext(filename)
|
||||||
# 去掉原来的标识符,添加新的
|
# 去掉原来的标识符,添加新的
|
||||||
base_name = name_part.rsplit('_', 1)[0] # 移除最后一个_后的部分
|
base_name = name_part.rsplit("_", 1)[0] # 移除最后一个_后的部分
|
||||||
filename = f"{base_name}_{short_id}{ext}"
|
filename = f"{base_name}_{short_id}{ext}"
|
||||||
temp_file_path = os.path.join(EMOJI_DIR, filename)
|
temp_file_path = os.path.join(EMOJI_DIR, filename)
|
||||||
attempts += 1
|
attempts += 1
|
||||||
@@ -406,7 +408,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
if os.path.exists(temp_file_path):
|
if os.path.exists(temp_file_path):
|
||||||
uuid_short = str(uuid.uuid4())[:8]
|
uuid_short = str(uuid.uuid4())[:8]
|
||||||
name_part, ext = os.path.splitext(filename)
|
name_part, ext = os.path.splitext(filename)
|
||||||
base_name = name_part.rsplit('_', 1)[0]
|
base_name = name_part.rsplit("_", 1)[0]
|
||||||
filename = f"{base_name}_{uuid_short}{ext}"
|
filename = f"{base_name}_{uuid_short}{ext}"
|
||||||
temp_file_path = os.path.join(EMOJI_DIR, filename)
|
temp_file_path = os.path.join(EMOJI_DIR, filename)
|
||||||
|
|
||||||
@@ -428,7 +430,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 5. 保存base64图片到emoji目录
|
# 5. 保存base64图片到emoji目录
|
||||||
@@ -443,7 +445,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(f"[EmojiAPI] 图片已保存到临时文件: {temp_file_path}")
|
logger.debug(f"[EmojiAPI] 图片已保存到临时文件: {temp_file_path}")
|
||||||
@@ -456,7 +458,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 6. 调用注册方法
|
# 6. 调用注册方法
|
||||||
@@ -483,8 +485,8 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
# 通过文件名查找新注册的表情包(注意:文件名在注册后可能已经改变)
|
# 通过文件名查找新注册的表情包(注意:文件名在注册后可能已经改变)
|
||||||
for emoji_obj in reversed(emoji_manager.emoji_objects):
|
for emoji_obj in reversed(emoji_manager.emoji_objects):
|
||||||
if not emoji_obj.is_deleted and (
|
if not emoji_obj.is_deleted and (
|
||||||
emoji_obj.filename == filename or # 直接匹配
|
emoji_obj.filename == filename # 直接匹配
|
||||||
(hasattr(emoji_obj, 'full_path') and filename in emoji_obj.full_path) # 路径包含匹配
|
or (hasattr(emoji_obj, "full_path") and filename in emoji_obj.full_path) # 路径包含匹配
|
||||||
):
|
):
|
||||||
new_emoji_info = emoji_obj
|
new_emoji_info = emoji_obj
|
||||||
break
|
break
|
||||||
@@ -501,7 +503,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": description,
|
"description": description,
|
||||||
"emotions": emotions,
|
"emotions": emotions,
|
||||||
"replaced": replaced,
|
"replaced": replaced,
|
||||||
"hash": emoji_hash
|
"hash": emoji_hash,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
@@ -510,7 +512,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -521,7 +523,7 @@ async def register_emoji(image_base64: str, filename: Optional[str] = None) -> D
|
|||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None,
|
"emotions": None,
|
||||||
"replaced": None,
|
"replaced": None,
|
||||||
"hash": None
|
"hash": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -585,16 +587,16 @@ async def delete_emoji(emoji_hash: str) -> Dict[str, Any]:
|
|||||||
"count_before": count_before,
|
"count_before": count_before,
|
||||||
"count_after": count_after,
|
"count_after": count_after,
|
||||||
"description": description,
|
"description": description,
|
||||||
"emotions": emotions
|
"emotions": emotions,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"message": f"表情包删除失败,可能因为哈希值不存在或删除过程出错",
|
"message": "表情包删除失败,可能因为哈希值不存在或删除过程出错",
|
||||||
"count_before": count_before,
|
"count_before": count_before,
|
||||||
"count_after": count_after,
|
"count_after": count_after,
|
||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None
|
"emotions": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -605,7 +607,7 @@ async def delete_emoji(emoji_hash: str) -> Dict[str, Any]:
|
|||||||
"count_before": None,
|
"count_before": None,
|
||||||
"count_after": None,
|
"count_after": None,
|
||||||
"description": None,
|
"description": None,
|
||||||
"emotions": None
|
"emotions": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -659,7 +661,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
|
|||||||
"message": f"未找到匹配描述 '{description}' 的表情包",
|
"message": f"未找到匹配描述 '{description}' 的表情包",
|
||||||
"deleted_count": 0,
|
"deleted_count": 0,
|
||||||
"deleted_hashes": [],
|
"deleted_hashes": [],
|
||||||
"matched_count": 0
|
"matched_count": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
# 删除匹配的表情包
|
# 删除匹配的表情包
|
||||||
@@ -681,7 +683,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
|
|||||||
"message": f"成功删除 {deleted_count} 个表情包 (匹配到 {matched_count} 个)",
|
"message": f"成功删除 {deleted_count} 个表情包 (匹配到 {matched_count} 个)",
|
||||||
"deleted_count": deleted_count,
|
"deleted_count": deleted_count,
|
||||||
"deleted_hashes": deleted_hashes,
|
"deleted_hashes": deleted_hashes,
|
||||||
"matched_count": matched_count
|
"matched_count": matched_count,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
@@ -689,7 +691,7 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
|
|||||||
"message": f"匹配到 {matched_count} 个表情包,但删除全部失败",
|
"message": f"匹配到 {matched_count} 个表情包,但删除全部失败",
|
||||||
"deleted_count": 0,
|
"deleted_count": 0,
|
||||||
"deleted_hashes": [],
|
"deleted_hashes": [],
|
||||||
"matched_count": matched_count
|
"matched_count": matched_count,
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -699,5 +701,5 @@ async def delete_emoji_by_description(description: str, exact_match: bool = Fals
|
|||||||
"message": f"删除过程中发生错误: {str(e)}",
|
"message": f"删除过程中发生错误: {str(e)}",
|
||||||
"deleted_count": 0,
|
"deleted_count": 0,
|
||||||
"deleted_hashes": [],
|
"deleted_hashes": [],
|
||||||
"matched_count": 0
|
"matched_count": 0,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ from src.chat.frequency_control.frequency_control import frequency_control_manag
|
|||||||
|
|
||||||
logger = get_logger("frequency_api")
|
logger = get_logger("frequency_api")
|
||||||
|
|
||||||
|
|
||||||
def get_current_talk_frequency(chat_id: str) -> float:
|
def get_current_talk_frequency(chat_id: str) -> float:
|
||||||
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()
|
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()
|
||||||
|
|
||||||
|
|
||||||
def set_talk_frequency_adjust(chat_id: str, talk_frequency_adjust: float) -> None:
|
def set_talk_frequency_adjust(chat_id: str, talk_frequency_adjust: float) -> None:
|
||||||
frequency_control_manager.get_or_create_frequency_control(
|
frequency_control_manager.get_or_create_frequency_control(chat_id).set_talk_frequency_adjust(talk_frequency_adjust)
|
||||||
chat_id
|
|
||||||
).set_talk_frequency_adjust(talk_frequency_adjust)
|
|
||||||
|
|
||||||
def get_talk_frequency_adjust(chat_id: str) -> float:
|
def get_talk_frequency_adjust(chat_id: str) -> float:
|
||||||
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()
|
return frequency_control_manager.get_or_create_frequency_control(chat_id).get_talk_frequency_adjust()
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
from typing import Optional
|
||||||
import time
|
|
||||||
from typing import Optional, Union, Dict, List, TYPE_CHECKING, Tuple
|
|
||||||
|
|
||||||
from src.chat.message_receive import message
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.mood.mood_manager import mood_manager
|
from src.mood.mood_manager import mood_manager
|
||||||
|
|
||||||
|
|||||||
@@ -451,7 +451,9 @@ def _parse_content_to_seg(reply_content: "ReplyContent") -> Tuple[Seg, bool]:
|
|||||||
single_node_content.append(sub_seg)
|
single_node_content.append(sub_seg)
|
||||||
message_segment = Seg(type="seglist", data=single_node_content)
|
message_segment = Seg(type="seglist", data=single_node_content)
|
||||||
forward_message_list.append(
|
forward_message_list.append(
|
||||||
MessageBase(message_segment=message_segment, message_info=BaseMessageInfo(user_info=user_info)).to_dict()
|
MessageBase(
|
||||||
|
message_segment=message_segment, message_info=BaseMessageInfo(user_info=user_info)
|
||||||
|
).to_dict()
|
||||||
)
|
)
|
||||||
return Seg(type="forward", data=forward_message_list), False # type: ignore
|
return Seg(type="forward", data=forward_message_list), False # type: ignore
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ from typing import Tuple
|
|||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.utils.prompt_builder import Prompt
|
from src.chat.utils.prompt_builder import Prompt
|
||||||
|
from src.llm_models.payload_content.tool_option import ToolParamType
|
||||||
from src.plugin_system import BaseAction, ActionActivationType
|
from src.plugin_system import BaseAction, ActionActivationType
|
||||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||||
from src.chat.utils.utils import cut_key_words
|
from src.chat.utils.utils import cut_key_words
|
||||||
|
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||||
|
from src.plugin_system.base.base_tool import BaseTool
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
logger = get_logger("memory")
|
logger = get_logger("memory")
|
||||||
|
|
||||||
@@ -66,30 +70,127 @@ def init_prompt():
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class BuildMemoryAction(BaseAction):
|
# class BuildMemoryAction(BaseAction):
|
||||||
"""关系动作 - 构建关系"""
|
# """关系动作 - 构建关系"""
|
||||||
|
|
||||||
|
# activation_type = ActionActivationType.LLM_JUDGE
|
||||||
|
# parallel_action = True
|
||||||
|
|
||||||
|
# # 动作基本信息
|
||||||
|
# action_name = "build_memory"
|
||||||
|
# action_description = (
|
||||||
|
# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息"
|
||||||
|
# )
|
||||||
|
|
||||||
|
# # 动作参数定义
|
||||||
|
# action_parameters = {
|
||||||
|
# "concept_name": "需要了解或记忆的概念或事件的名称",
|
||||||
|
# "concept_description": "需要了解或记忆的概念或事件的描述,需要具体且明确",
|
||||||
|
# }
|
||||||
|
|
||||||
|
# # 动作使用场景
|
||||||
|
# action_require = [
|
||||||
|
# "了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息",
|
||||||
|
# "有你不了解的概念",
|
||||||
|
# "有人要求你记住某个概念或者事件",
|
||||||
|
# "你对某件事或概念有新的理解,或产生了兴趣",
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# # 关联类型
|
||||||
|
# associated_types = ["text"]
|
||||||
|
|
||||||
|
# async def execute(self) -> Tuple[bool, str]:
|
||||||
|
# """执行关系动作"""
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# # 1. 获取构建关系的原因
|
||||||
|
# concept_description = self.action_data.get("concept_description", "")
|
||||||
|
# logger.info(f"{self.log_prefix} 添加记忆原因: {self.reasoning}")
|
||||||
|
# concept_name = self.action_data.get("concept_name", "")
|
||||||
|
# # 2. 获取目标用户信息
|
||||||
|
|
||||||
|
# # 对 concept_name 进行jieba分词
|
||||||
|
# concept_name_tokens = cut_key_words(concept_name)
|
||||||
|
# # logger.info(f"{self.log_prefix} 对 concept_name 进行分词结果: {concept_name_tokens}")
|
||||||
|
|
||||||
|
# filtered_concept_name_tokens = [
|
||||||
|
# token
|
||||||
|
# for token in concept_name_tokens
|
||||||
|
# if all(keyword not in token for keyword in global_config.memory.memory_ban_words)
|
||||||
|
# ]
|
||||||
|
|
||||||
|
# if not filtered_concept_name_tokens:
|
||||||
|
# logger.warning(f"{self.log_prefix} 过滤后的概念名称列表为空,跳过添加记忆")
|
||||||
|
# return False, "过滤后的概念名称列表为空,跳过添加记忆"
|
||||||
|
|
||||||
|
# similar_topics_dict = (
|
||||||
|
# hippocampus_manager.get_hippocampus().parahippocampal_gyrus.get_similar_topics_from_keywords(
|
||||||
|
# filtered_concept_name_tokens
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
# await hippocampus_manager.get_hippocampus().parahippocampal_gyrus.add_memory_with_similar(
|
||||||
|
# concept_description, similar_topics_dict
|
||||||
|
# )
|
||||||
|
|
||||||
|
# return True, f"成功添加记忆: {concept_name}"
|
||||||
|
|
||||||
|
# except Exception as e:
|
||||||
|
# logger.error(f"{self.log_prefix} 构建记忆时出错: {e}")
|
||||||
|
# return False, f"构建记忆时出错: {e}"
|
||||||
|
|
||||||
|
class GetMemoryTool(BaseTool):
|
||||||
|
"""获取用户信息"""
|
||||||
|
|
||||||
|
name = "get_memory"
|
||||||
|
description = "在记忆中搜索,获取某个问题的答案"
|
||||||
|
parameters = [
|
||||||
|
("question", ToolParamType.STRING, "需要获取答案的问题", True, None)
|
||||||
|
]
|
||||||
|
|
||||||
|
available_for_llm = True
|
||||||
|
|
||||||
|
async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
"""执行比较两个数的大小
|
||||||
|
|
||||||
|
Args:
|
||||||
|
function_args: 工具参数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 工具执行结果
|
||||||
|
"""
|
||||||
|
question: str = function_args.get("question") # type: ignore
|
||||||
|
|
||||||
|
answer = await global_memory_chest.get_answer_by_question(question=question)
|
||||||
|
if not answer:
|
||||||
|
return {"content": f"没有找到相关记忆"}
|
||||||
|
|
||||||
|
return {"content": f"问题:{question},答案:{answer}"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GetMemoryAction(BaseAction):
|
||||||
|
"""关系动作 - 获取记忆"""
|
||||||
|
|
||||||
activation_type = ActionActivationType.LLM_JUDGE
|
activation_type = ActionActivationType.LLM_JUDGE
|
||||||
parallel_action = True
|
parallel_action = True
|
||||||
|
|
||||||
# 动作基本信息
|
# 动作基本信息
|
||||||
action_name = "build_memory"
|
action_name = "get_memory"
|
||||||
action_description = (
|
action_description = (
|
||||||
"了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息"
|
"在记忆中搜寻某个问题的答案"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 动作参数定义
|
# 动作参数定义
|
||||||
action_parameters = {
|
action_parameters = {
|
||||||
"concept_name": "需要了解或记忆的概念或事件的名称",
|
"question": "需要搜寻或回答的问题",
|
||||||
"concept_description": "需要了解或记忆的概念或事件的描述,需要具体且明确",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# 动作使用场景
|
# 动作使用场景
|
||||||
action_require = [
|
action_require = [
|
||||||
"了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息",
|
"在记忆中搜寻某个问题的答案",
|
||||||
"有你不了解的概念",
|
"有你不了解的概念",
|
||||||
"有人要求你记住某个概念或者事件",
|
"有人提问关于过去的事情"
|
||||||
"你对某件事或概念有新的理解,或产生了兴趣",
|
"你需要根据记忆回答某个问题",
|
||||||
]
|
]
|
||||||
|
|
||||||
# 关联类型
|
# 关联类型
|
||||||
@@ -98,41 +199,24 @@ class BuildMemoryAction(BaseAction):
|
|||||||
async def execute(self) -> Tuple[bool, str]:
|
async def execute(self) -> Tuple[bool, str]:
|
||||||
"""执行关系动作"""
|
"""执行关系动作"""
|
||||||
|
|
||||||
try:
|
question = self.action_data.get("question", "")
|
||||||
# 1. 获取构建关系的原因
|
answer = await global_memory_chest.get_answer_by_question(self.chat_id, question)
|
||||||
concept_description = self.action_data.get("concept_description", "")
|
if not answer:
|
||||||
logger.info(f"{self.log_prefix} 添加记忆原因: {self.reasoning}")
|
await self.store_action_info(
|
||||||
concept_name = self.action_data.get("concept_name", "")
|
action_build_into_prompt=True,
|
||||||
# 2. 获取目标用户信息
|
action_prompt_display=f"你回忆了有关问题:{question}的记忆,但是没有找到相关记忆",
|
||||||
|
action_done=True,
|
||||||
# 对 concept_name 进行jieba分词
|
|
||||||
concept_name_tokens = cut_key_words(concept_name)
|
|
||||||
# logger.info(f"{self.log_prefix} 对 concept_name 进行分词结果: {concept_name_tokens}")
|
|
||||||
|
|
||||||
filtered_concept_name_tokens = [
|
|
||||||
token
|
|
||||||
for token in concept_name_tokens
|
|
||||||
if all(keyword not in token for keyword in global_config.memory.memory_ban_words)
|
|
||||||
]
|
|
||||||
|
|
||||||
if not filtered_concept_name_tokens:
|
|
||||||
logger.warning(f"{self.log_prefix} 过滤后的概念名称列表为空,跳过添加记忆")
|
|
||||||
return False, "过滤后的概念名称列表为空,跳过添加记忆"
|
|
||||||
|
|
||||||
similar_topics_dict = (
|
|
||||||
hippocampus_manager.get_hippocampus().parahippocampal_gyrus.get_similar_topics_from_keywords(
|
|
||||||
filtered_concept_name_tokens
|
|
||||||
)
|
|
||||||
)
|
|
||||||
await hippocampus_manager.get_hippocampus().parahippocampal_gyrus.add_memory_with_similar(
|
|
||||||
concept_description, similar_topics_dict
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return True, f"成功添加记忆: {concept_name}"
|
return False, f"没有找到相关记忆"
|
||||||
|
|
||||||
except Exception as e:
|
await self.store_action_info(
|
||||||
logger.error(f"{self.log_prefix} 构建记忆时出错: {e}")
|
action_build_into_prompt=True,
|
||||||
return False, f"构建记忆时出错: {e}"
|
action_prompt_display=f"你回忆了有关问题:{question}的记忆,答案是:{answer}",
|
||||||
|
action_done=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return True, f"成功获取记忆: {answer}"
|
||||||
|
|
||||||
|
|
||||||
# 还缺一个关系的太多遗忘和对应的提取
|
# 还缺一个关系的太多遗忘和对应的提取
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
from typing import List, Tuple, Type
|
from typing import List, Tuple, Type
|
||||||
|
|
||||||
# 导入新插件系统
|
# 导入新插件系统
|
||||||
from src.plugin_system import BasePlugin, ComponentInfo
|
from src.plugin_system import BasePlugin, ComponentInfo, register_plugin
|
||||||
from src.plugin_system.base.config_types import ConfigField
|
from src.plugin_system.base.config_types import ConfigField
|
||||||
|
|
||||||
# 导入依赖的系统组件
|
# 导入依赖的系统组件
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
from src.plugins.built_in.memory.build_memory import BuildMemoryAction
|
from src.plugins.built_in.memory.build_memory import GetMemoryAction, GetMemoryTool
|
||||||
|
|
||||||
logger = get_logger("relation_actions")
|
logger = get_logger("memory_build")
|
||||||
|
|
||||||
|
|
||||||
# @register_plugin
|
@register_plugin
|
||||||
class MemoryBuildPlugin(BasePlugin):
|
class MemoryBuildPlugin(BasePlugin):
|
||||||
"""关系动作插件
|
"""记忆构建插件
|
||||||
|
|
||||||
系统内置插件,提供基础的聊天交互功能:
|
系统内置插件,提供基础的聊天交互功能:
|
||||||
- Reply: 回复动作
|
- GetMemory: 获取记忆
|
||||||
- NoReply: 不回复动作
|
|
||||||
- Emoji: 表情动作
|
|
||||||
|
|
||||||
注意:插件基本信息优先从_manifest.json文件中读取
|
注意:插件基本信息优先从_manifest.json文件中读取
|
||||||
"""
|
"""
|
||||||
@@ -43,9 +41,6 @@ class MemoryBuildPlugin(BasePlugin):
|
|||||||
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
"enabled": ConfigField(type=bool, default=True, description="是否启用插件"),
|
||||||
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
"config_version": ConfigField(type=str, default="1.1.0", description="配置文件版本"),
|
||||||
},
|
},
|
||||||
"components": {
|
|
||||||
"memory_max_memory_num": ConfigField(type=int, default=10, description="记忆最大数量"),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||||
@@ -53,6 +48,7 @@ class MemoryBuildPlugin(BasePlugin):
|
|||||||
|
|
||||||
# --- 根据配置注册组件 ---
|
# --- 根据配置注册组件 ---
|
||||||
components = []
|
components = []
|
||||||
components.append((BuildMemoryAction.get_action_info(), BuildMemoryAction))
|
components.append((GetMemoryAction.get_action_info(), GetMemoryAction))
|
||||||
|
components.append((GetMemoryTool.get_tool_info(), GetMemoryTool))
|
||||||
|
|
||||||
return components
|
return components
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from src.plugins.built_in.relation.relation import BuildRelationAction
|
|||||||
logger = get_logger("relation_actions")
|
logger = get_logger("relation_actions")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GetPersonInfoTool(BaseTool):
|
class GetPersonInfoTool(BaseTool):
|
||||||
"""获取用户信息"""
|
"""获取用户信息"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user