feat:新的记忆系统,deepthink插件,修复平行动作

pull/1273/head
SengokuCola 2025-09-28 02:03:43 +08:00
parent 5cc1e56904
commit 0cd39476d8
15 changed files with 667 additions and 72 deletions

2
.gitignore vendored
View File

@ -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

View File

View 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": "发送深度思考"
}
]
}
}

View 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

View File

@ -427,7 +427,7 @@ class ExpressionLearner:
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:

View File

@ -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,
@ -102,6 +103,7 @@ class HeartFChatting:
self.talk_threshold = global_config.chat.talk_value self.talk_threshold = global_config.chat.talk_value
self.no_reply_until_call = False self.no_reply_until_call = False
async def start(self): async def start(self):
"""检查是否需要启动主循环,如果未激活则启动。""" """检查是否需要启动主循环,如果未激活则启动。"""
@ -284,6 +286,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}次思考")

View 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()

View File

@ -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},
@ -421,6 +421,11 @@ class ActionPlanner:
for require_item in action_info.action_require: for require_item in action_info.action_require:
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")
@ -429,6 +434,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

View File

@ -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
@ -315,6 +316,17 @@ class DefaultReplyer:
# memory_str += f"- {instant_memory}\n" # memory_str += f"- {instant_memory}\n"
# 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:
"""构建工具信息块 """构建工具信息块
@ -701,6 +713,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 +727,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 +756,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"]
@ -779,6 +794,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 +814,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 +963,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")

View File

@ -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,

View File

@ -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}

View File

@ -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:

View File

@ -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):

View File

@ -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,73 +70,153 @@ 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 = [
"了解对于某个概念或者某件事的记忆,并存储下来,在之后的聊天中,你可以根据这条记忆来获取相关信息", "在记忆中搜寻某个问题的答案",
"有你不了解的概念", "有你不了解的概念",
"有人要求你记住某个概念或者事件", "有人提问关于过去的事情"
"你对某件事或概念有新的理解,或产生了兴趣", "需要根据记忆回答某个问题",
] ]
# 关联类型 # 关联类型
associated_types = ["text"] associated_types = ["text"]
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 False, f"没有找到相关记忆"
)
await self.store_action_info(
return True, f"成功添加记忆: {concept_name}" action_build_into_prompt=True,
action_prompt_display=f"你回忆了有关问题:{question}的记忆,答案是:{answer}",
except Exception as e: action_done=True,
logger.error(f"{self.log_prefix} 构建记忆时出错: {e}") )
return False, f"构建记忆时出错: {e}"
return True, f"成功获取记忆: {answer}"
# 还缺一个关系的太多遗忘和对应的提取 # 还缺一个关系的太多遗忘和对应的提取

View File

@ -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