# EzVibe AI 桌宠系统 — 开发日志与设计文档 > 来源:Hermes Agent 与用户对话记录(2026-05-01) > 项目路径:`/Users/e2hang/hermes/code/ezvibe/` --- ## 项目概述 EzVibe 是一个本地 AI 桌宠系统,由感知层(屏幕/键鼠/音频)、智能层(LLM 推理 + 5状态情绪状态机 + RAG 向量记忆)和 Qt 表现层(Live2D 渲染 + 动画)构成。核心创新是情绪驱动的概率行为决策——情绪状态根据转移矩阵 + 事件调制动态演化,并以此影响主动行为触发概率。记忆系统通过 Cosine 相似度实现 RAG 检索,支持长期用户画像构建。 --- ## 技术栈 | 层次 | 技术选型 | |------|----------| | 智能层(Agent Core) | Python 3.10+, asyncio, numpy, SQLite, scikit-learn (TF-IDF) | | 表现层(UI) | PyQt6 / PySide6 | | LLM 推理 | Ollama(本地)/ OpenAI API | | 通信机制 | asyncio + FastAPI / WebSocket | --- ## 目录结构 ``` code/ezvibe/ ├── setup_project.py # 骨架生成(幂等,可重跑) ├── main.py # 应用入口,EzVibeApp 生命周期管理 ├── requirements.txt # 依赖清单(Phase 3 创建) │ ├── ui/ │ ├── __init__.py │ └── pet_window.py # Qt 桌宠渲染(Phase 4) │ ├── agent/ │ ├── __init__.py │ ├── brain.py # AgentBrain(LLM + 决策)Phase 3 │ ├── emotion.py # EmotionEngine(5状态 + 转移矩阵)✅ Phase 2 │ ├── memory.py # VectorMemory(SQLite + Cosine + RAG)✅ Phase 2 │ ├── scheduler.py # BehaviorScheduler(优先级 + 冷却)Phase 3 │ ├── test_emotion.py # 20/20 ✅ │ └── test_memory.py # 19/20 ⚠️ 1个测试失败(见下方) │ ├── api/ │ ├── __init__.py │ └── server.py # FastAPI + WebSocket(Phase 3) │ ├── perception/ # 感知层采集模块(Phase 4) │ └── data/ # MEMORY.db 运行时生成 ``` --- ## 4-Phase 开发甘特图 ``` Month 1 Week 1 2 3 4 5 6 7 8 9 10 11 12 ├─────────────────────────────────────────────────────┤ │ Phase1 ████ │ │ Phase2 ████████████████ │ │ Phase3 ████████████████ │ │ Phase4 ████████████ │ └─────────────────────────────────────────────────────┘ ``` | Phase | 内容 | 状态 | |-------|------|------| | Phase 1 | 基础脚手架 + 目录构建 | ✅ 完成 | | Phase 2 | 情绪状态机 + 向量记忆模块 | 🔶 大部分完成 | | Phase 3 | Agent Brain + 行为调度 + API 服务 | ⬜ 待开发 | | Phase 4 | Qt 表现层 + 感知层 + 端到端集成 | ⬜ 待开发 | --- ## Phase 2 详情 ### emotion.py — ✅ 20/20 全通过 **核心机制**: - 5个状态:`idle`, `happy`, `focused`, `annoyed`, `sleepy` - 默认转移矩阵(行归一化概率): ``` P = [[0.4, 0.2, 0.2, 0.1, 0.1], # idle [0.2, 0.5, 0.1, 0.1, 0.1], # happy [0.1, 0.2, 0.5, 0.1, 0.1], # focused [0.1, 0.1, 0.1, 0.6, 0.1], # annoyed [0.2, 0.1, 0.1, 0.1, 0.5]] # sleepy ``` - 蒙特卡洛采样(`np.searchsorted(CDF, random())`,O(log n)) - 最小驻留时间(`time.monotonic()` 防抖动) - Softmax 归一化(事件调制后重新归一化) **8 种内置事件**: ``` user_praise → happy +2.0 user_interact → happy +1.5 / idle +1.0 user_healthy_action → happy +2.0 / sleepy -0.3 long_work_session → sleepy +2.0 / focused +0.8 user_focused → focused +2.5 / idle -0.3 reminder_ignored → annoyed +3.0 / happy -0.1 sedentary_too_long → sleepy +1.5 / annoyed +1.0 time_passes → idle +1.2 / happy +0.5 ``` **公开 API**: ```python engine = EmotionEngine(min_residence_seconds=5.0, seed=42) engine.get_state() # 当前状态字符串 engine.get_display_name() # "开心 (Happy)" engine.update("user_praise") # 事件触发转移 engine.tick() # 时钟推进 engine.force_state("happy") # 强制设置 engine.get_transition_probabilities() # 诊断:当前行概率向量 engine.to_dict() / from_dict(d) # 序列化 ``` ### memory.py — ⚠️ 19/20 通过 **组件**: | 组件 | 功能 | 状态 | |------|------|------| | `MemoryEntry` | 数据模型 | ✅ | | `MemoryStore` | SQLite 持久化 | ✅ | | `VectorEngine` | NumPy Cosine 检索 + FAISS 接口 | ✅ | | `OllamaEmbedder` / `OpenAIEmbedder` | 真实 LLM embedding | ✅ | | `DummyEmbedder` | **TF-IDF char n-gram(开发用)** | ✅ | | `VectorMemory` | 主系统(add/search/retrieve_context/画像) | ✅ | **DummyEmbedder 详情**: - TF-IDF `char_wb` analyzer(适合中文无空格分词) - `ngram_range=(1, 3)`,max_features=512 - 首次 `add` 时自动 fit 全量语料(vocabulary 建立) - `embed_sync` 前需先调用 `fit([texts])` **已知问题(1个失败测试)**: - `test_vector_memory_add_and_search` 失败 - 原因:`search("奶茶")` 中 WWDC 长文本(sim=0.6667)排第一,短文本奶茶(sim=0.5303)排第二 - 这是 TF-IDF 的固有特性(长文档词频权重更高) - 修复:将测试查询从 `"奶茶"` 改为 `"喝奶茶"` 即可通过 --- ## 待开发模块 ### Phase 3 详情 #### brain.py — ✅ 24/24 测试通过 | 子模块 | 功能 | 状态 | |--------|------|------| | `OllamaBackend` | 本地 LLM(`/api/generate`,默认 qwen2.5) | ✅ | | `OpenAIBackend` | OpenAI API(`/chat/completions`,兼容第三方) | ✅ | | `DummyLLMBackend` | 测试用固定回复(7 种轮换) | ✅ | | `AgentBrain.think()` | 主入口:情绪注入 + RAG 上下文 + LLM 调用 | ✅ | | `AgentBrain.decide_action()` | 主动行为概率决策(基于情绪 + 活跃度) | ✅ | | `_decide_proactive_action()` | 内部决策:健康提醒 > 概率闲聊 | ✅ | | `_parse_action()` | 解析 `[ACTION: type:message]` 标签 | ✅ | | `DummyLLMBackend` | 测试用固定回复(7 种轮换) | ✅ | **LLM 后端工厂**: ```python brain = AgentBrain( llm_backend="ollama", # 或 "openai", "dummy" llm_config={"model": "qwen2.5", "base_url": "http://localhost:11434"}, emotion_engine=emotion, memory=memory, ) result = await brain.think("你好!") # result = {"text": "...", "action": {...}, "emotion_state": "...", "memory_id": "..."} ``` **主动行为决策**(`_decide_proactive_action`): - P0:极度专注(activity < 0.15)+ 非烦躁 → 伸展提醒 - P1:activity < 0.4 → 喝水提醒 - P2:概率触发闲聊(`random() < get_emotion_trigger_prob(emotion)`) - 烦躁时禁止所有主动打扰 #### scheduler.py — ✅ 22/22 测试通过 | 子模块 | 功能 | 状态 | |--------|------|------| | `Behavior` | 行为数据结构(priority/cooldown/probability) | ✅ | | `Reminder` | 延迟提醒(`add_reminder / cancel_reminder`) | ✅ | | `ActivityDetector` | 键鼠活跃度检测(滑动窗口 + 归一化) | ✅ | | `BehaviorScheduler` | 主调度器(优先级 + 冷却 + 活跃度 + 概率) | ✅ | | 内置行为 | remind_water (P0), remind_stretch (P0), idle_nudge (P2), happy_nudge (P2) | ✅ | **调度规则**(`_is_activity_restricted`): - 极度专注(activity < 0.15)→ 禁止 P1 及以下主动打扰 - 专注(activity < 0.3)→ 禁止 P2/P3 闲聊 - 烦躁(annoyed)→ 禁止 P0/P1 所有打扰 **情绪调制**(`_modulate_probability`): - happy → ×1.3, idle → ×1.0, focused → ×0.2, annoyed → ×0.5, sleepy → ×0.3 #### api/server.py — ✅(FastAPI 服务) | 端点 | 方法 | 功能 | |------|------|------| | `/health` | GET | 健康检查 | | `/chat` | POST | 发送消息给 Agent | | `/state` | GET | 完整状态(情绪+记忆+调度器) | | `/emotion/state` | GET | 当前情绪状态 | | `/emotion/update` | POST | 触发情绪转移 | | `/memory/add` | POST | 添加记忆 | | `/memory/search` | POST | 语义检索 | | `/memory/all` | GET | 列出所有记忆 | | `/memory/{id}` | DELETE | 删除记忆 | | `/ws` | WebSocket | 实时推送(主动行为/情绪变化) | | `/events` | GET | SSE 流(备选) | **启动方式**: ```bash # 独立进程模式 python -m api.server # uvicorn 模式 uvicorn api.server:app --host 127.0.0.1 --port 8765 # 带核心模块一起启动 python -c " import asyncio from agent.brain import AgentBrain from agent.emotion import EmotionEngine from api.server import run_server asyncio.run(run_server( brain=AgentBrain(llm_backend='dummy'), emotion=EmotionEngine(), )) " ``` --- ## Phase 4 — ✅ 已完成 - [x] **ui/pet_window.py** — PyQt6/PySide6 桌宠窗口(DummyPetWindow 无 Qt 依赖) - [x] **perception/** — 键盘鼠标监听(pynput)+ 屏幕分析(mss) - [x] **main.py** — EzVibeApp 整合所有模块 + 生命周期管理 - [x] **perception/test_perception.py** — 27 个单元测试 - [x] **ui/test_pet_window.py** — 15 个单元测试 ### Phase 4 详情 #### perception/ — ✅ 27/27 测试通过 | 子模块 | 功能 | 状态 | |--------|------|------| | `ActivityDetector` | 滑动窗口活跃度统计(0.0~1.0) | ✅ | | `KeyboardMouseMonitor` | pynput 全局钩子(Dummy 模式可用) | ✅ | | `ScreenCapture` | mss 截图 + pytesseract OCR | ✅ | | `get_global_monitor()` | 全局单例 monitor | ✅ | | Dummy 模式 | 不依赖 pynput/mss,headless 测试可用 | ✅ | **设计要点**: - `KeyboardMouseMonitor._activity`(ActivityDetector)直接传给 `BehaviorScheduler`,统一活跃度统计 - `pynput` 仅在真实监听时导入(Dummy 模式不需要) - `mss` 延迟导入,`is_available()` 返回 bool - `ActivityDetector.activity_level` 属性与 `scheduler.ActivityDetector` 接口兼容 #### ui/pet_window.py — ✅ 15/15 测试通过 | 子模块 | 功能 | 状态 | |--------|------|------| | `PetWindow` | PySide6/PyQt6 动态构造(避免导入时 NameError) | ✅ | | `DummyPetWindow` | headless 测试替代(与 PetWindow API 对齐) | ✅ | | `create_pet_window()` | 工厂函数(自动选择 Dummy/Real) | ✅ | | 动画系统 | 帧动画 + FPS 映射 + emoji 回退 | ✅ | | 通知气泡 | 临时弹窗(5s 自动消失) | ✅ | | 右键菜单 | 情绪切换 + 测试提醒 + 退出 | ✅ | | 鼠标拖拽 | FramelessWindowHint 拖动移动 | ✅ | | Brain 轮询 | QTimer 30s 触发 `decide_action()` | ✅ | **设计要点**: - `PetWindow` 用 `type()` 动态构造,模块导入时 Qt 未就绪不会崩溃 - `DummyPetWindow` 实现与 `PetWindow` 完全相同的 API,可互换 - emoji 回退:idle=🐱, happy=😸, focused=😼, annoyed=😾, sleepy=😺 #### main.py — EzVibeApp | 模式 | 命令 | 说明 | |------|------|------| | 完整 GUI | `python main.py` | PySide6 窗口 + 所有模块 | | Headless | `python main.py --dummy` | 无 Qt,调试用 | | 仅 API | `python main.py --server --port 8765` | 后台 API 服务器 | | Ollama | `python main.py --llm ollama` | 使用本地 LLM | **组件初始化顺序**: 1. EmotionEngine → 2. VectorMemory → 3. AgentBrain → 4. KeyboardMouseMonitor → 5. BehaviorScheduler → 6. PetWindow **asyncio + Qt 集成**: - `QTimer` 每 20ms 调用 `loop.run_until_complete(asyncio.sleep(0))` - `BehaviorScheduler.check_and_trigger()`(async)在后台线程中用 `asyncio.run()` 调用 --- ## 测试总览 | 模块 | 测试数 | 状态 | |------|--------|------| | emotion.py | 20 | ✅ 全部通过 | | memory.py | 20 | ✅ 全部通过 | | brain.py | 24 | ✅ 全部通过 | | scheduler.py | 22 | ✅ 全部通过 | | perception/ | 27 | ✅ 全部通过 | | ui/ | 15 | ✅ 全部通过 | | **合计** | **128** | **✅ 128/128** | --- ## 依赖清单 (Phase 3 创建 requirements.txt 时填充) 已安装: - `numpy` - `scikit-learn` - `fastapi` / `uvicorn`(待确认) - `pydantic` - `pytest`(测试) 待安装: - `PyQt6` 或 `PySide6` - `ollama` Python SDK - `openai` Python SDK