关于 bodylog
bodylog 是供 Agent 使用的个人训练与饮食记录工具,包含命令行工具、Python 库和 Claude 技能。数据保存在本地 SQLite 文件中,营养数据来自 USDA 和 Open Food Facts,无需账户或云存储。
主要功能
- 01通过聊天记录训练和饮食
- 02追踪热量、宏量营养素、目标与连续记录
- 03使用 USDA 和 Open Food Facts 数据
- 04将记录存储在本地 SQLite 文件
- 05查看训练历史和个人纪录
- 06生成可分享的训练与饮食卡片
适用场景
- 01训练时记录组数和重量
- 02记录餐食并查看每日营养总量
- 03查看每周训练量和个人纪录
- 04制作可分享的训练或饮食日卡片
MCP 相关
bodylog 内含 MCP 服务,可通过工具记录和修改训练与饮食、查看进度及生成卡片,并可与任何 MCP 客户端配合使用。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx bodylogBODYLOG_DBSQLite file for the log (default ~/.bodylog/log.db)BODYLOG_CARDSDirectory for rendered card PNGs (default ~/.bodylog/cards)FDC_API_KEY密钥USDA FoodData Central API key (free at api.data.gov); DEMO_KEY is used without oneBODYLOG_OFFLINESet to 1 to use only the bundled USDA table, no network lookups本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
15收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| rpe | any | 否 | |
| kind | string | 否 | |
| reps | any | 否 | |
| text | any | 否 | |
| unit | any | 否 | |
| count | integer | 否 | |
| notes | any | 否 | |
| weight | any | 否 | |
| exercise | any | 否 |
详细介绍
bodylog
面向个人 Agent 的训练与饮食日志。你可以像平时口头描述那样,在聊天中告诉 Agent 你的训练组数和饮食。它会记录下来,根据真实营养数据计算卡路里和宏量营养素、帮你保持连续记录,并生成可分享的卡片。
一个软件包,适用于任何 Agent:MCP 服务(bodylog-mcp)、命令行工具(bodylog)、Python 库和 Claude 技能。所有数据都保存在一个本地 SQLite 文件中。无需账户、云服务或社交功能。
| 饮食日 | 故事 | 训练 |
|---|---|---|
![]() | ![]() | ![]() |
更多:饮食,浅色、同一天的饮食和训练 (完整图)、训练故事、 8 项训练动作,分两页、混合使用 kg 和 lb、 照片贴纸。
安装
Python 3.10+。
pip install bodylog # CLI + library + MCP server
uvx --from bodylog bodylog-mcp # run the MCP server without installing
Claude Code 插件版(技能和 MCP 服务合二为一):
/plugin marketplace add abhaymettu/bodylog
/plugin install bodylog@bodylog
Claude Code,仅安装 MCP 服务:
claude mcp add bodylog -- uvx --from bodylog bodylog-mcp
其他 MCP 客户端:
{
"mcpServers": {
"bodylog": {
"command": "uvx",
"args": ["--from", "bodylog", "bodylog-mcp"],
"env": { "FDC_API_KEY": "optional, free at https://api.data.gov/signup/" }
}
}
}
设置项,均为可选:
| 变量 | 默认值 | 说明 |
|---|---|---|
BODYLOG_DB | ~/.bodylog/log.db | SQLite 文件路径 |
BODYLOG_CARDS | ~/.bodylog/cards/ | 卡片 PNG 文件的保存位置 |
BODYLOG_UNIT | 记录最多的单位 | 训练总量使用 kg 或 lb |
FDC_API_KEY | DEMO_KEY | USDA FoodData Central 密钥;DEMO_KEY 每小时仅允许查询少数几次 |
BODYLOG_OFFLINE | 关闭 | 1 表示仅使用内置食物表,不访问网络 |
技能文件位于 skills/bodylog/SKILL.md。如果不使用插件,请将 skills/bodylog/ 复制或符号链接到 Agent 的技能文件夹中。
饮食
CLI 会打印 MCP 工具返回的内容(bodylog eat 对应 log_food)。一次真实的离线运行:
$ bodylog eat "2 eggs, toast and a protein shake"
#1 breakfast: 2 × egg, 100 g: 143 kcal, P 12.6 C 0.7 F 9.5
#2 breakfast: toast, 22 g: 64 kcal, P 2 C 12 F 0.9
#3 breakfast: whey protein, 26 g: 92 kcal, P 20.3 C 1.6 F 0.4
$ bodylog eat "greek yogurt with blueberries and a handful of almonds"
#4 breakfast: greek yogurt, 170 g: 100 kcal, P 17.3 C 6.1 F 0.7
#5 breakfast: blueberries, 148 g: 84 kcal, P 1.1 C 21.4 F 0.5
#6 breakfast: 1 handful almonds: not counted (no weight for a handful of almonds; give grams or one of: almond, cup, oz)
$ bodylog food-fix 6 --grams 28
#6 breakfast: 28 g almonds: 162 kcal, P 5.9 C 6 F 14
只给出数量时,会使用 USDA 针对该食物的份量:一个大鸡蛋(50 g)、一片吐司、一勺乳清蛋白粉 (FNDDS 中的“1 scoop, NFS”,26 g)。通过 MCP,Agent 会针对第 6 项提出一个简短问题,而不是猜测。
食物营养数据的来源:
- 内置表格。 来自 USDA FoodData Central(SR Legacy 和 FNDDS)的 62 种常见食物,每种都包含
FDC id、每 100 g 的数值以及 USDA 自带的份量重量(一个大鸡蛋、一片面包、一杯米饭)。可离线使用。
由
scripts/build_common_foods.py根据 USDA 下载的数据重新构建。 - 缓存。 之前查询过的内容,存储在 SQLite 文件中。
- USDA FoodData Central 搜索(
POST /fdc/v1/foods/search,survey、SR Legacy 和 Foundation 食物),使用FDC_API_KEY或DEMO_KEY。 - Open Food Facts 搜索品牌和包装食品,并通过条形码查询产品
(
GET /api/v2/product/<barcode>.json,无需密钥)。
只有当搜索结果名称包含你说出的每个词时,才会使用该结果。否则,该项目会保留为 unknown,并附上最接近的候选项,以便 Agent 询问你指的是哪一个。若某项的数量无法换算成克(例如一把,或没有 USDA 体积重量数据的食物的毫升数),则会保留为 needs_amount。修正之前,这两类项目都不会计入总量,也绝不会填入编造的数值。你从标签上读取的数值会按原样存储(manual)。
此外还包括:每日 kcal、蛋白质、碳水化合物和脂肪目标;以天为单位的记录连续天数(尚未记录的当天不会中断连续记录)和以周为单位的训练连续周数;以及深色、浅色、清晰或故事格式的每日卡片。当天还进行了训练时,卡片会添加一个训练区块。
训练
30 秒演示
下面的对话来自一次真实运行,调用方式为 log_set(text=...)。右栏显示了存储中为每条消息记录的内容。
you: push day -> title: Push Day
you: bench 20 kg x 10 warm up -> Bench Press: 20 kg x 10 (warmup)
you: 60kg x 10 -> Bench Press: 60 kg x 10
you: 67.5 kg x 4 -> Bench Press: 67.5 kg x 4
you: 62.5 kg x 8 rpe 9 -> Bench Press: 62.5 kg x 8
you: next incline db press 55 lb x 10 -> Incline Dumbbell Press: 55 lb x 10
you: same -> Incline Dumbbell Press: 55 lb x 10
you: 55lb x 9 -> Incline Dumbbell Press: 55 lb x 9
you: 12.5 kg x 12 -> Incline Dumbbell Press: 12.5 kg x 12
you: that was triceps pushdowns on the cable -> named 1 set(s) Triceps Cable Pushdown
you: 12.5 kg x 11 -> Triceps Cable Pushdown: 12.5 kg x 11
you: lateral raise 15 lb x 14 -> Lateral Raise: 15 lb x 14
you: 15 lb x 12 -> Lateral Raise: 15 lb x 12
you: one more -> Lateral Raise: 15 lb x 12
you: wrapped up, workout took 1h 3m -> ended session 4
**Push Day**
Thursday, Sep 17 · 6:05 PM
1h 3m · 2,640 kg · 11 sets · 12 PRs
Muscles: Chest 55%, Shoulders 27%, Triceps 18%
**Bench Press** · Chest · 3 sets · best 60 kg x 10 · PR: Heaviest weight, Best 1RM, Best set volume, Most reps
W. 20 kg x 10 (warm-up)
1. 60 kg x 10 🏆 Best 1RM, Best set volume, Most reps
2. 67.5 kg x 4 🏆 Heaviest weight
3. 62.5 kg x 8 @ RPE 9
**Incline Dumbbell Press** · Chest · 3 sets · best 55 lb x 10 · PR: Best 1RM, Best set volume, Most reps
1. 55 lb x 10 🏆 Best 1RM, Best set volume, Most reps
2. 55 lb x 10
3. 55 lb x 9
**Triceps Cable Pushdown** · Triceps · 2 sets · best 12.5 kg x 12 · PR: Heaviest weight, Best 1RM
1. 12.5 kg x 12 🏆 Heaviest weight, Best 1RM
2. 12.5 kg x 11
**Lateral Raise** · Shoulders · 3 sets · best 15 lb x 14 · PR: Best 1RM, Best set volume, Most reps
1. 15 lb x 14 🏆 Best 1RM, Best set volume, Most reps
2. 15 lb x 12
3. 15 lb x 12
This week: 2 workouts, 4,687 kg (−14% vs last week to date)
最后一条消息会结束训练,因此工具会返回这张卡片以及本页顶部的训练 PNG(end_session 也会执行相同操作)。个人纪录(PR)会根据 tests/fixtures/chat.txt 中此前的三次训练记录进行统计。
它如何解析聊天内容
- 组数:
60kg x 8、60 x 8、50lbs for 9、8 reps at 60kg、3x10 @ 90lb、60kg 3x8、12 reps(自重)。标记:warmup、drop set、to failure、rpe 9。 - 训练动作:在消息开头命名(
incline db press 55 lb x 10)、提前说明(now squats), 或事后命名(this is tricep cable pushdowns btw)。未命名的组会沿用上一个训练动作。 - 事后命名会应用到尚未指定训练动作的组。如果每组都已指定动作,则会应用到最后一组重量相同的连续组。
这涵盖了通常的情况:你换到新器械后,过一两组才说出动作名称。如果你在命名之前换了重量,请对较早的组使用
edit_last_set(exercise=...)。 - 名称会统一归为同一个训练动作:忽略大小写、复数形式、
db/bb简写、词序、填充词(on the) 以及不改变动作本身的肌肉名称。因此,tricep cable pushdowns、cable pushdown和triceps pushdowns on the cable会被视为同一个动作,而triceps curl和biceps curl则会区分开。bodylog alias可用于添加其他名称。 - 命令:
same/one more重复上一组;done, 1h 5m/workout took 45 min结束训练。粘贴记录中 Agent 的回复(Agent:、Assistant:、Claude:)会被跳过。
数字的含义
- 训练量是工作组的重量 x 次数。热身组会列在卡片上,但不计入训练量、组数和 PR。每组都会显示记录时使用的单位。总量会将每组精确换算为一个单位:你传入的
unit,否则使用$BODYLOG_UNIT,再否则使用组数最多的单位。 - 肌群分布是各主要肌群所占工作组的比例,依据
muscles.py中涵盖常见举重动作的名称映射(未知名称计为“其他”)。百分比总和始终为 100。 - 估算 1RM 使用 Epley 公式:
weight x (1 + reps / 30)。 - PR 以更早开始的训练为基准统计:最重重量、最佳估算 1RM、最佳单组训练量(单组中的重量 x 次数),以及在你之前练过的某个重量下完成的最多次数(每个动作只记一项,取增幅最大者)。首次进行某个动作时不会产生 PR,因此首次训练不会每组都显示 PR。
- 周度数据行会将本周与上周截至相同星期和时间的数据进行比较,因此周一训练不会显示为下降 90%。
- 时长以你结束时提供的数值为准。训练开始时间设为结束时间减去该时长。
MCP 工具
| 工具 | 功能 |
|---|---|
log_set | 向正在进行的训练中添加一组(如果没有,则开启一场训练)。可提供结构化字段或原始消息作为 text。返回该组打破的任何 PR。 |
edit_last_set | 修正或删除最后一组。 |
end_session | 以用户提供的时长(“1h 5m”)结束训练。以文本和 PNG 页面形式返回卡片。 |
session_card | 任意一场训练的卡片:theme 可选 dark、light 或 clear;style 可选 full 或 story;unit 可选 kg 或 lb。 |
exercise_history、prs、weekly_volume | 分别查看每个动作的进展、历史记录和每周训练量。 |
import_chat | 从粘贴的聊天记录中补录训练。可安全重复执行。 |
log_food | 根据用户描述记录食物;也可按名称、条形码或食物 ID 记录单项食物并指定数量,或提供标签上的数值。返回每项食物的状态和当日总量。 |
edit_food | 修正某项食物的数量、食物、餐次或数值;也可删除该项。 |
lookup_food | 按名称或条形码搜索,不记录食物。 |
food_day | 查看某一天按餐次分类的食物项目、总量、目标、剩余量、连续记录、标记项目和训练。 |
food_card | 以文本和 PNG 形式返回每日卡片:theme、style 可选 full 或 story。 |
set_goals、streaks | 每日目标;记录和训练连续天数。 |
CLI 和库
bodylog log "bench 60kg x 8"
bodylog end "1h 5m" --title "Push Day"
bodylog eat "chicken breast 180g, 1.5 cups rice and broccoli"
bodylog food-fix 12 --grams 28
bodylog goals --kcal 2400 --protein 160
bodylog food-card --style story
bodylog --help
from bodylog import Store, log_text, log_food, day_summary, summary, render_png
from bodylog.foodcard import render_png as food_png
store = Store() # ~/.bodylog/log.db
log_text(store, "bench 60kg x 8")
log_food(store, "2 eggs, toast and a protein shake")
food_png(day_summary(store), "today.png", style="story")
为什么选择 Python
MCP Python SDK 为每个工具只需几行代码,Pillow 无需浏览器即可绘制卡片,sqlite3 和 urllib 则随 Python 一同提供。运行时依赖项为 Pillow 和 mcp。卡片使用 Inter 字体(SIL Open Font License,随附于 src/bodylog/fonts/),因此在任何环境中的显示效果都一致。
开发
uv sync
uv run pytest # offline: API calls replay recorded responses
uv run python scripts/render_examples.py # regenerate examples/ (offline)
测试绝不会访问网络或仓库:tests/fixtures/http/ 中保存了真实的 USDA 和 Open Food Facts 响应,测试写入的所有文件都会放到临时目录中。
目录结构:store.py(SQLite)、names.py、chatlog.py、stats.py、muscles.py、card.py(训练)、food.py(餐食解析、匹配、总量、连续记录)、sources.py(USDA 和 Open Food Facts)、foodcard.py(每日卡片)、cli.py、server.py(MCP)、data/common_foods.json(内置 USDA 表)。
致谢
食物数据来自:USDA FoodData Central(公有领域)和 Open Food Facts(Open Database License;产品数据 注明来源为 Open Food Facts 贡献者)。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。


