mirror of https://github.com/Mai-with-u/MaiBot.git
feat:新增麦麦好奇功能,优化记忆构建
parent
d519406e4a
commit
e2310de6b5
|
|
@ -324,6 +324,7 @@ run_pet.bat
|
|||
!/plugins/emoji_manage_plugin
|
||||
!/plugins/take_picture_plugin
|
||||
!/plugins/deep_think
|
||||
!/plugins/MaiFrequencyControl
|
||||
!/plugins/__init__.py
|
||||
|
||||
config.toml
|
||||
|
|
|
|||
|
|
@ -25,7 +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.mais4u.mai_think import mai_thinking_manager
|
||||
from src.mais4u.s4u_config import s4u_config
|
||||
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||
from src.memory_system.Memory_chest import global_memory_chest
|
||||
from src.chat.utils.chat_message_builder import (
|
||||
build_readable_messages_with_id,
|
||||
get_raw_msg_before_timestamp_with_chat,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import re
|
|||
|
||||
from typing import List, Optional, Dict, Any, Tuple
|
||||
from datetime import datetime
|
||||
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||
from src.memory_system.Memory_chest import global_memory_chest
|
||||
from src.mais4u.mai_think import mai_thinking_manager
|
||||
from src.common.logger import get_logger
|
||||
from src.common.data_models.database_data_model import DatabaseMessages
|
||||
|
|
@ -27,7 +27,7 @@ from src.chat.utils.chat_message_builder import (
|
|||
)
|
||||
from src.chat.express.expression_selector import expression_selector
|
||||
|
||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||
# from src.memory_system.memory_activator import MemoryActivator
|
||||
from src.person_info.person_info import Person
|
||||
from src.plugin_system.base.component_types import ActionInfo, EventType
|
||||
from src.plugin_system.apis import llm_api
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import re
|
|||
|
||||
from typing import List, Optional, Dict, Any, Tuple
|
||||
from datetime import datetime
|
||||
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||
from src.memory_system.Memory_chest import global_memory_chest
|
||||
from src.mais4u.mai_think import mai_thinking_manager
|
||||
from src.common.logger import get_logger
|
||||
from src.common.data_models.database_data_model import DatabaseMessages
|
||||
|
|
@ -27,7 +27,7 @@ from src.chat.utils.chat_message_builder import (
|
|||
)
|
||||
from src.chat.express.expression_selector import expression_selector
|
||||
|
||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||
# from src.memory_system.memory_activator import MemoryActivator
|
||||
|
||||
from src.person_info.person_info import Person, is_person_known
|
||||
from src.plugin_system.base.component_types import ActionInfo, EventType
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from src.chat.utils.prompt_builder import Prompt
|
||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||
# from src.memory_system.memory_activator import MemoryActivator
|
||||
|
||||
|
||||
def init_lpmm_prompt():
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from src.chat.utils.prompt_builder import Prompt
|
||||
# from src.chat.memory_system.memory_activator import MemoryActivator
|
||||
# from src.memory_system.memory_activator import MemoryActivator
|
||||
|
||||
|
||||
def init_rewrite_prompt():
|
||||
|
|
|
|||
|
|
@ -326,6 +326,19 @@ class MemoryChest(BaseModel):
|
|||
|
||||
class Meta:
|
||||
table_name = "memory_chest"
|
||||
|
||||
class MemoryConflict(BaseModel):
|
||||
"""
|
||||
用于存储记忆整合过程中冲突内容的模型
|
||||
"""
|
||||
|
||||
conflict_content = TextField() # 冲突内容
|
||||
answer = TextField(null=True) # 回答内容
|
||||
create_time = FloatField() # 创建时间
|
||||
update_time = FloatField() # 更新时间
|
||||
|
||||
class Meta:
|
||||
table_name = "memory_conflicts"
|
||||
|
||||
|
||||
|
||||
|
|
@ -382,6 +395,7 @@ def create_tables():
|
|||
GraphEdges, # 添加图边表
|
||||
ActionRecords, # 添加 ActionRecords 到初始化列表
|
||||
MemoryChest,
|
||||
MemoryConflict, # 添加记忆冲突表
|
||||
]
|
||||
)
|
||||
|
||||
|
|
@ -410,6 +424,7 @@ def initialize_database(sync_constraints=False):
|
|||
GraphEdges,
|
||||
ActionRecords, # 添加 ActionRecords 到初始化列表
|
||||
MemoryChest,
|
||||
MemoryConflict,
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
@ -508,6 +523,7 @@ def sync_field_constraints():
|
|||
GraphEdges,
|
||||
ActionRecords,
|
||||
MemoryChest,
|
||||
MemoryConflict,
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
@ -692,6 +708,8 @@ def check_field_constraints():
|
|||
GraphNodes,
|
||||
GraphEdges,
|
||||
ActionRecords,
|
||||
MemoryChest,
|
||||
MemoryConflict,
|
||||
]
|
||||
|
||||
inconsistencies = {}
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ MODULE_COLORS = {
|
|||
# s4u
|
||||
"context_web_api": "\033[38;5;240m", # 深灰色
|
||||
"S4U_chat": "\033[92m", # 深灰色
|
||||
"conflict_tracker": "\033[38;5;82m", # 柔和的粉色,不显眼但保持粉色系
|
||||
}
|
||||
|
||||
# 定义模块别名映射 - 将真实的logger名称映射到显示的别名
|
||||
|
|
|
|||
|
|
@ -0,0 +1,245 @@
|
|||
import time
|
||||
import asyncio
|
||||
from rich.traceback import install
|
||||
from src.common.logger import get_logger
|
||||
from src.common.database.database_model import MemoryConflict
|
||||
from src.chat.utils.chat_message_builder import (
|
||||
get_raw_msg_by_timestamp_with_chat,
|
||||
build_readable_messages,
|
||||
)
|
||||
from src.llm_models.utils_model import LLMRequest
|
||||
from src.config.config import model_config, global_config
|
||||
|
||||
logger = get_logger("conflict_tracker")
|
||||
|
||||
logger = get_logger("conflict_tracker")
|
||||
|
||||
install(extra_lines=3)
|
||||
|
||||
class QuestionTracker:
|
||||
"""
|
||||
用于跟踪一个问题在后续聊天中的解答情况
|
||||
"""
|
||||
|
||||
def __init__(self, question: str, chat_id: str) -> None:
|
||||
self.question = question
|
||||
self.chat_id = chat_id
|
||||
now = time.time()
|
||||
self.start_time = now
|
||||
self.last_read_time = now
|
||||
self.active = True
|
||||
# 将 LLM 实例作为类属性,使用 utils 模型
|
||||
self.llm_request = LLMRequest(model_set=model_config.model_task_config.utils, request_type="conflict.judge")
|
||||
|
||||
def stop(self) -> None:
|
||||
self.active = False
|
||||
|
||||
async def judge_answer(self, conversation_text: str) -> tuple[bool, str]:
|
||||
"""
|
||||
使用小模型判定问题是否已得到解答。
|
||||
返回 (已解答, 答案)
|
||||
"""
|
||||
prompt = (
|
||||
"你是一个严谨的判定器。下面给出聊天记录以及一个问题。\n"
|
||||
"任务:判断在这段聊天中,该问题是否已经得到明确解答。或从聊天内容中可以整理出答案\n"
|
||||
"如果已解答,请只输出:YES: <简短答案>\n"
|
||||
"如果没有,请只输出:NO\n\n"
|
||||
f"问题:{self.question}\n"
|
||||
"聊天记录如下:\n"
|
||||
f"{conversation_text}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
logger.info(f"判定提示词: {prompt}")
|
||||
else:
|
||||
logger.debug("已发送判定提示词")
|
||||
|
||||
result_text, _ = await self.llm_request.generate_response_async(prompt, temperature=0.2)
|
||||
if not result_text:
|
||||
return False, ""
|
||||
|
||||
logger.info(f"判定提示词: {prompt},问题: {self.question},result: {result_text}")
|
||||
|
||||
text = result_text.strip()
|
||||
if text.upper().startswith("YES:"):
|
||||
answer = text[4:].strip()
|
||||
return True, answer
|
||||
if text.upper().startswith("YES"):
|
||||
# 兼容仅输出 YES 或 YES <answer>
|
||||
answer = text[3:].strip().lstrip(":").strip()
|
||||
return True, answer
|
||||
return False, ""
|
||||
|
||||
class ConflictTracker:
|
||||
"""
|
||||
记忆整合冲突追踪器
|
||||
|
||||
用于记录和存储记忆整合过程中的冲突内容
|
||||
"""
|
||||
|
||||
async def record_conflict(self, conflict_content: str, start_following: bool = False,chat_id: str = "") -> bool:
|
||||
"""
|
||||
记录冲突内容
|
||||
|
||||
Args:k
|
||||
conflict_content: 冲突内容
|
||||
|
||||
Returns:
|
||||
bool: 是否成功记录
|
||||
"""
|
||||
try:
|
||||
if not conflict_content or conflict_content.strip() == "":
|
||||
return False
|
||||
|
||||
# 若需要跟随后续消息以判断是否得到解答,则进入跟踪流程
|
||||
if start_following and chat_id:
|
||||
tracker = QuestionTracker(conflict_content.strip(), chat_id)
|
||||
# 后台启动跟踪任务,避免阻塞
|
||||
asyncio.create_task(self._follow_and_record(tracker, conflict_content.strip()))
|
||||
return True
|
||||
|
||||
# 默认:直接记录,不进行跟踪
|
||||
MemoryConflict.create(
|
||||
conflict_content=conflict_content,
|
||||
create_time=time.time(),
|
||||
update_time=time.time(),
|
||||
answer="",
|
||||
)
|
||||
|
||||
logger.info(f"记录冲突内容: {len(conflict_content)} 字符")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"记录冲突内容时出错: {e}")
|
||||
return False
|
||||
|
||||
async def _follow_and_record(self, tracker: QuestionTracker, original_question: str) -> None:
|
||||
"""
|
||||
后台任务:跟踪问题是否被解答,并写入数据库。
|
||||
"""
|
||||
try:
|
||||
max_duration = 30 * 60 # 30 分钟
|
||||
max_messages = 100 # 最多 100 条消息
|
||||
poll_interval = 2.0 # 秒
|
||||
|
||||
while tracker.active:
|
||||
now_ts = time.time()
|
||||
# 终止条件:时长达到上限
|
||||
if now_ts - tracker.start_time >= max_duration:
|
||||
logger.info("问题跟踪达到30分钟上限,判定为未解答")
|
||||
break
|
||||
|
||||
# 统计最近一段是否有新消息(不过滤机器人,过滤命令)
|
||||
recent_msgs = get_raw_msg_by_timestamp_with_chat(
|
||||
chat_id=tracker.chat_id,
|
||||
timestamp_start=tracker.last_read_time,
|
||||
timestamp_end=now_ts,
|
||||
limit=0,
|
||||
limit_mode="latest",
|
||||
filter_bot=False,
|
||||
filter_command=True,
|
||||
)
|
||||
|
||||
if len(recent_msgs) > 0:
|
||||
tracker.last_read_time = now_ts
|
||||
|
||||
# 统计从开始到现在的总消息数(用于触发100条上限)
|
||||
all_msgs = get_raw_msg_by_timestamp_with_chat(
|
||||
chat_id=tracker.chat_id,
|
||||
timestamp_start=tracker.start_time,
|
||||
timestamp_end=now_ts,
|
||||
limit=0,
|
||||
limit_mode="latest",
|
||||
filter_bot=False,
|
||||
filter_command=True,
|
||||
)
|
||||
|
||||
# 构建可读聊天文本
|
||||
chat_text = build_readable_messages(
|
||||
all_msgs,
|
||||
replace_bot_name=True,
|
||||
timestamp_mode="relative",
|
||||
read_mark=0.0,
|
||||
truncate=False,
|
||||
show_actions=False,
|
||||
show_pic=False,
|
||||
remove_emoji_stickers=True,
|
||||
)
|
||||
|
||||
# 让小模型判断是否有答案
|
||||
answered, answer_text = await tracker.judge_answer(chat_text)
|
||||
if answered:
|
||||
logger.info("问题已得到解答,结束跟踪并写入答案")
|
||||
tracker.stop()
|
||||
MemoryConflict.create(
|
||||
conflict_content=tracker.question,
|
||||
create_time=tracker.start_time,
|
||||
update_time=time.time(),
|
||||
answer=answer_text or "",
|
||||
)
|
||||
return
|
||||
|
||||
if len(all_msgs) >= max_messages:
|
||||
logger.info("问题跟踪达到100条消息上限,判定为未解答")
|
||||
break
|
||||
|
||||
# 无新消息时稍作等待
|
||||
await asyncio.sleep(poll_interval)
|
||||
|
||||
# 未获取到答案,仅存储问题
|
||||
MemoryConflict.create(
|
||||
conflict_content=original_question,
|
||||
create_time=time.time(),
|
||||
update_time=time.time(),
|
||||
answer="",
|
||||
)
|
||||
logger.info(f"记录冲突内容(未解答): {len(original_question)} 字符")
|
||||
except Exception as e:
|
||||
logger.error(f"后台问题跟踪任务异常: {e}")
|
||||
|
||||
async def record_memory_merge_conflict(self, part2_content: str) -> bool:
|
||||
"""
|
||||
记录记忆整合过程中的冲突内容(part2)
|
||||
|
||||
Args:
|
||||
part2_content: 冲突内容(part2)
|
||||
|
||||
Returns:
|
||||
bool: 是否成功记录
|
||||
"""
|
||||
if not part2_content or part2_content.strip() == "":
|
||||
return False
|
||||
|
||||
return await self.record_conflict(part2_content)
|
||||
|
||||
async def get_all_conflicts(self) -> list:
|
||||
"""
|
||||
获取所有冲突记录
|
||||
|
||||
Returns:
|
||||
list: 冲突记录列表
|
||||
"""
|
||||
try:
|
||||
conflicts = list(MemoryConflict.select())
|
||||
return conflicts
|
||||
except Exception as e:
|
||||
logger.error(f"获取冲突记录时出错: {e}")
|
||||
return []
|
||||
|
||||
async def get_conflict_count(self) -> int:
|
||||
"""
|
||||
获取冲突记录数量
|
||||
|
||||
Returns:
|
||||
int: 记录数量
|
||||
"""
|
||||
try:
|
||||
return MemoryConflict.select().count()
|
||||
except Exception as e:
|
||||
logger.error(f"获取冲突记录数量时出错: {e}")
|
||||
return 0
|
||||
|
||||
# 全局冲突追踪器实例
|
||||
global_conflict_tracker = ConflictTracker()
|
||||
|
|
@ -13,9 +13,9 @@ from src.common.logger import get_logger
|
|||
from src.common.server import get_global_server, Server
|
||||
from src.mood.mood_manager import mood_manager
|
||||
from src.chat.knowledge import lpmm_start_up
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.chat.memory_system.hippocampus_to_memory_chest_task import HippocampusToMemoryChestTask
|
||||
from src.chat.memory_system.memory_management_task import MemoryManagementTask
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.memory_system.hippocampus_to_memory_chest_task import HippocampusToMemoryChestTask
|
||||
from src.memory_system.memory_management_task import MemoryManagementTask
|
||||
from rich.traceback import install
|
||||
from src.migrate_helper.migrate import check_and_run_migrations
|
||||
# from src.api.main import start_api_server
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import asyncio
|
|||
import math
|
||||
from typing import Tuple
|
||||
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.chat.message_receive.message import MessageRecv, MessageRecvS4U
|
||||
from maim_message.message_base import GroupInfo
|
||||
from src.chat.message_receive.storage import MessageStorage
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from src.chat.utils.prompt_builder import Prompt, global_prompt_manager
|
|||
from src.chat.utils.chat_message_builder import build_readable_messages, get_raw_msg_before_timestamp_with_chat
|
||||
import time
|
||||
from src.chat.utils.utils import get_recent_group_speaker
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
import random
|
||||
from datetime import datetime
|
||||
import asyncio
|
||||
|
|
|
|||
|
|
@ -99,19 +99,34 @@ class MemoryChest:
|
|||
current_running_content = self.running_content_list[chat_id]["content"]
|
||||
|
||||
prompt = f"""
|
||||
以下是你的记忆内容:
|
||||
以下是你的记忆内容和新的聊天记录,请你将他们整合和修改:
|
||||
记忆内容:
|
||||
<memory_content>
|
||||
{current_running_content}
|
||||
</memory_content>
|
||||
|
||||
请将下面的新聊天记录内的有用的信息,添加到你的记忆中
|
||||
请主要关注概念和知识,而不是聊天的琐事
|
||||
重要!!你要关注的概念和知识必须是较为不常见的信息,或者时效性较强的信息!!
|
||||
不要!!关注常见的只是,或者已经过时的信息!!
|
||||
<聊天记录>
|
||||
{message_str}
|
||||
</聊天记录>
|
||||
聊天记录中可能包含有效信息,也可能信息密度很低,请你根据聊天记录中的信息,修改<part1>中的内容与<part2>中的内容
|
||||
--------------------------------
|
||||
请将上面的新聊天记录内的有用的信息进行整合到现有的记忆中
|
||||
请主要关注概念和知识或者时效性较强的信息!!,而不是聊天的琐事
|
||||
1.不要关注诸如某个用户做了什么,说了什么,不要关注某个用户的行为,而是关注其中的概念性信息
|
||||
2.概念要求精确,不啰嗦,像科普读物或教育课本那样
|
||||
3.如果有图片,请只关注图片和文本结合的知识和概念性内容
|
||||
记忆为一段纯文本,逻辑清晰,指出概念的含义,并说明关系
|
||||
请输出添加后的记忆内容,不要输出其他内容:
|
||||
{message_str}
|
||||
4.记忆为一段纯文本,逻辑清晰,指出概念的含义,并说明关系
|
||||
|
||||
记忆内容的格式,你必须仿照下面的格式,但不一定全部使用:
|
||||
[概念] 是 [概念的含义(简短描述,不超过十个字)]
|
||||
[概念] 不是 [对概念的负面含义(简短描述,不超过十个字)]
|
||||
[概念1] 与 [概念2] 是 [概念1和概念2的关联(简短描述,不超过二十个字)]
|
||||
[概念1] 包含 [概念2] 和 [概念3]
|
||||
[概念1] 属于 [概念2]
|
||||
......(不要包含中括号)
|
||||
|
||||
请仿照上述格式输出,每个知识点一句话。输出成一段平文本
|
||||
现在请你输出,不要输出其他内容,注意一定要直白,白话,口语化不要浮夸,修辞。:
|
||||
"""
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
|
|
@ -120,7 +135,7 @@ class MemoryChest:
|
|||
logger.debug(f"记忆仓库构建运行内容 prompt: {prompt}")
|
||||
|
||||
running_content, (reasoning_content, model_name, tool_calls) = await self.LLMRequest_build.generate_response_async(prompt)
|
||||
|
||||
|
||||
print(f"记忆仓库构建运行内容: {running_content}")
|
||||
|
||||
# 如果有chat_id,更新对应的running_content
|
||||
|
|
@ -136,7 +151,7 @@ class MemoryChest:
|
|||
"create_time": create_time
|
||||
}
|
||||
|
||||
# 检查running_content长度是否大于500
|
||||
# 检查running_content长度是否大于限制
|
||||
if len(running_content) > self.memory_size_limit:
|
||||
await self._save_to_database_and_clear(chat_id, running_content)
|
||||
|
||||
|
|
@ -147,8 +162,7 @@ class MemoryChest:
|
|||
f"内容大小 {len(running_content)} 达到限制的 {int(self.memory_size_limit * 0.3)} 字符,强制保存")
|
||||
await self._save_to_database_and_clear(chat_id, running_content)
|
||||
|
||||
|
||||
|
||||
|
||||
return running_content
|
||||
|
||||
|
||||
|
|
@ -478,6 +492,54 @@ class MemoryChest:
|
|||
logger.error(f"根据标题查找记忆时出错: {e}")
|
||||
return []
|
||||
|
||||
def _parse_merged_parts(self, merged_response: str) -> tuple[str, str]:
|
||||
"""
|
||||
解析合并记忆的part1和part2内容
|
||||
|
||||
Args:
|
||||
merged_response: LLM返回的合并记忆响应
|
||||
|
||||
Returns:
|
||||
tuple[str, str]: (part1_content, part2_content)
|
||||
"""
|
||||
try:
|
||||
# 使用正则表达式提取part1和part2内容
|
||||
import re
|
||||
|
||||
# 提取part1内容
|
||||
part1_pattern = r'<part1>(.*?)</part1>'
|
||||
part1_match = re.search(part1_pattern, merged_response, re.DOTALL)
|
||||
part1_content = part1_match.group(1).strip() if part1_match else ""
|
||||
|
||||
# 提取part2内容
|
||||
part2_pattern = r'<part2>(.*?)</part2>'
|
||||
part2_match = re.search(part2_pattern, merged_response, re.DOTALL)
|
||||
part2_content = part2_match.group(1).strip() if part2_match else ""
|
||||
|
||||
# 检查是否包含none或None(不区分大小写)
|
||||
def is_none_content(content: str) -> bool:
|
||||
if not content:
|
||||
return True
|
||||
# 检查是否只包含"none"或"None"(不区分大小写)
|
||||
return re.match(r'^\s*none\s*$', content, re.IGNORECASE) is not None
|
||||
|
||||
# 如果包含none,则设置为空字符串
|
||||
if is_none_content(part1_content):
|
||||
part1_content = ""
|
||||
logger.info("part1内容为none,设置为空")
|
||||
|
||||
if is_none_content(part2_content):
|
||||
part2_content = ""
|
||||
logger.info("part2内容为none,设置为空")
|
||||
|
||||
logger.info(f"解析合并记忆结果: part1={len(part1_content)}字符, part2={len(part2_content)}字符")
|
||||
|
||||
return part1_content, part2_content
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"解析合并记忆part1/part2时出错: {e}")
|
||||
return "", ""
|
||||
|
||||
def _parse_merge_target_json(self, json_text: str) -> list[str]:
|
||||
"""
|
||||
解析choose_merge_target生成的JSON响应
|
||||
|
|
@ -542,19 +604,35 @@ class MemoryChest:
|
|||
content += f"{memory}\n"
|
||||
|
||||
prompt = f"""
|
||||
以下是多段记忆内容,请将它们合并成一段记忆:
|
||||
以下是多段记忆内容,请将它们进行整合和修改:
|
||||
{content}
|
||||
|
||||
请将下面的多段记忆内容,合并成一段记忆
|
||||
--------------------------------
|
||||
请将上面的多段记忆内容,合并成两部分内容,第一部分是可以整合,不冲突的概念和知识,第二部分是相互有冲突的概念和知识
|
||||
请主要关注概念和知识,而不是聊天的琐事
|
||||
重要!!你要关注的概念和知识必须是较为不常见的信息,或者时效性较强的信息!!
|
||||
不要!!关注常见的只是,或者已经过时的信息!!
|
||||
1.不要关注诸如某个用户做了什么,说了什么,不要关注某个用户的行为,而是关注其中的概念性信息
|
||||
2.概念要求精确,不啰嗦,像科普读物或教育课本那样
|
||||
3.如果有图片,请只关注图片和文本结合的知识和概念性内容
|
||||
4.如果记忆中有冲突的地方,可以进行整合。如果无法整合,需要在此处标注存在冲突的不同信息
|
||||
记忆为一段纯文本,逻辑清晰,指出概念的含义,并说明关系
|
||||
请输出合并的记忆内容,不要输出其他内容:
|
||||
4.记忆为一段纯文本,逻辑清晰,指出概念的含义,并说明关系
|
||||
**第一部分**
|
||||
1.如果两个概念在描述同一件事情,且相互之间逻辑不冲突(请你严格判断),且相互之间没有矛盾,请将它们整合成一个概念,并输出到第一部分
|
||||
2.如果某个概念在时间上更新了另一个概念,请用新概念更新就概念来整合,并输出到第一部分
|
||||
3.如果没有可整合的概念,请你输出none
|
||||
**第二部分**
|
||||
1.如果记忆中有无法整合的地方,例如概念不一致,有逻辑上的冲突,请你输出到第二部分
|
||||
2.如果两个概念在描述同一件事情,但相互之间逻辑冲突,请将它们输出到第二部分
|
||||
3.如果没有无法整合的概念,请你输出none
|
||||
|
||||
**输出格式要求**
|
||||
请你按以下格式输出:
|
||||
<part1>
|
||||
第一部分内容,整合后的概念,如果第一部分为none,请输出none
|
||||
</part1>
|
||||
<part2>
|
||||
第二部分内容,无法整合,冲突的概念,如果第二部分为none,请输出none
|
||||
</part2>
|
||||
不要输出其他内容,现在请你输出,不要输出其他内容,注意一定要直白,白话,口语化不要浮夸,修辞。:
|
||||
"""
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
|
|
@ -564,18 +642,33 @@ class MemoryChest:
|
|||
|
||||
merged_memory, (reasoning_content, model_name, tool_calls) = await self.LLMRequest_build.generate_response_async(prompt)
|
||||
|
||||
# 生成合并后的标题
|
||||
merged_title = await self._generate_title_for_merged_memory(merged_memory)
|
||||
|
||||
# 保存合并后的记忆到数据库
|
||||
MemoryChestModel.create(
|
||||
title=merged_title,
|
||||
content=merged_memory
|
||||
)
|
||||
|
||||
logger.info(f"合并记忆已保存: {merged_title}")
|
||||
|
||||
return merged_title, merged_memory
|
||||
# 解析part1和part2
|
||||
part1_content, part2_content = self._parse_merged_parts(merged_memory)
|
||||
|
||||
# 处理part2:独立记录冲突内容(无论part1是否为空)
|
||||
if part2_content and part2_content.strip() != "none":
|
||||
logger.info(f"合并记忆part2记录冲突内容: {len(part2_content)} 字符")
|
||||
# 导入冲突追踪器
|
||||
from src.curiousity.questions import global_conflict_tracker
|
||||
# 记录冲突到数据库
|
||||
await global_conflict_tracker.record_memory_merge_conflict(part2_content)
|
||||
|
||||
# 处理part1:生成标题并保存
|
||||
if part1_content and part1_content.strip() != "none":
|
||||
merged_title = await self._generate_title_for_merged_memory(part1_content)
|
||||
|
||||
# 保存part1到数据库
|
||||
MemoryChestModel.create(
|
||||
title=merged_title,
|
||||
content=part1_content
|
||||
)
|
||||
|
||||
logger.info(f"合并记忆part1已保存: {merged_title}")
|
||||
|
||||
return merged_title, part1_content
|
||||
else:
|
||||
logger.warning("合并记忆part1为空,跳过保存")
|
||||
return "", ""
|
||||
except Exception as e:
|
||||
logger.error(f"合并记忆时出错: {e}")
|
||||
return "", ""
|
||||
|
|
@ -5,7 +5,7 @@ import re
|
|||
from typing import List
|
||||
|
||||
from src.manager.async_task_manager import AsyncTask
|
||||
from src.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("hippocampus_to_memory_chest")
|
||||
|
|
@ -4,7 +4,7 @@ import random
|
|||
from typing import List
|
||||
|
||||
from src.manager.async_task_manager import AsyncTask
|
||||
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||
from src.memory_system.Memory_chest import global_memory_chest
|
||||
from src.common.logger import get_logger
|
||||
from src.common.database.database_model import MemoryChest as MemoryChestModel
|
||||
from src.config.config import global_config
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"manifest_version": 1,
|
||||
"name": "MaiCurious插件 (MaiCurious 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": ["curious", "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": "maicurious",
|
||||
"description": "发送好奇"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
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
|
||||
from src.plugin_system.base.base_action import BaseAction
|
||||
from src.plugin_system.base.component_types import ActionActivationType
|
||||
from src.plugin_system.apis import config_api
|
||||
from src.plugin_system.apis import frequency_api
|
||||
from src.plugin_system.apis import generator_api
|
||||
from src.curiousity.questions import global_conflict_tracker
|
||||
|
||||
logger = get_logger("relation_actions")
|
||||
|
||||
|
||||
|
||||
class CuriousAction(BaseAction):
|
||||
"""频率调节动作 - 调整聊天发言频率"""
|
||||
|
||||
activation_type = ActionActivationType.LLM_JUDGE
|
||||
parallel_action = False
|
||||
|
||||
# 动作基本信息
|
||||
action_name = "question"
|
||||
|
||||
action_description = "对现有的存在疑问的信息生成一个问题"
|
||||
|
||||
# 动作参数定义
|
||||
action_parameters = {
|
||||
"question": "存在疑问的信息,提出一个问题",
|
||||
}
|
||||
|
||||
# 动作使用场景
|
||||
bot_name = config_api.get_global_config("bot.nickname")
|
||||
|
||||
|
||||
action_require = [
|
||||
f"当聊天记录中的信息存在逻辑上的矛盾时使用",
|
||||
f"当有人反对或否定你提出的信息时使用",
|
||||
f"或当你对现有的信息存在疑问时使用",
|
||||
]
|
||||
|
||||
# 关联类型
|
||||
associated_types = ["text"]
|
||||
|
||||
async def execute(self) -> Tuple[bool, str]:
|
||||
"""执行频率调节动作"""
|
||||
try:
|
||||
question = self.action_data.get("question", "")
|
||||
|
||||
# 存储问题到冲突追踪器
|
||||
if question:
|
||||
await global_conflict_tracker.record_conflict(conflict_content=question, start_following=True,chat_id=self.chat_id)
|
||||
logger.info(f"已存储问题到冲突追踪器: {len(question)} 字符")
|
||||
await self.store_action_info(
|
||||
action_build_into_prompt=True,
|
||||
action_prompt_display=f"你产生了一个问题,原因:{question},尝试向其他人提问或回忆吧",
|
||||
action_done=True,
|
||||
)
|
||||
return True, "问题已记录"
|
||||
except Exception as e:
|
||||
error_msg = f"问题生成失败: {str(e)}"
|
||||
logger.error(f"{self.log_prefix} {error_msg}", exc_info=True)
|
||||
await self.send_text("问题生成失败")
|
||||
return False, error_msg
|
||||
|
||||
|
||||
@register_plugin
|
||||
class CuriousPlugin(BasePlugin):
|
||||
"""关系动作插件
|
||||
|
||||
系统内置插件,提供基础的聊天交互功能:
|
||||
- Reply: 回复动作
|
||||
- NoReply: 不回复动作
|
||||
- Emoji: 表情动作
|
||||
|
||||
注意:插件基本信息优先从_manifest.json文件中读取
|
||||
"""
|
||||
|
||||
# 插件基本信息
|
||||
plugin_name: str = "maicurious" # 内部标识符
|
||||
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="3.0.0", description="配置文件版本"),
|
||||
}
|
||||
}
|
||||
|
||||
def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]:
|
||||
"""返回插件包含的组件列表"""
|
||||
|
||||
# --- 根据配置注册组件 ---
|
||||
components = []
|
||||
components.append((CuriousAction.get_action_info(), CuriousAction))
|
||||
|
||||
return components
|
||||
|
|
@ -5,9 +5,9 @@ from src.config.config import global_config
|
|||
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.chat.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.chat.utils.utils import cut_key_words
|
||||
from src.chat.memory_system.Memory_chest import global_memory_chest
|
||||
from src.memory_system.Memory_chest import global_memory_chest
|
||||
from src.plugin_system.base.base_tool import BaseTool
|
||||
from typing import Any
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue