这是以下挑战的参赛作品Hacktoberfest 开源 AI 挑战赛第 3 周:慢而稳
我构建了什么
说实话:你上次在工作时喝水是什么时候?
我可以连续四个小时坐在笔记本电脑前,唯一移动的东西就是我的光标。我的眼睛发烫,后背酸痛,而旁边那瓶水跟早上一样满。
我试过提醒类应用。它们都以同样的方式失败:每次都说着同样无聊的话,不到一天我的大脑就学会了跳过它。一条我已经读过 50 遍的通知不再是通知,而是壁纸。
所以我构建了Break Buddy🦸
Break Buddy 是一个在后台安静运行的 Windows 应用。到了该休息的时候,一个像素风英雄会弹到屏幕角落,指着一个 RPG 风格的对话框,打出一段每次都不同的消息,语气用我选的那种人格。下面这条是它为我写的:
“你的眼皮是在为耐力赛试镜吗?”
这不是我写的。我永远不会看到同一条提醒两次。说实话,它真管用。我笑了,然后我真的让眼睛休息了。
它的功能如下:
- 设置只需输入一句话。我输入了“提醒我每 45 分钟喝水,每 20 分钟让眼睛休息,每小时伸展一下,早上 9 点前和晚上 8 点后不要提醒,用讽刺朋友语气”然后一个本地模型把它变成了配置文件。没有设置界面,没有滑块,没有下拉菜单。
- 任何你喜欢的人格。讽刺朋友、冷静教练、海盗、暴躁健身哥。改一个词,整个应用的说话方式就变了。
- 它知道什么时候不该打扰你。它会读取我当前活动窗口的标题,标注我在做什么(写代码、看视频、开会、看文档……),并在 Zoom 或 Google Meet 通话期间暂缓提醒。当我离开通话时,我会收到一个合并弹窗,而不是三个叠在一起的弹窗。
- 长休息提醒。连续用屏 2 小时后,它会让我好好休息一下,并提到我主要在做什么。
- 稍后提醒、暂停和退出都可以从弹窗和系统托盘图标操作。
它随 Windows 启动,没有控制台窗口。我设置了一次就再也没碰过它。这就是重点。🦥
演示
代码
https://github.com/kumudasrip/Break-Buddy
README 里有完整的安装说明。你需要 Windows、Python 和Ollama,从克隆到第一次弹窗大约五分钟。
我是如何构建它的
我很熟悉 Python,但在此之前我从未用过开源权重模型。我装好了 Ollama 并拉取了几个模型,仅此而已。没有微调,没有 GPU 技巧,只是从 Python 调用本地模型,看看会发生什么。
技术栈
- Ollama在我的机器上运行模型。我像调用其他任何库一样从 Python 调用它。
- gemma4:e4b是默认模型。它足够小,可以整天保持加载状态。
- Pydantic + Ollama 结构化输出强制设置步骤返回有效的 JSON,该 JSON 会被验证并最多重试 3 次。
-
pygetwindow读取活动窗口标题,而 Windows 的
GetLastInputInfo调用则测量空闲时间。 - tkinter绘制弹窗,pystray绘制托盘图标。
- 主角是完全由 Python 绘制(
make_sprite.py)。没有需要下载的图片文件。它是一个原创的戴兜帽、披斗篷的守护者,带有新月徽章。
AI 真正发挥作用的地方
一个普通的“每 45 分钟”计时器根本不需要 AI,我也不想让模型只是装饰。所以它有三个真正的职责:
- 理解我。把输入的一句话变成结构化配置。
- 理解我的屏幕。根据窗口标题标注我正在做什么,这样在开会时提醒可以等待。
- 撰写消息。每条提醒都有一句新的话,用我选择的语气。它只收到像“coding”这样的标签和分钟数,永远不会收到真实的窗口标题。
出了什么问题(我真正学到东西的部分)
1. 模型忽略了我的语气。
我输入了“sarcastic friend”,而配置却一直保存"friendly"。一次又一次。原因是我自己的 schema:我预先填了"friendly"作为默认值,而小模型复制了默认值,而不是读取我的句子。修复方法是让该字段成为必填项且没有默认值,并添加一个示例(“pirate voice”变成“pirate”)。之后它每次都正常工作。小模型会遵循默认值而非指令,现在我知道了。
2. 弹窗重叠了。
我猜了弹窗高度,而在我的高 DPI 屏幕上,实际窗口更高。弹窗堆叠在一起,遮住了下面的那个。我改为在文本布局完成后测量每个弹窗,并开启了 DPI 感知。
3. 会议检测过于轻信。
我用一个名为Zoom meeting.txt,而模型正确地说那是不是一场会议。但那个测试让我意识到一件令人不安的事:如果模型误判了一通真实的通话,应用就会在通话中途打断我,而这是最糟糕的失败情形。所以我为 Zoom、Meet、Teams 和 Webex 添加了固定模式,它们会优先运行,模型作为后备。在我的日志里,模型捕捉到了我的模式漏掉的标题(比如Kumuda Sri Padmanabhuni's Zoom Meeting),所以这两层互相补足。
4. 我已知的局限。
Zoom 的主界面("Zoom Workplace")也会拦截提醒,即使我并不在通话中。而通话开始时出现的"正在连接…"窗口,可能会让提醒在几秒钟内溜过去。我宁愿告诉你,也不想隐瞒。
慢而稳:一个常驻应用该用哪个模型?
我想知道自己是否真的需要一个大型模型,所以我写了benchmark.py。它导入应用自身的提示词,并用三个模型运行相同的任务:10 条手动输入的设置语句、36 个窗口标题(包括我真实的那些),以及五种语气的提醒消息。设置和标注在 temperature 0 下运行,每个用例只尝试一次。真实应用会最多重试设置 3 次,所以实际结果会稍好一些。
| gemma4:e4b | gemma4:26b | qwen3.6:35b | |
|---|---|---|---|
| 设置:完美解析 | 9/10 | 10/10 | 10/10 |
| 窗口标注(模式 + 模型) | 97% | 97% | 92% |
| 会议召回 | 7/7 | 7/7 | 7/7 |
| 非会议被误判为会议 | 0 | 0 | 0 |
| 符合规则的消息 | 100% | 100% | 80% |
| 冷启动 | 5.8 秒 | 64.7 秒 | 25.7 秒 |
| 每条消息的中位耗时 | 0.2 秒 | 0.4 秒 | 0.5 秒 |
| 每秒 token 数 | 118 | 56 | 61 |
| 加载时的内存占用 | 3.3 GB | 18.3 GB | 23.3 GB |
| 在我的 GPU 上运行的比例 | 100% | 51% | 41% |
这些数字告诉我的:
- 小模型就够了。更大的模型只是多答对了一条设置语句(e4b 漏掉了来自"每 90 分钟散一次步,中午前不提醒,励志演讲者"的"每 90 分钟散一次步"提醒)。在标注和消息质量上,e4b 与它们持平或更胜一筹。
- 用大模型代价很高。gemma4:26b 需要 18 GB,冷启动要一分多钟。在我的笔记本上它只有一半能放进 GPU。对于一个整天驻留在内存里的东西来说,一个 3.3 GB、完全跑在 GPU 上的模型是显而易见的选择。这就是树懒之道:用最少的能量完成工作。
- 最大的模型并不是最好的那个。qwen3.6:35b 把我的 Copilot 窗口标记为“coding”,而有一条讽刺消息根本不是关于喝水的:“我想总得有人提醒你今天要活下去吧。”挺好笑,但不是喝水提醒。😅
- 安全规则胜过模型规模。三个模型都捕捉到了每一场会议,且都没有误报,部分归功于固定模式。
- 坦诚的注意事项。这是一次小规模测试(10 句话、36 个标题),正确标签是我自己写的,而且有些“错误”是有争议的。一个我会称为“other”的 Chrome 标签页,被三个模型都标记成了“browsing”。
为什么开放式创新很重要?
我直说吧:ChatGPT 或 Claude 能写出比我的本地模型更好的提醒,而且大概能更好地给奇怪的窗口标题打标签。如果这是一次性任务,我会用它们。
但 Break Buddy 不是一次性任务。它是一个每天监视我屏幕、并会在我余生中一直运行的工具。对于这一点,开放和本地不是锦上添花。它是我唯一真正愿意运行的版本。
1. 它让我的屏幕内容不落到别人的服务器上。
Break Buddy 每隔几秒读取我的活动窗口标题。那包括我打开的每个标签页、我碰过的每个文件、每个聊天窗口、每次求职搜索、每个银行页面。我绝不会把这些流式传输到我无法控制的服务器上,哪怕对方承诺不存储,我也会感到不安。因为模型运行在我的笔记本电脑上,我才能构建出那个让应用变聪明的功能——知道我在做什么——而无需付出任何隐私代价。如果用的是封闭 API,这个功能就会是我无法上线的部分。
2. 一个常开工具,一直开着也应该零成本。
这个应用整天、每天都会调用模型。在按量计费的 API 上,“设置一次就忘掉”会变成一张永不停歇的账单。在本地它是免费的,而且gemma4:e4b闲置时占用 3.3 GB,我毫无察觉。模型下载完成后,应用里没有任何东西需要联网。
3. 我可以测量,而不是猜测。
改一行代码,我就让三个模型跑同样的测试,并比较准确率、速度和内存。我就是这样发现小模型已经够用,而最大的那个并没有更好。如果用的是封闭 API,我只能用提供商选定的模型,而且根本无法测量内存、GPU 占用或冷启动。
4. 我能控制和调试模型的行为。
当模型一直保存“friendly”而不是“sarcastic friend”时,我可以查看自己的 schema,把 temperature 设为 0,关闭思考,强制输出为 JSON,从而找到真正的原因。当视觉模型失败时,我可以从命令行测试它,以证明问题不在我的代码。把每一层都掌握在手里,正是把“它不工作”变成“原因在这里”的关键。
5. 没有人能在背后改动它。
这个应用明天和今天的行为完全一样。服务提供商无法悄悄更新模型、更改定价,或停用我日常工具所依赖的端点。
诚实的取舍是在不寻常情况下的质量。大型云端模型在那里可能更胜一筹。但对于这项工作,我自己机器上的小模型完成了我需要的一切,而这正是重点。开源模型让我能为问题挑选合适的规模,而不是默认租用最大的那个。
偷懒的回报
我设置 Break Buddy只用了一句话。现在它在我打开笔记本电脑时启动,而且自从我切换到真实模式后,提醒会按时出现,无需我打开设置界面或碰任何配置文件。
最棒的是:它确实改变了我的行为。当英雄让我喝水、休息眼睛、站起来时,我真的照做了。普通的计时器我可能看都不看就关掉。而一个每次都用新句子吐槽我的像素英雄,就难忽略得多了。😄
在我的 Meet 和 Zoom 测试中,整个通话期间提醒都被暂缓,之后我只收到一个合并后的弹窗,而不是一堆弹窗。所以这个应用不只是提醒我。它知道什么时候该保持安静,而这正是懒人真正想从助手那里得到的。
我省下的精力:不用记、不用设置界面、不用切换应用、不用把同一句话关掉 50 次。我花的精力:输入一句话。
奖项类别
-
最佳 Gemma 应用。默认模型是
gemma4:e4b,我将它与gemma4:26b在设置解析、窗口标注、消息质量、速度和内存方面进行了基准测试。结果发现,小型 Gemma 才是适合常驻工具的正确模型。
把目光从屏幕上移开。喝点水。英雄正在看着。👀😂




