- Fix window drag: install eventFilter on live2d_container, central, and _live2d_widget; fix super() call in dynamic class - Fix text input: remove WA_TransparentForMouseEvents from _chat_container - Force QT_QPA_PLATFORM=xcb on Linux (wayland has mouse event issues) - Add HealthTracker module, update AgentBrain with health integration - Update scheduler and memory modules - Add v5_modify documentation Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
415 lines
13 KiB
Markdown
415 lines
13 KiB
Markdown
# EzVibe v2 架构设计文档
|
||
|
||
> 本文档描述 EzVibe 系统当前(v1.x)的整体架构设计。
|
||
|
||
---
|
||
|
||
## 1. 系统概览
|
||
|
||
EzVibe 是一个运行在用户桌面上的 AI 桌宠系统,核心目标是**健康监测与提醒**(久坐提醒、喝水提醒)。
|
||
|
||
**技术栈**:
|
||
- Python 3.10+
|
||
- PySide6 / PyQt6(Qt 图形界面)
|
||
- SQLite + NumPy(本地持久化 + 向量检索)
|
||
- Ollama / OpenAI(LLM 推理)
|
||
- pynput(全局键鼠监听)
|
||
|
||
**运行模式**:
|
||
- 完整 GUI 模式(`python main.py`)
|
||
- Headless Dummy 模式(`python main.py --dummy`)
|
||
- API Server 模式(`python main.py --server`)
|
||
|
||
---
|
||
|
||
## 2. 分层架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────┐
|
||
│ 表现层 (UI) │
|
||
│ ui/pet_window.py │
|
||
│ - PetWindow / DummyPetWindow │
|
||
│ - 情绪动画渲染、右键菜单、托盘 │
|
||
└────────────────────┬────────────────────────┘
|
||
│
|
||
┌────────────────────▼────────────────────────┐
|
||
│ 智能层 (Agent) │
|
||
│ │
|
||
│ ┌─────────┐ ┌─────────┐ ┌──────────────┐ │
|
||
│ │ brain │ │ emotion │ │ scheduler │ │
|
||
│ │ .py │ │ .py │ │ .py │ │
|
||
│ │ LLM决策 │ │ 状态机 │ │ 行为调度 │ │
|
||
│ └────┬────┘ └────┬────┘ └──────┬───────┘ │
|
||
│ └───────┬──┴────────────┘ │
|
||
│ ┌──────▼──────┐ │
|
||
│ │ memory │ │
|
||
│ │ .py │ │
|
||
│ │ RAG向量记忆 │ │
|
||
│ └─────────────┘ │
|
||
└────────────────────┬────────────────────────┘
|
||
│
|
||
┌────────────────────▼────────────────────────┐
|
||
│ 感知层 (Perception) │
|
||
│ perception/keyboard_mouse_monitor.py │
|
||
│ - 键鼠事件监听、活跃度检测 │
|
||
│ - ScreenCapture(截图+OCR) │
|
||
└─────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 模块职责
|
||
|
||
### 3.1 表现层 — `ui/pet_window.py`
|
||
|
||
**职责**:
|
||
- 渲染桌宠窗口(无边框、置顶、透明背景)
|
||
- 情绪状态驱动动画(idle/happy/focused/annoyed/sleepy)
|
||
- 提醒通知弹窗(健康喝水/伸展提醒)
|
||
- 拖拽移动、右键菜单、最小化到托盘
|
||
|
||
**关键类**:
|
||
- `PetWindow`:PySide6/PyQt6 实现,延迟构造避免导入冲突
|
||
- `DummyPetWindow`:headless 测试替代,不依赖 Qt
|
||
|
||
**图像资源**:
|
||
```
|
||
assets/pet/
|
||
├── idle/
|
||
├── happy/
|
||
├── focused/
|
||
├── annoyed/
|
||
└── sleepy/
|
||
```
|
||
每个情绪目录下存放 16 帧 PNG 动画(180×180px)。无图像时回退到 emoji 显示。
|
||
|
||
**设计亮点**:
|
||
- `_make_pet_window_class()` 使用 `type()` 动态构造类,避免模块级导入 QtWidgets
|
||
- `create_pet_window()` 工厂函数,支持 `force_dummy` 强制降级
|
||
|
||
---
|
||
|
||
### 3.2 智能层 — `agent/`
|
||
|
||
#### 3.2.1 `brain.py` — AgentBrain(LLM 推理引擎)
|
||
|
||
**职责**:
|
||
- 整合记忆上下文 + 情绪状态 + 用户输入
|
||
- 调用 LLM(Ollama/OpenAI/Dummy)生成回复
|
||
- 决策是否触发主动行为
|
||
- 管理会话历史(短期上下文窗口)
|
||
|
||
**LLM 后端适配器(策略模式)**:
|
||
```
|
||
LLMBackend (基类)
|
||
├── OllamaBackend — http://localhost:11434
|
||
├── OpenAIBackend — OpenAI API / 兼容第三方
|
||
└── DummyLLMBackend — 测试用固定回复
|
||
```
|
||
|
||
**核心方法**:
|
||
- `think(user_input)`:接收用户输入,返回 `{"text", "emotion_state", "action"}`
|
||
- `decide_action()`:异步决策是否触发主动行为
|
||
- `_parse_action()`:从 LLM 回复中解析 `[ACTION: type:description]` 标签
|
||
|
||
**System Prompt 模板**:
|
||
```python
|
||
DEFAULT_SYSTEM_PROMPT = """你是「EzVibe」,一个运行在用户桌面上的 AI 桌宠。
|
||
【当前情绪状态】{emotion_display}
|
||
【性格设定】友善、活泼,偶尔会犯懒或闹小脾气...
|
||
【主动行为能力】可以提醒喝水/休息,或做出可爱小动作。
|
||
请用自然对话风格回复。如果想触发主动行为,在回复末尾加上:
|
||
[ACTION: <action_type>:<description>]
|
||
"""
|
||
```
|
||
|
||
#### 3.2.2 `emotion.py` — EmotionEngine(情绪状态机)
|
||
|
||
**状态集合**:`S = {idle, happy, focused, annoyed, sleepy}`
|
||
|
||
**核心机制**:
|
||
- 5×5 状态转移矩阵 P
|
||
- 上下文调制因子(事件驱动增益)
|
||
- 蒙特卡洛随机采样
|
||
- 最小驻留时间限制(防止抖动)
|
||
|
||
**状态转移矩阵**(行=当前状态,列=目标状态):
|
||
```
|
||
→ idle happy focused annoyed sleepy
|
||
idle │ 0.4 0.2 0.2 0.1 0.1
|
||
happy │ 0.2 0.5 0.1 0.1 0.1
|
||
focused │ 0.1 0.2 0.5 0.1 0.1
|
||
annoyed │ 0.1 0.1 0.1 0.6 0.1
|
||
sleepy │ 0.2 0.1 0.1 0.1 0.5
|
||
```
|
||
|
||
**事件增益**:`ContextBoost.EVENT_BOOSTS`
|
||
```python
|
||
"long_work_session" → sleepy +2.0, focused +0.8
|
||
"user_focused" → focused +2.5
|
||
"reminder_ignored" → annoyed +3.0
|
||
"user_praise" → happy +3.0
|
||
"user_healthy_action" → happy +2.0
|
||
```
|
||
|
||
**关键方法**:
|
||
- `update(event)`:根据事件触发状态转移
|
||
- `tick()`:时钟推进,触发 `time_passes` 事件
|
||
- `force_state(state)`:强制设置状态(测试用)
|
||
|
||
#### 3.2.3 `scheduler.py` — BehaviorScheduler(行为调度器)
|
||
|
||
**行为优先级定义**:
|
||
- `PRIORITY_HIGHEST = 0`:健康/高危提醒(打断所有)
|
||
- `PRIORITY_USER_INPUT = 1`:用户主动输入响应
|
||
- `PRIORITY_PROACTIVE = 2`:系统主动闲聊/行为
|
||
- `PRIORITY_LLM_TRIGGER = 3`:LLM 自触发
|
||
|
||
**内置行为**:
|
||
| 行为 | 优先级 | 冷却 | 说明 |
|
||
|------|--------|------|------|
|
||
| `remind_water` | 0 | 1800s | 喝水提醒 |
|
||
| `remind_stretch` | 0 | 3600s | 久坐伸展提醒 |
|
||
| `idle_nudge` | 2 | 300s | 空闲闲聊触发 |
|
||
| `happy_nudge` | 2 | 600s | 开心时主动搭话 |
|
||
|
||
**核心逻辑**:
|
||
- 活跃度限制:极度专注(activity < 0.15)时禁止 P2/P3 打扰
|
||
- 情绪调制:focused 状态概率×0.2,sleepy 状态概率×0.3
|
||
- 提醒队列:`Reminder` 延迟执行任务
|
||
|
||
#### 3.2.4 `memory.py` — VectorMemory(向量记忆系统)
|
||
|
||
**数据模型**:
|
||
```python
|
||
@dataclass
|
||
class MemoryEntry:
|
||
text: str # 记忆文本
|
||
embedding: list[float] # 向量嵌入
|
||
tags: list[str] # 标签
|
||
metadata: dict # 元数据
|
||
created_at: float # 时间戳
|
||
id: str | None # UUID
|
||
```
|
||
|
||
**存储层**:`SQLite` + `MemoryStore`
|
||
```sql
|
||
CREATE TABLE memories (
|
||
id TEXT PRIMARY KEY,
|
||
text TEXT NOT NULL,
|
||
embedding BLOB NOT NULL, -- numpy 序列化
|
||
tags TEXT NOT NULL DEFAULT '[]',
|
||
metadata TEXT NOT NULL DEFAULT '{}',
|
||
created_at REAL NOT NULL
|
||
);
|
||
```
|
||
|
||
**嵌入适配器**:
|
||
```
|
||
make_embedder(backend)
|
||
├── OllamaEmbedder — nomic-embed-text
|
||
├── OpenAIEmbedder — text-embedding-3-small
|
||
└── DummyEmbedder — TF-IDF(开发/测试用)
|
||
```
|
||
|
||
**RAG 工作流**:
|
||
1. Query → Embedding
|
||
2. 向量检索 Top-k
|
||
3. 格式化上下文
|
||
4. 注入 System Prompt
|
||
|
||
---
|
||
|
||
### 3.3 感知层 — `perception/`
|
||
|
||
#### 3.3.1 `keyboard_mouse_monitor.py` — KeyboardMouseMonitor
|
||
|
||
**职责**:全局键盘/鼠标监听,使用 pynput 全局钩子。
|
||
|
||
**核心组件**:
|
||
- `ActivityDetector`:滑动窗口活跃度统计
|
||
- `KeyboardMouseMonitor`:pynput listener + queue
|
||
- `ScreenCapture`:mss 截图 + pytesseract OCR
|
||
|
||
**活跃度计算**:
|
||
```python
|
||
baseline = window_seconds / 60.0 * 30.0 # 30 events/min = normal
|
||
activity = min(raw / baseline, 1.0)
|
||
```
|
||
|
||
**阈值定义**:
|
||
- activity < 0.15:极度专注(可能深度工作/游戏)
|
||
- activity < 0.3:比较专注
|
||
- 0.3 ≤ activity < 0.7:适度活跃
|
||
- activity ≥ 0.7:非常活跃
|
||
|
||
**全局单例**:
|
||
```python
|
||
_global_monitor: KeyboardMouseMonitor | None = None
|
||
|
||
def get_global_monitor(...) → KeyboardMouseMonitor
|
||
def stop_global_monitor() → None
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 主入口 — `main.py`
|
||
|
||
### 4.1 EzVibeApp 初始化流程
|
||
|
||
```python
|
||
def _init_components(self):
|
||
1. EmotionEngine()
|
||
2. VectorMemory(embedder_backend="dummy") # 避免依赖 Ollama
|
||
3. AgentBrain(llm_backend=...)
|
||
4. KeyboardMouseMonitor + ScreenCapture
|
||
5. BehaviorScheduler(emotion_engine, activity_detector)
|
||
6. PetWindow(延迟到 QApplication 之后)
|
||
```
|
||
|
||
### 4.2 运行模式
|
||
|
||
**Dummy 模式**(headless 测试):
|
||
```python
|
||
def _run_dummy(self):
|
||
# 初始化后直接打印状态摘要,模拟行为触发
|
||
```
|
||
|
||
**Qt 模式**:
|
||
```python
|
||
def _run_qt(self):
|
||
app = QtWidgets.QApplication(sys.argv)
|
||
self._window = create_pet_window(...) # QApplication 之后才能创建
|
||
# QTimer 20ms 驱动 asyncio 轮询(临时方案,待改进)
|
||
timer.timeout.connect(lambda: self._loop.run_until_complete(asyncio.sleep(0)))
|
||
app.exec()
|
||
```
|
||
|
||
---
|
||
|
||
## 5. API 服务器 — `api/server.py`
|
||
|
||
FastAPI 服务器,提供 REST/WebSocket 接口:
|
||
|
||
| 端点 | 方法 | 说明 |
|
||
|------|------|------|
|
||
| `/status` | GET | 返回系统状态 |
|
||
| `/emotion` | GET/POST | 获取/设置情绪状态 |
|
||
| `/remind` | POST | 添加提醒 |
|
||
| `/memory/search` | POST | RAG 检索 |
|
||
| `/memory/add` | POST | 添加记忆 |
|
||
| `/ws` | WebSocket | 实时事件流 |
|
||
|
||
---
|
||
|
||
## 6. 数据流
|
||
|
||
### 6.1 用户交互流程
|
||
|
||
```
|
||
用户输入文本
|
||
↓
|
||
AgentBrain.think()
|
||
↓
|
||
┌─ 注入情绪状态到 System Prompt
|
||
├─ RAG 检索相关记忆(VectorMemory.search)
|
||
├─ 追加对话历史
|
||
└─ 调用 LLM(Ollama/OpenAI/Dummy)
|
||
↓
|
||
解析 [ACTION: ...] 标签
|
||
↓
|
||
返回 {"text": "...", "action": {...}, "emotion_state": "..."}
|
||
↓
|
||
PetWindow 显示回复 + 动画
|
||
```
|
||
|
||
### 6.2 健康监测流程
|
||
|
||
```
|
||
定时调度(每 10s)
|
||
↓
|
||
BehaviorScheduler.check_and_trigger()
|
||
↓
|
||
┌─ 冷却时间检查
|
||
├─ 活跃度限制检查(极度专注时禁止打扰)
|
||
├─ 概率触发(结合情绪调制)
|
||
└─ 执行 action_fn()
|
||
↓
|
||
show_reminder() → 气泡通知
|
||
↓
|
||
记录到记忆(VectorMemory.add)
|
||
```
|
||
|
||
### 6.3 感知数据流
|
||
|
||
```
|
||
pynput 全局钩子(独立线程)
|
||
↓
|
||
queue.Queue 传递事件
|
||
↓
|
||
_worker_loop 消费队列
|
||
↓
|
||
ActivityDetector.record() 记录到滑动窗口
|
||
↓
|
||
get_activity() → 0.0~1.0 活跃度
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 文件结构
|
||
|
||
```
|
||
EzVibe/
|
||
├── main.py # 主入口,EzVibeApp
|
||
├── requirements.txt # 依赖列表
|
||
├── setup_project.py # 项目初始化脚本
|
||
│
|
||
├── agent/
|
||
│ ├── __init__.py
|
||
│ ├── brain.py # AgentBrain + LLM后端
|
||
│ ├── emotion.py # EmotionEngine 状态机
|
||
│ ├── scheduler.py # BehaviorScheduler + ActivityDetector
|
||
│ ├── memory.py # VectorMemory + MemoryStore + VectorEngine
|
||
│ └── test_*.py # 单元测试
|
||
│
|
||
├── perception/
|
||
│ ├── __init__.py
|
||
│ ├── keyboard_mouse_monitor.py # KeyboardMouseMonitor + ActivityDetector
|
||
│ └── test_perception.py
|
||
│
|
||
├── ui/
|
||
│ ├── __init__.py
|
||
│ ├── pet_window.py # PetWindow + DummyPetWindow
|
||
│ └── test_pet_window.py
|
||
│
|
||
├── api/
|
||
│ ├── __init__.py
|
||
│ └── server.py # FastAPI 服务器
|
||
│
|
||
├── data/ # SQLite 数据库目录
|
||
├── assets/ # 桌宠图像资源
|
||
│
|
||
└── docs/
|
||
├── v2_design/
|
||
│ ├── ARCHITECTURE.md # 本文档
|
||
│ ├── ARCHITECTURE_REVIEW.md # 架构评审
|
||
│ ├── EVOLUTION_ROADMAP.md # 演进路线
|
||
│ ├── HEALTH_TRACKER_SPEC.md # HealthTracker 规格
|
||
│ └── DESIGN_DECISIONS.md # 设计决策
|
||
└── *.md # 其他文档
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 已知限制与改进方向
|
||
|
||
| 问题 | 现状 | 改进方向 |
|
||
|------|------|----------|
|
||
| Qt + asyncio 融合 | QTimer 轮询(不稳定) | 引入 qasync |
|
||
| OCR 性能消耗 | mss + pytesseract 每分钟执行 | 轻量级焦点窗口提取 |
|
||
| LLM 延迟 | 同步等待 5 秒 | 解耦动作与文案,Fallback 缓存 |
|
||
| annoyed 状态屏蔽 P0 | 完全禁止打扰 | 柔性降级(安静模式) |
|
||
| 健康数据缺乏结构化 | RAG 模糊检索 | HealthTracker 结构化数据库 |
|
||
| 奖惩机制无闭环 | 单向提醒 | 隐式确认 + 正向反馈 |
|
||
|
||
详见 `ARCHITECTURE_REVIEW.md`。 |