减脂用微信零碎记录饮食,剩下的交给 Agent

背景

最近在刷脂,每天热量有个硬指标:TDEE 2400,缺口最多 25%,也就是目标 1800 kcal。

正餐这块好办。我每周备餐,吃什么、吃多少克都是定死的,一顿饭的营养算一次就固定下来了。

麻烦的是零碎饮食。

馒头、嘴馋了的加餐,这些不在备餐计划里的东西,才是热量失控的源头。它们没有规律,全靠”当时吃了什么”这个记忆,而这恰恰是最容易丢的。

以前我也试过正经记录:打开 App,找食物,填克数,算热量。坚持了几天就放弃了。一套动作步骤太多,只要哪一步觉得麻烦,整个习惯就断了。

后来我把这个流程拆开了:记录只留一步,就是发微信;剩下的识别、算热量、汇总、通知,全部交给 Agent。

现在我在微信里发一句”今天吃了一个馒头”,之后 Telegram 就收到一条通知,告诉我这馒头多少热量,今天累计还差多少到目标。

我额外还做了一个页面,可以直观统计我的所有饮食情况:


整条链路

先看完整的数据流:

1
2
3
4
5
6
7
8
9
10
11
微信服务号 ──→ Obsidian Messager 插件 ──→ 当天日记 ### Logs

Claude Code /loop Agent 定期轮询

语义识别:这条是不是在说吃了/喝了什么?
是食物 → 查 foods.json → 算热量 → 写入 intake.json
非食物 → 行尾打标记,下次跳过

build.py 刷新 nutrition.html + 周报
notify.py 推 Telegram(本次摄入 + 今日累计 + 目标对比)
git commit + push 同步到远程

人只需要做最前面那一小步,后面全是自动的。


记录端:发一条微信,别的都不用管

Obsidian 里装了个插件叫 Messager,它绑定了一个微信服务号。我在微信里给它发消息,消息就会被追加到当天日记的 ### Logs 区块里。

所以记录的动作就变成:吃了什么,顺手在微信里打一句话。

微信几乎总是开着的,比专门打开一个记录 App 的成本低太多。这也是整套流程能坚持下来的前提:记录动作必须轻到不需要”专门去做”。


识别端:Agent /loop 轮询

这是核心部分。

日记不是专门给饮食记录用的,### Logs 下面什么都有:工作任务、随想、系统配置想法、吃了什么,全混在一起。所以不能让它无脑解析,得逐条做语义判断。

我把这套逻辑写成了一个提示词(prompts/snack-loop.md),用 CC 的 /loop 模式定期跑,每轮做这几件事:

  1. 读当天的日记文件;
  2. 扫描全文,找出所有”未标记”的行;
  3. 逐条语义判断:这条是不是在说吃了/喝了什么;
  4. 是食物 → 提取食物名和份量,查 foods.json 算营养,写入 data/intake.json
  5. 不是食物 → 在行尾打标记,下次跳过;
  6. build.py 刷新 dashboard,有新食物就推 Telegram。

判断的标准:

信号 例子 处理
进食动词 吃、喝、啃、点了(外卖) 当饮食处理
食物名词 烤肠、咖啡、奶茶、可乐、包子 当饮食处理
份量词 + 食物 一根、一杯、一碗、几块 当饮食处理
工作任务 - [ ] checkbox、#StarRocks 技术标签 标记 ⏭️ 非饮食
心情/随想 没有食物名词的感想 标记 ⏭️ 非饮食
像食物但识别不了 “刚才吃了点东西” 标记 ⚠️ 无法识别

最后一条我特意强调了保守原则:描述模糊、明显在吃吃喝喝、但又定位不到具体食物时,宁可标”无法识别”,也不强行估算。估算错了就是污染数据,还不如不记。


入库与校验:intake.json 和 build.py

识别出来的食物会追加到当天记录的 零碎饮食 meal 里,再重算当天总热量和三大营养素。

1
2
3
4
5
6
7
8
9
10
11
12
{
"date": "2026-08-11",
"meals": {
"零碎饮食": {
"desc": "红糖馒头120g(早餐额外) + 李子500g(晚餐额外10颗)",
"foods": [
{ "name": "红糖馒头", "amount": "120g", "calories": 300.0 },
{ "name": "李子", "amount": "500g(10颗)", "calories": 180.0 }
]
}
}
}

营养值不是随手填的。foods.json 里有一份查表,Agent 按名字查 per / kcal / protein / fat / carb / fiber,再乘上实际份量。查不到的就常识估算,build.py 只会 warn,不会挂。

还有个硬校验:build.py 会把当天的顶层合计和所有 meal 加起来的值对一遍,差超过 0.5 就直接失败。所以 Agent 每次写入后必须重算 totals,算错一步就推不出去。这个校验看起来是给自己找麻烦,实际上保证了数据的一致性。

份量这块,Agent 有一张估算参考表,把口语折算成克数:

口语描述 默认估算
一根烤肠 / 一根香肠 80g
一杯奶茶 / 一杯咖啡 中杯 500ml / 350ml
一瓶可乐 500ml(罐装 330ml)
一块饼干 15g
尝了一口 / 一小口 ~20g / 20ml

通知:Telegram 实时反馈

每轮只要识别到了新食物,就会推一条 Telegram 通知。这是整个流程里最重要的一环:反馈。

1
2
3
4
5
6
7
8
9
🍽️ 零碎饮食已记录 (2026-08-11)

• 红糖馒头 120g ≈ 300 kcal P8.4/F1.2/C62.4
• 李子 500g ≈ 180 kcal P3.5/F1/C43.5

📊 本次合计:480 kcal P11.9/F2.2/C105.9
📅 今日累计:1885 kcal P150/F40/C210
🎯 目标热量:1800 kcal
已超目标 85 kcal ❌

最后那个”已超/未超目标”的对比是点睛之笔。目标热量从 config.json 里算出来(TDEE × (1 − 缺口比例) = 1800),没超就显示”还可吃”,超了就红叉。它把我从”记了但不知道够不够”,变成了”记完立刻知道今天还能不能吃”。


幂等和历史不可变

这两个是我比较在意的设计。

幂等:日记里每条处理过的内容,行尾都会被打上 HTML 注释标记。

1
2
早餐额外吃了120g的红糖馒头 <!-- ✅ 红糖馒头 ≈300kcal P8.4/F1.2/C62.4 -->
- [ ] 16:03 #StarRocks drop cn 之后... <!-- ⏭️ 非饮食 -->

这个标记是”已处理”的唯一状态。下次轮询扫到带标记的行直接跳过,所以同一个 loop 跑多少遍都不会重复入库。loop 是长期挂着跑的,一旦重复入库,数据就废了,这个设计很关键。

历史不可变:旧的周报和旧的 intake 记录视为历史,只追加新记录,不改写旧数据。减脂是连续观察,昨天的数字一旦被改掉,趋势线就是假的。


数据同步

每轮只要写了新数据,Agent 会自动 git commit 然后 push 到远程。这样即使本机出问题,数据也不会丢。

push 失败也没关系,本地提交会保留,联网后补推就行,不阻塞整个 loop。


总结

回头看这套流程,核心思路很简单:把”记录”这个高频动作的成本降到最低,剩下的计算和汇总全部甩给 Agent。

人这一侧,只有一步:吃的时候顺手发条微信。
Agent 那一侧:识别、查表、算热量、更新 dashboard、推通知、同步数据,全自动。

这套链路能跑起来,主要靠三件事:

  • 记录够轻(微信发消息),所以坚持得下来;
  • 反馈够快(Telegram 实时通知 + 目标对比),所以愿意记;
  • 状态够明确(幂等标记 + 历史不可变),所以长期挂着跑不出乱子。

如果你也在减脂或者控热量,零碎饮食怎么记一直是个老大难,这套”微信 + Agent”的组合值得安利。成本就是一条微信消息,剩下的交给 Agent 就行。

当然整个链路还有优化的地方,比如消息不够及时,因为我的 Agent 是部署在家里的一台闲置的 MacBook 上的,他也是通过定时拉取 Obsidian 数据来同步消息的,Agent loop 也有时间间隔,所以还做不到非常的实时。

同样的受限于现在的 Agent 限制,他们的 loop 周期都不能开很久,导致我隔一段时间就要新建一个 loop。

这个后续考虑会基于 pi 自己定制一个 Agent;可能有人会为为啥不用 openclaw 或者是 workbuddy 这种成熟的产品?

主要还是这些产品满足的都是通用的需求,往往功能复杂、安装包也很大,我的老 mac 运行起来确实有点吃不消,所以一开始就用最简单的方式把整个链路串起来。


减脂用微信零碎记录饮食,剩下的交给 Agent
http://crossoverjie.top/2026/08/11/AI/微信零碎记录饮食-交给Agent/
Author
crossoverJie
Posted on
August 11, 2026
Licensed under