feat:添加频率精准控制,移除s4u代码,优化tool返回值
This commit is contained in:
@@ -77,9 +77,9 @@ class MemoryChest:
|
||||
should_update = new_messages_count > self.memory_build_threshold or forced_update
|
||||
|
||||
if forced_update:
|
||||
logger.info(f"chat_id {chat_id} 距离上次更新已 {time_diff_minutes:.1f} 分钟,有 {new_messages_count} 条新消息,强制构建")
|
||||
logger.debug(f"chat_id {chat_id} 距离上次更新已 {time_diff_minutes:.1f} 分钟,有 {new_messages_count} 条新消息,强制构建")
|
||||
else:
|
||||
logger.info(f"chat_id {chat_id} 自上次更新后有 {new_messages_count} 条新消息,{'需要' if should_update else '不需要'}更新")
|
||||
logger.debug(f"chat_id {chat_id} 自上次更新后有 {new_messages_count} 条新消息,{'需要' if should_update else '不需要'}更新")
|
||||
|
||||
|
||||
if should_update:
|
||||
@@ -89,7 +89,7 @@ class MemoryChest:
|
||||
replace_bot_name=True,
|
||||
timestamp_mode="relative",
|
||||
read_mark=0.0,
|
||||
show_actions=True,
|
||||
show_actions=False,
|
||||
remove_emoji_stickers=True,
|
||||
)
|
||||
|
||||
@@ -233,11 +233,12 @@ class MemoryChest:
|
||||
type = "要求提取简短的内容"
|
||||
|
||||
prompt = f"""
|
||||
目标文段:
|
||||
{content}
|
||||
|
||||
请根据问题:{question}
|
||||
在上方内容中,提取相关信息的原文并输出,{type}
|
||||
请务必提取上面原文,不要输出其他内容:
|
||||
你现在需要从目标文段中找出合适的信息来回答问题:{question}
|
||||
请务必从目标文段中提取相关信息的**原文**并输出,{type}
|
||||
如果没有原文能够回答问题,输出"无有效信息"即可,不要输出其他内容:
|
||||
"""
|
||||
|
||||
if global_config.debug.show_prompt:
|
||||
@@ -247,6 +248,9 @@ class MemoryChest:
|
||||
|
||||
answer, (reasoning_content, model_name, tool_calls) = await self.LLMRequest.generate_response_async(prompt)
|
||||
|
||||
if "无有效" in answer or "无有效信息" in answer or "无信息" in answer:
|
||||
logger.info(f"没有能够回答{question}的记忆")
|
||||
return ""
|
||||
|
||||
logger.info(f"记忆仓库对问题 “{question}” 获取答案: {answer}")
|
||||
|
||||
@@ -283,7 +287,7 @@ class MemoryChest:
|
||||
# 用换行符连接所有记忆
|
||||
result = "\n".join(memories)
|
||||
|
||||
logger.info(f"chat_id {chat_id} 共有 {len(memories)} 条记忆")
|
||||
# logger.info(f"chat_id {chat_id} 共有 {len(memories)} 条记忆")
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import asyncio
|
||||
import random
|
||||
import re
|
||||
from typing import List
|
||||
|
||||
from src.manager.async_task_manager import AsyncTask
|
||||
from src.memory_system.Hippocampus import hippocampus_manager
|
||||
from src.common.logger import get_logger
|
||||
|
||||
logger = get_logger("hippocampus_to_memory_chest")
|
||||
|
||||
|
||||
class HippocampusToMemoryChestTask(AsyncTask):
|
||||
"""海马体到记忆仓库的转换任务
|
||||
|
||||
每10秒执行一次转换,每次最多处理50批,每批15个节点,
|
||||
当没有新节点时停止任务运行
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
task_name="Hippocampus to Memory Chest Task",
|
||||
wait_before_start=5, # 启动后等待5秒再开始
|
||||
run_interval=10 # 每10秒运行一次
|
||||
)
|
||||
self.task_stopped = False # 标记任务是否已停止
|
||||
|
||||
async def start_task(self, abort_flag: asyncio.Event):
|
||||
"""重写start_task方法,支持任务停止"""
|
||||
if self.wait_before_start > 0:
|
||||
# 等待指定时间后开始任务
|
||||
await asyncio.sleep(self.wait_before_start)
|
||||
|
||||
while not abort_flag.is_set() and not self.task_stopped:
|
||||
await self.run()
|
||||
if self.run_interval > 0:
|
||||
await asyncio.sleep(self.run_interval)
|
||||
else:
|
||||
break
|
||||
|
||||
if self.task_stopped:
|
||||
logger.info("[海马体转换] 任务已完全停止,不再执行")
|
||||
|
||||
async def run(self):
|
||||
"""执行转换任务"""
|
||||
try:
|
||||
# 检查任务是否已停止
|
||||
if self.task_stopped:
|
||||
logger.info("[海马体转换] 任务已停止,跳过执行")
|
||||
return
|
||||
|
||||
logger.info("[海马体转换] 开始执行海马体到记忆仓库的转换任务")
|
||||
|
||||
# 检查海马体管理器是否已初始化
|
||||
if not hippocampus_manager._initialized:
|
||||
logger.warning("[海马体转换] 海马体管理器尚未初始化,跳过本次转换")
|
||||
return
|
||||
|
||||
# 获取海马体实例
|
||||
hippocampus = hippocampus_manager.get_hippocampus()
|
||||
memory_graph = hippocampus.memory_graph.G
|
||||
|
||||
# 执行10批转换
|
||||
total_processed = 0
|
||||
total_success = 0
|
||||
|
||||
for batch_num in range(1, 51): # 执行10批
|
||||
logger.info(f"[海马体转换] 开始执行第 {batch_num} 批转换")
|
||||
|
||||
# 检查剩余节点
|
||||
remaining_nodes = list(memory_graph.nodes())
|
||||
if len(remaining_nodes) == 0:
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:没有剩余节点,停止任务运行")
|
||||
self.task_stopped = True
|
||||
break
|
||||
|
||||
# 如果剩余节点不足10个,使用所有剩余节点
|
||||
if len(remaining_nodes) < 5:
|
||||
selected_nodes = remaining_nodes
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:剩余节点不足10个({len(remaining_nodes)}个),使用所有剩余节点")
|
||||
else:
|
||||
# 随机选择10个节点
|
||||
selected_nodes = random.sample(remaining_nodes, 5)
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:选择了 {len(selected_nodes)} 个节点")
|
||||
|
||||
# 拼接节点内容
|
||||
content_parts = []
|
||||
valid_nodes = []
|
||||
|
||||
for node in selected_nodes:
|
||||
node_data = memory_graph.nodes[node]
|
||||
memory_items = node_data.get("memory_items", "")
|
||||
|
||||
if memory_items and memory_items.strip():
|
||||
# 添加节点名称和内容
|
||||
content_parts.append(f"【{node}】{memory_items}")
|
||||
valid_nodes.append(node)
|
||||
else:
|
||||
logger.debug(f"[海马体转换] 第 {batch_num} 批:节点 {node} 没有记忆内容,跳过")
|
||||
|
||||
if not content_parts:
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:没有找到有效的记忆内容,跳过")
|
||||
continue
|
||||
|
||||
# 拼接所有内容
|
||||
combined_content = "\n\n".join(content_parts)
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:拼接完成,内容长度: {len(combined_content)} 字符")
|
||||
|
||||
# 生成标题并存储到记忆仓库
|
||||
success = await self._save_to_memory_chest(combined_content, batch_num)
|
||||
|
||||
# 如果保存成功,删除已转换的节点
|
||||
if success:
|
||||
await self._remove_converted_nodes(valid_nodes)
|
||||
total_success += 1
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:转换成功")
|
||||
else:
|
||||
logger.warning(f"[海马体转换] 第 {batch_num} 批:转换失败")
|
||||
|
||||
total_processed += 1
|
||||
|
||||
# 批次间短暂休息,避免过于频繁的数据库操作
|
||||
if batch_num < 10:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
logger.info(f"[海马体转换] 本次执行完成:共处理 {total_processed} 批,成功 {total_success} 批")
|
||||
|
||||
logger.info("[海马体转换] 转换任务完成")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[海马体转换] 执行转换任务时发生错误: {e}", exc_info=True)
|
||||
|
||||
async def _save_to_memory_chest(self, content: str, batch_num: int = 1) -> bool:
|
||||
"""将内容保存到记忆仓库
|
||||
|
||||
Args:
|
||||
content: 要保存的内容
|
||||
batch_num: 批次号
|
||||
|
||||
Returns:
|
||||
bool: 保存是否成功
|
||||
"""
|
||||
try:
|
||||
# 从内容中提取节点名称作为标题
|
||||
title = self._generate_title_from_content(content, batch_num)
|
||||
|
||||
if title:
|
||||
# 保存到数据库
|
||||
from src.common.database.database_model import MemoryChest as MemoryChestModel
|
||||
|
||||
MemoryChestModel.create(
|
||||
title=title,
|
||||
content=content
|
||||
)
|
||||
|
||||
logger.info(f"[海马体转换] 第 {batch_num} 批:已保存到记忆仓库,标题: {title}")
|
||||
return True
|
||||
else:
|
||||
logger.warning("[海马体转换] 生成标题失败,跳过保存")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[海马体转换] 保存到记忆仓库时发生错误: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
def _generate_title_from_content(self, content: str, batch_num: int = 1) -> str:
|
||||
"""从内容中提取节点名称生成标题
|
||||
|
||||
Args:
|
||||
content: 拼接的内容
|
||||
batch_num: 批次号
|
||||
|
||||
Returns:
|
||||
str: 生成的标题
|
||||
"""
|
||||
try:
|
||||
# 提取所有【节点名称】中的节点名称
|
||||
node_pattern = r'【([^】]+)】'
|
||||
nodes = re.findall(node_pattern, content)
|
||||
|
||||
if nodes:
|
||||
# 去重并限制数量(最多显示前5个)
|
||||
unique_nodes = list(dict.fromkeys(nodes))[:5]
|
||||
title = f"关于{','.join(unique_nodes)}的记忆"
|
||||
return title
|
||||
else:
|
||||
logger.warning("[海马体转换] 无法从内容中提取节点名称")
|
||||
return ""
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[海马体转换] 生成标题时发生错误: {e}", exc_info=True)
|
||||
return ""
|
||||
|
||||
async def _remove_converted_nodes(self, nodes_to_remove: List[str]):
|
||||
"""删除已转换的海马体节点
|
||||
|
||||
Args:
|
||||
nodes_to_remove: 要删除的节点列表
|
||||
"""
|
||||
try:
|
||||
# 获取海马体实例
|
||||
hippocampus = hippocampus_manager.get_hippocampus()
|
||||
memory_graph = hippocampus.memory_graph.G
|
||||
|
||||
removed_count = 0
|
||||
for node in nodes_to_remove:
|
||||
if node in memory_graph:
|
||||
# 删除节点(这会自动删除相关的边)
|
||||
memory_graph.remove_node(node)
|
||||
removed_count += 1
|
||||
logger.info(f"[海马体转换] 已删除节点: {node}")
|
||||
else:
|
||||
logger.debug(f"[海马体转换] 节点 {node} 不存在,跳过删除")
|
||||
|
||||
# 同步到数据库
|
||||
if removed_count > 0:
|
||||
await hippocampus.entorhinal_cortex.sync_memory_to_db()
|
||||
logger.info(f"[海马体转换] 已删除 {removed_count} 个节点并同步到数据库")
|
||||
else:
|
||||
logger.info("[海马体转换] 没有节点需要删除")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[海马体转换] 删除节点时发生错误: {e}", exc_info=True)
|
||||
@@ -95,7 +95,6 @@ class MemoryManagementTask(AsyncTask):
|
||||
|
||||
# 如果记忆数量为0,跳过执行
|
||||
if current_count < 10:
|
||||
logger.info("[记忆管理] 没有太多记忆,跳过执行")
|
||||
return
|
||||
|
||||
# 随机选择一个记忆标题
|
||||
@@ -104,15 +103,13 @@ class MemoryManagementTask(AsyncTask):
|
||||
logger.warning("[记忆管理] 无法获取随机记忆标题,跳过执行")
|
||||
return
|
||||
|
||||
logger.info(f"[记忆管理] 随机选择的记忆标题: {selected_title}")
|
||||
|
||||
# 执行choose_merge_target获取相关记忆内容
|
||||
related_contents_titles = await global_memory_chest.choose_merge_target(selected_title)
|
||||
if not related_contents_titles:
|
||||
logger.warning("[记忆管理] 未找到相关记忆内容,跳过合并")
|
||||
logger.info("无合适合并内容,跳过本次合并")
|
||||
return
|
||||
|
||||
logger.info(f"[记忆管理] 找到 {len(related_contents_titles)} 条相关记忆")
|
||||
logger.info(f"为 [{selected_title}] 找到 {len(related_contents_titles)} 条相关记忆:related_contents_titles")
|
||||
|
||||
# 执行merge_memory合并记忆
|
||||
merged_title, merged_content = await global_memory_chest.merge_memory(related_contents_titles)
|
||||
|
||||
@@ -94,7 +94,7 @@ def fuzzy_find_memory_by_title(target_title: str, similarity_threshold: float =
|
||||
# 按相似度降序排序
|
||||
matches.sort(key=lambda x: x[2], reverse=True)
|
||||
|
||||
logger.info(f"模糊查找标题 '{target_title}' 找到 {len(matches)} 个匹配项")
|
||||
# logger.info(f"模糊查找标题 '{target_title}' 找到 {len(matches)} 个匹配项")
|
||||
return matches
|
||||
|
||||
except Exception as e:
|
||||
@@ -118,7 +118,7 @@ def find_best_matching_memory(target_title: str, similarity_threshold: float = 0
|
||||
|
||||
if matches:
|
||||
best_match = matches[0] # 已经按相似度排序,第一个是最佳匹配
|
||||
logger.info(f"找到最佳匹配: '{best_match[0]}' (相似度: {best_match[2]:.3f})")
|
||||
# logger.info(f"找到最佳匹配: '{best_match[0]}' (相似度: {best_match[2]:.3f})")
|
||||
return best_match
|
||||
else:
|
||||
logger.info(f"未找到相似度 >= {similarity_threshold} 的记忆")
|
||||
|
||||
Reference in New Issue
Block a user