feat:添加频率精准控制,移除s4u代码,优化tool返回值

This commit is contained in:
SengokuCola
2025-10-02 01:19:05 +08:00
parent 0692fcac37
commit 92e90a0bee
32 changed files with 173 additions and 4536 deletions

View File

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

View File

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

View File

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

View File

@@ -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} 的记忆")