Files
EzVibe/docs/开发日志与设计文档.md
e2hang 2a844e83a8 Initial commit: EzVibe AI 桌宠系统
- EmotionEngine: 5状态马尔可夫情绪机 + 蒙特卡洛转移
- VectorMemory: TF-IDF向量记忆 + SQLite持久化 + RAG检索
- AgentBrain: Ollama/OpenAI/Dummy三后端LLM
- BehaviorScheduler: 优先级/冷却/活跃度调度
- FastAPI服务器 + WebSocket实时推送
- perception: 键鼠监控 + 屏幕截图
- ui/pet_window: PySide6桌宠窗口 + 像素动画
- assets/pet: 5情绪各2帧像素艺术资源
2026-05-01 23:26:43 +08:00

12 KiB
Raw Permalink Blame History

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         # AgentBrainLLM + 决策Phase 3
│   ├── emotion.py       # EmotionEngine5状态 + 转移矩阵)✅ Phase 2
│   ├── memory.py         # VectorMemorySQLite + 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 + WebSocketPhase 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

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 后端工厂

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+ 非烦躁 → 伸展提醒
  • P1activity < 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 流(备选)

启动方式

# 独立进程模式
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 — 已完成

  • ui/pet_window.py — PyQt6/PySide6 桌宠窗口DummyPetWindow 无 Qt 依赖)
  • perception/ — 键盘鼠标监听pynput+ 屏幕分析mss
  • main.py — EzVibeApp 整合所有模块 + 生命周期管理
  • perception/test_perception.py — 27 个单元测试
  • 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/mssheadless 测试可用

设计要点

  • KeyboardMouseMonitor._activityActivityDetector直接传给 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()

设计要点

  • PetWindowtype() 动态构造,模块导入时 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(测试)

待安装:

  • PyQt6PySide6
  • ollama Python SDK
  • openai Python SDK