Compare commits
7 Commits
cf500a47de
...
bbe16046c8
| Author | SHA1 | Date | |
|---|---|---|---|
|
bbe16046c8
|
|||
|
8a4719e6ac
|
|||
|
|
dd0dd94e76 | ||
|
|
3207aa31b1 | ||
|
|
6de5cd9902 | ||
|
|
1ad9c932bb | ||
|
|
8f2a6606eb |
@@ -1,102 +0,0 @@
|
|||||||
# AWS Bedrock 集成完成 ✅
|
|
||||||
|
|
||||||
## 快速开始
|
|
||||||
|
|
||||||
### 1. 安装依赖
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip install aioboto3 botocore
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 配置凭证
|
|
||||||
|
|
||||||
在 `config/model_config.toml` 添加:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[[api_providers]]
|
|
||||||
name = "bedrock_us_east"
|
|
||||||
base_url = ""
|
|
||||||
api_key = "YOUR_AWS_ACCESS_KEY_ID"
|
|
||||||
client_type = "bedrock"
|
|
||||||
timeout = 60
|
|
||||||
|
|
||||||
[api_providers.extra_params]
|
|
||||||
aws_secret_key = "YOUR_AWS_SECRET_ACCESS_KEY"
|
|
||||||
region = "us-east-1"
|
|
||||||
|
|
||||||
[[models]]
|
|
||||||
model_identifier = "us.anthropic.claude-3-5-sonnet-20240620-v1:0"
|
|
||||||
name = "claude-3.5-sonnet-bedrock"
|
|
||||||
api_provider = "bedrock_us_east"
|
|
||||||
price_in = 3.0
|
|
||||||
price_out = 15.0
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 使用示例
|
|
||||||
|
|
||||||
```python
|
|
||||||
from src.llm_models import get_llm_client
|
|
||||||
from src.llm_models.payload_content.message import MessageBuilder
|
|
||||||
|
|
||||||
client = get_llm_client("bedrock_us_east")
|
|
||||||
builder = MessageBuilder()
|
|
||||||
builder.add_user_message("你好,AWS Bedrock!")
|
|
||||||
|
|
||||||
response = await client.get_response(
|
|
||||||
model_info=get_model_info("claude-3.5-sonnet-bedrock"),
|
|
||||||
message_list=[builder.build()],
|
|
||||||
max_tokens=1024
|
|
||||||
)
|
|
||||||
|
|
||||||
print(response.content)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 新增文件
|
|
||||||
|
|
||||||
- ✅ `src/llm_models/model_client/bedrock_client.py` - Bedrock 客户端实现
|
|
||||||
- ✅ `docs/integrations/Bedrock.md` - 完整文档
|
|
||||||
- ✅ `scripts/test_bedrock_client.py` - 测试脚本
|
|
||||||
|
|
||||||
## 修改文件
|
|
||||||
|
|
||||||
- ✅ `src/llm_models/model_client/__init__.py` - 添加 Bedrock 导入
|
|
||||||
- ✅ `src/config/api_ada_configs.py` - 添加 `bedrock` client_type
|
|
||||||
- ✅ `template/model_config_template.toml` - 添加 Bedrock 配置示例(注释形式)
|
|
||||||
- ✅ `requirements.txt` - 添加 aioboto3 和 botocore 依赖
|
|
||||||
- ✅ `pyproject.toml` - 添加 aioboto3 和 botocore 依赖
|
|
||||||
|
|
||||||
## 支持功能
|
|
||||||
|
|
||||||
- ✅ **对话生成**:支持多轮对话
|
|
||||||
- ✅ **流式输出**:支持流式响应
|
|
||||||
- ✅ **工具调用**:完整支持 Tool Use
|
|
||||||
- ✅ **多模态**:支持图片输入
|
|
||||||
- ✅ **文本嵌入**:支持 Titan Embeddings
|
|
||||||
- ✅ **跨区推理**:支持 Inference Profile
|
|
||||||
|
|
||||||
## 支持模型
|
|
||||||
|
|
||||||
- Amazon Nova 系列 (Micro/Lite/Pro)
|
|
||||||
- Anthropic Claude 3/3.5 系列
|
|
||||||
- Meta Llama 2/3 系列
|
|
||||||
- Mistral AI 系列
|
|
||||||
- Cohere Command 系列
|
|
||||||
- AI21 Jamba 系列
|
|
||||||
- Stability AI SDXL
|
|
||||||
|
|
||||||
## 测试
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 修改凭证后运行测试
|
|
||||||
python scripts/test_bedrock_client.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## 文档
|
|
||||||
|
|
||||||
详细文档:`docs/integrations/Bedrock.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**集成状态**: ✅ 生产就绪
|
|
||||||
**集成时间**: 2025年12月6日
|
|
||||||
|
|
||||||
@@ -1,471 +0,0 @@
|
|||||||
# Bot 内存分析工具使用指南
|
|
||||||
|
|
||||||
一个统一的内存诊断工具,提供进程监控、对象分析和数据可视化功能。
|
|
||||||
|
|
||||||
## 🚀 快速开始
|
|
||||||
|
|
||||||
> **提示**: 建议使用虚拟环境运行脚本(`.\.venv\Scripts\python.exe`)
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 查看帮助
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --help
|
|
||||||
|
|
||||||
# 进程监控模式(最简单)
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --monitor
|
|
||||||
|
|
||||||
# 对象分析模式(深度分析)
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --objects --output memory_data.txt
|
|
||||||
|
|
||||||
# 可视化模式(生成图表)
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --visualize --input memory_data.txt.jsonl
|
|
||||||
```
|
|
||||||
|
|
||||||
**或者使用简短命令**(如果你的系统 `python` 已指向虚拟环境):
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
python scripts/memory_profiler.py --monitor
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 依赖安装
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 基础功能(进程监控)
|
|
||||||
pip install psutil
|
|
||||||
|
|
||||||
# 对象分析功能
|
|
||||||
pip install pympler
|
|
||||||
|
|
||||||
# 可视化功能
|
|
||||||
pip install matplotlib
|
|
||||||
|
|
||||||
# 一次性安装全部
|
|
||||||
pip install psutil pympler matplotlib
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 三种模式详解
|
|
||||||
|
|
||||||
### 1. 进程监控模式 (--monitor)
|
|
||||||
|
|
||||||
**用途**: 从外部监控 bot 进程的总内存、子进程情况
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- ✅ 自动启动 bot.py(使用虚拟环境)
|
|
||||||
- ✅ 实时显示进程内存(RSS、VMS)
|
|
||||||
- ✅ 列出所有子进程及其内存占用
|
|
||||||
- ✅ 显示 bot 输出日志
|
|
||||||
- ✅ 自动保存监控历史
|
|
||||||
|
|
||||||
**使用示例**:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 基础用法
|
|
||||||
python scripts/memory_profiler.py --monitor
|
|
||||||
|
|
||||||
# 自定义监控间隔(10秒)
|
|
||||||
python scripts/memory_profiler.py --monitor --interval 10
|
|
||||||
|
|
||||||
# 简写
|
|
||||||
python scripts/memory_profiler.py -m -i 5
|
|
||||||
```
|
|
||||||
|
|
||||||
**输出示例**:
|
|
||||||
|
|
||||||
```
|
|
||||||
================================================================================
|
|
||||||
检查点 #1 - 14:23:15
|
|
||||||
Bot 进程 (PID: 12345)
|
|
||||||
RSS: 45.82 MB
|
|
||||||
VMS: 12.34 MB
|
|
||||||
占比: 0.25%
|
|
||||||
子进程: 2 个
|
|
||||||
子进程内存: 723.64 MB
|
|
||||||
总内存: 769.46 MB
|
|
||||||
|
|
||||||
📋 子进程详情:
|
|
||||||
[1] PID 12346: python.exe - 520.15 MB
|
|
||||||
命令: python.exe -m chromadb.server ...
|
|
||||||
[2] PID 12347: python.exe - 203.49 MB
|
|
||||||
命令: python.exe -m uvicorn ...
|
|
||||||
================================================================================
|
|
||||||
```
|
|
||||||
|
|
||||||
**保存位置**: `data/memory_diagnostics/process_monitor_<timestamp>_pid<PID>.txt`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 2. 对象分析模式 (--objects)
|
|
||||||
|
|
||||||
**用途**: 在 bot 进程内部统计所有 Python 对象的内存占用
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- ✅ 统计所有对象类型(dict、list、str、AsyncOpenAI 等)
|
|
||||||
- ✅ **按模块统计内存占用(新增)** - 显示哪个模块占用最多内存
|
|
||||||
- ✅ 包含所有线程的对象
|
|
||||||
- ✅ 显示对象变化(diff)
|
|
||||||
- ✅ 线程信息和 GC 统计
|
|
||||||
- ✅ 保存 JSONL 数据用于可视化
|
|
||||||
|
|
||||||
**使用示例**:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 基础用法(推荐指定输出文件)
|
|
||||||
python scripts/memory_profiler.py --objects --output memory_data.txt
|
|
||||||
|
|
||||||
# 自定义参数
|
|
||||||
python scripts/memory_profiler.py --objects \
|
|
||||||
--interval 10 \
|
|
||||||
--output memory_data.txt \
|
|
||||||
--object-limit 30
|
|
||||||
|
|
||||||
# 简写
|
|
||||||
python scripts/memory_profiler.py -o -i 10 --output data.txt -l 30
|
|
||||||
```
|
|
||||||
|
|
||||||
**输出示例**:
|
|
||||||
|
|
||||||
```
|
|
||||||
================================================================================
|
|
||||||
🔍 对象级内存分析 #1 - 14:25:30
|
|
||||||
================================================================================
|
|
||||||
|
|
||||||
📦 对象统计 (前 20 个类型):
|
|
||||||
|
|
||||||
类型 数量 总大小
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
<class 'dict'> 125,843 45.23 MB
|
|
||||||
<class 'str'> 234,567 23.45 MB
|
|
||||||
<class 'list'> 56,789 12.34 MB
|
|
||||||
<class 'tuple'> 89,012 8.90 MB
|
|
||||||
<class 'openai.resources.chat.completions'> 12 5.67 MB
|
|
||||||
...
|
|
||||||
|
|
||||||
📚 模块内存占用 (前 20 个模块):
|
|
||||||
|
|
||||||
模块名 对象数 总内存
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
builtins 169,144 26.20 MB
|
|
||||||
src 12,345 5.67 MB
|
|
||||||
openai 3,456 2.34 MB
|
|
||||||
chromadb 2,345 1.89 MB
|
|
||||||
...
|
|
||||||
|
|
||||||
总模块数: 85
|
|
||||||
|
|
||||||
🧵 线程信息 (8 个):
|
|
||||||
[1] ✓ MainThread
|
|
||||||
[2] ✓ AsyncOpenAIClient (守护)
|
|
||||||
[3] ✓ ChromaDBWorker (守护)
|
|
||||||
...
|
|
||||||
|
|
||||||
🗑️ 垃圾回收:
|
|
||||||
代 0: 1,234 次
|
|
||||||
代 1: 56 次
|
|
||||||
代 2: 3 次
|
|
||||||
追踪对象: 456,789
|
|
||||||
|
|
||||||
📊 总对象数: 567,890
|
|
||||||
================================================================================
|
|
||||||
```
|
|
||||||
|
|
||||||
**每 3 次迭代会显示对象变化**:
|
|
||||||
|
|
||||||
```
|
|
||||||
📈 对象变化分析:
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
types | # objects | total size
|
|
||||||
==================== | =========== | ============
|
|
||||||
<class 'dict'> | +1234 | +1.23 MB
|
|
||||||
<class 'str'> | +567 | +0.56 MB
|
|
||||||
...
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
```
|
|
||||||
|
|
||||||
**保存位置**:
|
|
||||||
- 文本: `<output>.txt`
|
|
||||||
- 结构化数据: `<output>.txt.jsonl`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 3. 可视化模式 (--visualize)
|
|
||||||
|
|
||||||
**用途**: 将对象分析模式生成的 JSONL 数据绘制成图表
|
|
||||||
|
|
||||||
**特点**:
|
|
||||||
- ✅ 显示对象类型随时间的内存变化
|
|
||||||
- ✅ 自动选择内存占用最高的 N 个类型
|
|
||||||
- ✅ 生成高清 PNG 图表
|
|
||||||
|
|
||||||
**使用示例**:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 基础用法
|
|
||||||
python scripts/memory_profiler.py --visualize \
|
|
||||||
--input memory_data.txt.jsonl
|
|
||||||
|
|
||||||
# 自定义参数
|
|
||||||
python scripts/memory_profiler.py --visualize \
|
|
||||||
--input memory_data.txt.jsonl \
|
|
||||||
--top 15 \
|
|
||||||
--plot-output my_plot.png
|
|
||||||
|
|
||||||
# 简写
|
|
||||||
python scripts/memory_profiler.py -v -i data.txt.jsonl -t 15
|
|
||||||
```
|
|
||||||
|
|
||||||
**输出**: PNG 图像,展示前 N 个对象类型的内存占用随时间的变化曲线
|
|
||||||
|
|
||||||
**保存位置**: 默认 `memory_analysis_plot.png`,可通过 `--plot-output` 指定
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 使用场景
|
|
||||||
|
|
||||||
| 场景 | 推荐模式 | 命令 |
|
|
||||||
|------|----------|------|
|
|
||||||
| 快速查看总内存 | `--monitor` | `python scripts/memory_profiler.py -m` |
|
|
||||||
| 查看子进程占用 | `--monitor` | `python scripts/memory_profiler.py -m` |
|
|
||||||
| 分析具体对象占用 | `--objects` | `python scripts/memory_profiler.py -o --output data.txt` |
|
|
||||||
| 追踪内存泄漏 | `--objects` | `python scripts/memory_profiler.py -o --output data.txt` |
|
|
||||||
| 可视化分析趋势 | `--visualize` | `python scripts/memory_profiler.py -v -i data.txt.jsonl` |
|
|
||||||
|
|
||||||
## 📊 完整工作流程
|
|
||||||
|
|
||||||
### 场景 1: 快速诊断内存问题
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 1. 运行进程监控(查看总体情况)
|
|
||||||
python scripts/memory_profiler.py --monitor --interval 5
|
|
||||||
|
|
||||||
# 观察输出,如果发现内存异常,进入场景 2
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 2: 深度分析对象占用
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 1. 启动对象分析(保存数据)
|
|
||||||
python scripts/memory_profiler.py --objects \
|
|
||||||
--interval 10 \
|
|
||||||
--output data/memory_diagnostics/analysis_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt
|
|
||||||
|
|
||||||
# 2. 运行一段时间(建议至少 5-10 分钟),按 Ctrl+C 停止
|
|
||||||
|
|
||||||
# 3. 生成可视化图表
|
|
||||||
python scripts/memory_profiler.py --visualize \
|
|
||||||
--input data/memory_diagnostics/analysis_<timestamp>.txt.jsonl \
|
|
||||||
--top 15 \
|
|
||||||
--plot-output data/memory_diagnostics/plot_<timestamp>.png
|
|
||||||
|
|
||||||
# 4. 查看图表,分析哪些对象类型随时间增长
|
|
||||||
```
|
|
||||||
|
|
||||||
### 场景 3: 持续监控
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 在后台运行对象分析(Windows)
|
|
||||||
Start-Process powershell -ArgumentList "-Command", "python scripts/memory_profiler.py -o -i 30 --output logs/memory_continuous.txt" -WindowStyle Minimized
|
|
||||||
|
|
||||||
# 定期查看 JSONL 并生成图表
|
|
||||||
python scripts/memory_profiler.py -v -i logs/memory_continuous.txt.jsonl -t 20
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🎯 参数参考
|
|
||||||
|
|
||||||
### 通用参数
|
|
||||||
|
|
||||||
| 参数 | 简写 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `--interval` | `-i` | 10 | 监控间隔(秒) |
|
|
||||||
|
|
||||||
### 对象分析模式参数
|
|
||||||
|
|
||||||
| 参数 | 简写 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `--output` | - | 无 | 输出文件路径(强烈推荐) |
|
|
||||||
| `--object-limit` | `-l` | 20 | 显示的对象类型数量 |
|
|
||||||
|
|
||||||
### 可视化模式参数
|
|
||||||
|
|
||||||
| 参数 | 简写 | 默认值 | 说明 |
|
|
||||||
|------|------|--------|------|
|
|
||||||
| `--input` | - | **必需** | 输入 JSONL 文件路径 |
|
|
||||||
| `--top` | `-t` | 10 | 展示前 N 个对象类型 |
|
|
||||||
| `--plot-output` | - | `memory_analysis_plot.png` | 输出图表路径 |
|
|
||||||
|
|
||||||
## ⚠️ 注意事项
|
|
||||||
|
|
||||||
### 性能影响
|
|
||||||
|
|
||||||
| 模式 | 性能影响 | 说明 |
|
|
||||||
|------|----------|------|
|
|
||||||
| `--monitor` | < 1% | 几乎无影响,适合生产环境 |
|
|
||||||
| `--objects` | 5-15% | 有一定影响,建议在测试环境使用 |
|
|
||||||
| `--visualize` | 0% | 离线分析,无影响 |
|
|
||||||
|
|
||||||
### 常见问题
|
|
||||||
|
|
||||||
**Q: 对象分析模式报错 "pympler 未安装"?**
|
|
||||||
```powershell
|
|
||||||
pip install pympler
|
|
||||||
```
|
|
||||||
|
|
||||||
**Q: 可视化模式报错 "matplotlib 未安装"?**
|
|
||||||
```powershell
|
|
||||||
pip install matplotlib
|
|
||||||
```
|
|
||||||
|
|
||||||
**Q: 对象分析模式提示 "bot.py 未找到 main_async() 或 main() 函数"?**
|
|
||||||
|
|
||||||
这是正常的。如果你的 bot.py 的主逻辑在 `if __name__ == "__main__":` 中,监控线程仍会在后台运行。你可以:
|
|
||||||
- 保持 bot 运行,监控会持续统计
|
|
||||||
- 或者在 bot.py 中添加一个 `main_async()` 或 `main()` 函数
|
|
||||||
|
|
||||||
**Q: 进程监控模式看不到子进程?**
|
|
||||||
|
|
||||||
确保 bot.py 已经启动了子进程(例如 ChromaDB)。如果刚启动就查看,可能还没有创建子进程。
|
|
||||||
|
|
||||||
**Q: JSONL 文件在哪里?**
|
|
||||||
|
|
||||||
当你使用 `--output <file>` 时,会生成:
|
|
||||||
- `<file>`: 人类可读的文本
|
|
||||||
- `<file>.jsonl`: 结构化数据(用于可视化)
|
|
||||||
|
|
||||||
## 📁 输出文件说明
|
|
||||||
|
|
||||||
### 进程监控输出
|
|
||||||
|
|
||||||
**位置**: `data/memory_diagnostics/process_monitor_<timestamp>_pid<PID>.txt`
|
|
||||||
|
|
||||||
**内容**: 每次检查点的进程内存信息
|
|
||||||
|
|
||||||
### 对象分析输出
|
|
||||||
|
|
||||||
**文本文件**: `<output>`
|
|
||||||
- 人类可读格式
|
|
||||||
- 包含每次迭代的对象统计
|
|
||||||
|
|
||||||
**JSONL 文件**: `<output>.jsonl`
|
|
||||||
- 每行一个 JSON 对象
|
|
||||||
- 包含: timestamp, iteration, total_objects, summary, threads, gc_stats
|
|
||||||
- 用于可视化分析
|
|
||||||
|
|
||||||
### 可视化输出
|
|
||||||
|
|
||||||
**PNG 图像**: 默认 `memory_analysis_plot.png`
|
|
||||||
- 折线图,展示对象类型随时间的内存变化
|
|
||||||
- 高清 150 DPI
|
|
||||||
|
|
||||||
## 🔍 诊断技巧
|
|
||||||
|
|
||||||
### 1. 识别内存泄漏
|
|
||||||
|
|
||||||
使用对象分析模式运行较长时间,观察:
|
|
||||||
- 某个对象类型的数量或大小持续增长
|
|
||||||
- 对象变化 diff 中始终为正数
|
|
||||||
|
|
||||||
### 2. 定位大内存对象
|
|
||||||
|
|
||||||
**查看对象统计**:
|
|
||||||
- 如果 `<class 'dict'>` 占用很大,可能是缓存未清理
|
|
||||||
- 如果看到特定类(如 `AsyncOpenAI`),检查该类的实例数
|
|
||||||
|
|
||||||
**查看模块统计**(推荐):
|
|
||||||
- 查看 📚 模块内存占用部分
|
|
||||||
- 如果 `src` 模块占用很大,说明你的代码中有大量对象
|
|
||||||
- 如果 `openai`、`chromadb` 等第三方模块占用大,可能是这些库的使用问题
|
|
||||||
- 对比不同时间点,看哪个模块的内存持续增长
|
|
||||||
|
|
||||||
### 3. 分析子进程占用
|
|
||||||
|
|
||||||
使用进程监控模式:
|
|
||||||
- 查看子进程详情中的命令行
|
|
||||||
- 识别哪个子进程占用大量内存(如 ChromaDB)
|
|
||||||
|
|
||||||
### 4. 对比不同时间点
|
|
||||||
|
|
||||||
使用可视化模式:
|
|
||||||
- 生成图表后,观察哪些对象类型的曲线持续上升
|
|
||||||
- 对比不同功能运行时的内存变化
|
|
||||||
|
|
||||||
## 🎓 高级用法
|
|
||||||
|
|
||||||
### 长期监控脚本
|
|
||||||
|
|
||||||
创建 `monitor_continuously.ps1`:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 持续监控脚本
|
|
||||||
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
|
||||||
$logPath = "logs/memory_analysis_$timestamp.txt"
|
|
||||||
|
|
||||||
Write-Host "开始持续监控,数据保存到: $logPath"
|
|
||||||
Write-Host "按 Ctrl+C 停止监控"
|
|
||||||
|
|
||||||
python scripts/memory_profiler.py --objects --interval 30 --output $logPath
|
|
||||||
```
|
|
||||||
|
|
||||||
### 自动生成日报
|
|
||||||
|
|
||||||
创建 `generate_daily_report.ps1`:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 生成内存分析日报
|
|
||||||
$date = Get-Date -Format "yyyyMMdd"
|
|
||||||
$jsonlFiles = Get-ChildItem "logs" -Filter "*$date*.jsonl"
|
|
||||||
|
|
||||||
foreach ($file in $jsonlFiles) {
|
|
||||||
$outputPlot = $file.FullName -replace ".jsonl", "_plot.png"
|
|
||||||
python scripts/memory_profiler.py --visualize --input $file.FullName --plot-output $outputPlot --top 20
|
|
||||||
Write-Host "生成图表: $outputPlot"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 扩展阅读
|
|
||||||
|
|
||||||
- **Python 内存管理**: https://docs.python.org/3/c-api/memory.html
|
|
||||||
- **psutil 文档**: https://psutil.readthedocs.io/
|
|
||||||
- **Pympler 文档**: https://pympler.readthedocs.io/
|
|
||||||
- **Matplotlib 文档**: https://matplotlib.org/
|
|
||||||
|
|
||||||
## 🆘 获取帮助
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 查看完整帮助信息
|
|
||||||
python scripts/memory_profiler.py --help
|
|
||||||
|
|
||||||
# 查看特定模式示例
|
|
||||||
python scripts/memory_profiler.py --help | Select-String "示例"
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**快速开始提醒**:
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# 使用虚拟环境(推荐)
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --monitor
|
|
||||||
|
|
||||||
# 或者使用系统 Python
|
|
||||||
python scripts/memory_profiler.py --monitor
|
|
||||||
|
|
||||||
# 深度分析
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --objects --output memory.txt
|
|
||||||
|
|
||||||
# 可视化
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py --visualize --input memory.txt.jsonl
|
|
||||||
```
|
|
||||||
|
|
||||||
### 💡 虚拟环境说明
|
|
||||||
|
|
||||||
**Windows**:
|
|
||||||
```powershell
|
|
||||||
.\.venv\Scripts\python.exe scripts/memory_profiler.py [选项]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Linux/Mac**:
|
|
||||||
```bash
|
|
||||||
./.venv/bin/python scripts/memory_profiler.py [选项]
|
|
||||||
```
|
|
||||||
|
|
||||||
脚本会自动检测并使用项目虚拟环境来启动 bot(进程监控模式),对象分析模式会自动添加项目根目录到 Python 路径。
|
|
||||||
|
|
||||||
🎉 现在你已经掌握了完整的内存分析工具!
|
|
||||||
@@ -121,7 +121,7 @@ async def conversation_loop(
|
|||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.info(f" [生成器] stream={stream_id[:8]}, 被取消")
|
logger.info(f" [生成器] stream={stream_id[:8]}, 被取消")
|
||||||
break
|
break
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e:
|
||||||
logger.error(f" [生成器] stream={stream_id[:8]}, 出错: {e}")
|
logger.error(f" [生成器] stream={stream_id[:8]}, 出错: {e}")
|
||||||
await asyncio.sleep(5.0)
|
await asyncio.sleep(5.0)
|
||||||
|
|
||||||
@@ -151,10 +151,10 @@ async def run_chat_stream(
|
|||||||
# 创建生成器
|
# 创建生成器
|
||||||
tick_generator = conversation_loop(
|
tick_generator = conversation_loop(
|
||||||
stream_id=stream_id,
|
stream_id=stream_id,
|
||||||
get_context_func=manager._get_stream_context, # noqa: SLF001
|
get_context_func=manager._get_stream_context,
|
||||||
calculate_interval_func=manager._calculate_interval, # noqa: SLF001
|
calculate_interval_func=manager._calculate_interval,
|
||||||
flush_cache_func=manager._flush_cached_messages_to_unread, # noqa: SLF001
|
flush_cache_func=manager._flush_cached_messages_to_unread,
|
||||||
check_force_dispatch_func=manager._needs_force_dispatch_for_context, # noqa: SLF001
|
check_force_dispatch_func=manager._needs_force_dispatch_for_context,
|
||||||
is_running_func=lambda: manager.is_running,
|
is_running_func=lambda: manager.is_running,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -162,13 +162,13 @@ async def run_chat_stream(
|
|||||||
async for tick in tick_generator:
|
async for tick in tick_generator:
|
||||||
try:
|
try:
|
||||||
# 获取上下文
|
# 获取上下文
|
||||||
context = await manager._get_stream_context(stream_id) # noqa: SLF001
|
context = await manager._get_stream_context(stream_id)
|
||||||
if not context:
|
if not context:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 并发保护:检查是否正在处理
|
# 并发保护:检查是否正在处理
|
||||||
if context.is_chatter_processing:
|
if context.is_chatter_processing:
|
||||||
if manager._recover_stale_chatter_state(stream_id, context): # noqa: SLF001
|
if manager._recover_stale_chatter_state(stream_id, context):
|
||||||
logger.warning(f" [驱动器] stream={stream_id[:8]}, 处理标志残留已修复")
|
logger.warning(f" [驱动器] stream={stream_id[:8]}, 处理标志残留已修复")
|
||||||
else:
|
else:
|
||||||
logger.debug(f" [驱动器] stream={stream_id[:8]}, Chatter正在处理,跳过此Tick")
|
logger.debug(f" [驱动器] stream={stream_id[:8]}, Chatter正在处理,跳过此Tick")
|
||||||
@@ -182,7 +182,7 @@ async def run_chat_stream(
|
|||||||
|
|
||||||
# 更新能量值
|
# 更新能量值
|
||||||
try:
|
try:
|
||||||
await manager._update_stream_energy(stream_id, context) # noqa: SLF001
|
await manager._update_stream_energy(stream_id, context)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug(f"更新能量失败: {e}")
|
logger.debug(f"更新能量失败: {e}")
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ async def run_chat_stream(
|
|||||||
try:
|
try:
|
||||||
async with manager._processing_semaphore:
|
async with manager._processing_semaphore:
|
||||||
success = await asyncio.wait_for(
|
success = await asyncio.wait_for(
|
||||||
manager._process_stream_messages(stream_id, context), # noqa: SLF001
|
manager._process_stream_messages(stream_id, context),
|
||||||
global_config.chat.thinking_timeout,
|
global_config.chat.thinking_timeout,
|
||||||
)
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
@@ -209,7 +209,7 @@ async def run_chat_stream(
|
|||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
raise
|
raise
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e:
|
||||||
logger.error(f" [驱动器] stream={stream_id[:8]}, 处理Tick时出错: {e}")
|
logger.error(f" [驱动器] stream={stream_id[:8]}, 处理Tick时出错: {e}")
|
||||||
manager.stats["total_failures"] += 1
|
manager.stats["total_failures"] += 1
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ async def run_chat_stream(
|
|||||||
if context and context.stream_loop_task:
|
if context and context.stream_loop_task:
|
||||||
context.stream_loop_task = None
|
context.stream_loop_task = None
|
||||||
logger.debug(f" [驱动器] stream={stream_id[:8]}, 清理任务记录")
|
logger.debug(f" [驱动器] stream={stream_id[:8]}, 清理任务记录")
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e:
|
||||||
logger.debug(f"清理任务记录失败: {e}")
|
logger.debug(f"清理任务记录失败: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -110,10 +110,10 @@ class MessageManager:
|
|||||||
if not (context.stream_loop_task and not context.stream_loop_task.done()):
|
if not (context.stream_loop_task and not context.stream_loop_task.done()):
|
||||||
# 异步启动驱动器任务;避免在高并发下阻塞消息入队
|
# 异步启动驱动器任务;避免在高并发下阻塞消息入队
|
||||||
await stream_loop_manager.start_stream_loop(stream_id)
|
await stream_loop_manager.start_stream_loop(stream_id)
|
||||||
|
|
||||||
# 检查并处理消息打断
|
# 检查并处理消息打断
|
||||||
await self._check_and_handle_interruption(chat_stream, message)
|
await self._check_and_handle_interruption(chat_stream, message)
|
||||||
|
|
||||||
# 入队消息
|
# 入队消息
|
||||||
await chat_stream.context.add_message(message)
|
await chat_stream.context.add_message(message)
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
import sys
|
||||||
import typing
|
|
||||||
import types
|
import types
|
||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, get_args, get_origin
|
from typing import Any, get_args, get_origin
|
||||||
@@ -30,8 +30,8 @@ from src.config.official_configs import (
|
|||||||
ExperimentalConfig,
|
ExperimentalConfig,
|
||||||
ExpressionConfig,
|
ExpressionConfig,
|
||||||
InnerConfig,
|
InnerConfig,
|
||||||
LogConfig,
|
|
||||||
KokoroFlowChatterConfig,
|
KokoroFlowChatterConfig,
|
||||||
|
LogConfig,
|
||||||
LPMMKnowledgeConfig,
|
LPMMKnowledgeConfig,
|
||||||
MemoryConfig,
|
MemoryConfig,
|
||||||
MessageBusConfig,
|
MessageBusConfig,
|
||||||
@@ -515,7 +515,7 @@ class Config(ValidatedConfigBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def MMC_VERSION(self) -> str: # noqa: N802
|
def MMC_VERSION(self) -> str:
|
||||||
return MMC_VERSION
|
return MMC_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,13 @@ class LongTermMemoryManager:
|
|||||||
self._similar_memory_cache: dict[str, list[Memory]] = {}
|
self._similar_memory_cache: dict[str, list[Memory]] = {}
|
||||||
self._cache_max_size = 100
|
self._cache_max_size = 100
|
||||||
|
|
||||||
|
# 错误/重试统计与配置
|
||||||
|
self._max_process_retries = 2
|
||||||
|
self._retry_backoff = 0.5
|
||||||
|
self._total_processed = 0
|
||||||
|
self._failed_single_memory_count = 0
|
||||||
|
self._retry_attempts = 0
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"长期记忆管理器已创建 (batch_size={batch_size}, "
|
f"长期记忆管理器已创建 (batch_size={batch_size}, "
|
||||||
f"search_top_k={search_top_k}, decay_factor={long_term_decay_factor:.2f})"
|
f"search_top_k={search_top_k}, decay_factor={long_term_decay_factor:.2f})"
|
||||||
@@ -202,6 +209,10 @@ class LongTermMemoryManager:
|
|||||||
else:
|
else:
|
||||||
result["failed_count"] += 1
|
result["failed_count"] += 1
|
||||||
|
|
||||||
|
# 更新全局计数
|
||||||
|
self._total_processed += result["processed_count"]
|
||||||
|
self._failed_single_memory_count += result["failed_count"]
|
||||||
|
|
||||||
# 处理完批次后,批量生成embeddings
|
# 处理完批次后,批量生成embeddings
|
||||||
await self._flush_pending_embeddings()
|
await self._flush_pending_embeddings()
|
||||||
|
|
||||||
@@ -217,26 +228,45 @@ class LongTermMemoryManager:
|
|||||||
Returns:
|
Returns:
|
||||||
处理结果或None(如果失败)
|
处理结果或None(如果失败)
|
||||||
"""
|
"""
|
||||||
try:
|
# 增加重试机制以应对 LLM/执行的临时失败
|
||||||
# 步骤1: 在长期记忆中检索相似记忆
|
attempt = 0
|
||||||
similar_memories = await self._search_similar_long_term_memories(stm)
|
last_exc: Exception | None = None
|
||||||
|
while attempt <= self._max_process_retries:
|
||||||
|
try:
|
||||||
|
# 步骤1: 在长期记忆中检索相似记忆
|
||||||
|
similar_memories = await self._search_similar_long_term_memories(stm)
|
||||||
|
|
||||||
# 步骤2: LLM 决策如何更新图结构
|
# 步骤2: LLM 决策如何更新图结构
|
||||||
operations = await self._decide_graph_operations(stm, similar_memories)
|
operations = await self._decide_graph_operations(stm, similar_memories)
|
||||||
|
|
||||||
# 步骤3: 执行图操作
|
# 步骤3: 执行图操作
|
||||||
success = await self._execute_graph_operations(operations, stm)
|
success = await self._execute_graph_operations(operations, stm)
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"operations": [op.operation_type for op in operations]
|
"operations": [op.operation_type for op in operations]
|
||||||
}
|
}
|
||||||
return None
|
|
||||||
|
|
||||||
except Exception as e:
|
# 如果执行返回 False,视为一次失败,准备重试
|
||||||
logger.error(f"处理短期记忆 {stm.id} 失败: {e}")
|
last_exc = RuntimeError("_execute_graph_operations 返回 False")
|
||||||
return None
|
raise last_exc
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
last_exc = e
|
||||||
|
attempt += 1
|
||||||
|
if attempt <= self._max_process_retries:
|
||||||
|
self._retry_attempts += 1
|
||||||
|
backoff = self._retry_backoff * attempt
|
||||||
|
logger.warning(
|
||||||
|
f"处理短期记忆 {stm.id} 时发生可恢复错误,重试 {attempt}/{self._max_process_retries},等待 {backoff}s: {e}"
|
||||||
|
)
|
||||||
|
await asyncio.sleep(backoff)
|
||||||
|
continue
|
||||||
|
# 超过重试次数,记录失败并返回 None
|
||||||
|
logger.error(f"处理短期记忆 {stm.id} 最终失败: {last_exc}")
|
||||||
|
self._failed_single_memory_count += 1
|
||||||
|
return None
|
||||||
|
|
||||||
async def _search_similar_long_term_memories(
|
async def _search_similar_long_term_memories(
|
||||||
self, stm: ShortTermMemory
|
self, stm: ShortTermMemory
|
||||||
|
|||||||
@@ -648,15 +648,15 @@ class ShortTermMemoryManager:
|
|||||||
else:
|
else:
|
||||||
low_importance_memories.append(mem)
|
low_importance_memories.append(mem)
|
||||||
|
|
||||||
# 如果低重要性记忆数量超过了上限(说明积压严重)
|
# 如果总体记忆数量超过了上限,优先清理低重要性最早创建的记忆
|
||||||
# 我们需要清理掉一部分,而不是转移它们
|
if len(self.memories) > self.max_memories:
|
||||||
if len(low_importance_memories) > self.max_memories:
|
|
||||||
# 目标保留数量(降至上限的 90%)
|
# 目标保留数量(降至上限的 90%)
|
||||||
target_keep_count = int(self.max_memories * 0.9)
|
target_keep_count = int(self.max_memories * 0.9)
|
||||||
num_to_remove = len(low_importance_memories) - target_keep_count
|
# 需要删除的数量(从当前总数降到 target_keep_count)
|
||||||
|
num_to_remove = len(self.memories) - target_keep_count
|
||||||
|
|
||||||
if num_to_remove > 0:
|
if num_to_remove > 0 and low_importance_memories:
|
||||||
# 按创建时间排序,删除最早的
|
# 按创建时间排序,删除最早的低重要性记忆
|
||||||
low_importance_memories.sort(key=lambda x: x.created_at)
|
low_importance_memories.sort(key=lambda x: x.created_at)
|
||||||
to_remove = low_importance_memories[:num_to_remove]
|
to_remove = low_importance_memories[:num_to_remove]
|
||||||
|
|
||||||
@@ -664,7 +664,7 @@ class ShortTermMemoryManager:
|
|||||||
remove_ids = {mem.id for mem in to_remove}
|
remove_ids = {mem.id for mem in to_remove}
|
||||||
self.memories = [mem for mem in self.memories if mem.id not in remove_ids]
|
self.memories = [mem for mem in self.memories if mem.id not in remove_ids]
|
||||||
for mem_id in remove_ids:
|
for mem_id in remove_ids:
|
||||||
del self._memory_id_index[mem_id]
|
self._memory_id_index.pop(mem_id, None)
|
||||||
self._similarity_cache.pop(mem_id, None)
|
self._similarity_cache.pop(mem_id, None)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -675,6 +675,16 @@ class ShortTermMemoryManager:
|
|||||||
# 触发保存
|
# 触发保存
|
||||||
asyncio.create_task(self._save_to_disk())
|
asyncio.create_task(self._save_to_disk())
|
||||||
|
|
||||||
|
# 优先返回高重要性候选
|
||||||
|
if candidates:
|
||||||
|
return candidates
|
||||||
|
|
||||||
|
# 如果没有高重要性候选但总体超过上限,返回按创建时间最早的低重要性记忆作为后备转移候选
|
||||||
|
if len(self.memories) > self.max_memories:
|
||||||
|
needed = len(self.memories) - self.max_memories + 1
|
||||||
|
low_importance_memories.sort(key=lambda x: x.created_at)
|
||||||
|
return low_importance_memories[:needed]
|
||||||
|
|
||||||
return candidates
|
return candidates
|
||||||
|
|
||||||
async def clear_transferred_memories(self, memory_ids: list[str]) -> None:
|
async def clear_transferred_memories(self, memory_ids: list[str]) -> None:
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ class PokeAction(BaseAction):
|
|||||||
|
|
||||||
# === 基本信息(必须填写)===
|
# === 基本信息(必须填写)===
|
||||||
action_name = "poke_user"
|
action_name = "poke_user"
|
||||||
action_description = "可以让你戳其他用户,为互动增添一份小小的乐趣。"
|
action_description = "戳一戳其他用户。这是一个需要谨慎使用的互动方式,默认只戳一次。群聊中应当克制使用,私聊中可以适当主动。"
|
||||||
activation_type = ActionActivationType.ALWAYS
|
activation_type = ActionActivationType.ALWAYS
|
||||||
parallel_action = True
|
parallel_action = True
|
||||||
|
|
||||||
@@ -148,17 +148,48 @@ class PokeAction(BaseAction):
|
|||||||
action_parameters: ClassVar[dict] = {
|
action_parameters: ClassVar[dict] = {
|
||||||
"user_name": "需要戳一戳的用户的名字 (可选)",
|
"user_name": "需要戳一戳的用户的名字 (可选)",
|
||||||
"user_id": "需要戳一戳的用户的ID (可选,优先级更高)",
|
"user_id": "需要戳一戳的用户的ID (可选,优先级更高)",
|
||||||
"times": "需要戳一戳的次数 (默认为 1)",
|
"times": "需要戳一戳的次数 (默认为 1,最多3次)",
|
||||||
}
|
}
|
||||||
action_require: ClassVar[list] = ["当需要戳某个用户时使用", "当你想提醒特定用户时使用"]
|
action_require: ClassVar[list] = [
|
||||||
|
"用户明确要求戳某人时必须使用",
|
||||||
|
"私聊场景:可以在适当的互动时机主动使用(如回应戳一戳、俏皮互动等)",
|
||||||
|
"群聊场景:应当非常克制,仅在用户明确要求或有充分理由时才使用",
|
||||||
|
]
|
||||||
llm_judge_prompt = """
|
llm_judge_prompt = """
|
||||||
判定是否需要使用戳一戳动作的条件:
|
判定是否需要使用戳一戳动作的条件:
|
||||||
1. **互动时机**: 这是一个有趣的互动方式,可以在想提醒某人,或者单纯想开个玩笑时使用。
|
|
||||||
2. **用户请求**: 当用户明确要求使用戳一戳时。
|
**必须遵守的严格规则:**
|
||||||
3. **上下文需求**: 当上下文明确需要你戳一个或多个人时。
|
1. **用户明确要求**: 当用户明确说"戳XX"、"戳一下XX"等直接指令时,必须使用。
|
||||||
4. **频率与情绪**: 如果最近已经戳过,或者感觉对方情绪不高,请避免使用,不要打扰到别人哦。
|
|
||||||
|
2. **群聊场景(非常克制)**:
|
||||||
请根据上述规则,回答“是”或“否”。
|
- 群聊中应当非常谨慎,避免在公共场合频繁打扰他人
|
||||||
|
- 仅在以下情况考虑使用:用户明确要求、需要紧急提醒某人、或有特别充分的互动理由
|
||||||
|
- 如果最近已经在群里戳过任何人,必须回答"否"
|
||||||
|
- 群聊中不要随意主动使用,除非有明确必要性
|
||||||
|
|
||||||
|
3. **私聊场景(可以主动)**:
|
||||||
|
- 私聊中可以更加主动和俏皮
|
||||||
|
- 在以下情况可以使用:回应对方的戳一戳、轻松愉快的互动氛围、想要增添趣味时
|
||||||
|
- 但如果最近已经戳过对方,应避免频繁使用
|
||||||
|
|
||||||
|
4. **频率限制(重要)**:
|
||||||
|
- 如果最近已经戳过同一个人,必须回答"否"
|
||||||
|
- 默认只戳一次,不要多次戳别人(除非用户明确要求多次)
|
||||||
|
- 注意对方的情绪反应,如果对方看起来不高兴或不想被打扰,必须回答"否"
|
||||||
|
|
||||||
|
5. **禁止情况**:
|
||||||
|
- 对方情绪低落、生气、不耐烦时,严禁使用
|
||||||
|
- 严肃的对话场景中,严禁使用
|
||||||
|
- 刚刚戳过的情况下,严禁再次使用
|
||||||
|
|
||||||
|
**判断逻辑**:
|
||||||
|
- 首先判断是群聊还是私聊
|
||||||
|
- 群聊:除非用户明确要求或有特殊必要性,否则回答"否"
|
||||||
|
- 私聊:可以在合适的互动氛围中主动使用,但要注意频率
|
||||||
|
- 检查是否最近已经戳过,如果是则回答"否"
|
||||||
|
- 评估对方情绪和对话氛围是否适合
|
||||||
|
|
||||||
|
请严格根据上述规则,仅回答"是"或"否"。
|
||||||
"""
|
"""
|
||||||
associated_types: ClassVar[list[str]] = ["text"]
|
associated_types: ClassVar[list[str]] = ["text"]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user