删除无用文档和测试文件

This commit is contained in:
Windpicker-owo
2025-12-13 20:50:19 +08:00
parent 9a0163d06b
commit d7ab785ced
20 changed files with 0 additions and 7186 deletions

View File

@@ -1,654 +0,0 @@
# Affinity Flow Chatter 插件优化总结
## 更新日期
2025年11月3日
## 优化概述
本次对 Affinity Flow Chatter 插件进行了全面的重构和优化主要包括目录结构优化、性能改进、bug修复和新功能添加。
## <20> 任务-1: 细化提及分数机制(强提及 vs 弱提及)
### 变更内容
将原有的统一提及分数细化为**强提及**和**弱提及**两种类型,使用不同的分值。
### 原设计问题
**旧逻辑**
- ❌ 所有提及方式使用同一个分值(`mention_bot_interest_score`
- ❌ 被@、私聊、文本提到名字都是相同的重要性
- ❌ 无法区分用户的真实意图
### 新设计
#### 强提及Strong Mention
**定义**:用户**明确**想与bot交互
- ✅ 被 @ 提及
- ✅ 被回复
- ✅ 私聊消息
**分值**`strong_mention_interest_score = 2.5`(默认)
#### 弱提及Weak Mention
**定义**:在讨论中**顺带**提到bot
- ✅ 消息中包含bot名字
- ✅ 消息中包含bot别名
**分值**`weak_mention_interest_score = 1.5`(默认)
### 检测逻辑
```python
def is_mentioned_bot_in_message(message) -> tuple[bool, float]:
"""
Returns:
tuple[bool, float]: (是否提及, 提及类型)
提及类型: 0=未提及, 1=弱提及, 2=强提及
"""
# 1. 检查私聊 → 强提及
if is_private_chat:
return True, 2.0
# 2. 检查 @ → 强提及
if is_at:
return True, 2.0
# 3. 检查回复 → 强提及
if is_replied:
return True, 2.0
# 4. 检查文本匹配 → 弱提及
if text_contains_bot_name_or_alias:
return True, 1.0
return False, 0.0
```
### 配置参数
**config/bot_config.toml**:
```toml
[affinity_flow]
# 提及bot相关参数
strong_mention_interest_score = 2.5 # 强提及(@/回复/私聊)
weak_mention_interest_score = 1.5 # 弱提及(文本匹配)
```
### 实际效果对比
**场景1被@**
```
用户: "@小狐 你好呀"
旧逻辑: 提及分 = 2.5
新逻辑: 提及分 = 2.5 (强提及) ✅ 保持不变
```
**场景2回复bot**
```
用户: [回复 小狐:...] "是的"
旧逻辑: 提及分 = 2.5
新逻辑: 提及分 = 2.5 (强提及) ✅ 保持不变
```
**场景3私聊**
```
用户: "在吗"
旧逻辑: 提及分 = 2.5
新逻辑: 提及分 = 2.5 (强提及) ✅ 保持不变
```
**场景4文本提及**
```
用户: "小狐今天没来吗"
旧逻辑: 提及分 = 2.5 (可能过高)
新逻辑: 提及分 = 1.5 (弱提及) ✅ 更合理
```
**场景5讨论bot**
```
用户A: "小狐这个bot挺有意思的"
旧逻辑: 提及分 = 2.5 (bot可能会插话)
新逻辑: 提及分 = 1.5 (弱提及,降低打断概率) ✅ 更自然
```
### 优势
-**意图识别**:区分"想对话"和"在讨论"
-**减少误判**:降低在他人讨论中插话的概率
-**灵活调节**:可以独立调整强弱提及的权重
-**向后兼容**:保持原有强提及的行为不变
### 影响文件
- `config/bot_config.toml`:添加 `strong/weak_mention_interest_score` 配置
- `template/bot_config_template.toml`:同步模板配置
- `src/config/official_configs.py`:添加配置字段定义
- `src/chat/utils/utils.py`:修改 `is_mentioned_bot_in_message()` 函数
- `src/plugins/built_in/affinity_flow_chatter/core/affinity_interest_calculator.py`:使用新的强弱提及逻辑
- `docs/affinity_flow_guide.md`:更新文档说明
---
## <20>🆔 任务0: 修改 Personality ID 生成逻辑
### 变更内容
`bot_person_id` 从固定值改为基于人设文本的 hash 生成,实现人设变化时自动触发兴趣标签重新生成。
### 原设计问题
**旧逻辑**
```python
self.bot_person_id = person_info_manager.get_person_id("system", "bot_id")
# 结果md5("system_bot_id") = 固定值
```
- ❌ personality_id 固定不变
- ❌ 人设修改后不会重新生成兴趣标签
- ❌ 需要手动清空数据库才能触发重新生成
### 新设计
**新逻辑**
```python
personality_hash, _ = self._get_config_hash(bot_nickname, personality_core, personality_side, identity)
self.bot_person_id = personality_hash
# 结果md5(人设配置的JSON) = 动态值
```
### Hash 生成规则
```python
personality_config = {
"nickname": bot_nickname,
"personality_core": personality_core,
"personality_side": personality_side,
"compress_personality": global_config.personality.compress_personality,
}
personality_hash = md5(json_dumps(personality_config, sorted=True))
```
### 工作原理
1. **初始化时**:根据当前人设配置计算 hash 作为 personality_id
2. **配置变化检测**
- 计算当前人设的 hash
- 与上次保存的 hash 对比
- 如果不同,触发重新生成
3. **兴趣标签生成**
- `bot_interest_manager` 根据 personality_id 查询数据库
- 如果 personality_id 不存在(人设变化了),自动生成新的兴趣标签
- 保存时使用新的 personality_id
### 优势
-**自动检测**:人设改变后无需手动操作
-**数据隔离**:不同人设的兴趣标签分开存储
-**版本管理**:可以保留历史人设的兴趣标签(如果需要)
-**逻辑清晰**personality_id 直接反映人设内容
### 示例
```
人设 A:
nickname: "小狐"
personality_core: "活泼开朗"
personality_side: "喜欢编程"
→ personality_id: a1b2c3d4e5f6...
人设 B (修改后):
nickname: "小狐"
personality_core: "冷静理性" ← 改变
personality_side: "喜欢编程"
→ personality_id: f6e5d4c3b2a1... ← 自动生成新ID
结果:
- 数据库查询时找不到 f6e5d4c3b2a1 的兴趣标签
- 自动触发重新生成
- 新兴趣标签保存在 f6e5d4c3b2a1 下
```
### 影响范围
- `src/individuality/individuality.py`personality_id 生成逻辑
- `src/chat/interest_system/bot_interest_manager.py`:兴趣标签加载/保存(已支持)
- 数据库:`bot_personality_interests` 表通过 personality_id 字段关联
---
## 📁 任务1: 优化插件目录结构
### 变更内容
将原本扁平的文件结构重组为分层目录,提高代码可维护性:
```
affinity_flow_chatter/
├── core/ # 核心模块
│ ├── __init__.py
│ ├── affinity_chatter.py # 主聊天处理器
│ └── affinity_interest_calculator.py # 兴趣度计算器
├── planner/ # 规划器模块
│ ├── __init__.py
│ ├── planner.py # 动作规划器
│ ├── planner_prompts.py # 提示词模板
│ ├── plan_generator.py # 计划生成器
│ ├── plan_filter.py # 计划过滤器
│ └── plan_executor.py # 计划执行器
├── proactive/ # 主动思考模块
│ ├── __init__.py
│ ├── proactive_thinking_scheduler.py # 主动思考调度器
│ ├── proactive_thinking_executor.py # 主动思考执行器
│ └── proactive_thinking_event.py # 主动思考事件
├── tools/ # 工具模块
│ ├── __init__.py
│ ├── chat_stream_impression_tool.py # 聊天印象工具
│ └── user_profile_tool.py # 用户档案工具
├── plugin.py # 插件注册
├── __init__.py # 插件元数据
└── README.md # 文档
```
### 优势
-**逻辑清晰**:相关功能集中在同一目录
-**易于维护**:模块职责明确,便于定位和修改
-**可扩展性**:新功能可以轻松添加到对应目录
-**团队协作**:多人开发时减少文件冲突
---
## 💾 任务2: 修改 Embedding 存储策略
### 问题分析
**原设计**:兴趣标签的 embedding 向量2560维度浮点数组直接存储在数据库中
- ❌ 数据库存储过长,可能导致写入失败
- ❌ 每次加载需要反序列化大量数据
- ❌ 数据库体积膨胀
### 解决方案
**新设计**Embedding 改为启动时动态生成并缓存在内存中
#### 实现细节
**1. 数据库存储**(不再包含 embedding
```python
# 保存时
tag_dict = {
"tag_name": tag.tag_name,
"weight": tag.weight,
"expanded": tag.expanded, # 扩展描述
"created_at": tag.created_at.isoformat(),
"updated_at": tag.updated_at.isoformat(),
"is_active": tag.is_active,
# embedding 不再存储
}
```
**2. 启动时动态生成**
```python
async def _generate_embeddings_for_tags(self, interests: BotPersonalityInterests):
"""为所有兴趣标签生成embedding仅缓存在内存中"""
for tag in interests.interest_tags:
if tag.tag_name in self.embedding_cache:
# 使用内存缓存
tag.embedding = self.embedding_cache[tag.tag_name]
else:
# 动态生成新的embedding
embedding = await self._get_embedding(tag.tag_name)
tag.embedding = embedding # 设置到内存对象
self.embedding_cache[tag.tag_name] = embedding # 缓存
```
**3. 加载时处理**
```python
tag = BotInterestTag(
tag_name=tag_data.get("tag_name", ""),
weight=tag_data.get("weight", 0.5),
expanded=tag_data.get("expanded"),
embedding=None, # 不从数据库加载,改为动态生成
# ...
)
```
### 优势
-**数据库轻量化**:数据库只存储标签名和权重等元数据
-**避免写入失败**:不再因为数据过长导致数据库操作失败
-**灵活性**:可以随时切换 embedding 模型而无需迁移数据
-**性能**:内存缓存访问速度快
### 权衡
- ⚠️ 启动时需要生成 embedding首次启动稍慢约10-20秒
- ✅ 后续运行时使用内存缓存,性能与原来相当
---
## 🔧 任务3: 修复连续不回复阈值调整问题
### 问题描述
原实现中,连续不回复调整只提升了分数,但阈值保持不变:
```python
# ❌ 错误的实现
adjusted_score = self._apply_no_reply_boost(total_score) # 只提升分数
should_reply = adjusted_score >= self.reply_threshold # 阈值不变
```
**问题**:动作阈值(`non_reply_action_interest_threshold`)没有被调整,导致即使回复阈值满足,动作阈值可能仍然不满足。
### 解决方案
改为**同时降低回复阈值和动作阈值**
```python
def _apply_no_reply_threshold_adjustment(self) -> tuple[float, float]:
"""应用阈值调整(包括连续不回复和回复后降低机制)"""
base_reply_threshold = self.reply_threshold
base_action_threshold = global_config.affinity_flow.non_reply_action_interest_threshold
total_reduction = 0.0
# 连续不回复的阈值降低
if self.no_reply_count > 0:
no_reply_reduction = self.no_reply_count * self.probability_boost_per_no_reply
total_reduction += no_reply_reduction
# 应用到两个阈值
adjusted_reply_threshold = max(0.0, base_reply_threshold - total_reduction)
adjusted_action_threshold = max(0.0, base_action_threshold - total_reduction)
return adjusted_reply_threshold, adjusted_action_threshold
```
**使用**
```python
# ✅ 正确的实现
adjusted_reply_threshold, adjusted_action_threshold = self._apply_no_reply_threshold_adjustment()
should_reply = adjusted_score >= adjusted_reply_threshold
should_take_action = adjusted_score >= adjusted_action_threshold
```
### 优势
-**逻辑一致**:回复阈值和动作阈值同步调整
-**避免矛盾**:不会出现"满足回复但不满足动作"的情况
-**更合理**连续不回复时bot更容易采取任何行动
---
## ⏱️ 任务4: 添加兴趣度计算超时机制
### 问题描述
兴趣匹配计算调用 embedding API可能因为网络问题或模型响应慢导致
- ❌ 长时间等待(>5秒
- ❌ 整体超时导致强制使用默认分值
-**丢失了提及分和关系分**(因为整个计算被中断)
### 解决方案
为兴趣匹配计算添加**1.5秒超时保护**,超时时返回默认分值:
```python
async def _calculate_interest_match_score(self, content: str, keywords: list[str] | None = None) -> float:
"""计算兴趣匹配度(带超时保护)"""
try:
# 使用 asyncio.wait_for 添加1.5秒超时
match_result = await asyncio.wait_for(
bot_interest_manager.calculate_interest_match(content, keywords or []),
timeout=1.5
)
if match_result:
# 正常计算分数
final_score = match_result.overall_score * 1.15 * match_result.confidence + match_count_bonus
return final_score
else:
return 0.0
except asyncio.TimeoutError:
# 超时时返回默认分值 0.5
logger.warning("⏱️ 兴趣匹配计算超时(>5秒)返回默认分值0.5以保留其他分数")
return 0.5 # 避免丢失提及分和关系分
except Exception as e:
logger.warning(f"智能兴趣匹配失败: {e}")
return 0.0
```
### 工作流程
```
正常情况(<1.5秒):
兴趣匹配分: 0.8 + 关系分: 0.3 + 提及分: 2.5 = 3.6 ✅
超时情况(>1.5秒):
兴趣匹配分: 0.5(默认)+ 关系分: 0.3 + 提及分: 2.5 = 3.3 ✅
(保留了关系分和提及分)
强制中断(无超时保护):
整体计算失败 = 0.0(默认) ❌
(丢失了所有分数)
```
### 优势
-**防止阻塞**不会因为一个API调用卡住整个流程
-**保留分数**:即使兴趣匹配超时,提及分和关系分依然有效
-**用户体验**:响应更快,不会长时间无反应
-**降级优雅**:超时时仍能给出合理的默认值
---
## 🔄 任务5: 实现回复后阈值降低机制
### 需求背景
**目标**让bot在回复后更容易进行连续对话提升对话的连贯性和自然性。
**场景示例**
```
用户: "你好呀"
Bot: "你好!今天过得怎么样?" ← 此时激活连续对话模式
用户: "还不错"
Bot: "那就好~有什么有趣的事情吗?" ← 阈值降低,更容易回复
用户: "没什么"
Bot: "嗯嗯,那要不要聊聊别的?" ← 仍然更容易回复
用户: "..."
(如果一直不回复,降低效果会逐渐衰减)
```
### 配置项
`bot_config.toml` 中添加:
```toml
# 回复后连续对话机制参数
enable_post_reply_boost = true # 是否启用回复后阈值降低机制
post_reply_threshold_reduction = 0.15 # 回复后初始阈值降低值
post_reply_boost_max_count = 3 # 回复后阈值降低的最大持续次数
post_reply_boost_decay_rate = 0.5 # 每次回复后阈值降低衰减率0-1
```
### 实现细节
**1. 初始化计数器**
```python
def __init__(self):
# 回复后阈值降低机制
self.enable_post_reply_boost = affinity_config.enable_post_reply_boost
self.post_reply_boost_remaining = 0 # 剩余的回复后降低次数
self.post_reply_threshold_reduction = affinity_config.post_reply_threshold_reduction
self.post_reply_boost_max_count = affinity_config.post_reply_boost_max_count
self.post_reply_boost_decay_rate = affinity_config.post_reply_boost_decay_rate
```
**2. 阈值调整**
```python
def _apply_no_reply_threshold_adjustment(self) -> tuple[float, float]:
"""应用阈值调整"""
total_reduction = 0.0
# 1. 连续不回复的降低
if self.no_reply_count > 0:
no_reply_reduction = self.no_reply_count * self.probability_boost_per_no_reply
total_reduction += no_reply_reduction
# 2. 回复后的降低(带衰减)
if self.enable_post_reply_boost and self.post_reply_boost_remaining > 0:
# 计算衰减因子
decay_factor = self.post_reply_boost_decay_rate ** (
self.post_reply_boost_max_count - self.post_reply_boost_remaining
)
post_reply_reduction = self.post_reply_threshold_reduction * decay_factor
total_reduction += post_reply_reduction
# 应用总降低量
adjusted_reply_threshold = max(0.0, base_reply_threshold - total_reduction)
adjusted_action_threshold = max(0.0, base_action_threshold - total_reduction)
return adjusted_reply_threshold, adjusted_action_threshold
```
**3. 状态更新**
```python
def on_reply_sent(self):
"""当机器人发送回复后调用"""
if self.enable_post_reply_boost:
# 重置回复后降低计数器
self.post_reply_boost_remaining = self.post_reply_boost_max_count
# 同时重置不回复计数
self.no_reply_count = 0
def on_message_processed(self, replied: bool):
"""消息处理完成后调用"""
# 更新不回复计数
self.update_no_reply_count(replied)
# 如果已回复,激活回复后降低机制
if replied:
self.on_reply_sent()
else:
# 如果没有回复,减少回复后降低剩余次数
if self.post_reply_boost_remaining > 0:
self.post_reply_boost_remaining -= 1
```
### 衰减机制说明
**衰减公式**
```
decay_factor = decay_rate ^ (max_count - remaining_count)
actual_reduction = base_reduction * decay_factor
```
**示例**`base_reduction=0.15`, `decay_rate=0.5`, `max_count=3`
```
第1次回复后: decay_factor = 0.5^0 = 1.00, reduction = 0.15 * 1.00 = 0.15
第2次回复后: decay_factor = 0.5^1 = 0.50, reduction = 0.15 * 0.50 = 0.075
第3次回复后: decay_factor = 0.5^2 = 0.25, reduction = 0.15 * 0.25 = 0.0375
```
### 实际效果
**配置示例**
- 回复阈值: 0.7
- 初始降低值: 0.15
- 最大次数: 3
- 衰减率: 0.5
**对话流程**
```
初始状态:
回复阈值: 0.7
Bot发送回复 → 激活连续对话模式:
剩余次数: 3
第1条消息:
阈值降低: 0.15
实际阈值: 0.7 - 0.15 = 0.55 ✅ 更容易回复
第2条消息:
阈值降低: 0.075 (衰减)
实际阈值: 0.7 - 0.075 = 0.625
第3条消息:
阈值降低: 0.0375 (继续衰减)
实际阈值: 0.7 - 0.0375 = 0.6625
第4条消息:
降低结束,恢复正常阈值: 0.7
```
### 优势
-**连贯对话**bot回复后更容易继续对话
-**自然衰减**:避免无限连续回复,逐渐恢复正常
-**可配置**:可以根据需求调整降低值、次数和衰减率
-**灵活控制**:可以随时启用/禁用此功能
---
## 📊 整体影响
### 性能优化
-**内存优化**:不再在数据库中存储大量 embedding 数据
-**响应速度**:超时保护避免长时间等待
-**启动速度**:首次启动需要生成 embedding10-20秒后续运行使用缓存
### 功能增强
-**阈值调整**:修复了回复和动作阈值不一致的问题
-**连续对话**:新增回复后阈值降低机制,提升对话连贯性
-**容错能力**超时保护确保即使API失败也能保留其他分数
### 代码质量
-**目录结构**:清晰的模块划分,易于维护
-**可扩展性**:新功能可以轻松添加到对应目录
-**可配置性**:关键参数可通过配置文件调整
---
## 🔧 使用说明
### 配置调整
`config/bot_config.toml` 中调整回复后连续对话参数:
```toml
[affinity_flow]
# 回复后连续对话机制
enable_post_reply_boost = true # 启用/禁用
post_reply_threshold_reduction = 0.15 # 初始降低值建议0.1-0.2
post_reply_boost_max_count = 3 # 持续次数建议2-5
post_reply_boost_decay_rate = 0.5 # 衰减率建议0.3-0.7
```
### 调用方式
在 planner 或其他需要的地方调用:
```python
# 计算兴趣值
result = await interest_calculator.execute(message)
# 消息处理完成后更新状态
interest_calculator.on_message_processed(replied=result.should_reply)
```
---
## 🐛 已知问题
暂无
---
## 📝 后续优化建议
1. **监控日志**:观察实际使用中的阈值调整效果
2. **A/B测试**:对比启用/禁用回复后降低机制的对话质量
3. **参数调优**:根据实际使用情况调整默认配置值
4. **性能监控**:监控 embedding 生成的时间和缓存命中率
---
## 👥 贡献者
- GitHub Copilot - 代码实现和文档编写
---
## 📅 更新历史
- 2025-11-03: 完成所有5个任务的实现
- ✅ 优化插件目录结构
- ✅ 修改 embedding 存储策略
- ✅ 修复连续不回复阈值调整
- ✅ 添加超时保护机制
- ✅ 实现回复后阈值降低

View File

@@ -1,170 +0,0 @@
# affinity_flow 配置项详解与调整指南
本指南详细说明了 MoFox-Bot `bot_config.toml` 配置文件中 `[affinity_flow]` 区块的各项参数,帮助你根据实际需求调整兴趣评分系统与回复决策系统的行为。
---
## 一、affinity_flow 作用简介
`affinity_flow` 主要用于控制 AI 对消息的兴趣评分afc并据此决定是否回复、如何回复、是否发送表情包等。通过合理调整这些参数可以让 Bot 的回复行为更贴合你的预期。
---
## 二、配置项说明
### 1. 兴趣评分相关参数
- `reply_action_interest_threshold`
回复动作兴趣阈值。只有兴趣分高于此值Bot 才会主动回复消息。
- **建议调整**提高此值Bot 回复更谨慎;降低则更容易回复。
- `non_reply_action_interest_threshold`
非回复动作兴趣阈值如发送表情包等。兴趣分高于此值时Bot 可能采取非回复行为。
- `high_match_interest_threshold`
高匹配兴趣阈值。关键词匹配度高于此值时,视为高匹配。
- `medium_match_interest_threshold`
中匹配兴趣阈值。
- `low_match_interest_threshold`
低匹配兴趣阈值。
- `high_match_keyword_multiplier`
高匹配关键词兴趣倍率。高匹配关键词对兴趣分的加成倍数。
- `medium_match_keyword_multiplier`
中匹配关键词兴趣倍率。
- `low_match_keyword_multiplier`
低匹配关键词兴趣倍率。
匹配关键词数量的加成值。匹配越多,兴趣分越高。
- `max_match_bonus`
匹配数加成的最大值。
### 2. 回复决策相关参数
- `no_reply_threshold_adjustment`
不回复兴趣阈值调整值。用于动态调整不回复的兴趣阈值。bot每不回复一次就会在基础阈值上降低该值。
- `reply_cooldown_reduction`
回复后减少的不回复计数。回复后Bot 会更快恢复到基础阈值的状态。
- `max_no_reply_count`
最大不回复计数次数。防止 Bot 的回复阈值被过度降低。
### 3. 综合评分权重
- `keyword_match_weight`
兴趣关键词匹配度权重。关键词匹配对总兴趣分的影响比例。
- `mention_bot_weight`
提及 Bot 分数权重。被提及时兴趣分提升的权重。
- `relationship_weight`
### 4. 提及 Bot 相关参数
- `mention_bot_adjustment_threshold`
提及 Bot 后的调整阈值。当bot被提及后回复阈值会改变为这个值。
- `strong_mention_interest_score`
强提及的兴趣分。强提及包括:被@、被回复、私聊消息。这类提及表示用户明确想与bot交互。
- `weak_mention_interest_score`
弱提及的兴趣分。弱提及包括消息中包含bot的名字或别名文本匹配。这类提及可能只是在讨论中提到bot。
- `base_relationship_score`
---
1. **Bot 太冷漠/回复太少**
- 降低 `reply_action_interest_threshold`,或降低高中低关键词匹配的阈值。
2. **Bot 太热情/回复太多**
- 提高 `reply_action_interest_threshold`,或降低关键词相关倍率。
3. **希望 Bot 更关注被 @ 或回复的消息**
- 提高 `strong_mention_interest_score``mention_bot_weight`
4. **希望 Bot 对文本提及也积极回应**
- 提高 `weak_mention_interest_score`
5. **希望 Bot 更看重关系好的用户**
- 提高 `relationship_weight``base_relationship_score`
6. **表情包行为过于频繁/稀少**
- 调整 `non_reply_action_interest_threshold`
---
## 四、参数调整建议流程
1. 明确你希望 Bot 的行为(如更活跃/更安静/更关注特定用户等)。
2. 根据上表找到相关参数,优先调整权重和阈值。
3. 每次只微调一两个参数,观察实际效果。
4. 如需更细致的行为控制,可结合关键词、关系等多项参数综合调整。
---
## 五、示例配置片段
```toml
[affinity_flow]
reply_action_interest_threshold = 1.1
non_reply_action_interest_threshold = 0.9
high_match_interest_threshold = 0.7
medium_match_interest_threshold = 0.4
low_match_interest_threshold = 0.2
high_match_keyword_multiplier = 5
medium_match_keyword_multiplier = 3.75
low_match_keyword_multiplier = 1.3
match_count_bonus = 0.02
max_match_bonus = 0.25
no_reply_threshold_adjustment = 0.01
reply_cooldown_reduction = 5
max_no_reply_count = 20
keyword_match_weight = 0.4
mention_bot_weight = 0.3
relationship_weight = 0.3
mention_bot_adjustment_threshold = 0.5
strong_mention_interest_score = 2.5 # 强提及(@/回复/私聊)
weak_mention_interest_score = 1.5 # 弱提及(文本匹配)
base_relationship_score = 0.3
```
## 六、afc兴趣度评分决策流程详解
MoFox-Bot 在收到每条消息时会通过一套“兴趣度评分afc”决策流程综合多种因素计算出对该消息的兴趣分并据此决定是否回复、如何回复或采取其他动作。以下为典型流程说明
### 1. 关键词匹配与兴趣加成
- Bot 首先分析消息内容,查找是否包含高、中、低匹配的兴趣关键词。
- 不同匹配度的关键词会乘以对应的倍率high/medium/low_match_keyword_multiplier并根据匹配数量叠加加成match_count_bonusmax_match_bonus
### 2. 提及与关系加分
- 如果消息中提及了 Bot会根据提及类型获得不同的兴趣分
* **强提及**(被@、被回复、私聊): 获得 `strong_mention_interest_score` 分值表示用户明确想与bot交互
* **弱提及**文本中包含bot名字或别名: 获得 `weak_mention_interest_score` 分值表示在讨论中提到bot
* 提及分按权重(`mention_bot_weight`)计入总分
- 与用户的关系分base_relationship_score 及动态关系分)也会按 relationship_weight 计入总分。
### 3. 综合评分计算
- 最终兴趣分 = 关键词匹配分 × keyword_match_weight + 提及分 × mention_bot_weight + 关系分 × relationship_weight。
- 你可以通过调整各权重,决定不同因素对总兴趣分的影响。
### 4. 阈值判定与回复决策
- 若兴趣分高于 reply_action_interest_thresholdBot 会主动回复。
- 若兴趣分高于 non_reply_action_interest_threshold但低于回复阈值Bot 可能采取如发送表情包等非回复行为。
- 若兴趣分均未达到阈值,则不回复。
### 5. 动态阈值调整机制
- Bot 连续多次不回复时reply_action_interest_threshold 会根据 no_reply_threshold_adjustment 逐步降低,最多降低 max_no_reply_count 次,防止长时间沉默。
- 回复后,阈值通过 reply_cooldown_reduction 恢复。
-@时,阈值可临时调整为 mention_bot_adjustment_threshold。
### 6. 典型决策流程图
1. 收到消息 → 2. 关键词/提及/关系分计算 → 3. 综合兴趣分加权 → 4. 与阈值比较 → 5. 决定回复/表情/忽略
通过理解上述流程,你可以有针对性地调整各项参数,让 Bot 的回复行为更贴合你的需求。

View File

@@ -1,374 +0,0 @@
# 数据库API迁移检查清单
## 概述
本文档列出了项目中需要从直接数据库查询迁移到使用优化后API的代码位置。
## 为什么需要迁移?
优化后的API具有以下优势
1. **自动缓存**: 高频查询已集成多级缓存减少90%+数据库访问
2. **批量处理**: 消息存储使用批处理,减少连接池压力
3. **统一接口**: 标准化的错误处理和日志记录
4. **性能监控**: 内置性能统计和慢查询警告
5. **代码简洁**: 简化的API调用减少样板代码
## 迁移优先级
### 🔴 高优先级(高频查询)
#### 1. PersonInfo 查询 - `src/person_info/person_info.py`
**当前实现**:直接使用 SQLAlchemy `session.execute(select(PersonInfo)...)`
**影响范围**
- `get_value()` - 每条消息都会调用
- `get_values()` - 批量查询用户信息
- `update_one_field()` - 更新用户字段
- `is_person_known()` - 检查用户是否已知
- `get_person_info_by_name()` - 根据名称查询
**迁移目标**:使用 `src.common.database.api.specialized` 中的:
```python
from src.common.database.api.specialized import (
get_or_create_person,
update_person_affinity,
)
# 替代直接查询
person, created = await get_or_create_person(
platform=platform,
person_id=person_id,
defaults={"nickname": nickname, ...}
)
```
**优势**
- ✅ 10分钟缓存减少90%+数据库查询
- ✅ 自动缓存失效机制
- ✅ 标准化的错误处理
**预计工作量**:⏱️ 2-4小时
---
#### 2. UserRelationships 查询 - `src/person_info/relationship_fetcher.py`
**当前实现**:使用 `db_query(UserRelationships, ...)`
**影响代码**
- `build_relation_info()` 第189行
- 查询用户关系数据
**迁移目标**
```python
from src.common.database.api.specialized import (
get_user_relationship,
update_relationship_affinity,
)
# 替代 db_query
relationship = await get_user_relationship(
platform=platform,
user_id=user_id,
target_id=target_id,
)
```
**优势**
- ✅ 5分钟缓存
- ✅ 高频场景减少80%+数据库访问
- ✅ 自动缓存失效
**预计工作量**:⏱️ 1-2小时
---
#### 3. ChatStreams 查询 - `src/person_info/relationship_fetcher.py`
**当前实现**:使用 `db_query(ChatStreams, ...)`
**影响代码**
- `build_chat_stream_impression()` 第250行
**迁移目标**
```python
from src.common.database.api.specialized import get_or_create_chat_stream
stream, created = await get_or_create_chat_stream(
stream_id=stream_id,
platform=platform,
defaults={...}
)
```
**优势**
- ✅ 5分钟缓存
- ✅ 减少重复查询
- ✅ 活跃会话期间性能提升75%+
**预计工作量**:⏱️ 30分钟-1小时
---
### 🟡 中优先级(中频查询)
#### 4. ActionRecords 查询 - `src/chat/utils/statistic.py`
**当前实现**:使用 `db_query(ActionRecords, ...)`
**影响代码**
- 第73行更新行为记录
- 第97行插入新记录
- 第105行查询记录
**迁移目标**
```python
from src.common.database.api.specialized import store_action_info, get_recent_actions
# 存储行为
await store_action_info(
user_id=user_id,
action_type=action_type,
...
)
# 获取最近行为
actions = await get_recent_actions(
user_id=user_id,
limit=10
)
```
**优势**
- ✅ 标准化的API
- ✅ 更好的性能监控
- ✅ 未来可添加缓存
**预计工作量**:⏱️ 1-2小时
---
#### 5. CacheEntries 查询 - `src/common/cache_manager.py`
**当前实现**:使用 `db_query(CacheEntries, ...)`
**注意**:这是旧的基于数据库的缓存系统
**建议**
- ⚠️ 考虑完全迁移到新的 `MultiLevelCache` 系统
- ⚠️ 新系统使用内存缓存,性能更好
- ⚠️ 如需持久化,可以添加持久化层
**预计工作量**:⏱️ 4-8小时如果重构整个缓存系统
---
### 🟢 低优先级(低频查询或测试代码)
#### 6. 测试代码 - `tests/test_api_utils_compatibility.py`
**当前实现**:测试中使用直接查询
**建议**
- 测试代码可以保持现状
- 但可以添加新的测试用例测试优化后的API
**预计工作量**:⏱️ 可选
---
## 迁移步骤
### 第一阶段:高频查询(推荐立即进行)
1. **迁移 PersonInfo 查询**
- [ ] 修改 `person_info.py``get_value()`
- [ ] 修改 `person_info.py``get_values()`
- [ ] 修改 `person_info.py``update_one_field()`
- [ ] 修改 `person_info.py``is_person_known()`
- [ ] 测试缓存效果
2. **迁移 UserRelationships 查询**
- [ ] 修改 `relationship_fetcher.py` 的关系查询
- [ ] 测试缓存效果
3. **迁移 ChatStreams 查询**
- [ ] 修改 `relationship_fetcher.py` 的流查询
- [ ] 测试缓存效果
### 第二阶段:中频查询(可以分批进行)
4. **迁移 ActionRecords**
- [ ] 修改 `statistic.py` 的行为记录
- [ ] 添加单元测试
### 第三阶段:系统优化(长期目标)
5. **重构旧缓存系统**
- [ ] 评估 `cache_manager.py` 的使用情况
- [ ] 制定迁移到 MultiLevelCache 的计划
- [ ] 逐步迁移
---
## 性能提升预期
基于当前测试数据:
| 查询类型 | 迁移前 QPS | 迁移后 QPS | 提升 | 数据库负载降低 |
|---------|-----------|-----------|------|--------------|
| PersonInfo | ~50 | ~500+ | **10倍** | **90%+** |
| UserRelationships | ~30 | ~150+ | **5倍** | **80%+** |
| ChatStreams | ~40 | ~160+ | **4倍** | **75%+** |
**总体效果**
- 📈 高峰期数据库连接数减少 **80%+**
- 📈 平均响应时间降低 **70%+**
- 📈 系统吞吐量提升 **5-10倍**
---
## 注意事项
### 1. 缓存一致性
迁移后需要确保:
- ✅ 所有更新操作都正确使缓存失效
- ✅ 缓存键的生成逻辑一致
- ✅ TTL设置合理
### 2. 测试覆盖
每次迁移后需要:
- ✅ 运行单元测试
- ✅ 测试缓存命中率
- ✅ 监控性能指标
- ✅ 检查日志中的缓存统计
### 3. 回滚计划
如果遇到问题:
- 🔄 保留原有代码在注释中
- 🔄 使用 git 标签标记迁移点
- 🔄 准备快速回滚脚本
### 4. 逐步迁移
建议:
- ⭐ 一次迁移一个模块
- ⭐ 在测试环境充分验证
- ⭐ 监控生产环境指标
- ⭐ 根据反馈调整策略
---
## 迁移示例
### 示例1PersonInfo 查询迁移
**迁移前**
```python
# src/person_info/person_info.py
async def get_value(self, person_id: str, field_name: str):
async with get_db_session() as session:
result = await session.execute(
select(PersonInfo).where(PersonInfo.person_id == person_id)
)
person = result.scalar_one_or_none()
if person:
return getattr(person, field_name, None)
return None
```
**迁移后**
```python
# src/person_info/person_info.py
async def get_value(self, person_id: str, field_name: str):
from src.common.database.api.crud import CRUDBase
from src.common.database.core.models import PersonInfo
from src.common.database.utils.decorators import cached
@cached(ttl=600, key_prefix=f"person_field_{field_name}")
async def _get_cached_value(pid: str):
crud = CRUDBase(PersonInfo)
person = await crud.get_by(person_id=pid)
if person:
return getattr(person, field_name, None)
return None
return await _get_cached_value(person_id)
```
或者更简单,使用现有的 `get_or_create_person`
```python
async def get_value(self, person_id: str, field_name: str):
from src.common.database.api.specialized import get_or_create_person
# 解析 person_id 获取 platform 和 user_id
# (需要调整 get_or_create_person 支持 person_id 查询,
# 或者在 PersonInfoManager 中缓存映射关系)
person, _ = await get_or_create_person(
platform=self._platform_cache.get(person_id),
person_id=person_id,
)
if person:
return getattr(person, field_name, None)
return None
```
### 示例2UserRelationships 迁移
**迁移前**
```python
# src/person_info/relationship_fetcher.py
relationships = await db_query(
UserRelationships,
filters={"user_id": user_id},
limit=1,
)
```
**迁移后**
```python
from src.common.database.api.specialized import get_user_relationship
relationship = await get_user_relationship(
platform=platform,
user_id=user_id,
target_id=target_id,
)
# 如果需要查询某个用户的所有关系可以添加新的API函数
```
---
## 进度跟踪
| 任务 | 状态 | 负责人 | 预计完成时间 | 实际完成时间 | 备注 |
|-----|------|--------|------------|------------|------|
| PersonInfo 迁移 | ⏳ 待开始 | - | - | - | 高优先级 |
| UserRelationships 迁移 | ⏳ 待开始 | - | - | - | 高优先级 |
| ChatStreams 迁移 | ⏳ 待开始 | - | - | - | 高优先级 |
| ActionRecords 迁移 | ⏳ 待开始 | - | - | - | 中优先级 |
| 缓存系统重构 | ⏳ 待开始 | - | - | - | 长期目标 |
---
## 相关文档
- [数据库缓存系统使用指南](./database_cache_guide.md)
- [数据库重构完成报告](./database_refactoring_completion.md)
- [优化后的API文档](../src/common/database/api/specialized.py)
---
## 联系与支持
如果在迁移过程中遇到问题:
1. 查看相关文档
2. 检查示例代码
3. 运行测试验证
4. 查看日志中的缓存统计
**最后更新**: 2025-11-01

View File

@@ -1,224 +0,0 @@
# 数据库重构完成总结
## 📊 重构概览
**重构周期**: 2025年11月1日完成
**分支**: `feature/database-refactoring`
**总提交数**: 8次
**总测试通过率**: 26/26 (100%)
---
## 🎯 重构目标达成
### ✅ 核心目标
1. **6层架构实现** - 完成所有6层的设计和实现
2. **完全向后兼容** - 旧代码无需修改即可工作
3. **性能优化** - 实现多级缓存、智能预加载、批量调度
4. **代码质量** - 100%测试覆盖,清晰的架构设计
### ✅ 实施成果
#### 1. 核心层 (Core Layer)
-`DatabaseEngine`: 单例模式SQLite优化 (WAL模式)
-`SessionFactory`: 异步会话工厂,连接池管理
-`models.py`: 25个数据模型统一定义
-`migration.py`: 数据库迁移和检查
#### 2. API层 (API Layer)
-`CRUDBase`: 通用CRUD操作支持缓存
-`QueryBuilder`: 链式查询构建器
-`AggregateQuery`: 聚合查询支持 (sum, avg, count等)
-`specialized.py`: 特殊业务API (人物、LLM统计等)
#### 3. 优化层 (Optimization Layer)
-`CacheManager`: 3级缓存 (L1内存/L2 SQLite/L3预加载)
-`IntelligentPreloader`: 智能数据预加载,访问模式学习
-`AdaptiveBatchScheduler`: 自适应批量调度器
#### 4. 配置层 (Config Layer)
-`DatabaseConfig`: 数据库配置管理
-`CacheConfig`: 缓存策略配置
-`PreloaderConfig`: 预加载器配置
#### 5. 工具层 (Utils Layer)
-`decorators.py`: 重试、超时、缓存、性能监控装饰器
-`monitoring.py`: 数据库性能监控
#### 6. 兼容层 (Compatibility Layer)
-`adapter.py`: 向后兼容适配器
-`MODEL_MAPPING`: 25个模型映射
- ✅ 旧API兼容: `db_query`, `db_save`, `db_get`, `store_action_info`
---
## 📈 测试结果
### Stage 4-6 测试 (兼容性层)
```
✅ 26/26 测试通过 (100%)
测试覆盖:
- CRUDBase: 6/6 ✅
- QueryBuilder: 3/3 ✅
- AggregateQuery: 1/1 ✅
- SpecializedAPI: 3/3 ✅
- Decorators: 4/4 ✅
- Monitoring: 2/2 ✅
- Compatibility: 6/6 ✅
- Integration: 1/1 ✅
```
### Stage 1-3 测试 (基础架构)
```
✅ 18/21 测试通过 (85.7%)
测试覆盖:
- Core Layer: 4/4 ✅
- Cache Manager: 5/5 ✅
- Preloader: 3/3 ✅
- Batch Scheduler: 4/5 (1个超时测试)
- Integration: 1/2 (1个并发测试)
- Performance: 1/2 (1个吞吐量测试)
```
### 总体评估
- **核心功能**: 100% 通过 ✅
- **性能优化**: 85.7% 通过 (非关键超时测试失败)
- **向后兼容**: 100% 通过 ✅
---
## 🔄 导入路径迁移
### 批量更新统计
- **更新文件数**: 37个
- **修改次数**: 67处
- **自动化工具**: `scripts/update_database_imports.py`
### 导入映射表
| 旧路径 | 新路径 | 用途 |
|--------|--------|------|
| `sqlalchemy_models` | `core.models` | 数据模型 |
| `sqlalchemy_models` | `core` | get_db_session, get_engine |
| `sqlalchemy_database_api` | `compatibility` | db_*, MODEL_MAPPING |
| `database.database` | `core` | initialize, stop |
### 更新文件列表
主要更新了以下模块:
- `bot.py`, `main.py` - 主程序入口
- `src/schedule/` - 日程管理 (3个文件)
- `src/plugin_system/` - 插件系统 (4个文件)
- `src/plugins/built_in/` - 内置插件 (8个文件)
- `src/chat/` - 聊天系统 (20+个文件)
- `src/person_info/` - 人物信息 (2个文件)
- `scripts/` - 工具脚本 (2个文件)
---
## 🗃️ 旧文件归档
已将6个旧数据库文件移动到 `src/common/database/old/`:
- `sqlalchemy_models.py` (783行) → 已被 `core/models.py` 替代
- `sqlalchemy_database_api.py` (600+行) → 已被 `compatibility/adapter.py` 替代
- `database.py` (200+行) → 已被 `core/__init__.py` 替代
- `db_migration.py` → 已被 `core/migration.py` 替代
- `db_batch_scheduler.py` → 已被 `optimization/batch_scheduler.py` 替代
- `sqlalchemy_init.py` → 已被 `core/engine.py` 替代
---
## 📝 提交历史
```bash
f6318fdb refactor: 清理旧数据库文件并完成导入更新
a1dc03ca refactor: 完成数据库重构 - 批量更新导入路径
62c644c1 fix: 修复get_or_create返回值和MODEL_MAPPING
51940f1d fix(database): 修复get_or_create返回元组的处理
59d2a4e9 fix(database): 修复record_llm_usage函数的字段映射
b58f69ec fix(database): 修复decorators循环导入问题
61de975d feat(database): 完成API层、Utils层和兼容层重构 (Stage 4-6)
aae84ec4 docs(database): 添加重构测试报告
```
---
## 🎉 重构收益
### 1. 性能提升
- **3级缓存系统**: 减少数据库查询 ~70%
- **智能预加载**: 访问模式学习,命中率 >80%
- **批量调度**: 自适应批处理,吞吐量提升 ~50%
- **WAL模式**: 并发性能提升 ~3x
### 2. 代码质量
- **架构清晰**: 6层分离职责明确
- **高度模块化**: 每层独立,易于维护
- **完全测试**: 26个测试用例100%通过
- **向后兼容**: 旧代码0改动即可工作
### 3. 可维护性
- **统一接口**: CRUDBase提供一致的API
- **装饰器模式**: 重试、缓存、监控统一管理
- **配置驱动**: 所有策略可通过配置调整
- **文档完善**: 每层都有详细文档
### 4. 扩展性
- **插件化设计**: 易于添加新的数据模型
- **策略可配**: 缓存、预加载策略可灵活调整
- **监控完善**: 实时性能数据,便于优化
- **未来支持**: 预留PostgreSQL/MySQL适配接口
---
## 🔮 后续优化建议
### 短期 (1-2周)
1.**完成导入迁移** - 已完成
2.**清理旧文件** - 已完成
3. 📝 **更新文档** - 进行中
4. 🔄 **合并到主分支** - 待进行
### 中期 (1-2月)
1. **监控优化**: 收集生产环境数据,调优缓存策略
2. **压力测试**: 模拟高并发场景,验证性能
3. **错误处理**: 完善异常处理和降级策略
4. **日志完善**: 增加更详细的性能日志
### 长期 (3-6月)
1. **PostgreSQL支持**: 添加PostgreSQL适配器
2. **分布式缓存**: Redis集成支持多实例
3. **读写分离**: 主从复制支持
4. **数据分析**: 实现复杂的分析查询优化
---
## 📚 参考文档
- [数据库重构计划](./database_refactoring_plan.md) - 原始计划文档
- [统一调度器指南](./unified_scheduler_guide.md) - 批量调度器使用
- [测试报告](./database_refactoring_test_report.md) - 详细测试结果
---
## 🙏 致谢
感谢项目组成员在重构过程中的支持和反馈!
本次重构历时约2周涉及
- **新增代码**: ~3000行
- **重构代码**: ~1500行
- **测试代码**: ~800行
- **文档**: ~2000字
---
**重构状态**: ✅ **已完成**
**下一步**: 合并到主分支并部署
---
*生成时间: 2025-11-01*
*文档版本: v1.0*

File diff suppressed because it is too large Load Diff

View File

@@ -1,187 +0,0 @@
# 数据库重构测试报告
**测试时间**: 2025-11-01 13:00
**测试环境**: Python 3.13.2, pytest 8.4.2
**测试范围**: 核心层 + 优化层
## 📊 测试结果总览
**总计**: 21个测试
**通过**: 19个 ✅ (90.5%)
**失败**: 1个 ❌ (超时)
**跳过**: 1个 ⏭️
## ✅ 通过的测试 (19/21)
### 核心层 (Core Layer) - 4/4 ✅
1. **test_engine_singleton**
- 引擎单例模式正常工作
- 多次调用返回同一实例
2. **test_session_factory**
- 会话工厂创建会话正常
- 连接池复用机制工作
3. **test_database_migration**
- 数据库迁移成功
- 25个表结构全部一致
- 自动检测和更新功能正常
4. **test_model_crud**
- 模型CRUD操作正常
- ChatStreams创建、查询、删除成功
### 缓存管理器 (Cache Manager) - 5/5 ✅
5. **test_cache_basic_operations**
- set/get/delete基本操作正常
6. **test_cache_levels**
- L1和L2两级缓存同时工作
- 数据正确写入两级缓存
7. **test_cache_expiration**
- TTL过期机制正常
- 过期数据自动清理
8. **test_cache_lru_eviction**
- LRU淘汰策略正确
- 最近使用的数据保留
9. **test_cache_stats**
- 统计信息准确
- 命中率/未命中率正确记录
### 数据预加载器 (Preloader) - 3/3 ✅
10. **test_access_pattern_tracking**
- 访问模式追踪正常
- 访问次数统计准确
11. **test_preload_data**
- 数据预加载功能正常
- 预加载的数据正确写入缓存
12. **test_related_keys**
- 关联键识别正确
- 关联关系记录准确
### 批量调度器 (Batch Scheduler) - 4/5 ✅
13. **test_scheduler_lifecycle**
- 启动/停止生命周期正常
- 状态管理正确
14. **test_batch_priority**
- 优先级队列工作正常
- LOW/NORMAL/HIGH/URGENT四级优先级
15. **test_adaptive_parameters**
- 自适应参数调整正常
- 根据拥塞评分动态调整批次大小
16. **test_batch_stats**
- 统计信息准确
- 拥塞评分、操作数等指标正常
17. **test_batch_operations** - 跳过(待优化)
- 批量操作功能基本正常
- 需要优化等待时间
### 集成测试 (Integration) - 1/2 ✅
18. **test_cache_and_preloader_integration**
- 缓存与预加载器协同工作
- 预加载数据正确进入缓存
19. **test_full_stack_query** ❌ 超时
- 完整查询流程测试超时
- 需要优化批处理响应时间
### 性能测试 (Performance) - 1/2 ✅
20. **test_cache_performance**
- **写入性能**: 196k ops/s (0.51ms/100项)
- **读取性能**: 1.6k ops/s (59.53ms/100项)
- 性能达标,读取可进一步优化
21. **test_batch_throughput** - 跳过
- 需要优化测试用例
## 📈 性能指标
### 缓存性能
- **写入吞吐**: 195,996 ops/s
- **读取吞吐**: 1,680 ops/s
- **L1命中率**: >80% (预期)
- **L2命中率**: >60% (预期)
### 批处理性能
- **批次大小**: 10-100 (自适应)
- **等待时间**: 50-200ms (自适应)
- **拥塞控制**: 实时调节
### 数据库连接
- **连接池**: 最大10个连接
- **连接复用**: 正常工作
- **WAL模式**: SQLite优化启用
## 🐛 待解决问题
### 1. 批处理超时 (优先级: 中)
- **问题**: `test_full_stack_query` 超时
- **原因**: 批处理调度器等待时间过长
- **影响**: 某些场景下响应慢
- **方案**: 调整等待时间和批次触发条件
### 2. 警告信息 (优先级: 低)
- **SQLAlchemy 2.0**: `declarative_base()` 已废弃
- 建议: 迁移到 `sqlalchemy.orm.declarative_base()`
- **pytest-asyncio**: fixture警告
- 建议: 使用 `@pytest_asyncio.fixture`
## ✨ 测试亮点
### 1. 核心功能稳定
- ✅ 引擎单例、会话管理、模型迁移全部正常
- ✅ 25个数据库表结构完整
### 2. 缓存系统高效
- ✅ L1/L2两级缓存正常工作
- ✅ LRU淘汰和TTL过期机制正确
- ✅ 写入性能达到196k ops/s
### 3. 预加载智能
- ✅ 访问模式追踪准确
- ✅ 关联数据识别正常
- ✅ 与缓存系统集成良好
### 4. 批处理自适应
- ✅ 动态调整批次大小
- ✅ 优先级队列工作正常
- ✅ 拥塞控制有效
## 📋 下一步建议
### 立即行动 (P0)
1. ✅ 核心层和优化层功能完整,可以进入阶段四
2. ⏭️ 优化批处理超时问题可以并行进行
### 短期优化 (P1)
1. 优化批处理调度器的等待策略
2. 提升缓存读取性能目前1.6k ops/s
3. 修复SQLAlchemy 2.0警告
### 长期改进 (P2)
1. 增加更多边界情况测试
2. 添加并发测试和压力测试
3. 完善性能基准测试
## 🎯 结论
**重构成功率**: 90.5% ✅
核心层和优化层的重构基本完成功能测试通过率高性能指标达标。仅有1个超时问题不影响核心功能使用可以进入下一阶段的API层重构工作。
**建议**: 继续推进阶段四API层重构同时并行优化批处理性能。

View File

@@ -1,216 +0,0 @@
# JSON 解析统一化改进文档
## 改进目标
统一项目中所有 LLM 响应的 JSON 解析逻辑,使用 `json_repair` 库和统一的解析工具,简化代码并提高解析成功率。
## 创建的新工具模块
### `src/utils/json_parser.py`
提供统一的 JSON 解析功能:
#### 主要函数:
1. **`extract_and_parse_json(response, strict=False)`**
- 从 LLM 响应中提取并解析 JSON
- 自动处理 Markdown 代码块标记
- 使用 json_repair 修复格式问题
- 支持严格模式和容错模式
2. **`safe_parse_json(json_str, default=None)`**
- 安全解析 JSON失败时返回默认值
3. **`extract_json_field(response, field_name, default=None)`**
- 从 LLM 响应中提取特定字段的值
#### 处理策略:
1. 清理 Markdown 代码块标记(```json 和 ```
2. 提取 JSON 对象或数组(使用栈匹配算法)
3. 尝试直接解析
4. 如果失败,使用 json_repair 修复后解析
5. 容错模式下返回空字典或空列表
## 已修改的文件
### 1. `src/chat/memory_system/memory_query_planner.py` ✅
- 移除了自定义的 `_extract_json_payload` 方法
- 使用 `extract_and_parse_json` 替代原有的解析逻辑
- 简化了代码,提高了可维护性
**修改前:**
```python
payload = self._extract_json_payload(response)
if not payload:
return self._default_plan(query_text)
try:
data = orjson.loads(payload)
except orjson.JSONDecodeError as exc:
...
```
**修改后:**
```python
data = extract_and_parse_json(response, strict=False)
if not data or not isinstance(data, dict):
return self._default_plan(query_text)
```
### 2. `src/chat/memory_system/memory_system.py` ✅
- 移除了自定义的 `_extract_json_payload` 方法
-`_evaluate_information_value` 方法中使用统一解析工具
- 简化了错误处理逻辑
### 3. `src/chat/interest_system/bot_interest_manager.py` ✅
- 移除了自定义的 `_clean_llm_response` 方法
- 使用 `extract_and_parse_json` 解析兴趣标签数据
- 改进了错误处理和日志输出
### 4. `src/plugins/built_in/affinity_flow_chatter/chat_stream_impression_tool.py` ✅
-`_clean_llm_json_response` 标记为已废弃
- 使用 `extract_and_parse_json` 解析聊天流印象数据
- 添加了类型检查和错误处理
## 待修改的文件
### 需要类似修改的其他文件:
1. `src/plugins/built_in/affinity_flow_chatter/proactive_thinking_executor.py`
- 包含自定义的 JSON 清理逻辑
2. `src/plugins/built_in/affinity_flow_chatter/user_profile_tool.py`
- 包含自定义的 JSON 清理逻辑
3. 其他包含自定义 JSON 解析逻辑的文件
## 改进效果
### 1. 代码简化
- 消除了重复的 JSON 提取和清理代码
- 减少了代码行数和维护成本
- 统一了错误处理模式
### 2. 解析成功率提升
- 使用 json_repair 自动修复常见的 JSON 格式问题
- 支持多种 JSON 包装格式(代码块、纯文本等)
- 更好的容错处理
### 3. 可维护性提升
- 集中管理 JSON 解析逻辑
- 易于添加新的解析策略
- 便于调试和日志记录
### 4. 一致性提升
- 所有 LLM 响应使用相同的解析流程
- 统一的日志输出格式
- 一致的错误处理
## 使用示例
### 基本用法:
```python
from src.utils.json_parser import extract_and_parse_json
# LLM 响应可能包含 Markdown 代码块或其他文本
llm_response = '```json\\n{"key": "value"}\\n```'
# 自动提取和解析
data = extract_and_parse_json(llm_response, strict=False)
# 返回: {'key': 'value'}
# 如果解析失败,返回空字典(非严格模式)
# 严格模式下返回 None
```
### 提取特定字段:
```python
from src.utils.json_parser import extract_json_field
llm_response = '{"score": 0.85, "reason": "Good quality"}'
score = extract_json_field(llm_response, "score", default=0.0)
# 返回: 0.85
```
## 测试建议
1. **单元测试**
- 测试各种 JSON 格式(带/不带代码块标记)
- 测试格式错误的 JSON验证 json_repair 的修复能力)
- 测试嵌套 JSON 结构
- 测试空响应和无效响应
2. **集成测试**
- 在实际 LLM 调用场景中测试
- 验证不同模型的响应格式兼容性
- 测试错误处理和日志输出
3. **性能测试**
- 测试大型 JSON 的解析性能
- 验证缓存和优化策略
## 迁移指南
### 旧代码模式:
```python
# 旧的自定义解析逻辑
def _extract_json(response: str) -> str | None:
stripped = response.strip()
code_block_match = re.search(r"```(?:json)?\\s*(.*?)```", stripped, re.DOTALL)
if code_block_match:
return code_block_match.group(1)
# ... 更多自定义逻辑
# 使用
payload = self._extract_json(response)
if payload:
data = orjson.loads(payload)
```
### 新代码模式:
```python
# 使用统一工具
from src.utils.json_parser import extract_and_parse_json
# 直接解析
data = extract_and_parse_json(response, strict=False)
if data and isinstance(data, dict):
# 使用数据
pass
```
## 注意事项
1. **导入语句**:确保添加正确的导入
```python
from src.utils.json_parser import extract_and_parse_json
```
2. **错误处理**:统一工具已包含错误处理,无需额外 try-except
```python
# 不需要
try:
data = extract_and_parse_json(response)
except Exception:
...
# 应该
data = extract_and_parse_json(response, strict=False)
if not data:
# 处理失败情况
pass
```
3. **类型检查**:始终验证返回值类型
```python
data = extract_and_parse_json(response)
if isinstance(data, dict):
# 处理字典
elif isinstance(data, list):
# 处理列表
```
## 后续工作
1. 完成剩余文件的迁移
2. 添加完整的单元测试
3. 更新相关文档
4. 考虑添加性能监控和统计
## 日期
2025年11月2日

View File

@@ -1,267 +0,0 @@
# 对象级内存分析指南
## 🎯 概述
对象级内存分析可以帮助你:
- 查看哪些 Python 对象类型占用最多内存
- 追踪对象数量和大小的变化
- 识别内存泄漏的具体对象
- 监控垃圾回收效率
## 🚀 快速开始
### 1. 安装依赖
```powershell
pip install pympler
```
### 2. 启用对象级分析
```powershell
# 基本用法 - 启用对象分析
python scripts/run_bot_with_tracking.py --objects
# 自定义监控间隔10 秒)
python scripts/run_bot_with_tracking.py --objects --interval 10
# 显示更多对象类型(前 20 个)
python scripts/run_bot_with_tracking.py --objects --object-limit 20
# 完整示例(简写参数)
python scripts/run_bot_with_tracking.py -o -i 10 -l 20
```
## 📊 输出示例
### 进程级信息
```
================================================================================
检查点 #1 - 12:34:56
Bot 进程 (PID: 12345)
RSS: 45.23 MB
VMS: 125.45 MB
占比: 0.35%
子进程: 1 个
子进程内存: 32.10 MB
总内存: 77.33 MB
变化:
RSS: +2.15 MB
```
### 对象级分析信息
```
📦 对象级内存分析 (检查点 #1)
--------------------------------------------------------------------------------
类型 数量 总大小
--------------------------------------------------------------------------------
dict 12,345 15.23 MB
str 45,678 8.92 MB
list 8,901 5.67 MB
tuple 23,456 4.32 MB
type 1,234 3.21 MB
code 2,345 2.10 MB
set 1,567 1.85 MB
function 3,456 1.23 MB
method 4,567 890.45 KB
weakref 2,345 678.12 KB
🗑️ 垃圾回收统计:
- 代 0 回收: 125 次
- 代 1 回收: 12 次
- 代 2 回收: 2 次
- 未回收对象: 0
- 追踪对象数: 89,456
📊 总对象数: 123,456
--------------------------------------------------------------------------------
```
## 🔍 如何解读输出
### 1. 对象类型统计
每一行显示:
- **类型名称**: Python 对象类型dict、str、list 等)
- **数量**: 该类型的对象实例数量
- **总大小**: 该类型所有对象占用的总内存
**关键指标**
- `dict` 多是正常的Python 大量使用字典)
- `str` 多也是正常的(字符串无处不在)
- 如果看到某个自定义类型数量异常增长 → 可能存在泄漏
- 如果某个类型占用内存异常大 → 需要优化
### 2. 垃圾回收统计
**代 0/1/2 回收次数**
- 代 0最频繁新创建的对象
- 代 1中等频率存活一段时间的对象
- 代 2最少长期存活的对象
**未回收对象**
- 应该是 0 或很小的数字
- 如果持续增长 → 可能存在循环引用导致的内存泄漏
**追踪对象数**
- Python 垃圾回收器追踪的对象总数
- 持续增长可能表示内存泄漏
### 3. 总对象数
当前进程中所有 Python 对象的数量。
## 🎯 常见使用场景
### 场景 1: 查找内存泄漏
```powershell
# 长时间运行,频繁检查
python scripts/run_bot_with_tracking.py -o -i 5
```
**观察**
- 哪些对象类型数量持续增长?
- RSS 内存增长和对象数量增长是否一致?
- 垃圾回收是否正常工作?
### 场景 2: 优化内存占用
```powershell
# 较长间隔,查看稳定状态
python scripts/run_bot_with_tracking.py -o -i 30 -l 25
```
**分析**
- 前 25 个对象类型中,哪些是你的代码创建的?
- 是否有不必要的大对象缓存?
- 能否使用更轻量的数据结构?
### 场景 3: 调试特定功能
```powershell
# 短间隔,快速反馈
python scripts/run_bot_with_tracking.py -o -i 3
```
**用途**
- 触发某个功能后立即观察内存变化
- 检查对象是否正确释放
- 验证优化效果
## 📝 保存的历史文件
监控结束后,历史数据会自动保存到:
```
data/memory_diagnostics/bot_memory_monitor_YYYYMMDD_HHMMSS_pidXXXXX.txt
```
文件内容包括:
- 每个检查点的进程内存信息
- 每个检查点的对象统计(前 10 个类型)
- 总体统计信息(起始/结束/峰值/平均)
## 🔧 高级技巧
### 1. 结合代码修改
在你的代码中添加检查点:
```python
import gc
from pympler import muppy, summary
def debug_memory():
"""在关键位置调用此函数"""
gc.collect()
all_objects = muppy.get_objects()
sum_data = summary.summarize(all_objects)
summary.print_(sum_data, limit=10)
```
### 2. 比较不同时间点
```powershell
# 运行 1 分钟
python scripts/run_bot_with_tracking.py -o -i 10
# Ctrl+C 停止,查看文件
# 等待 5 分钟后再运行
python scripts/run_bot_with_tracking.py -o -i 10
# 比较两次的对象统计
```
### 3. 专注特定对象类型
修改 `run_bot_with_tracking.py` 中的 `get_object_stats()` 函数,添加过滤:
```python
def get_object_stats(limit: int = 10) -> Dict:
# ...现有代码...
# 只显示特定类型
filtered_summary = [
row for row in sum_data
if 'YourClassName' in row[0]
]
return {
"summary": filtered_summary[:limit],
# ...
}
```
## ⚠️ 注意事项
### 性能影响
对象级分析会影响性能:
- **pympler 分析**: ~10-20% 性能影响
- **gc.collect()**: 每次检查点触发垃圾回收,可能导致短暂卡顿
**建议**
- 开发/调试时使用对象分析
- 生产环境使用普通监控(不加 `--objects`
### 内存开销
对象分析本身也会占用内存:
- `muppy.get_objects()` 会创建对象列表
- 统计数据会保存在历史中
**建议**
- 不要设置过小的 `--interval`(建议 >= 5 秒)
- 长时间运行时考虑关闭对象分析
### 准确性
- 对象统计是**快照**,不是实时的
- `gc.collect()` 后才统计,确保垃圾已回收
- 子进程的对象无法统计(只统计主进程)
## 📚 相关工具
| 工具 | 用途 | 对象级分析 |
|------|------|----------|
| `run_bot_with_tracking.py` | 一键启动+监控 | ✅ 支持 |
| `memory_monitor.py` | 手动监控 | ✅ 支持 |
| `windows_memory_profiler.py` | 详细分析 | ✅ 支持 |
| `run_bot_with_pympler.py` | 专门的对象追踪 | ✅ 专注此功能 |
## 🎓 学习资源
- [Pympler 文档](https://pympler.readthedocs.io/)
- [Python GC 模块](https://docs.python.org/3/library/gc.html)
- [内存泄漏调试技巧](https://docs.python.org/3/library/tracemalloc.html)
---
**快速开始**:
```powershell
pip install pympler
python scripts/run_bot_with_tracking.py --objects
```
🎉

View File

@@ -1,391 +0,0 @@
# 记忆去重工具使用指南
## 📋 功能说明
`deduplicate_memories.py` 是一个用于清理重复记忆的工具。它会:
1. 扫描所有标记为"相似"关系的记忆对
2. 根据重要性、激活度和创建时间决定保留哪个
3. 删除重复的记忆,保留最有价值的那个
4. 提供详细的去重报告
## 🚀 快速开始
### 步骤1: 预览模式(推荐)
**首次使用前,建议先运行预览模式,查看会删除哪些记忆:**
```bash
python scripts/deduplicate_memories.py --dry-run
```
输出示例:
```
============================================================
记忆去重工具
============================================================
数据目录: data/memory_graph
相似度阈值: 0.85
模式: 预览模式(不实际删除)
============================================================
✅ 记忆管理器初始化成功,共 156 条记忆
找到 23 对相似记忆(阈值>=0.85
[预览] 去重相似记忆对 (相似度=0.904):
保留: mem_20251106_202832_887727
- 主题: 今天天气很好
- 重要性: 0.60
- 激活度: 0.55
- 创建时间: 2024-11-06 20:28:32
删除: mem_20251106_202828_883440
- 主题: 今天天气晴朗
- 重要性: 0.50
- 激活度: 0.50
- 创建时间: 2024-11-06 20:28:28
[预览模式] 不执行实际删除
============================================================
去重报告
============================================================
总记忆数: 156
相似记忆对: 23
发现重复: 23
预览通过: 23
错误数: 0
耗时: 2.35秒
⚠️ 这是预览模式,未实际删除任何记忆
💡 要执行实际删除,请运行: python scripts/deduplicate_memories.py
============================================================
```
### 步骤2: 执行去重
**确认预览结果无误后,执行实际去重:**
```bash
python scripts/deduplicate_memories.py
```
输出示例:
```
============================================================
记忆去重工具
============================================================
数据目录: data/memory_graph
相似度阈值: 0.85
模式: 执行模式(会实际删除)
============================================================
✅ 记忆管理器初始化成功,共 156 条记忆
找到 23 对相似记忆(阈值>=0.85
[执行] 去重相似记忆对 (相似度=0.904):
保留: mem_20251106_202832_887727
...
删除: mem_20251106_202828_883440
...
✅ 删除成功
正在保存数据...
✅ 数据已保存
============================================================
去重报告
============================================================
总记忆数: 156
相似记忆对: 23
成功删除: 23
错误数: 0
耗时: 5.67秒
✅ 去重完成!
📊 最终记忆数: 133 (减少 23 条)
============================================================
```
## 🎛️ 命令行参数
### `--dry-run`(推荐先使用)
预览模式,不实际删除任何记忆。
```bash
python scripts/deduplicate_memories.py --dry-run
```
### `--threshold <相似度>`
指定相似度阈值,只处理相似度大于等于此值的记忆对。
```bash
# 只处理高度相似(>=0.95)的记忆
python scripts/deduplicate_memories.py --threshold 0.95
# 处理中等相似(>=0.8)的记忆
python scripts/deduplicate_memories.py --threshold 0.8
```
**阈值建议**
- `0.95-1.0`: 极高相似度,几乎完全相同(最安全)
- `0.9-0.95`: 高度相似,内容基本一致(推荐)
- `0.85-0.9`: 中等相似,可能有细微差别(谨慎使用)
- `<0.85`: 低相似度,可能误删(不推荐)
### `--data-dir <目录>`
指定记忆数据目录。
```bash
# 对测试数据去重
python scripts/deduplicate_memories.py --data-dir data/test_memory
# 对备份数据去重
python scripts/deduplicate_memories.py --data-dir data/memory_backup
```
## 📖 使用场景
### 场景1: 定期维护
**建议频率**: 每周或每月运行一次
```bash
# 1. 先预览
python scripts/deduplicate_memories.py --dry-run --threshold 0.92
# 2. 确认后执行
python scripts/deduplicate_memories.py --threshold 0.92
```
### 场景2: 清理大量重复
**适用于**: 导入外部数据后,或发现大量重复记忆
```bash
# 使用较低阈值,清理更多重复
python scripts/deduplicate_memories.py --threshold 0.85
```
### 场景3: 保守清理
**适用于**: 担心误删,只想删除极度相似的记忆
```bash
# 使用高阈值,只删除几乎完全相同的记忆
python scripts/deduplicate_memories.py --threshold 0.98
```
### 场景4: 测试环境
**适用于**: 在测试数据上验证效果
```bash
# 对测试数据执行去重
python scripts/deduplicate_memories.py --data-dir data/test_memory --dry-run
```
## 🔍 去重策略
### 保留原则(按优先级)
脚本会按以下优先级决定保留哪个记忆:
1. **重要性更高** (`importance` 值更大)
2. **激活度更高** (`activation` 值更大)
3. **创建时间更早** (更早创建的记忆)
### 增强保留记忆
保留的记忆会获得以下增强:
- **重要性** +0.05最高1.0
- **激活度** +0.05最高1.0
- **访问次数** 累加被删除记忆的访问次数
### 示例
```
记忆A: 重要性0.8, 激活度0.6, 创建于 2024-11-01
记忆B: 重要性0.7, 激活度0.9, 创建于 2024-11-05
结果: 保留记忆A重要性更高
增强: 重要性 0.8 → 0.85, 激活度 0.6 → 0.65
```
## ⚠️ 注意事项
### 1. 备份数据
**在执行实际去重前,建议备份数据:**
```bash
# Windows
xcopy data\memory_graph data\memory_graph_backup /E /I /Y
# Linux/Mac
cp -r data/memory_graph data/memory_graph_backup
```
### 2. 先预览再执行
**务必先运行 `--dry-run` 预览:**
```bash
# 错误示范 ❌
python scripts/deduplicate_memories.py # 直接执行
# 正确示范 ✅
python scripts/deduplicate_memories.py --dry-run # 先预览
python scripts/deduplicate_memories.py # 再执行
```
### 3. 阈值选择
**过低的阈值可能导致误删:**
```bash
# 风险较高 ⚠️
python scripts/deduplicate_memories.py --threshold 0.7
# 推荐范围 ✅
python scripts/deduplicate_memories.py --threshold 0.92
```
### 4. 不可恢复
**删除的记忆无法恢复!** 如果不确定,请:
1. 先备份数据
2. 使用 `--dry-run` 预览
3. 使用较高的阈值(如 0.95
### 5. 中断恢复
如果执行过程中中断Ctrl+C已删除的记忆无法恢复。建议
- 在低负载时段运行
- 确保足够的执行时间
- 使用 `--threshold` 限制处理数量
## 🐛 故障排查
### 问题1: 找不到相似记忆对
```
找到 0 对相似记忆(阈值>=0.85
```
**原因**
- 没有标记为"相似"的边
- 阈值设置过高
**解决**
1. 降低阈值:`--threshold 0.7`
2. 检查记忆系统是否正确创建了相似关系
3. 先运行自动关联任务
### 问题2: 初始化失败
```
❌ 记忆管理器初始化失败
```
**原因**
- 数据目录不存在
- 配置文件错误
- 数据文件损坏
**解决**
1. 检查数据目录是否存在
2. 验证配置文件:`config/bot_config.toml`
3. 查看详细日志定位问题
### 问题3: 删除失败
```
❌ 删除失败: ...
```
**原因**
- 权限不足
- 数据库锁定
- 文件损坏
**解决**
1. 检查文件权限
2. 确保没有其他进程占用数据
3. 恢复备份后重试
## 📊 性能参考
| 记忆数量 | 相似对数 | 执行时间(预览) | 执行时间(实际) |
|---------|---------|----------------|----------------|
| 100 | 10 | ~1秒 | ~2秒 |
| 500 | 50 | ~3秒 | ~6秒 |
| 1000 | 100 | ~5秒 | ~12秒 |
| 5000 | 500 | ~15秒 | ~45秒 |
**注**: 实际时间取决于服务器性能和数据复杂度
## 🔗 相关工具
- **记忆整理**: `src/memory_graph/manager.py::consolidate_memories()`
- **自动关联**: `src/memory_graph/manager.py::auto_link_memories()`
- **配置验证**: `scripts/verify_config_update.py`
## 💡 最佳实践
### 1. 定期维护流程
```bash
# 每周执行
cd /path/to/bot
# 1. 备份
cp -r data/memory_graph data/memory_graph_backup_$(date +%Y%m%d)
# 2. 预览
python scripts/deduplicate_memories.py --dry-run --threshold 0.92
# 3. 执行
python scripts/deduplicate_memories.py --threshold 0.92
# 4. 验证
python scripts/verify_config_update.py
```
### 2. 保守去重策略
```bash
# 只删除极度相似的记忆
python scripts/deduplicate_memories.py --dry-run --threshold 0.98
python scripts/deduplicate_memories.py --threshold 0.98
```
### 3. 批量清理策略
```bash
# 先清理高相似度的
python scripts/deduplicate_memories.py --threshold 0.95
# 再清理中相似度的(可选)
python scripts/deduplicate_memories.py --dry-run --threshold 0.9
python scripts/deduplicate_memories.py --threshold 0.9
```
## 📝 总结
-**务必先备份数据**
-**务必先运行 `--dry-run`**
-**建议使用阈值 >= 0.92**
-**定期运行,保持记忆库清洁**
-**避免过低阈值(< 0.85**
-**避免跳过预览直接执行**
---
**创建日期**: 2024-11-06
**版本**: v1.0
**维护者**: MoFox-Bot Team

View File

@@ -1,451 +0,0 @@
# 优化架构可视化
## 📐 优化前后架构对比
### ❌ 优化前:线性+串行架构
```
搜索记忆请求
|
v
┌─────────────┐
│ 生成查询向量 │
└──────┬──────┘
|
v
┌─────────────────────────────┐
│ for each memory in list: │
│ - 线性扫描 O(n) │
│ - 计算相似度 await │
│ - 串行等待 1500ms │
│ - 每次都重复计算! │
└──────┬──────────────────────┘
|
v
┌──────────────┐
│ 排序结果 │
│ Top-K 返回 │
└──────────────┘
查询记忆流程:
ID 查找 → for 循环遍历 O(n) → 30 次比较
性能问题:
- ❌ 串行计算: 等待太久
- ❌ 重复计算: 缓存为空
- ❌ 线性查找: 列表遍历太多
```
---
### ✅ 优化后:哈希+并发+缓存架构
```
搜索记忆请求
|
v
┌─────────────┐
│ 生成查询向量 │
└──────┬──────┘
|
v
┌──────────────────────┐
│ 检查缓存存在? │
│ cache[query_id]? │
└────────┬────────┬───┘
命中 YES | | NO (首次查询)
| v v
┌────┴──────┐ ┌────────────────────────┐
│ 直接返回 │ │ 创建并发任务列表 │
│ 缓存结果 │ │ │
│ < 1ms ⚡ │ │ tasks = [ │
└──────┬────┘ │ sim_async(...), │
| │ sim_async(...), │
| │ ... (30 个任务) │
| │ ] │
| └────────┬───────────────┘
| |
| v
| ┌────────────────────────┐
| │ 并发执行所有任务 │
| │ await asyncio.gather() │
| │ │
| │ 任务1 ─┐ │
| │ 任务2 ─┼─ 并发执行 │
| │ 任务3 ─┤ 只需 50ms │
| │ ... │ │
| │ 任务30 ┘ │
| └────────┬───────────────┘
| |
| v
| ┌────────────────────────┐
| │ 存储到缓存 │
| │ cache[query_id] = ... │
| │ (下次查询直接用) │
| └────────┬───────────────┘
| |
└──────────┬──────┘
|
v
┌──────────────┐
│ 排序结果 │
│ Top-K 返回 │
└──────────────┘
ID 查找流程:
_memory_id_index.get(id) → O(1) 直接返回
性能优化:
- ✅ 并发计算: asyncio.gather() 并行
- ✅ 智能缓存: 缓存命中 < 1ms
- ✅ 哈希查找: O(1) 恒定时间
```
---
## 🏗️ 数据结构演进
### ❌ 优化前:单一列表
```
ShortTermMemoryManager
├── memories: List[ShortTermMemory]
│ ├── Memory#1 {id: "stm_123", content: "...", ...}
│ ├── Memory#2 {id: "stm_456", content: "...", ...}
│ ├── Memory#3 {id: "stm_789", content: "...", ...}
│ └── ... (30 个记忆)
└── 查找: 线性扫描
for mem in memories:
if mem.id == "stm_456":
return mem ← O(n) 最坏 30 次比较
缺点:
- 查找慢: O(n)
- 删除慢: O(n²)
- 无缓存: 重复计算
```
---
### ✅ 优化后:多层索引+缓存
```
ShortTermMemoryManager
├── memories: List[ShortTermMemory] 主存储
│ ├── Memory#1
│ ├── Memory#2
│ ├── Memory#3
│ └── ...
├── _memory_id_index: Dict[str, Memory] 哈希索引
│ ├── "stm_123" → Memory#1 ⭐ O(1)
│ ├── "stm_456" → Memory#2 ⭐ O(1)
│ ├── "stm_789" → Memory#3 ⭐ O(1)
│ └── ...
└── _similarity_cache: Dict[str, Dict] 相似度缓存
├── "query_1" → {
│ ├── "mem_id_1": 0.85
│ ├── "mem_id_2": 0.72
│ └── ...
│ } ⭐ O(1) 命中 < 1ms
├── "query_2" → {...}
└── ...
优化:
- 查找快: O(1) 恒定
- 删除快: O(n) 一次遍历
- 有缓存: 复用计算结果
- 同步安全: 三个结构保持一致
```
---
## 🔄 操作流程演进
### 内存添加流程
```
优化前:
添加记忆 → 追加到列表 → 完成
├─ self.memories.append(mem)
└─ (不更新索引!)
问题: 后续查找需要 O(n) 扫描
优化后:
添加记忆 → 追加到列表 → 同步索引 → 完成
├─ self.memories.append(mem)
├─ self._memory_id_index[mem.id] = mem ⭐
└─ 后续查找 O(1) 完成!
```
---
### 记忆删除流程
```
优化前 (O(n²)):
─────────────────────
to_remove = [mem1, mem2, mem3]
for mem in to_remove:
self.memories.remove(mem) ← O(n) 每次都要搜索
# 第一次: 30 次比较
# 第二次: 29 次比较
# 第三次: 28 次比较
# 总计: 87 次 😭
优化后 (O(n)):
─────────────────────
remove_ids = {"id1", "id2", "id3"}
# 一次遍历
self.memories = [m for m in self.memories
if m.id not in remove_ids]
# 同步清理索引
for mem_id in remove_ids:
del self._memory_id_index[mem_id]
self._similarity_cache.pop(mem_id, None)
总计: 3 次遍历 O(n) ✅ 快 87/30 = 3 倍!
```
---
### 相似度计算流程
```
优化前 (串行):
─────────────────────────────────────────
embedding = generate_embedding(query)
results = []
for mem in memories: ← 30 次迭代
sim = await cosine_similarity_async(embedding, mem.embedding)
# 第 1 次: 等待 50ms ⏳
# 第 2 次: 等待 50ms ⏳
# ...
# 第 30 次: 等待 50ms ⏳
# 总计: 1500ms 😭
时间线:
0ms 50ms 100ms ... 1500ms
|──T1─|──T2─|──T3─| ... |──T30─|
串行执行,一个一个等待
优化后 (并发):
─────────────────────────────────────────
embedding = generate_embedding(query)
# 创建任务列表
tasks = [
cosine_similarity_async(embedding, m.embedding) for m in memories
]
# 并发执行
results = await asyncio.gather(*tasks)
# 第 1 次: 启动任务 (不等待)
# 第 2 次: 启动任务 (不等待)
# ...
# 第 30 次: 启动任务 (不等待)
# 等待所有: 等待 50ms ✅
时间线:
0ms 50ms
|─T1─T2─T3─...─T30─────────|
并发启动,同时等待
缓存优化:
─────────────────────────────────────────
首次查询: 50ms (并发计算)
第二次查询 (相同): < 1ms (缓存命中) ✅
多次相同查询:
1500ms (串行) → 50ms + <1ms + <1ms + ... = ~50ms
性能提升: 30 倍! 🚀
```
---
## 💾 内存状态演变
### 单个记忆的生命周期
```
创建阶段:
─────────────────
memory = ShortTermMemory(id="stm_123", ...)
执行决策:
─────────────────
if decision == CREATE_NEW:
✅ self.memories.append(memory)
✅ self._memory_id_index["stm_123"] = memory ⭐
if decision == MERGE:
target = self._find_memory_by_id(id) ← O(1) 快速找到
target.content = ... ✅ 修改内容
✅ self._similarity_cache.pop(target.id, None) ⭐ 清除缓存
使用阶段:
─────────────────
search_memories("query")
→ 缓存命中?
→ 是: 使用缓存结果 < 1ms
→ 否: 计算相似度, 存储到缓存
转移/删除阶段:
─────────────────
if importance >= threshold:
return memory ← 转移到长期记忆
else:
✅ 从列表移除
✅ del index["stm_123"] ⭐
✅ cache.pop("stm_123", None) ⭐
```
---
## 🧵 并发执行时间线
### 搜索 30 个记忆的时间对比
#### ❌ 优化前:串行等待
```
时间 →
0ms │ 查询编码
50ms │ 等待mem1计算
100ms│ 等待mem2计算
150ms│ 等待mem3计算
...
1500ms│ 等待mem30计算 ← 完成! (总耗时 1500ms)
任务执行:
[mem1] ─────────────→
[mem2] ─────────────→
[mem3] ─────────────→
...
[mem30] ─────────────→
资源利用: ❌ CPU 大部分时间空闲,等待 I/O
```
---
#### ✅ 优化后:并发执行
```
时间 →
0ms │ 查询编码
5ms │ 启动所有任务 (mem1~mem30)
50ms │ 所有任务完成! ← 完成 (总耗时 50ms, 提升 30 倍!)
任务执行:
[mem1] ───────────→
[mem2] ───────────→
[mem3] ───────────→
...
[mem30] ───────────→
并行执行, 同时完成
资源利用: ✅ CPU 和网络充分利用, 高效并发
```
---
## 📈 性能增长曲线
### 随着记忆数量增加的性能对比
```
耗时
(ms)
|
| ❌ 优化前 (线性增长)
| /
|/
2000├───
1500├──
1000├
500│ ✅ 优化后 (常数时间)
│ ──────────────
100│
0└─────────────────────────────────
0 10 20 30 40 50
记忆数量
优化前: 串行计算
y = n × 50ms (n = 记忆数)
30 条: 1500ms
60 条: 3000ms
100 条: 5000ms
优化后: 并发计算
y = 50ms (恒定)
无论 30 条还是 100 条都是 50ms!
缓存命中时:
y = 1ms (超低)
```
---
## 🎯 关键优化点速览表
```
┌──────────────────────────────────────────────────────┐
│ │
│ 优化 1: 哈希索引 ├─ O(n) → O(1) │
│ ─────────────────────────────────┤ 查找加速 30 倍 │
│ _memory_id_index[id] = memory │ 应用: 全局 │
│ │ │
│ 优化 2: 相似度缓存 ├─ 无 → LRU │
│ ─────────────────────────────────┤ 热查询 5-10x │
│ _similarity_cache[query] = {...} │ 应用: 频繁查询│
│ │ │
│ 优化 3: 并发计算 ├─ 串行 → 并发 │
│ ─────────────────────────────────┤ 搜索加速 30 倍 │
│ await asyncio.gather(*tasks) │ 应用: I/O密集 │
│ │ │
│ 优化 4: 单次遍历 ├─ 多次 → 单次 │
│ ─────────────────────────────────┤ 管理加速 2-3x │
│ for mem in memories: 分类 │ 应用: 容量管理│
│ │ │
│ 优化 5: 批量删除 ├─ O(n²) → O(n)│
│ ─────────────────────────────────┤ 清理加速 n 倍 │
│ [m for m if id not in remove_ids] │ 应用: 批量操作│
│ │ │
│ 优化 6: 索引同步 ├─ 无 → 完整 │
│ ─────────────────────────────────┤ 数据一致性保证│
│ 所有修改都同步三个数据结构 │ 应用: 数据完整│
│ │ │
└──────────────────────────────────────────────────────┘
总体效果:
⚡ 平均性能提升: 10-15 倍
🚀 最大提升场景: 37.5 倍 (多次搜索)
💾 额外内存: < 1%
✅ 向后兼容: 100%
```
---
---
**最后更新**: 2025-12-13
**可视化版本**: v1.0
**类型**: 架构图表

View File

@@ -1,345 +0,0 @@
# 🎯 MoFox-Core 统一记忆管理器优化完成报告
## 📋 执行概览
**优化目标**: 提升 `src/memory_graph/unified_manager.py` 运行速度
**执行状态**: ✅ **已完成**
**关键数据**:
- 优化项数: **8 项**
- 代码改进: **735 行文件**
- 性能提升: **25-40%** (典型场景) / **5-50x** (批量操作)
- 兼容性: **100% 向后兼容**
---
## 🚀 优化成果详表
### 优化项列表
| 序号 | 优化项 | 方法名 | 优化内容 | 预期提升 | 状态 |
|------|--------|--------|----------|----------|------|
| 1 | **任务创建消除** | `search_memories()` | 消除不必要的 Task 对象创建 | 2-3% | ✅ |
| 2 | **查询去重单遍** | `_build_manual_multi_queries()` | 从两次扫描优化为一次 | 5-15% | ✅ |
| 3 | **多态支持** | `_deduplicate_memories()` | 支持 dict 和 object 去重 | 1-3% | ✅ |
| 4 | **查表法优化** | `_calculate_auto_sleep_interval()` | 链式判断 → 查表法 | 1-2% | ✅ |
| 5 | **块转移并行化** ⭐⭐⭐ | `_transfer_blocks_to_short_term()` | 串行 → 并行处理块 | **5-50x** | ✅ |
| 6 | **缓存批量构建** | `_auto_transfer_loop()` | 逐条 append → 批量 extend | 2-4% | ✅ |
| 7 | **直接转移列表** | `_auto_transfer_loop()` | 避免不必要的 list() 复制 | 1-2% | ✅ |
| 8 | **上下文延迟创建** | `_retrieve_long_term_memories()` | 条件化创建 dict | <1% | |
---
## 📊 性能基准测试结果
### 关键性能指标
#### 块转移并行化 (最重要)
```
块数 串行耗时 并行耗时 加速比
───────────────────────────────────
1 14.11ms 15.49ms 0.91x
5 77.28ms 15.49ms 4.99x ⚡
10 155.50ms 15.66ms 9.93x ⚡⚡
20 311.02ms 15.53ms 20.03x ⚡⚡⚡
```
**关键发现**: 块数5时并行处理的优势明显10+ 块时加速比超过 10x
#### 查询去重优化
```
场景 旧算法 新算法 改善
──────────────────────────────────────
小查询 (2项) 2.90μs 0.79μs 72.7% ↓
中查询 (50项) 3.46μs 3.19μs 8.1% ↓
```
**发现**: 小规模查询优化最显著大规模时优势减弱Python 对象开销
---
## 💡 关键优化详解
### 1⃣ 块转移并行化(核心优化)
**问题**: 块转移采用串行循环N 个块需要 N×T 时间
```python
# ❌ 原代码 (串行,性能瓶颈)
for block in blocks:
stm = await self.short_term_manager.add_from_block(block)
await self.perceptual_manager.remove_block(block.id)
self._trigger_transfer_wakeup() # 每个块都触发
# → 总耗时: 50个块 = 750ms
```
**优化**: 使用 `asyncio.gather()` 并行处理所有块
```python
# ✅ 优化后 (并行,高效)
async def _transfer_single(block: MemoryBlock) -> tuple[MemoryBlock, bool]:
stm = await self.short_term_manager.add_from_block(block)
await self.perceptual_manager.remove_block(block.id)
return block, True
results = await asyncio.gather(*[_transfer_single(block) for block in blocks])
# → 总耗时: 50个块 ≈ 15ms (I/O 并行)
```
**收益**:
- **5 **: 5x 加速
- **10 **: 10x 加速
- **20+ **: 20x+ 加速
---
### 2⃣ 查询去重单遍扫描
**问题**: 先构建去重列表再遍历添加权重共两次扫描
```python
# ❌ 原代码 (O(2n))
deduplicated = []
for raw in queries: # 第一次扫描
text = (raw or "").strip()
if not text or text in seen:
continue
deduplicated.append(text)
for idx, text in enumerate(deduplicated): # 第二次扫描
weight = max(0.3, 1.0 - idx * decay)
manual_queries.append({"text": text, "weight": round(weight, 2)})
```
**优化**: 合并为单遍扫描
```python
# ✅ 优化后 (O(n))
manual_queries = []
for raw in queries: # 单次扫描
text = (raw or "").strip()
if text and text not in seen:
seen.add(text)
weight = max(0.3, 1.0 - len(manual_queries) * decay)
manual_queries.append({"text": text, "weight": round(weight, 2)})
```
**收益**: 50% 扫描时间节省特别是大查询列表
---
### 3⃣ 多态支持 (dict 和 object)
**问题**: 仅支持对象类型字典对象去重失败
```python
# ❌ 原代码 (仅对象)
mem_id = getattr(mem, "id", None) # 字典会返回 None
```
**优化**: 支持两种访问方式
```python
# ✅ 优化后 (对象 + 字典)
if isinstance(mem, dict):
mem_id = mem.get("id")
else:
mem_id = getattr(mem, "id", None)
```
**收益**: 数据源兼容性提升支持混合格式数据
---
## 📈 性能提升预测
### 典型场景的综合提升
```
场景 A: 日常消息处理 (每秒 1-5 条)
├─ search_memories() 并行: +3%
├─ 查询去重: +8%
└─ 总体: +10-15% ⬆️
场景 B: 高负载批量转移 (30+ 块)
├─ 块转移并行化: +10-50x ⬆️⬆️⬆️
└─ 总体: +10-50x ⬆️⬆️⬆️ (显著!)
场景 C: 混合工作 (消息 + 转移)
├─ 消息处理: +5%
├─ 内存管理: +30%
└─ 总体: +25-40% ⬆️⬆️
```
---
## 📁 生成的文档和工具
### 1. 详细优化报告
📄 **[OPTIMIZATION_REPORT_UNIFIED_MANAGER.md](docs/OPTIMIZATION_REPORT_UNIFIED_MANAGER.md)**
- 8 项优化的完整技术说明
- 性能数据和基准数据
- 风险评估和测试建议
### 2. 可视化指南
📊 **[OPTIMIZATION_VISUAL_GUIDE.md](OPTIMIZATION_VISUAL_GUIDE.md)**
- 性能对比可视化
- 算法演进图解
- 时间轴和场景分析
### 3. 性能基准工具
🧪 **[scripts/benchmark_unified_manager.py](scripts/benchmark_unified_manager.py)**
- 可重复运行的基准测试
- 3 个核心优化的性能验证
- 多个测试场景
### 4. 本优化总结
📋 **[OPTIMIZATION_SUMMARY.md](OPTIMIZATION_SUMMARY.md)**
- 快速参考指南
- 成果总结和验证清单
---
## ✅ 质量保证
### 代码质量
- **语法检查通过** - Python 编译检查
- **类型兼容** - 支持 dict object
- **异常处理** - 完善的错误处理
### 兼容性
- **100% 向后兼容** - API 签名不变
- **无破坏性变更** - 仅内部实现优化
- **透明优化** - 调用方无感知
### 性能验证
- **基准测试完成** - 关键优化已验证
- **性能数据真实** - 基于实际测试
- **可重复测试** - 提供基准工具
---
## 🎯 使用说明
### 立即生效
优化已自动应用无需额外配置
```python
from src.memory_graph.unified_manager import UnifiedMemoryManager
manager = UnifiedMemoryManager()
await manager.initialize()
# 所有操作已自动获得优化效果
await manager.search_memories("query")
```
### 性能监控
```python
# 获取统计信息
stats = manager.get_statistics()
print(f"系统总记忆数: {stats['total_system_memories']}")
```
### 运行基准测试
```bash
python scripts/benchmark_unified_manager.py
```
---
## 🔮 后续优化空间
### 第一梯队 (可立即实施)
- [ ] **Embedding 缓存** - 为高频查询缓存 embedding预期 20-30% 提升
- [ ] **批量查询并行化** - 多查询并行检索预期 5-10% 提升
- [ ] **内存池管理** - 减少对象创建/销毁预期 5-8% 提升
### 第二梯队 (需要架构调整)
- [ ] **数据库连接池** - 优化 I/O预期 10-15% 提升
- [ ] **查询结果缓存** - 热点缓存预期 15-20% 提升
### 第三梯队 (算法创新)
- [ ] **BloomFilter 去重** - O(1) 去重检查
- [ ] **缓存预热策略** - 减少冷启动延迟
---
## 📊 优化效果总结表
| 维度 | 原状态 | 优化后 | 改善 |
|------|--------|--------|------|
| **块转移** (20块) | 311ms | 16ms | **19x** |
| **块转移** (5块) | 77ms | 15ms | **5x** |
| **查询去重** () | 2.90μs | 0.79μs | **73%** |
| **综合场景** | 100ms | 70ms | **30%** |
| **代码行数** | 721 | 735 | +14行 |
| **API 兼容性** | - | 100% | |
---
## 🏆 优化成就
### 技术成就
实现 8 项有针对性的优化
核心算法提升 5-50x
综合性能提升 25-40%
完全向后兼容
### 交付物
优化代码 (735 )
详细文档 (4 )
基准工具 (1 )
验证报告 (完整)
### 质量指标
语法检查: PASS
兼容性: 100%
文档完整度: 100%
可重复性: 支持
---
## 📞 支持与反馈
### 文档参考
- 快速参考: [OPTIMIZATION_SUMMARY.md](OPTIMIZATION_SUMMARY.md)
- 技术细节: [OPTIMIZATION_REPORT_UNIFIED_MANAGER.md](docs/OPTIMIZATION_REPORT_UNIFIED_MANAGER.md)
- 可视化: [OPTIMIZATION_VISUAL_GUIDE.md](OPTIMIZATION_VISUAL_GUIDE.md)
### 性能测试
运行基准测试验证优化效果:
```bash
python scripts/benchmark_unified_manager.py
```
### 监控与优化
使用 `manager.get_statistics()` 监控系统状态持续迭代改进
---
## 🎉 总结
通过 8 项目标性能优化MoFox-Core 的统一记忆管理器获得了显著的性能提升特别是在高负载批量操作中展现出 5-50x 的加速优势所有优化都保持了 100% 的向后兼容性无需修改调用代码即可立即生效
**优化完成时间**: 2025 12 13
**优化文件**: `src/memory_graph/unified_manager.py`
**代码变更**: +14 涉及 8 个关键方法
**预期收益**: 25-40% 综合提升 / 5-50x 批量操作提升
🚀 **立即开始享受性能提升!**
---
## 附录: 快速对比
```
性能改善等级 (以块转移为例)
原始性能: ████████████████████ (75ms)
优化后: ████ (15ms)
加速比: 5x ⚡ (基础)
10x ⚡⚡ (10块)
50x ⚡⚡⚡ (50块+)
```

View File

@@ -1,216 +0,0 @@
# 🚀 优化快速参考卡
## 📌 一句话总结
通过 8 项算法优化,统一记忆管理器性能提升 **25-40%**(典型场景)或 **5-50x**(批量操作)。
---
## ⚡ 核心优化排名
| 排名 | 优化 | 性能提升 | 重要度 |
|------|------|----------|--------|
| 🥇 1 | 块转移并行化 | **5-50x** | ⭐⭐⭐⭐⭐ |
| 🥈 2 | 查询去重单遍 | **5-15%** | ⭐⭐⭐⭐ |
| 🥉 3 | 缓存批量构建 | **2-4%** | ⭐⭐⭐ |
| 4 | 任务创建消除 | **2-3%** | ⭐⭐⭐ |
| 5-8 | 其他微优化 | **<3%** | ⭐⭐ |
---
## 🎯 场景性能收益
```
日常消息处理 +5-10% ⬆️
高负载批量转移 +10-50x ⬆️⬆️⬆️ (★最显著)
裁判模型评估 +5-15% ⬆️
综合场景 +25-40% ⬆️⬆️
```
---
## 📊 基准数据一览
### 块转移 (最重要)
- 5 块: 77ms 15ms = **5x**
- 10 块: 155ms 16ms = **10x**
- 20 块: 311ms 16ms = **20x**
### 查询去重
- (2项): 2.90μs 0.79μs = **73%**
- (50项): 3.46μs 3.19μs = **8%**
### 去重性能 (混合数据)
- 对象 100 个: 高效支持
- 字典 100 个: 高效支持
- 混合数据: 新增支持
---
## 🔧 关键改进代码片段
### 改进 1: 并行块转移
```python
# ✅ 新
results = await asyncio.gather(
*[_transfer_single(block) for block in blocks]
)
# 加速: 5-50x
```
### 改进 2: 单遍去重
```python
# ✅ 新 (O(n) vs O(2n))
for raw in queries:
if text and text not in seen:
seen.add(text)
manual_queries.append({...})
# 加速: 50% 扫描时间
```
### 改进 3: 多态支持
```python
# ✅ 新 (dict + object)
mem_id = mem.get("id") if isinstance(mem, dict) else getattr(mem, "id", None)
# 兼容性: +100%
```
---
## ✅ 验证清单
- [x] 8 项优化已实施
- [x] 语法检查通过
- [x] 性能基准验证
- [x] 向后兼容确认
- [x] 文档完整生成
- [x] 工具脚本提供
---
## 📚 关键文档
| 文档 | 用途 | 查看时间 |
|------|------|----------|
| [OPTIMIZATION_SUMMARY.md](OPTIMIZATION_SUMMARY.md) | 优化总结 | 5 分钟 |
| [OPTIMIZATION_REPORT_UNIFIED_MANAGER.md](docs/OPTIMIZATION_REPORT_UNIFIED_MANAGER.md) | 技术细节 | 15 分钟 |
| [OPTIMIZATION_VISUAL_GUIDE.md](OPTIMIZATION_VISUAL_GUIDE.md) | 可视化 | 10 分钟 |
| [OPTIMIZATION_COMPLETION_REPORT.md](OPTIMIZATION_COMPLETION_REPORT.md) | 完成报告 | 10 分钟 |
---
## 🧪 运行基准测试
```bash
python scripts/benchmark_unified_manager.py
```
**输出示例**:
```
块转移并行化性能基准测试
╔══════════════════════════════════════╗
║ 块数 串行(ms) 并行(ms) 加速比 ║
║ 5 77.28 15.49 4.99x ║
║ 10 155.50 15.66 9.93x ║
║ 20 311.02 15.53 20.03x ║
╚══════════════════════════════════════╝
```
---
## 💡 如何使用优化后的代码
### 自动生效
```python
from src.memory_graph.unified_manager import UnifiedMemoryManager
manager = UnifiedMemoryManager()
await manager.initialize()
# 无需任何改动,自动获得所有优化效果
await manager.search_memories("query")
await manager._auto_transfer_loop() # 优化的自动转移
```
### 监控效果
```python
stats = manager.get_statistics()
print(f"总记忆数: {stats['total_system_memories']}")
```
---
## 🎯 优化前后对比
```python
# ❌ 优化前 (低效)
for block in blocks: # 串行
await process(block) # 逐个处理
# ✅ 优化后 (高效)
await asyncio.gather(*[process(block) for block in blocks]) # 并行
```
**结果**:
- 5 块: 5 倍快
- 10 块: 10 倍快
- 20 块: 20 倍快
---
## 🚀 性能等级
```
⭐⭐⭐⭐⭐ 优秀 (块转移: 5-50x)
⭐⭐⭐⭐☆ 很好 (查询去重: 5-15%)
⭐⭐⭐☆☆ 良好 (其他: 1-5%)
════════════════════════════
总体评分: ⭐⭐⭐⭐⭐ 优秀
```
---
## 📞 常见问题
### Q: 是否需要修改调用代码?
**A**: 不需要所有优化都是透明的100% 向后兼容
### Q: 性能提升是否可信?
**A**: 是的基于真实性能测试可通过 `benchmark_unified_manager.py` 验证
### Q: 优化是否会影响功能?
**A**: 不会所有优化仅涉及实现细节功能完全相同
### Q: 能否回退到原版本?
**A**: 可以但建议保留优化版本新版本全面优于原版
---
## 🎉 立即体验
1. **查看优化**: `src/memory_graph/unified_manager.py` (已优化)
2. **验证性能**: `python scripts/benchmark_unified_manager.py`
3. **阅读文档**: `OPTIMIZATION_SUMMARY.md` (快速参考)
4. **了解细节**: `docs/OPTIMIZATION_REPORT_UNIFIED_MANAGER.md` (技术详解)
---
## 📈 预期收益
| 场景 | 性能提升 | 体验改善 |
|------|----------|----------|
| 日常聊天 | 5-10% | 更流畅 |
| 批量操作 | 10-50x | 显著加速 |
| 整体系统 | 25-40% | 明显改善 ⚡⚡ |
---
## 最后一句话
**8 项精心设计的优化,让你的 AI 聊天机器人的内存管理速度提升 5-50 倍!** 🚀
---
**优化完成**: 2025-12-13
**状态**: 就绪投入使用
**兼容性**: 完全兼容
**性能**: 验证通过

View File

@@ -1,347 +0,0 @@
# 统一记忆管理器性能优化报告
## 优化概述
`src/memory_graph/unified_manager.py` 进行了深度性能优化,实现了**8项关键算法改进**,预期性能提升 **25-40%**
---
## 优化项详解
### 1. **并行任务创建开销消除** ⭐ 高优先级
**位置**: `search_memories()` 方法
**问题**: 创建了两个不必要的 `asyncio.Task` 对象
```python
# ❌ 原代码(低效)
perceptual_blocks_task = asyncio.create_task(self.perceptual_manager.recall_blocks(query_text))
short_term_memories_task = asyncio.create_task(self.short_term_manager.search_memories(query_text))
perceptual_blocks, short_term_memories = await asyncio.gather(
perceptual_blocks_task,
short_term_memories_task,
)
# ✅ 优化后(高效)
perceptual_blocks, short_term_memories = await asyncio.gather(
self.perceptual_manager.recall_blocks(query_text),
self.short_term_manager.search_memories(query_text),
)
```
**性能提升**: 消除了 2 个任务对象创建的开销
**影响**: 高(每次搜索都会调用)
---
### 2. **去重查询单遍扫描优化** ⭐ 高优先级
**位置**: `_build_manual_multi_queries()` 方法
**问题**: 先构建 `deduplicated` 列表再遍历,导致二次扫描
```python
# ❌ 原代码(两次扫描)
deduplicated: list[str] = []
for raw in queries:
text = (raw or "").strip()
if not text or text in seen:
continue
deduplicated.append(text)
for idx, text in enumerate(deduplicated):
weight = max(0.3, 1.0 - idx * decay)
manual_queries.append({...})
# ✅ 优化后(单次扫描)
for raw in queries:
text = (raw or "").strip()
if text and text not in seen:
seen.add(text)
weight = max(0.3, 1.0 - len(manual_queries) * decay)
manual_queries.append({...})
```
**性能提升**: O(2n) → O(n),减少 50% 扫描次数
**影响**: 中(在裁判模型评估时调用)
---
### 3. **内存去重函数多态优化** ⭐ 中优先级
**位置**: `_deduplicate_memories()` 方法
**问题**: 仅支持对象类型,遗漏字典类型支持
```python
# ❌ 原代码
mem_id = getattr(mem, "id", None)
# ✅ 优化后
if isinstance(mem, dict):
mem_id = mem.get("id")
else:
mem_id = getattr(mem, "id", None)
```
**性能提升**: 避免类型转换,支持多数据源
**影响**: 中(在长期记忆去重时调用)
---
### 4. **睡眠间隔计算查表法优化** ⭐ 中优先级
**位置**: `_calculate_auto_sleep_interval()` 方法
**问题**: 链式 if 判断(线性扫描),存在分支预测失败
```python
# ❌ 原代码(链式判断)
if occupancy >= 0.8:
return max(2.0, base_interval * 0.1)
if occupancy >= 0.5:
return max(5.0, base_interval * 0.2)
if occupancy >= 0.3:
...
# ✅ 优化后(查表法)
occupancy_thresholds = [
(0.8, 2.0, 0.1),
(0.5, 5.0, 0.2),
(0.3, 10.0, 0.4),
(0.1, 15.0, 0.6),
]
for threshold, min_val, factor in occupancy_thresholds:
if occupancy >= threshold:
return max(min_val, base_interval * factor)
```
**性能提升**: 改善分支预测性能,代码更简洁
**影响**: 低(每次检查调用一次,但调用频繁)
---
### 5. **后台块转移并行化** ⭐⭐ 最高优先级
**位置**: `_transfer_blocks_to_short_term()` 方法
**问题**: 串行处理多个块的转移操作
```python
# ❌ 原代码(串行)
for block in blocks:
try:
stm = await self.short_term_manager.add_from_block(block)
await self.perceptual_manager.remove_block(block.id)
self._trigger_transfer_wakeup() # 每个块都触发
except Exception as exc:
logger.error(...)
# ✅ 优化后(并行)
async def _transfer_single(block: MemoryBlock) -> tuple[MemoryBlock, bool]:
try:
stm = await self.short_term_manager.add_from_block(block)
if not stm:
return block, False
await self.perceptual_manager.remove_block(block.id)
return block, True
except Exception as exc:
return block, False
results = await asyncio.gather(*[_transfer_single(block) for block in blocks])
# 批量触发唤醒
success_count = sum(1 for result in results if isinstance(result, tuple) and result[1])
if success_count > 0:
self._trigger_transfer_wakeup()
```
**性能提升**: 串行 → 并行取决于块数2-10 倍)
**影响**: 最高(后台大量块转移时效果显著)
---
### 6. **缓存批量构建优化** ⭐ 中优先级
**位置**: `_auto_transfer_loop()` 方法
**问题**: 逐条添加到缓存ID 去重计数不高效
```python
# ❌ 原代码(逐条)
for memory in memories_to_transfer:
mem_id = getattr(memory, "id", None)
if mem_id and mem_id in cached_ids:
continue
transfer_cache.append(memory)
if mem_id:
cached_ids.add(mem_id)
added += 1
# ✅ 优化后(批量)
new_memories = []
for memory in memories_to_transfer:
mem_id = getattr(memory, "id", None)
if not (mem_id and mem_id in cached_ids):
new_memories.append(memory)
if mem_id:
cached_ids.add(mem_id)
if new_memories:
transfer_cache.extend(new_memories)
```
**性能提升**: 减少单个 append 调用,使用 extend 批量操作
**影响**: 低(优化内存分配,当缓存较大时有效)
---
### 7. **直接转移列表避免复制** ⭐ 低优先级
**位置**: `_auto_transfer_loop()``_schedule_perceptual_block_transfer()` 方法
**问题**: 不必要的 `list(transfer_cache)``list(blocks)` 复制
```python
# ❌ 原代码
result = await self.long_term_manager.transfer_from_short_term(list(transfer_cache))
task = asyncio.create_task(self._transfer_blocks_to_short_term(list(blocks)))
# ✅ 优化后
result = await self.long_term_manager.transfer_from_short_term(transfer_cache)
task = asyncio.create_task(self._transfer_blocks_to_short_term(blocks))
```
**性能提升**: O(n) 复制消除
**影响**: 低(当列表较小时影响微弱)
---
### 8. **长期检索上下文延迟创建** ⭐ 低优先级
**位置**: `_retrieve_long_term_memories()` 方法
**问题**: 总是创建 context 字典,即使为空
```python
# ❌ 原代码
context: dict[str, Any] = {}
if recent_chat_history:
context["chat_history"] = recent_chat_history
if manual_queries:
context["manual_multi_queries"] = manual_queries
if context:
search_params["context"] = context
# ✅ 优化后(条件创建)
if recent_chat_history or manual_queries:
context: dict[str, Any] = {}
if recent_chat_history:
context["chat_history"] = recent_chat_history
if manual_queries:
context["manual_multi_queries"] = manual_queries
search_params["context"] = context
```
**性能提升**: 避免不必要的字典创建
**影响**: 极低(仅内存分配,不影响逻辑路径)
---
## 性能数据
### 预期性能提升估计
| 优化项 | 场景 | 提升幅度 | 优先级 |
|--------|------|----------|--------|
| 并行任务创建消除 | 每次搜索 | 2-3% | ⭐⭐⭐⭐ |
| 查询去重单遍扫描 | 裁判评估 | 5-8% | ⭐⭐⭐ |
| 块转移并行化 | 批量转移≥5块 | 8-15% | ⭐⭐⭐⭐⭐ |
| 缓存批量构建 | 大批量缓存 | 2-4% | ⭐⭐ |
| 直接转移列表 | 小对象 | 1-2% | ⭐ |
| **综合提升** | **典型场景** | **25-40%** | - |
### 基准测试建议
```python
# 在 tests/ 目录中创建性能测试
import asyncio
import time
from src.memory_graph.unified_manager import UnifiedMemoryManager
async def benchmark_transfer():
manager = UnifiedMemoryManager()
await manager.initialize()
# 构造 100 个块
blocks = [...]
start = time.perf_counter()
await manager._transfer_blocks_to_short_term(blocks)
end = time.perf_counter()
print(f"转移 100 个块耗时: {(end - start) * 1000:.2f}ms")
asyncio.run(benchmark_transfer())
```
---
## 兼容性与风险评估
### ✅ 完全向后兼容
- 所有公共 API 签名保持不变
- 调用方无需修改代码
- 内部优化对外部透明
### ⚠️ 风险评估
| 优化项 | 风险等级 | 缓解措施 |
|--------|----------|----------|
| 块转移并行化 | 低 | 已测试异常处理 |
| 查询去重逻辑 | 极低 | 逻辑等价性已验证 |
| 其他优化 | 极低 | 仅涉及实现细节 |
---
## 测试建议
### 1. 单元测试
```python
# 验证 _build_manual_multi_queries 去重逻辑
def test_deduplicate_queries():
manager = UnifiedMemoryManager()
queries = ["hello", "hello", "world", "", "hello"]
result = manager._build_manual_multi_queries(queries)
assert len(result) == 2
assert result[0]["text"] == "hello"
assert result[1]["text"] == "world"
```
### 2. 集成测试
```python
# 测试转移并行化
async def test_parallel_transfer():
manager = UnifiedMemoryManager()
await manager.initialize()
blocks = [create_test_block() for _ in range(10)]
await manager._transfer_blocks_to_short_term(blocks)
# 验证所有块都被处理
assert len(manager.short_term_manager.memories) > 0
```
### 3. 性能测试
```python
# 对比优化前后的转移速度
# 使用 pytest-benchmark 进行基准测试
```
---
## 后续优化空间
### 第一优先级
1. **embedding 缓存优化**: 为高频查询 embedding 结果做缓存
2. **批量搜索并行化**: 在 `_retrieve_long_term_memories` 中并行多个查询
### 第二优先级
3. **内存池管理**: 使用对象池替代频繁的列表创建/销毁
4. **异步 I/O 优化**: 数据库操作使用连接池
### 第三优先级
5. **算法改进**: 使用更快的去重算法BloomFilter 等)
---
## 总结
通过 8 项目标性能优化,统一记忆管理器的运行速度预期提升 **25-40%**,尤其是在高并发场景和大规模块转移时效果最佳。所有优化都保持了完全的向后兼容性,无需修改调用代码。

View File

@@ -1,219 +0,0 @@
# 🚀 统一记忆管理器优化总结
## 优化成果
已成功优化 `src/memory_graph/unified_manager.py`,实现了 **8 项关键性能改进**
---
## 📊 性能基准测试结果
### 1⃣ 查询去重性能(小规模查询提升最大)
```
小查询 (2项): 72.7% ⬆️ 2.90μs → 0.79μs
中等查询 (50项): 8.1% ⬆️ 3.46μs → 3.19μs
```
### 2⃣ 块转移并行化(核心优化,性能提升最显著)
```
5 个块: 4.99x 加速 77.28ms → 15.49ms
10 个块: 9.93x 加速 155.50ms → 15.66ms
20 个块: 20.03x 加速 311.02ms → 15.53ms
50 个块: ~50x 加速 (预期值)
```
**说明**: 并行化后,由于异步并发处理,多个块的转移时间接近单个块的时间
---
## ✅ 实施的优化清单
| # | 优化项 | 文件位置 | 复杂度 | 预期提升 |
|---|--------|---------|--------|----------|
| 1 | 消除任务创建开销 | `search_memories()` | 低 | 2-3% |
| 2 | 查询去重单遍扫描 | `_build_manual_multi_queries()` | 中 | 5-15% |
| 3 | 内存去重多态支持 | `_deduplicate_memories()` | 低 | 1-3% |
| 4 | 睡眠间隔查表法 | `_calculate_auto_sleep_interval()` | 低 | 1-2% |
| 5 | **块转移并行化** | `_transfer_blocks_to_short_term()` | 中 | **8-50x** ⭐⭐⭐ |
| 6 | 缓存批量构建 | `_auto_transfer_loop()` | 低 | 2-4% |
| 7 | 直接转移列表 | `_auto_transfer_loop()` | 低 | 1-2% |
| 8 | 上下文延迟创建 | `_retrieve_long_term_memories()` | 低 | <1% |
---
## 🎯 关键优化亮点
### 🏆 块转移并行化(最重要)
**改进前**: 逐个处理块N 个块需要 N×T 时间
```python
for block in blocks:
stm = await self.short_term_manager.add_from_block(block)
await self.perceptual_manager.remove_block(block.id)
```
**改进后**: 并行处理块N 个块只需约 T 时间
```python
async def _transfer_single(block):
stm = await self.short_term_manager.add_from_block(block)
await self.perceptual_manager.remove_block(block.id)
return block, True
results = await asyncio.gather(*[_transfer_single(block) for block in blocks])
```
**性能收益**:
- 5 块: **5x 加速**
- 10 块: **10x 加速**
- 20+ 块: **20x+ 加速**
---
## 📈 典型场景性能提升
### 场景 1: 日常聊天消息处理
- 搜索 感知+短期记忆并行检索
- 提升: **5-10%**相对较小但持续
### 场景 2: 批量记忆转移(高负载)
- 10-50 个块的批量转移 并行化处理
- 提升: **10-50x** 显著效果)⭐⭐⭐
### 场景 3: 裁判模型评估
- 查询去重优化
- 提升: **5-15%**
---
## 🔧 技术细节
### 新增并行转移函数签名
```python
async def _transfer_blocks_to_short_term(self, blocks: list[MemoryBlock]) -> None:
"""实际转换逻辑在后台执行(优化:并行处理多个块,批量触发唤醒)"""
async def _transfer_single(block: MemoryBlock) -> tuple[MemoryBlock, bool]:
# 单个块的转移逻辑
...
# 并行处理所有块
results = await asyncio.gather(*[_transfer_single(block) for block in blocks])
```
### 优化后的自动转移循环
```python
async def _auto_transfer_loop(self) -> None:
"""自动转移循环(优化:更高效的缓存管理)"""
# 批量构建缓存
new_memories = [...]
transfer_cache.extend(new_memories)
# 直接传递列表,避免复制
result = await self.long_term_manager.transfer_from_short_term(transfer_cache)
```
---
## ⚠️ 兼容性与风险
### ✅ 完全向后兼容
- 所有公开 API 保持不变
- 内部实现优化调用方无感知
- 测试覆盖已验证核心逻辑
### 🛡️ 风险等级:极低
| 优化项 | 风险等级 | 原因 |
|--------|---------|------|
| 并行转移 | | 已有完善的异常处理机制 |
| 查询去重 | 极低 | 逻辑等价结果一致 |
| 其他优化 | 极低 | 仅涉及实现细节 |
---
## 📚 文档与工具
### 📖 生成的文档
1. **[OPTIMIZATION_REPORT_UNIFIED_MANAGER.md](../docs/OPTIMIZATION_REPORT_UNIFIED_MANAGER.md)**
- 详细的优化说明和性能分析
- 8 项优化的完整描述
- 性能数据和测试建议
2. **[benchmark_unified_manager.py](../scripts/benchmark_unified_manager.py)**
- 性能基准测试脚本
- 可重复运行验证优化效果
- 包含多个测试场景
### 🧪 运行基准测试
```bash
python scripts/benchmark_unified_manager.py
```
---
## 📋 验证清单
- [x] **代码优化完成** - 8 项改进已实施
- [x] **静态代码分析** - 通过代码质量检查
- [x] **性能基准测试** - 验证了关键优化的性能提升
- [x] **兼容性验证** - 保持向后兼容
- [x] **文档完成** - 详细的优化报告已生成
---
## 🎉 快速开始
### 使用优化后的代码
优化已直接应用到源文件无需额外配置
```python
# 自动获得所有优化效果
from src.memory_graph.unified_manager import UnifiedMemoryManager
manager = UnifiedMemoryManager()
await manager.initialize()
# 关键操作已自动优化:
# - search_memories() 并行检索
# - _transfer_blocks_to_short_term() 并行转移
# - _build_manual_multi_queries() 单遍去重
```
### 监控性能
```python
# 获取统计信息(包括转移速度等)
stats = manager.get_statistics()
print(f"已转移记忆: {stats['long_term']['total_memories']}")
```
---
## 📞 后续改进方向
### 优先级 1可立即实施
- [ ] Embedding 结果缓存预期 20-30% 提升
- [ ] 批量查询并行化预期 5-10% 提升
### 优先级 2需要架构调整
- [ ] 对象池管理减少内存分配
- [ ] 数据库连接池优化 I/O
### 优先级 3算法创新
- [ ] BloomFilter 去重更快的去重
- [ ] 缓存预热策略减少冷启动
---
## 📊 预期收益总结
| 场景 | 原耗时 | 优化后 | 改善 |
|------|--------|--------|------|
| 单次搜索 | 10ms | 9.5ms | 5% |
| 转移 10 个块 | 155ms | 16ms | **9.6x** |
| 转移 20 个块 | 311ms | 16ms | **19x** ⭐⭐ |
| 日常操作综合 | 100ms | 70ms | **30%** |
---
**优化完成时间**: 2025-12-13
**优化文件**: `src/memory_graph/unified_manager.py` (721 )
**代码变更**: 8 个关键优化点
**预期性能提升**: **25-40%** (典型场景) / **10-50x** (批量操作)

View File

@@ -1,287 +0,0 @@
# 优化对比可视化
## 1. 块转移并行化 - 性能对比
```
原始实现(串行处理)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
块 1: [=====] (单个块 ~15ms)
块 2: [=====]
块 3: [=====]
块 4: [=====]
块 5: [=====]
总时间: ████████████████████ 75ms
优化后(并行处理)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
块 1,2,3,4,5: [=====] (并行 ~15ms)
总时间: ████ 15ms
加速比: 75ms ÷ 15ms = 5x ⚡
```
## 2. 查询去重 - 算法演进
```
❌ 原始实现(两次扫描)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入: ["hello", "hello", "world", "hello"]
↓ 第一次扫描: 去重
去重列表: ["hello", "world"]
↓ 第二次扫描: 添加权重
输出: [
{"text": "hello", "weight": 1.0},
{"text": "world", "weight": 0.85}
]
扫描次数: 2x
✅ 优化后(单次扫描)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
输入: ["hello", "hello", "world", "hello"]
↓ 单次扫描: 去重 + 权重
输出: [
{"text": "hello", "weight": 1.0},
{"text": "world", "weight": 0.85}
]
扫描次数: 1x
性能提升: 50% 扫描时间节省 ✓
```
## 3. 内存去重 - 多态支持
```
❌ 原始(仅支持对象)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
记忆对象: Memory(id="001") ✓
字典对象: {"id": "001"} ✗ (失败)
混合数据: [Memory(...), {...}] ✗ (部分失败)
✅ 优化后(支持对象和字典)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
记忆对象: Memory(id="001") ✓
字典对象: {"id": "001"} ✓ (支持)
混合数据: [Memory(...), {...}] ✓ (完全支持)
数据源兼容性: +100% 提升 ✓
```
## 4. 自动转移循环 - 缓存管理优化
```
❌ 原始实现(逐条添加)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
获取记忆列表: [M1, M2, M3, M4, M5]
for memory in list:
transfer_cache.append(memory) ← 逐条 append
cached_ids.add(memory.id)
内存分配: 5x append 操作
✅ 优化后(批量 extend
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
获取记忆列表: [M1, M2, M3, M4, M5]
new_memories = [...]
transfer_cache.extend(new_memories) ← 单次 extend
内存分配: 1x extend 操作
分配操作: -80% 减少 ✓
```
## 5. 性能改善曲线
```
块转移性能 (ms)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
350 │
│ ● 串行处理
300 │ /
│ /
250 │ /
│ /
200 │ ●
│ /
150 │ ●
│ /
100 │ /
│ /
50 │ /● ━━ ● ━━ ● ─── ● ─── ●
│ / (并行处理,基本线性)
0 │─────●──────────────────────────────
0 5 10 15 20 25
块数量
结论: 块数 ≥ 5 时,并行处理性能优势明显
```
## 6. 整体优化影响范围
```
统一记忆管理器
├─ search_memories() ← 优化 3% (并行任务)
│ ├─ recall_blocks()
│ └─ search_memories()
├─ _judge_retrieval_sufficiency() ← 优化 8% (去重)
│ └─ _build_manual_multi_queries()
├─ _retrieve_long_term_memories() ← 优化 2% (上下文)
│ └─ _deduplicate_memories() ← 优化 3% (多态)
└─ _auto_transfer_loop() ← 优化 15% ⭐⭐ (批量+并行)
├─ _calculate_auto_sleep_interval() ← 优化 1%
├─ _schedule_perceptual_block_transfer()
│ └─ _transfer_blocks_to_short_term() ← 优化 50x ⭐⭐⭐
└─ transfer_from_short_term()
总体优化覆盖: 100% 关键路径
```
## 7. 成本-收益矩阵
```
收益大小
5 │ ●[5] 块转移并行化
│ ○ 高收益,中等成本
4 │
│ ●[2] ●[6]
3 │ 查询去重 缓存批量
│ ○ ○
2 │ ○[8] ○[3] ○[7]
│ 上下文 多态 列表
1 │ ○[4] ○[1]
│ 查表 任务
0 └────────────────────────────►
0 1 2 3 4 5
实施成本
推荐优先级: [5] > [2] > [6] > [1]
```
## 8. 时间轴 - 优化历程
```
优化历程
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
│ 2025-12-13
│ ├─ 分析瓶颈 [完成] ✓
│ ├─ 设计优化方案 [完成] ✓
│ ├─ 实施 8 项优化 [完成] ✓
│ │ ├─ 并行化 [完成] ✓
│ │ ├─ 单遍去重 [完成] ✓
│ │ ├─ 多态支持 [完成] ✓
│ │ ├─ 查表法 [完成] ✓
│ │ ├─ 缓存批量 [完成] ✓
│ │ └─ ...
│ ├─ 性能基准测试 [完成] ✓
│ └─ 文档完成 [完成] ✓
└─ 下一步: 性能监控 & 迭代优化
```
## 9. 实际应用场景对比
```
场景 A: 日常对话消息处理
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
消息处理流程:
message → add_message() → search_memories() → generate_response()
性能改善:
add_message: 无明显改善 (感知层处理)
search_memories: ↓ 5% (并行检索)
judge + retrieve: ↓ 8% (查询去重)
───────────────────────
总体改善: ~ 5-10% 持续加速
场景 B: 高负载批量转移
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
内存压力场景 (50+ 条短期记忆待转移):
_auto_transfer_loop()
→ get_memories_for_transfer() [50 条]
→ transfer_from_short_term()
→ _transfer_blocks_to_short_term() [并行处理]
性能改善:
原耗时: 50 * 15ms = 750ms
优化后: ~15ms (并行)
───────────────────────
加速比: 50x ⚡ (显著优化!)
场景 C: 混合工作负载
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
典型一小时运行:
消息处理: 60% (每秒 1 条) = 3600 条消息
内存管理: 30% (转移 200 条) = 200 条转移
其他操作: 10%
性能改善:
消息处理: 3600 * 5% = 180 条消息快
转移操作: 1 * 50x ≈ 12ms 快 (缩放)
───────────────────────
总体感受: 显著加速 ✓
```
## 10. 优化效果等级
```
性能提升等级评分
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
★★★★★ 优秀 (>10x 提升)
└─ 块转移并行化: 5-50x ⭐ 最重要
★★★★☆ 很好 (5-10% 提升)
├─ 查询去重单遍: 5-15%
└─ 缓存批量构建: 2-4%
★★★☆☆ 良好 (1-5% 提升)
├─ 任务创建消除: 2-3%
├─ 上下文延迟: 1-2%
└─ 多态支持: 1-3%
★★☆☆☆ 可观 (<1% 提升)
└─ 列表复制避免: <1%
总体评分: ★★★★★ 优秀 (25-40% 综合提升)
```
---
## 总结
**8 项优化实施完成**
- 核心优化:块转移并行化 (5-50x)
- 支撑优化:查询去重、缓存管理、多态支持
- 微优化:任务创建、列表复制、上下文延迟
📊 **性能基准验证**
- 块转移: **5-50x 加速** (关键场景)
- 查询处理: **5-15% 提升**
- 综合性能: **25-40% 提升** (典型场景)
🎯 **预期收益**
- 日常使用:更流畅的消息处理
- 高负载:内存管理显著加速
- 整体:系统响应更快
🚀 **立即生效**
- 无需配置,自动应用所有优化
- 完全向后兼容,无破坏性变更
- 可通过基准测试验证效果

View File

@@ -1,210 +0,0 @@
# 消息分发器重构文档
## 重构日期
2025-11-04
## 重构目标
将基于异步任务循环的消息分发机制改为使用统一的 `unified_scheduler`,实现更优雅和可维护的消息处理流程。
## 重构内容
### 1. 修改 unified_scheduler 以支持完全并发执行
**文件**: `src/schedule/unified_scheduler.py`
**主要改动**:
- 修改 `_check_and_trigger_tasks` 方法,使用 `asyncio.create_task` 为每个到期任务创建独立的异步任务
- 新增 `_execute_task_callback` 方法,用于并发执行单个任务
- 使用 `asyncio.gather` 并发等待所有任务完成,确保不同 schedule 之间完全异步执行,不会相互阻塞
**关键改进**:
```python
# 为每个任务创建独立的异步任务,确保并发执行
execution_tasks = []
for task in tasks_to_trigger:
execution_task = asyncio.create_task(
self._execute_task_callback(task, current_time),
name=f"execute_{task.task_name}"
)
execution_tasks.append(execution_task)
# 等待所有任务完成(使用 return_exceptions=True 避免单个任务失败影响其他任务)
results = await asyncio.gather(*execution_tasks, return_exceptions=True)
```
### 2. 创建新的 SchedulerDispatcher
**文件**: `src/chat/message_manager/scheduler_dispatcher.py`
**功能**:
基于 `unified_scheduler` 的消息分发器,替代原有的 `stream_loop_task` 循环机制。
**工作流程**:
1. **接收消息时**: 将消息添加到聊天流上下文(缓存)
2. **检查 schedule**: 查看该聊天流是否有活跃的 schedule
3. **打断判定**: 如果有活跃 schedule检查是否需要打断
- 如果需要打断,移除旧 schedule 并创建新的
- 如果不需要打断,保持原有 schedule
4. **创建 schedule**: 如果没有活跃 schedule创建新的
5. **Schedule 触发**: 当 schedule 到期时,激活 chatter 进行处理
6. **处理完成**: 计算下次间隔并根据需要注册新的 schedule
**关键方法**:
- `on_message_received(stream_id)`: 消息接收时的处理入口
- `_check_interruption(stream_id, context)`: 检查是否应该打断
- `_create_schedule(stream_id, context)`: 创建新的 schedule
- `_cancel_and_recreate_schedule(stream_id, context)`: 取消并重新创建 schedule
- `_on_schedule_triggered(stream_id)`: schedule 触发时的回调
- `_process_stream(stream_id, context)`: 激活 chatter 处理消息
### 3. 修改 MessageManager 集成新分发器
**文件**: `src/chat/message_manager/message_manager.py`
**主要改动**:
1. 导入 `scheduler_dispatcher`
2. 启动时初始化 `scheduler_dispatcher` 而非 `stream_loop_manager`
3. 修改 `add_message` 方法:
- 将消息添加到上下文后
- 调用 `scheduler_dispatcher.on_message_received(stream_id)` 处理消息接收事件
4. 废弃 `_check_and_handle_interruption` 方法(打断逻辑已集成到 dispatcher
**新的消息接收流程**:
```python
async def add_message(self, stream_id: str, message: DatabaseMessages):
# 1. 检查 notice 消息
if self._is_notice_message(message):
await self._handle_notice_message(stream_id, message)
if not global_config.notice.enable_notice_trigger_chat:
return
# 2. 将消息添加到上下文
chat_stream = await chat_manager.get_stream(stream_id)
await chat_stream.context_manager.add_message(message)
# 3. 通知 scheduler_dispatcher 处理
await scheduler_dispatcher.on_message_received(stream_id)
```
### 4. 更新模块导出
**文件**: `src/chat/message_manager/__init__.py`
**改动**:
- 导出 `SchedulerDispatcher``scheduler_dispatcher`
## 架构对比
### 旧架构 (基于 stream_loop_task)
```
消息到达 -> add_message -> 添加到上下文 -> 检查打断 -> 取消 stream_loop_task
-> 重新创建 stream_loop_task
stream_loop_task: while True:
检查未读消息 -> 处理消息 -> 计算间隔 -> sleep(间隔)
```
**问题**:
- 每个聊天流维护一个独立的异步循环任务
- 即使没有消息也需要持续轮询
- 打断逻辑通过取消和重建任务实现,较为复杂
- 难以统一管理和监控
### 新架构 (基于 unified_scheduler)
```
消息到达 -> add_message -> 添加到上下文 -> dispatcher.on_message_received
-> 检查是否有活跃 schedule
-> 打断判定
-> 创建/更新 schedule
schedule 到期 -> _on_schedule_triggered -> 处理消息 -> 计算间隔 -> 创建新 schedule (如果需要)
```
**优势**:
- 使用统一的调度器管理所有聊天流
- 按需创建 schedule没有消息时不会创建
- 打断逻辑清晰:移除旧 schedule + 创建新 schedule
- 易于监控和统计(统一的 scheduler 统计)
- 完全异步并发,多个 schedule 可以同时触发而不相互阻塞
## 兼容性
### 保留的组件
- `stream_loop_manager`: 暂时保留但不启动,以便需要时回滚
- `_check_and_handle_interruption`: 保留方法签名但不执行,避免破坏现有调用
### 移除的组件
- 无(本次重构采用渐进式方式,先添加新功能,待稳定后再移除旧代码)
## 配置项
所有配置项保持不变,新分发器完全兼容现有配置:
- `chat.interruption_enabled`: 是否启用打断
- `chat.allow_reply_interruption`: 是否允许回复时打断
- `chat.interruption_max_limit`: 最大打断次数
- `chat.distribution_interval`: 基础分发间隔
- `chat.force_dispatch_unread_threshold`: 强制分发阈值
- `chat.force_dispatch_min_interval`: 强制分发最小间隔
## 测试建议
1. **基本功能测试**
- 单个聊天流接收消息并正常处理
- 多个聊天流同时接收消息并并发处理
2. **打断测试**
- 在 chatter 处理过程中发送新消息,验证打断逻辑
- 验证打断次数限制
- 验证打断概率计算
3. **间隔计算测试**
- 验证基于能量的动态间隔计算
- 验证强制分发阈值触发
4. **并发测试**
- 多个聊天流的 schedule 同时到期,验证并发执行
- 验证不同 schedule 之间不会相互阻塞
5. **长时间稳定性测试**
- 运行较长时间,观察是否有内存泄漏
- 观察 schedule 创建和销毁是否正常
## 回滚方案
如果新机制出现问题,可以通过以下步骤回滚:
1.`message_manager.py``start()` 方法中:
```python
# 注释掉新分发器
# await scheduler_dispatcher.start()
# scheduler_dispatcher.set_chatter_manager(self.chatter_manager)
# 启用旧分发器
await stream_loop_manager.start()
stream_loop_manager.set_chatter_manager(self.chatter_manager)
```
2. 在 `add_message()` 方法中:
```python
# 注释掉新逻辑
# await scheduler_dispatcher.on_message_received(stream_id)
# 恢复旧逻辑
await self._check_and_handle_interruption(chat_stream, message)
```
3. 在 `_check_and_handle_interruption()` 方法中移除开头的 `return` 语句
## 后续工作
1. 在确认新机制稳定后,完全移除 `stream_loop_manager` 相关代码
2. 清理 `StreamContext` 中的 `stream_loop_task` 字段
3. 移除 `_check_and_handle_interruption` 方法
4. 更新相关文档和注释
## 性能预期
- **资源占用**: 减少(不再为每个流维护独立循环)
- **响应延迟**: 不变(仍基于相同的间隔计算)
- **并发能力**: 提升(完全异步执行,无阻塞)
- **可维护性**: 提升(逻辑更清晰,统一管理)

View File

@@ -1,367 +0,0 @@
# 三层记忆系统集成完成报告
## ✅ 已完成的工作
### 1. 核心实现 (100%)
#### 数据模型 (`src/memory_graph/three_tier/models.py`)
-`MemoryBlock`: 感知记忆块5条消息/块)
-`ShortTermMemory`: 短期结构化记忆
-`GraphOperation`: 11种图操作类型
-`JudgeDecision`: Judge模型决策结果
-`ShortTermDecision`: 短期记忆决策枚举
#### 感知记忆层 (`perceptual_manager.py`)
- ✅ 全局记忆堆管理最多50块
- ✅ 消息累积与分块5条/块)
- ✅ 向量生成与相似度计算
- ✅ TopK召回机制top_k=3, threshold=0.55
- ✅ 激活次数统计≥3次激活→短期
- ✅ FIFO淘汰策略
- ✅ 持久化存储JSON
- ✅ 单例模式 (`get_perceptual_manager()`)
#### 短期记忆层 (`short_term_manager.py`)
- ✅ 结构化记忆提取(主语/话题/宾语)
- ✅ LLM决策引擎4种操作MERGE/UPDATE/CREATE_NEW/DISCARD
- ✅ 向量检索与相似度匹配
- ✅ 重要性评分系统
- ✅ 激活衰减机制decay_factor=0.98
- ✅ 转移阈值判断importance≥0.6→长期)
- ✅ 持久化存储JSON
- ✅ 单例模式 (`get_short_term_manager()`)
#### 长期记忆层 (`long_term_manager.py`)
- ✅ 批量转移处理10条/批)
- ✅ LLM生成图操作语言
- ✅ 11种图操作执行
- `CREATE_MEMORY`: 创建新记忆节点
- `UPDATE_MEMORY`: 更新现有记忆
- `MERGE_MEMORIES`: 合并多个记忆
- `CREATE_NODE`: 创建实体/事件节点
- `UPDATE_NODE`: 更新节点属性
- `DELETE_NODE`: 删除节点
- `CREATE_EDGE`: 创建关系边
- `UPDATE_EDGE`: 更新边属性
- `DELETE_EDGE`: 删除边
- `CREATE_SUBGRAPH`: 创建子图
- `QUERY_GRAPH`: 图查询
- ✅ 慢速衰减机制decay_factor=0.95
- ✅ 与现有MemoryManager集成
- ✅ 单例模式 (`get_long_term_manager()`)
#### 统一管理器 (`unified_manager.py`)
- ✅ 统一入口接口
-`add_message()`: 消息添加流程
-`search_memories()`: 智能检索Judge模型决策
-`transfer_to_long_term()`: 手动转移接口
- ✅ 自动转移任务每10分钟
- ✅ 统计信息聚合
- ✅ 生命周期管理
#### 单例管理 (`manager_singleton.py`)
- ✅ 全局单例访问器
-`initialize_unified_memory_manager()`: 初始化
-`get_unified_memory_manager()`: 获取实例
-`shutdown_unified_memory_manager()`: 关闭清理
### 2. 系统集成 (100%)
#### 配置系统集成
-`config/bot_config.toml`: 添加 `[three_tier_memory]` 配置节
-`src/config/official_configs.py`: 创建 `ThreeTierMemoryConfig`
-`src/config/config.py`:
- 添加 `ThreeTierMemoryConfig` 导入
-`Config` 类中添加 `three_tier_memory` 字段
#### 消息处理集成
-`src/chat/message_manager/context_manager.py`:
- 添加延迟导入机制(避免循环依赖)
-`add_message()` 中调用三层记忆系统
- 异常处理不影响主流程
#### 回复生成集成
-`src/chat/replyer/default_generator.py`:
- 创建 `build_three_tier_memory_block()` 方法
- 添加到并行任务列表
- 合并三层记忆与原记忆图结果
- 更新默认值字典和任务映射
#### 系统启动/关闭集成
-`src/main.py`:
-`_init_components()` 中初始化三层记忆
- 检查配置启用状态
-`_async_cleanup()` 中添加关闭逻辑
### 3. 文档与测试 (100%)
#### 用户文档
-`docs/three_tier_memory_user_guide.md`: 完整使用指南
- 快速启动教程
- 工作流程图解
- 使用示例3个场景
- 运维管理指南
- 最佳实践建议
- 故障排除FAQ
- 性能指标参考
#### 测试脚本
-`scripts/test_three_tier_memory.py`: 集成测试脚本
- 6个测试套件
- 单元测试覆盖
- 集成测试验证
#### 项目文档更新
- ✅ 本报告(实现完成总结)
## 📊 代码统计
### 新增文件
| 文件 | 行数 | 说明 |
|------|------|------|
| `models.py` | 311 | 数据模型定义 |
| `perceptual_manager.py` | 517 | 感知记忆层管理器 |
| `short_term_manager.py` | 686 | 短期记忆层管理器 |
| `long_term_manager.py` | 664 | 长期记忆层管理器 |
| `unified_manager.py` | 495 | 统一管理器 |
| `manager_singleton.py` | 75 | 单例管理 |
| `__init__.py` | 25 | 模块初始化 |
| **总计** | **2773** | **核心代码** |
### 修改文件
| 文件 | 修改说明 |
|------|----------|
| `config/bot_config.toml` | 添加 `[three_tier_memory]` 配置13个参数 |
| `src/config/official_configs.py` | 添加 `ThreeTierMemoryConfig`27行 |
| `src/config/config.py` | 添加导入和字段2处修改 |
| `src/chat/message_manager/context_manager.py` | 集成消息添加18行新增 |
| `src/chat/replyer/default_generator.py` | 添加检索方法和集成82行新增 |
| `src/main.py` | 启动/关闭集成10行新增 |
### 新增文档
- `docs/three_tier_memory_user_guide.md`: 400+行完整指南
- `scripts/test_three_tier_memory.py`: 400+行测试脚本
- `docs/three_tier_memory_completion_report.md`: 本报告
## 🎯 关键特性
### 1. 智能分层
- **感知层**: 短期缓冲,快速访问(<5ms
- **短期层**: 活跃记忆LLM结构化<100ms
- **长期层**: 持久图谱深度推理1-3s/
### 2. LLM决策引擎
- **短期决策**: 4种操作合并/更新/新建/丢弃
- **长期决策**: 11种图操作
- **Judge模型**: 智能检索充分性判断
### 3. 性能优化
- **异步执行**: 所有I/O操作非阻塞
- **批量处理**: 长期转移批量10条
- **缓存策略**: Judge结果缓存
- **延迟导入**: 避免循环依赖
### 4. 数据安全
- **JSON持久化**: 所有层次数据持久化
- **崩溃恢复**: 自动从最后状态恢复
- **异常隔离**: 记忆系统错误不影响主流程
## 🔄 工作流程
```
新消息
[感知层] 累积到5条 → 生成向量 → TopK召回
↓ (激活3次)
[短期层] LLM提取结构 → 决策操作 → 更新/合并
↓ (重要性≥0.6)
[长期层] 批量转移 → LLM生成图操作 → 更新记忆图谱
持久化存储
```
```
查询
检索感知层 (TopK=3)
检索短期层 (TopK=5)
Judge评估充分性
↓ (不充分)
检索长期层 (图谱查询)
返回综合结果
```
## ⚙️ 配置参数
### 关键参数说明
```toml
[three_tier_memory]
enable = true # 系统开关
perceptual_max_blocks = 50 # 感知层容量
perceptual_block_size = 5 # 块大小(固定)
activation_threshold = 3 # 激活阈值
short_term_max_memories = 100 # 短期层容量
short_term_transfer_threshold = 0.6 # 转移阈值
long_term_batch_size = 10 # 批量大小
judge_model_name = "utils_small" # Judge模型
enable_judge_retrieval = true # 启用智能检索
```
### 调优建议
- **高频群聊**: 增大 `perceptual_max_blocks` `short_term_max_memories`
- **私聊深度**: 降低 `activation_threshold` `short_term_transfer_threshold`
- **性能优先**: 禁用 `enable_judge_retrieval`减少LLM调用
## 🧪 测试结果
### 单元测试
- 配置系统加载
- 感知记忆添加/召回
- 短期记忆提取/决策
- 长期记忆转移/图操作
- 统一管理器集成
- 单例模式一致性
### 集成测试
- 端到端消息流程
- 跨层记忆转移
- 智能检索含Judge
- 自动转移任务
- 持久化与恢复
### 性能测试
- **感知层添加**: 3-5ms
- **短期层检索**: 50-100ms
- **长期层转移**: 1-3s/ ✅(LLM瓶颈
- **智能检索**: 200-500ms
## ⚠️ 已知问题与限制
### 静态分析警告
- **Pylance类型检查**: 多处可选类型警告不影响运行
- **原因**: 初始化前的 `None` 类型
- **解决方案**: 运行时检查 `_initialized` 标志
### LLM依赖
- **短期提取**: 需要LLM支持提取主谓宾
- **短期决策**: 需要LLM支持4种操作
- **长期图操作**: 需要LLM支持生成操作序列
- **Judge检索**: 需要LLM支持充分性判断
- **缓解**: 提供降级策略配置禁用Judge
### 性能瓶颈
- **LLM调用延迟**: 每次转移需1-3秒
- **缓解**: 批量处理10条/+ 异步执行
- **建议**: 使用快速模型gpt-4o-mini, utils_small
### 数据迁移
- **现有记忆图**: 不自动迁移到三层系统
- **共存模式**: 两套系统并行运行
- **建议**: 新项目启用老项目可选
## 🚀 后续优化建议
### 短期优化
1. **向量缓存**: ChromaDB持久化减少重启损失
2. **LLM池化**: 批量调用减少往返
3. **异步保存**: 更频繁的异步持久化
### 中期优化
4. **自适应参数**: 根据对话频率自动调整阈值
5. **记忆压缩**: 低重要性记忆自动归档
6. **智能预加载**: 基于上下文预测性加载
### 长期优化
7. **图谱可视化**: WebUI展示记忆图谱
8. **记忆编辑**: 用户界面手动管理记忆
9. **跨实例共享**: 多机器人记忆同步
## 📝 使用方式
### 启用系统
1. 编辑 `config/bot_config.toml`
2. 添加 `[three_tier_memory]` 配置
3. 设置 `enable = true`
4. 重启机器人
### 验证运行
```powershell
# 运行测试脚本
python scripts/test_three_tier_memory.py
# 查看日志
# 应看到 "三层记忆系统初始化成功"
```
### 查看统计
```python
from src.memory_graph.three_tier.manager_singleton import get_unified_memory_manager
manager = get_unified_memory_manager()
stats = await manager.get_statistics()
print(stats)
```
## 🎓 学习资源
- **用户指南**: `docs/three_tier_memory_user_guide.md`
- **测试脚本**: `scripts/test_three_tier_memory.py`
- **代码示例**: 各管理器中的文档字符串
- **在线文档**: https://mofox-studio.github.io/MoFox-Bot-Docs/
## 👥 贡献者
- **设计**: AI Copilot + 用户需求
- **实现**: AI Copilot (Claude Sonnet 4.5)
- **测试**: 集成测试脚本 + 用户反馈
- **文档**: 完整中文文档
## 📅 开发时间线
- **需求分析**: 2025-01-13
- **数据模型设计**: 2025-01-13
- **感知层实现**: 2025-01-13
- **短期层实现**: 2025-01-13
- **长期层实现**: 2025-01-13
- **统一管理器**: 2025-01-13
- **系统集成**: 2025-01-13
- **文档与测试**: 2025-01-13
- **总计**: 1天完成迭代式开发
## ✅ 验收清单
- [x] 核心功能实现完整
- [x] 配置系统集成
- [x] 消息处理集成
- [x] 回复生成集成
- [x] 系统启动/关闭集成
- [x] 用户文档编写
- [x] 测试脚本编写
- [x] 代码无语法错误
- [x] 日志输出规范
- [x] 异常处理完善
- [x] 单例模式正确
- [x] 持久化功能正常
## 🎉 总结
三层记忆系统已**完全实现并集成到 MoFox_Bot**包括
1. **2773行核心代码**6个文件
2. **6处系统集成点**配置/消息/回复/启动
3. **800+行文档**用户指南+测试脚本
4. **完整生命周期管理**初始化运行关闭
5. **智能LLM决策引擎**4种短期操作+11种图操作
6. **性能优化机制**异步+批量+缓存
系统已准备就绪可以通过配置文件启用并投入使用所有功能经过设计验证文档完整测试脚本可执行
---
**状态**: 完成
**版本**: 1.0.0
**日期**: 2025-01-13
**下一步**: 用户测试与反馈收集

View File

@@ -1,306 +0,0 @@
import asyncio
import time
import os
import sys
from dataclasses import dataclass
from typing import Any, Optional
# Benchmark the distribution manager's run_chat_stream/conversation_loop behavior
# by wiring a lightweight dummy manager and contexts. This avoids touching real DB or chat subsystems.
# Ensure project root is on sys.path when running as a script
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT_DIR not in sys.path:
sys.path.insert(0, ROOT_DIR)
# Avoid importing the whole 'src.chat' package to prevent heavy deps (e.g., redis)
# Local minimal implementation of loop and manager to isolate benchmark from heavy deps.
from collections.abc import AsyncIterator, Awaitable, Callable
from dataclasses import dataclass, field
@dataclass
class ConversationTick:
stream_id: str
tick_time: float = field(default_factory=time.time)
force_dispatch: bool = False
tick_count: int = 0
async def conversation_loop(
stream_id: str,
get_context_func: Callable[[str], Awaitable["DummyContext | None"]],
calculate_interval_func: Callable[[str, bool], Awaitable[float]],
flush_cache_func: Callable[[str], Awaitable[list[Any]]],
check_force_dispatch_func: Callable[["DummyContext", int], bool],
is_running_func: Callable[[], bool],
) -> AsyncIterator[ConversationTick]:
tick_count = 0
while is_running_func():
ctx = await get_context_func(stream_id)
if not ctx:
await asyncio.sleep(0.1)
continue
await flush_cache_func(stream_id)
unread = ctx.get_unread_messages()
ucnt = len(unread)
force = check_force_dispatch_func(ctx, ucnt)
if ucnt > 0 or force:
tick_count += 1
yield ConversationTick(stream_id=stream_id, force_dispatch=force, tick_count=tick_count)
interval = await calculate_interval_func(stream_id, ucnt > 0)
await asyncio.sleep(interval)
class StreamLoopManager:
def __init__(self, max_concurrent_streams: Optional[int] = None):
self.stats: dict[str, Any] = {
"active_streams": 0,
"total_loops": 0,
"total_process_cycles": 0,
"total_failures": 0,
"start_time": time.time(),
}
self.max_concurrent_streams = max_concurrent_streams or 100
self.force_dispatch_unread_threshold = 20
self.chatter_manager = DummyChatterManager()
self.is_running = False
self._stream_start_locks: dict[str, asyncio.Lock] = {}
self._processing_semaphore = asyncio.Semaphore(self.max_concurrent_streams)
self._chat_manager: Optional[DummyChatManager] = None
def set_chat_manager(self, chat_manager: "DummyChatManager") -> None:
self._chat_manager = chat_manager
async def start(self):
self.is_running = True
async def stop(self):
self.is_running = False
async def _get_stream_context(self, stream_id: str):
assert self._chat_manager is not None
stream = await self._chat_manager.get_stream(stream_id)
return stream.context if stream else None
async def _flush_cached_messages_to_unread(self, stream_id: str):
ctx = await self._get_stream_context(stream_id)
return ctx.flush_cached_messages() if ctx else []
def _needs_force_dispatch_for_context(self, context: "DummyContext", unread_count: int) -> bool:
return unread_count > (self.force_dispatch_unread_threshold or 20)
async def _process_stream_messages(self, stream_id: str, context: "DummyContext") -> bool:
res = await self.chatter_manager.process_stream_context(stream_id, context) # type: ignore[attr-defined]
return bool(res.get("success", False))
async def _update_stream_energy(self, stream_id: str, context: "DummyContext") -> None:
pass
async def _calculate_interval(self, stream_id: str, has_messages: bool) -> float:
return 0.005 if has_messages else 0.02
async def start_stream_loop(self, stream_id: str, force: bool = False) -> bool:
ctx = await self._get_stream_context(stream_id)
if not ctx:
return False
# create driver
loop_task = asyncio.create_task(run_chat_stream(stream_id, self))
ctx.stream_loop_task = loop_task
self.stats["active_streams"] += 1
self.stats["total_loops"] += 1
return True
async def run_chat_stream(stream_id: str, manager: StreamLoopManager) -> None:
try:
gen = conversation_loop(
stream_id=stream_id,
get_context_func=manager._get_stream_context,
calculate_interval_func=manager._calculate_interval,
flush_cache_func=manager._flush_cached_messages_to_unread,
check_force_dispatch_func=manager._needs_force_dispatch_for_context,
is_running_func=lambda: manager.is_running,
)
async for tick in gen:
ctx = await manager._get_stream_context(stream_id)
if not ctx:
continue
if ctx.is_chatter_processing:
continue
try:
async with manager._processing_semaphore:
ok = await manager._process_stream_messages(stream_id, ctx)
except Exception:
ok = False
manager.stats["total_process_cycles"] += 1
if not ok:
manager.stats["total_failures"] += 1
except asyncio.CancelledError:
pass
@dataclass
class DummyMessage:
time: float
processed_plain_text: str = ""
display_message: str = ""
is_at: bool = False
is_mentioned: bool = False
class DummyContext:
def __init__(self, stream_id: str, initial_unread: int):
self.stream_id = stream_id
self.unread_messages = [DummyMessage(time=time.time()) for _ in range(initial_unread)]
self.history_messages: list[DummyMessage] = []
self.is_chatter_processing: bool = False
self.processing_task: Optional[asyncio.Task] = None
self.stream_loop_task: Optional[asyncio.Task] = None
self.triggering_user_id: Optional[str] = None
def get_unread_messages(self) -> list[DummyMessage]:
return list(self.unread_messages)
def flush_cached_messages(self) -> list[DummyMessage]:
return []
def get_last_message(self) -> Optional[DummyMessage]:
return self.unread_messages[-1] if self.unread_messages else None
def get_history_messages(self, limit: int = 50) -> list[DummyMessage]:
return self.history_messages[-limit:]
class DummyStream:
def __init__(self, stream_id: str, ctx: DummyContext):
self.stream_id = stream_id
self.context = ctx
self.group_info = None # treat as private chat to accelerate
self._focus_energy = 0.5
class DummyChatManager:
def __init__(self, streams: dict[str, DummyStream]):
self._streams = streams
async def get_stream(self, stream_id: str) -> Optional[DummyStream]:
return self._streams.get(stream_id)
def get_all_streams(self) -> dict[str, DummyStream]:
return self._streams
class DummyChatterManager:
async def process_stream_context(self, stream_id: str, context: DummyContext) -> dict[str, Any]:
# Simulate some processing latency and consume one unread message
await asyncio.sleep(0.01)
if context.unread_messages:
context.unread_messages.pop(0)
return {"success": True}
class BenchStreamLoopManager(StreamLoopManager):
def __init__(self, chat_manager: DummyChatManager, max_concurrent_streams: int | None = None):
super().__init__(max_concurrent_streams=max_concurrent_streams)
self._chat_manager = chat_manager
self.chatter_manager = DummyChatterManager()
async def _get_stream_context(self, stream_id: str): # type: ignore[override]
stream = await self._chat_manager.get_stream(stream_id)
return stream.context if stream else None
async def _flush_cached_messages_to_unread(self, stream_id: str): # type: ignore[override]
ctx = await self._get_stream_context(stream_id)
return ctx.flush_cached_messages() if ctx else []
def _needs_force_dispatch_for_context(self, context, unread_count: int) -> bool: # type: ignore[override]
# force when unread exceeds threshold
return unread_count > (self.force_dispatch_unread_threshold or 20)
async def _process_stream_messages(self, stream_id: str, context): # type: ignore[override]
# delegate to chatter manager
res = await self.chatter_manager.process_stream_context(stream_id, context) # type: ignore[attr-defined]
return bool(res.get("success", False))
async def _should_skip_for_mute_group(self, stream_id: str, unread_messages: list) -> bool:
return False
async def _update_stream_energy(self, stream_id: str, context): # type: ignore[override]
# lightweight: compute based on unread size
focus = min(1.0, 0.1 + 0.02 * len(context.get_unread_messages()))
# set for compatibility
stream = await self._chat_manager.get_stream(stream_id)
if stream:
stream._focus_energy = focus
def make_streams(n_streams: int, initial_unread: int) -> dict[str, DummyStream]:
streams: dict[str, DummyStream] = {}
for i in range(n_streams):
sid = f"s{i:04d}"
ctx = DummyContext(sid, initial_unread)
streams[sid] = DummyStream(sid, ctx)
return streams
async def run_benchmark(n_streams: int, initial_unread: int, max_concurrent: Optional[int]) -> dict[str, Any]:
streams = make_streams(n_streams, initial_unread)
chat_mgr = DummyChatManager(streams)
mgr = BenchStreamLoopManager(chat_mgr, max_concurrent_streams=max_concurrent)
await mgr.start()
# start loops for all streams
start_ts = time.time()
for sid in list(streams.keys()):
await mgr.start_stream_loop(sid, force=True)
# run until all unread consumed or timeout
timeout = 5.0
end_deadline = start_ts + timeout
while time.time() < end_deadline:
remaining = sum(len(s.context.get_unread_messages()) for s in streams.values())
if remaining == 0:
break
await asyncio.sleep(0.02)
duration = time.time() - start_ts
total_cycles = mgr.stats.get("total_process_cycles", 0)
total_failures = mgr.stats.get("total_failures", 0)
remaining = sum(len(s.context.get_unread_messages()) for s in streams.values())
# stop all
await mgr.stop()
return {
"n_streams": n_streams,
"initial_unread": initial_unread,
"max_concurrent": max_concurrent,
"duration_sec": duration,
"total_cycles": total_cycles,
"total_failures": total_failures,
"remaining_unread": remaining,
"throughput_msgs_per_sec": (n_streams * initial_unread - remaining) / max(0.001, duration),
}
async def main():
cases = [
(50, 5, None), # baseline using configured default
(50, 5, 5), # constrained concurrency
(50, 5, 10), # moderate concurrency
(100, 3, 10), # scale streams
]
print("Running distribution manager benchmark...\n")
for n_streams, initial_unread, max_concurrent in cases:
res = await run_benchmark(n_streams, initial_unread, max_concurrent)
print(
f"streams={res['n_streams']} unread={res['initial_unread']} max_conc={res['max_concurrent']} | "
f"dur={res['duration_sec']:.3f}s cycles={res['total_cycles']} fail={res['total_failures']} rem={res['remaining_unread']} "+
f"thr={res['throughput_msgs_per_sec']:.1f}/s"
)
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -1,276 +0,0 @@
"""
统一记忆管理器性能基准测试
对优化前后的关键操作进行性能对比测试
"""
import asyncio
import time
class PerformanceBenchmark:
"""性能基准测试工具"""
def __init__(self):
self.results = {}
async def benchmark_query_deduplication(self):
"""测试查询去重性能"""
# 这里需要导入实际的管理器
# from src.memory_graph.unified_manager import UnifiedMemoryManager
test_cases = [
{
"name": "small_queries",
"queries": ["hello", "world"],
},
{
"name": "medium_queries",
"queries": ["q" + str(i % 5) for i in range(50)], # 10 个唯一
},
{
"name": "large_queries",
"queries": ["q" + str(i % 100) for i in range(1000)], # 100 个唯一
},
{
"name": "many_duplicates",
"queries": ["duplicate"] * 500, # 500 个重复
},
]
# 模拟旧算法
def old_build_manual_queries(queries):
deduplicated = []
seen = set()
for raw in queries:
text = (raw or "").strip()
if not text or text in seen:
continue
deduplicated.append(text)
seen.add(text)
if len(deduplicated) <= 1:
return []
manual_queries = []
decay = 0.15
for idx, text in enumerate(deduplicated):
weight = max(0.3, 1.0 - idx * decay)
manual_queries.append({"text": text, "weight": round(weight, 2)})
return manual_queries
# 新算法
def new_build_manual_queries(queries):
seen = set()
decay = 0.15
manual_queries = []
for raw in queries:
text = (raw or "").strip()
if text and text not in seen:
seen.add(text)
weight = max(0.3, 1.0 - len(manual_queries) * decay)
manual_queries.append({"text": text, "weight": round(weight, 2)})
return manual_queries if len(manual_queries) > 1 else []
print("\n" + "=" * 70)
print("查询去重性能基准测试")
print("=" * 70)
print(f"{'测试用例':<20} {'旧算法(μs)':<15} {'新算法(μs)':<15} {'提升比例':<15}")
print("-" * 70)
for test_case in test_cases:
name = test_case["name"]
queries = test_case["queries"]
# 测试旧算法
start = time.perf_counter()
for _ in range(100):
old_build_manual_queries(queries)
old_time = (time.perf_counter() - start) / 100 * 1e6
# 测试新算法
start = time.perf_counter()
for _ in range(100):
new_build_manual_queries(queries)
new_time = (time.perf_counter() - start) / 100 * 1e6
improvement = (old_time - new_time) / old_time * 100
print(
f"{name:<20} {old_time:>14.2f} {new_time:>14.2f} {improvement:>13.1f}%"
)
print()
async def benchmark_transfer_parallelization(self):
"""测试块转移并行化性能"""
print("\n" + "=" * 70)
print("块转移并行化性能基准测试")
print("=" * 70)
# 模拟旧算法(串行)
async def old_transfer_logic(num_blocks: int):
async def mock_operation():
await asyncio.sleep(0.001) # 模拟 1ms 操作
return True
results = []
for _ in range(num_blocks):
result = await mock_operation()
results.append(result)
return results
# 新算法(并行)
async def new_transfer_logic(num_blocks: int):
async def mock_operation():
await asyncio.sleep(0.001) # 模拟 1ms 操作
return True
results = await asyncio.gather(*[mock_operation() for _ in range(num_blocks)])
return results
block_counts = [1, 5, 10, 20, 50]
print(f"{'块数':<10} {'串行(ms)':<15} {'并行(ms)':<15} {'加速比':<15}")
print("-" * 70)
for num_blocks in block_counts:
# 测试串行
start = time.perf_counter()
for _ in range(10):
await old_transfer_logic(num_blocks)
serial_time = (time.perf_counter() - start) / 10 * 1000
# 测试并行
start = time.perf_counter()
for _ in range(10):
await new_transfer_logic(num_blocks)
parallel_time = (time.perf_counter() - start) / 10 * 1000
speedup = serial_time / parallel_time
print(
f"{num_blocks:<10} {serial_time:>14.2f} {parallel_time:>14.2f} {speedup:>14.2f}x"
)
print()
async def benchmark_deduplication_memory(self):
"""测试内存去重性能"""
print("\n" + "=" * 70)
print("内存去重性能基准测试")
print("=" * 70)
# 创建模拟对象
class MockMemory:
def __init__(self, mem_id: str):
self.id = mem_id
# 旧算法
def old_deduplicate(memories):
seen_ids = set()
unique_memories = []
for mem in memories:
mem_id = getattr(mem, "id", None)
if mem_id and mem_id in seen_ids:
continue
unique_memories.append(mem)
if mem_id:
seen_ids.add(mem_id)
return unique_memories
# 新算法
def new_deduplicate(memories):
seen_ids = set()
unique_memories = []
for mem in memories:
mem_id = None
if isinstance(mem, dict):
mem_id = mem.get("id")
else:
mem_id = getattr(mem, "id", None)
if mem_id and mem_id in seen_ids:
continue
unique_memories.append(mem)
if mem_id:
seen_ids.add(mem_id)
return unique_memories
test_cases = [
{
"name": "objects_100",
"data": [MockMemory(f"id_{i % 50}") for i in range(100)],
},
{
"name": "objects_1000",
"data": [MockMemory(f"id_{i % 500}") for i in range(1000)],
},
{
"name": "dicts_100",
"data": [{"id": f"id_{i % 50}"} for i in range(100)],
},
{
"name": "dicts_1000",
"data": [{"id": f"id_{i % 500}"} for i in range(1000)],
},
]
print(f"{'测试用例':<20} {'旧算法(μs)':<15} {'新算法(μs)':<15} {'提升比例':<15}")
print("-" * 70)
for test_case in test_cases:
name = test_case["name"]
data = test_case["data"]
# 测试旧算法
start = time.perf_counter()
for _ in range(100):
old_deduplicate(data)
old_time = (time.perf_counter() - start) / 100 * 1e6
# 测试新算法
start = time.perf_counter()
for _ in range(100):
new_deduplicate(data)
new_time = (time.perf_counter() - start) / 100 * 1e6
improvement = (old_time - new_time) / old_time * 100
print(
f"{name:<20} {old_time:>14.2f} {new_time:>14.2f} {improvement:>13.1f}%"
)
print()
async def run_all_benchmarks():
"""运行所有基准测试"""
benchmark = PerformanceBenchmark()
print("\n" + "" + "=" * 68 + "")
print("" + " " * 68 + "")
print("" + "统一记忆管理器优化性能基准测试".center(68) + "")
print("" + " " * 68 + "")
print("" + "=" * 68 + "")
await benchmark.benchmark_query_deduplication()
await benchmark.benchmark_transfer_parallelization()
await benchmark.benchmark_deduplication_memory()
print("\n" + "=" * 70)
print("性能基准测试完成")
print("=" * 70)
print("\n📊 关键发现:")
print(" 1. 查询去重:新算法在大规模查询时快 5-15%")
print(" 2. 块转移:并行化在 ≥5 块时有 2-10 倍加速")
print(" 3. 内存去重:新算法支持混合类型,性能相当或更优")
print("\n💡 建议:")
print(" • 定期运行此基准测试监控性能")
print(" • 在生产环境观察实际内存管理的转移块数")
print(" • 考虑对高频操作进行更深度的优化")
print()
if __name__ == "__main__":
asyncio.run(run_all_benchmarks())

View File

@@ -1,204 +0,0 @@
#!/usr/bin/env python3
"""
AWS Bedrock 客户端测试脚本
测试 BedrockClient 的基本功能
"""
import asyncio
import sys
from pathlib import Path
# 添加项目根目录到 Python 路径
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
from src.config.api_ada_configs import APIProvider, ModelInfo
from src.llm_models.model_client.bedrock_client import BedrockClient
from src.llm_models.payload_content.message import MessageBuilder
async def test_basic_conversation():
"""测试基本对话功能"""
print("=" * 60)
print("测试 1: 基本对话功能")
print("=" * 60)
# 配置 API Provider请替换为你的真实凭证
provider = APIProvider(
name="bedrock_test",
base_url="", # Bedrock 不需要
api_key="YOUR_AWS_ACCESS_KEY_ID", # 替换为你的 AWS Access Key
client_type="bedrock",
max_retry=2,
timeout=60,
retry_interval=10,
extra_params={
"aws_secret_key": "YOUR_AWS_SECRET_ACCESS_KEY", # 替换为你的 AWS Secret Key
"region": "us-east-1",
},
)
# 配置模型信息
model = ModelInfo(
model_identifier="us.anthropic.claude-3-5-sonnet-20240620-v1:0",
name="claude-3.5-sonnet-bedrock",
api_provider="bedrock_test",
price_in=3.0,
price_out=15.0,
force_stream_mode=False,
)
# 创建客户端
client = BedrockClient(provider)
# 构建消息
builder = MessageBuilder()
builder.add_user_message("你好!请用一句话介绍 AWS Bedrock。")
try:
# 发送请求
response = await client.get_response(
model_info=model, message_list=[builder.build()], max_tokens=200, temperature=0.7
)
print(f"✅ 响应内容: {response.content}")
if response.usage:
print(
f"📊 Token 使用: 输入={response.usage.prompt_tokens}, "
f"输出={response.usage.completion_tokens}, "
f"总计={response.usage.total_tokens}"
)
print("\n测试通过!✅\n")
except Exception as e:
print(f"❌ 测试失败: {e!s}")
import traceback
traceback.print_exc()
async def test_streaming():
"""测试流式输出功能"""
print("=" * 60)
print("测试 2: 流式输出功能")
print("=" * 60)
provider = APIProvider(
name="bedrock_test",
base_url="",
api_key="YOUR_AWS_ACCESS_KEY_ID",
client_type="bedrock",
max_retry=2,
timeout=60,
extra_params={
"aws_secret_key": "YOUR_AWS_SECRET_ACCESS_KEY",
"region": "us-east-1",
},
)
model = ModelInfo(
model_identifier="us.anthropic.claude-3-5-sonnet-20240620-v1:0",
name="claude-3.5-sonnet-bedrock",
api_provider="bedrock_test",
price_in=3.0,
price_out=15.0,
force_stream_mode=True, # 启用流式模式
)
client = BedrockClient(provider)
builder = MessageBuilder()
builder.add_user_message("写一个关于人工智能的三行诗。")
try:
print("🔄 流式响应中...")
response = await client.get_response(
model_info=model, message_list=[builder.build()], max_tokens=100, temperature=0.7
)
print(f"✅ 完整响应: {response.content}")
print("\n测试通过!✅\n")
except Exception as e:
print(f"❌ 测试失败: {e!s}")
async def test_multimodal():
"""测试多模态(图片输入)功能"""
print("=" * 60)
print("测试 3: 多模态功能(需要准备图片)")
print("=" * 60)
print("⏭️ 跳过(需要实际图片文件)\n")
async def test_tool_calling():
"""测试工具调用功能"""
print("=" * 60)
print("测试 4: 工具调用功能")
print("=" * 60)
from src.llm_models.payload_content.tool_option import ToolOptionBuilder, ToolParamType
provider = APIProvider(
name="bedrock_test",
base_url="",
api_key="YOUR_AWS_ACCESS_KEY_ID",
client_type="bedrock",
extra_params={
"aws_secret_key": "YOUR_AWS_SECRET_ACCESS_KEY",
"region": "us-east-1",
},
)
model = ModelInfo(
model_identifier="us.anthropic.claude-3-5-sonnet-20240620-v1:0",
name="claude-3.5-sonnet-bedrock",
api_provider="bedrock_test",
)
# 定义工具
tool_builder = ToolOptionBuilder()
tool_builder.set_name("get_weather").set_description("获取指定城市的天气信息").add_param(
name="city", param_type=ToolParamType.STRING, description="城市名称", required=True
)
tool = tool_builder.build()
client = BedrockClient(provider)
builder = MessageBuilder()
builder.add_user_message("北京今天天气怎么样?")
try:
response = await client.get_response(
model_info=model, message_list=[builder.build()], tool_options=[tool], max_tokens=200
)
if response.tool_calls:
print("✅ 模型调用了工具:")
for call in response.tool_calls:
print(f" - 工具名: {call.func_name}")
print(f" - 参数: {call.args}")
else:
print(f"⚠️ 模型没有调用工具,而是直接回复: {response.content}")
print("\n测试通过!✅\n")
except Exception as e:
print(f"❌ 测试失败: {e!s}")
async def main():
"""主测试函数"""
print("\n🚀 AWS Bedrock 客户端测试开始\n")
print("⚠️ 请确保已配置 AWS 凭证!")
print("⚠️ 修改脚本中的 'YOUR_AWS_ACCESS_KEY_ID''YOUR_AWS_SECRET_ACCESS_KEY'\n")
# 运行测试
await test_basic_conversation()
# await test_streaming()
# await test_multimodal()
# await test_tool_calling()
print("=" * 60)
print("🎉 所有测试完成!")
print("=" * 60)
if __name__ == "__main__":
asyncio.run(main())