Files
EzVibe/docs/v2_design/ARCHITECTURE_REVIEW.md
e2hang 96cb28fe08 feat: multi-layer eventFilter for window drag + text input fix + Qt platform xcb
- 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>
2026-05-19 23:36:58 +08:00

6.1 KiB
Raw Permalink Blame History

EzVibe 架构评审 — 健康监测场景深度分析

评审日期2026-05-18 评审者e2hang


一、架构设计隐患与优化建议(技术层)

1. Qt 与 asyncio 的事件循环融合(高危隐患)

文档现状 QTimer 每 20ms 调用 loop.run_until_complete(asyncio.sleep(0)),后台 asyncio.run()

问题所在 这是 PyQt/PySide 与 asyncio 结合时最容易出 Bug 的"土办法"。随着 LLM 推理甚至网络波动、OCR 截图的加入,这种粗暴的轮询极易导致:

  • UI 线程卡顿(桌宠拖动掉帧)
  • CPU 占用率异常升高
  • 对于一个需要常驻后台的健康软件是致命的(会耗尽笔记本电池)

改进建议 强烈建议引入 qasync 库。它实现了完美的 Qt 事件循环与 asyncio 的底层融合。主入口可以改写为原生的 async def main():,彻底告别由于跨线程/跨循环引发的死锁问题。

参考代码

import qasync
from PySide6.QtWidgets import QApplication

async def main():
    # 这里是真正的 asyncio 代码
    window = create_pet_window(...)
    window.show()
    await qasync.EventLoopPolicy().mainloop()

if __name__ == "__main__":
    qasync.run(main)

2. 感知层的性能消耗 (Battery Drain)

文档现状 ScreenCapture 使用 mss 截图 + pytesseract OCR。

问题所在 OCR 是 CPU 密集型任务。如果每分钟都在后台跑 Tesseract风扇会狂转。桌宠本来是辅助工具不能成为系统负担。

改进建议

  • 降级策略:在"健康监测"场景下,**当前焦点窗口的名字Window Title**比全屏 OCR 更重要也更轻量Windows 用 pygetwindow/win32guiMac 用 AppKit)。
  • 按需触发:仅在发现用户持续活跃(ActivityDetector > 0.8 且持续 1 小时)时,才触发一次 OCR 看看他在干嘛(写代码/看视频),从而生成更精准的 LLM 提醒。

3. LLM 调用的延迟处理 (Latency)

文档现状 主循环触发 decide_action(),依赖本地 Ollama 或 OpenAI API。

问题所在 如果判断需要提醒喝水,但调用大模型生成文本花了 5 秒,这 5 秒内桌宠会"发呆",体验不佳。

改进建议 解耦"动作触发"与"文案生成"

  • 对于 P0 级健康提醒(如久坐),桌宠应立刻执行 UI 动作(如跳出来递水杯)
  • LLM 文案可以异步加载
  • 或者使用本地预存的 Fallback 文案缓存池

二、贴合"健康监测"场景的业务逻辑评审

1. "烦躁 (annoyed)" 状态屏蔽 P0 提醒的悖论

文档现状 烦躁annoyed→ 禁止 P0/P1 所有打扰。

冲突分析 真实场景中,用户连续工作 3 小时不休息,可能因为卡 Bug 导致键鼠敲击极重(此时通过 pynput 算出的活跃度极高,情绪很容易转为 focused 甚至 annoyed)。如果此时桌宠因为"怕打扰他"而屏蔽久坐提醒,就失去了健康监测的意义。

优化方案(柔性降级)

  • 不要屏蔽 P0而是改变 P0 的表现形式
  • 正常状态:桌宠走到屏幕中间,大喊"喝水啦!"并强行弹窗
  • 烦躁/极度专注状态:桌宠缩在角落,默默举起一个带有 💧 图标的小牌子(不播放音效,不挡视线)
  • 这种"怂怂的但固执"的设定,反而能增加角色的情感厚度

设计变更Behavior 增加 display_mode 属性:

@dataclass
class Behavior:
    display_mode: DisplayMode = DisplayMode.NORMAL  # Normal | Quiet | Aggressive

2. 记忆系统 (RAG) 缺乏结构化健康数据

文档现状 使用 RAG (VectorMemory) 记录事件Cosine 检索。

冲突分析 RAG 擅长模糊语义检索(如:"我昨天提到喜欢喝什么奶茶?"),但不擅长精准的时序统计(如:"用户今天喝了几次水?"、"连续静坐了几个小时?")。

优化方案 引入混合记忆架构

  • 语义记忆 (RAG):记录用户的习惯、喜好、甚至抱怨("别烦我,在赶 Due"
  • 结构化健康数据库 (SQLite):记录 last_stand_time, daily_water_intake, screen_time

LLM 在 AgentBrain.think() 时,系统直接将这些精确数值注入 System Prompt 顶部,比让 LLM 自己去 RAG 里翻找要可靠得多。


3. 奖惩机制的闭环

文档现状 user_healthy_action → happy +2.0

深入设计 目前的互动是单向的(桌宠提醒 -> 用户去做)。如何判断用户真的做了?

优化方案

  • 微交互确认当桌宠提醒喝水时UI 上提供一个"吨吨吨(已喝)"和"等会儿"的按钮
  • 基于感知的隐式确认:如果在提醒起立后,ActivityDetector 监测到键鼠长达 2 分钟完全无动作,且摄像头/麦克风无明显活动,系统自动判定用户去休息了,等用户回来时,桌宠状态变为 happy 并给出正向反馈

三、关键设计变更汇总

优先级 变更项 影响模块 预计工时
P0 qasync 替换 QTimer main.py, ui/pet_window.py 0.5 天
P0 HealthTracker 模块 新增 agent/health_tracker.py 1 天
P1 焦点窗口替代 OCR perception/keyboard_mouse_monitor.py 0.5 天
P1 DisplayMode 柔性降级 agent/scheduler.py 0.5 天
P1 LLM 文案异步加载 agent/brain.py 0.5 天
P2 微交互确认 UI ui/pet_window.py 1 天
P2 隐式确认反馈机制 perception/keyboard_mouse_monitor.py 1 天

四、架构优势总结

尽管存在上述改进空间,当前架构的优势在于:

  1. 分层清晰:感知 → 决策 → 记忆 → 表现,边界明确
  2. 技术栈务实SQLite + 本地向量 + 本地 LLM隐私优先
  3. 测试覆盖128 个单元测试,底层基础牢固
  4. 策略模式LLM 后端可替换,支持 Ollama/OpenAI/Dummy
  5. 延迟构造PetWindow 动态构造,避免 Qt 导入冲突

接下来的重点:不需要再盲目堆砌 AI 能力,而是要把"健康数据采集"和"桌宠行为反馈"这两者结合得更自然、更不招人烦。