- 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>
13 KiB
13 KiB
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()动态构造类,避免模块级导入 QtWidgetscreate_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 模板:
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
"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(向量记忆系统)
数据模型:
@dataclass
class MemoryEntry:
text: str # 记忆文本
embedding: list[float] # 向量嵌入
tags: list[str] # 标签
metadata: dict # 元数据
created_at: float # 时间戳
id: str | None # UUID
存储层:SQLite + MemoryStore
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 工作流:
- Query → Embedding
- 向量检索 Top-k
- 格式化上下文
- 注入 System Prompt
3.3 感知层 — perception/
3.3.1 keyboard_mouse_monitor.py — KeyboardMouseMonitor
职责:全局键盘/鼠标监听,使用 pynput 全局钩子。
核心组件:
ActivityDetector:滑动窗口活跃度统计KeyboardMouseMonitor:pynput listener + queueScreenCapture:mss 截图 + pytesseract OCR
活跃度计算:
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:非常活跃
全局单例:
_global_monitor: KeyboardMouseMonitor | None = None
def get_global_monitor(...) → KeyboardMouseMonitor
def stop_global_monitor() → None
4. 主入口 — main.py
4.1 EzVibeApp 初始化流程
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 测试):
def _run_dummy(self):
# 初始化后直接打印状态摘要,模拟行为触发
Qt 模式:
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。