Skip to content
cccskills
FreePublished on GitHub

walk-kcal

走得越远,这一顿的卡路里额度越高 —— 用步行距离换额度的吃饭决策。当用户说「现在吃什么」「走过去能多吃多少」「这顿还能吃多少卡」「附近有什么低热量/快餐/西餐/小吃」「换一批」「还有别的吗」时使用。

① 按步行距离折算额度;② 麦当劳官方营养值 + 周边餐厅统一推荐,每道菜标「吃得下 / 超了」;③ 按业态与半径细化;④ 出竖屏长图 + 可点的下一步选项。

The English summary is being prepared. Showing the original description.

Run score Not tested

See how to install

Files included (100)

  • SKILL.md20.3 KB
  • .dockerignore522 B
  • .gitattributes142 B
  • .gitignore358 B
  • assets/moments-1x1.png67.7 KB
  • assets/moments-4x3.png73.8 KB
  • assets/screenshot-mobile.png248.9 KB
  • assets/screenshot-mobile.svg87.6 KB
  • assets/screenshot-preview.png124.4 KB
  • assets/screenshot-skill-chat.png313.2 KB
  • assets/screenshot-skill-chat.svg101.1 KB
  • assets/screenshot-three-shells.png89.4 KB
  • assets/social-preview.png68.6 KB
  • assets/xhs-cover.png94.0 KB
  • config.example.json653 B
  • CONTEST_DECLARATION.md1.5 KB
  • corpus/local_dish_heat.json196.4 KB
  • corpus/README.md2.3 KB
  • demo/conversation.html10.3 KB
  • demo/explore.html12.0 KB
  • demo/index.html21.2 KB
  • demo/scene-k11.js42.5 KB
  • Dockerfile1.2 KB
  • docs/业态归类.md6.1 KB
  • docs/交互模式.md11.0 KB
  • docs/工程历程.md5.9 KB
  • docs/快速开始.md10.5 KB
  • docs/扩展开发.md20.2 KB
  • docs/数据源与误差.md9.3 KB
  • docs/服务商边界.md19.2 KB
  • docs/源装配契约.md16.1 KB
  • docs/设计取舍.md38.3 KB
  • entry/bot.py13.7 KB
  • entry/mcp_server.py14.4 KB
  • entry/README.md1.7 KB
  • entry/run.sh769 B
  • evidence-real.md5.9 KB
  • examples/baidu_k11/detail.json17.8 KB
  • examples/baidu_k11/stores.json17.8 KB
  • examples/default/detail.json10.9 KB
  • examples/default/stores.json76.4 KB
  • examples/guangzhou/detail.json68.2 KB
  • examples/guangzhou/stores.json68.5 KB
  • examples/jintie/detail.json74.4 KB
  • examples/jintie/stores.json75.2 KB
  • examples/k11/detail.json76.7 KB
  • examples/k11/stores.json80.4 KB
  • examples/linghelu/detail.json35.7 KB
  • examples/linghelu/stores.json53.4 KB
  • examples/mchq/detail.json71.1 KB
  • examples/mchq/stores.json71.2 KB
  • examples/README.md1.9 KB
  • index.html11.9 KB
  • LICENSE1.1 KB
  • MCP_INTEGRATION.md5.7 KB
  • mcp-config.example.json711 B
  • packs/__init__.py1.0 KB
  • packs/base.py2.6 KB
  • packs/mcd_nutrition_snapshot.py6.4 KB
  • packs/pack_mcd.py15.2 KB
  • packs/pack_nearby_dining.py14.0 KB
  • packs/pack_template.py1.8 KB
  • packs/README.md3.6 KB
  • packs/registry.py11.3 KB
  • README.md9.7 KB
  • requirements.txt1.1 KB
  • tests/test_gate.py118.0 KB
  • tests/test_render_fallback.py5.7 KB
  • walkkcal/__init__.py2.0 KB
  • walkkcal/batch_heat.py15.7 KB
  • walkkcal/config.py7.3 KB
  • walkkcal/core.py40.7 KB
  • walkkcal/corpus.py3.1 KB
  • walkkcal/fetch_detail.py5.3 KB
  • walkkcal/fetch_pois_at.py9.9 KB
  • walkkcal/filter.py13.0 KB
  • walkkcal/geo.py13.8 KB
  • walkkcal/hints.py5.7 KB
  • walkkcal/icons.py12.6 KB
  • walkkcal/locate.py9.8 KB
  • walkkcal/olist.py1.3 KB
  • walkkcal/portion.py9.5 KB
  • walkkcal/prior.py33.8 KB
  • walkkcal/providers/__init__.py973 B
  • walkkcal/providers/corpus_dianping.py3.1 KB
  • walkkcal/providers/corpus_template.py2.0 KB
  • walkkcal/providers/corpus_xiachufang.py8.8 KB
  • walkkcal/providers/geo_amap.py20.4 KB
  • walkkcal/providers/geo_baidu.py20.8 KB
  • walkkcal/providers/geo_template.py8.8 KB
  • walkkcal/quality_gate.py5.2 KB
  • walkkcal/render.py73.8 KB
  • walkkcal/reply.py18.8 KB
  • walkkcal/select.py12.7 KB
  • walkkcal/shoot.py15.7 KB
  • walkkcal/snapshot.py17.0 KB
  • walkkcal/theme.py7.1 KB
  • walkkcal/tiers.py2.3 KB
  • walkkcal/tools/winrt_locate.cs7.0 KB
  • workbuddy.md10.8 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

walk-kcal · 走得越远,这一顿吃得越多

这份文件有两类读者,看不同章节就行:

一句话:你今天还能吃多少卡,取决于你愿意走多远。


一、运行手册(Agent)

1.1 何时启用

用户说启用
「现在吃什么 / 这顿吃什么」✅
「我走过去能多吃多少卡」✅
「附近有什么低热量选项」✅
「我今天还剩 300 卡,能吃什么」✅
「麦当劳有什么优惠券」❌ 与本技能无关

1.2 对话协议

这是渐进细化的多轮对话,不是单次问答。 用户首句通常很短(「吃什么」), 应答须先给可用结果,再给出可细化方向。

三条硬规则

  1. 零参数调用必须先出结果,不得先反问用户偏好。 唯一例外是位置:没有位置算不出距离,因此没有"走多远"。 但位置也先自行获取(见 1.3 四档),四档都失败才向用户询问。 不得把 examples/* 里的演示快照当作"用户附近"。
  2. 用 next 字段引导,不罗列参数。 next=[{'label':'只看快餐'},…] → 输出「要只看快餐吗?」; 禁止输出「可用参数:industry=快餐|小吃|西餐…」。
  3. 细化选项一律取自 next,不自行构造。 它是按当前数据算出来的 (K11 附近咖啡多则提示咖啡);自造的选项往往查不到结果。

用户话术 → 参数

用户说参数
「吃什么」「附近有什么」零参数调用 nearby_food
「只看快餐」「想吃日料」industry=快餐/西餐/日料/小吃…
「不想走太远」「500 米内」radius_m=500
「还有别的吗」「换一批」page 取上轮 meta.page + 1
「这家怎么样」「就那家」用 walkkcal/reply.describe_store() 输出纯文字详情,无需再出图

一轮回复的结构

<结论句>   ← 直接用返回值的 text 字段(已按「结论|依据|来源|下一步」写好)
<长图>     ← 渠道支持图片时
<选项>     ← 把 next 转成问句;Bot 渠道可直接渲染成按钮

不得只发图了事 —— 用户会不知道下一步能做什么。

★ text 中若出现「ⓘ 数据来源:这次没能走实时…」,必须原样保留。 它表示某条渠道回退到了本地快照(meta.source_notes 有机器可读版本)。 默认配置(读内置快照)不会出现这段,出现即表示真的回退过。

1.3 位置获取(四档,按代价从低到高)

档适用条件做法
①用户话中已含位置(地名 / 路名 / 地标 / 「我在 XX 附近」)直接采用,不再询问。 判据是「文本中是否含位置信息」,与是否配置 Key 无关
②话中未提 + 环境有地图 Keypython walkkcal/locate.py 自动定位,取 lat/lon
③话中未提 + 无地图 Key麦当劳 MCP delivery-query-addresses:无入参,读取用户在麦当劳 App 中存过的地址。不需要地图 Key(需 MCD_MCP_TOKEN)。取不到则静默跳过,不报错
④以上均不可用此时才询问用户位置。话术取 locate.py 返回的 hint,不要自拟
# ② 自动定位(机器可读;首次会编译一个 C# 小工具,约 3~7 秒,之后 <3 秒)
python walkkcal/locate.py
# 成功 → {"ok":true,"lat":31.26596,"lon":121.40968,"accuracy_m":159,"source":"WiFi",...}
# 失败 → {"ok":false,"reason":"...","hint":"..."}

# 拿到坐标后直接出图(不输入地址即可,shoot.py 内部会自动定位)
python walkkcal/shoot.py

★ locate.py 只接受 source 为 WiFi / Satellite 的结果。 开启代理时系统会退回 IP 定位、返回机房坐标(实测会指到境外), 此时它主动弃权(ok:false)—— 用这种坐标算「附近 800 米」没有意义。 ★ 也可绕过定位:--lat 31.23 --lon 121.41,或设 WALKKCAL_LAT / WALKKCAL_LON。 ★ locate.py 无第三方依赖(用系统自带的 .NET Framework csc.exe 编译一个 50 行 C# 工具, 产物缓存在 out/_tools/,不入仓库)。

没有地图 Key 时的行为(硬约束)

python walkkcal/config.py      # 一次看清:配置来自哪个文件 / 有哪些键 / 哪些有值

无地图 Key 时:档 ② 不可用(shoot.py 无法解析地址、也检索不到周边)。

情况允许的做法
有地图 Key按四档依次取
无地图 Key① 用户已给出地名 → 直接用;③ 试 delivery-query-addresses;④ 询问用户,并说明「实时查需要配一个地图 Key」;或者明确告知「这是 examples/<场景> 的预采集演示点位,不是你的位置」后再出图

禁止:把 examples/* 中的演示快照当作"用户附近"(等价于用演示数据冒充真实结果)。

演示点位清单(与用户位置无关)

场景目录点位
examples/default(render.py 默认)上海·静安虬江路
examples/linghelu上海·浦东凌河路 618 弄
examples/k11 / examples/baidu_k11上海·淮海路 K11
examples/jintie / examples/mchq另外两个点位

这些是仓库里预采集的演示点位(见 examples/README.md)—— 不是用户位置。

用离线场景出图时,回复中必须点明是哪个点位,且不得表述为「你附近」。

1.4 执行流程

① 确定位置          用户给的地址 / locate.py → 经纬度
② 拉周边门店        pack_mcd.search_stores()            → 麦当劳门店
                    pack_nearby_dining.search_stores() → 周边本地店
                    (市中心一个路口就近 200 家 → 只选 15 家,但如实显示总数)
③ 选店              walkkcal/select.py
                    ① 麦当劳保底,但 ≤ 1/3
                    ② 四象限轮转(避免地图偏向某一侧)
                    ③ 同象限按「额度内可吃道数 ↓、距离 ↑」
                    ④ 取满 15 止
④ 算额度            quota = 450 + (walk_m // 80 + 1) * 4
⑤ 逐菜取热量(分层,每层携带来源标签)
                    L1  pack.heat_of()      官方表 / 语料命中  →「菜谱推算」±25%
                    L2  prior.dish_prior()  菜名关键词定品类  →「品类估算」±35%
                    L3  prior.store_prior() 门店类型定品类    →「同类估算」±45%
                    L4  均不命中             → 弃权(不给数值)
                    verdict = '吃下' if kcal <= quota else '超了'
⑥ 再取网点定性      低于缺口(绿) / 适配缺口(黄) / 高于缺口(红)
⑦ 渲染长图          python walkkcal/render.py

★ 分层估算的效果(6 个演示场景 1236 条推荐菜实测):只用 L1 覆盖 70.6%, 加入 L2/L3 后 98.1%,弃权 1.9%。每个数值都携带自身可信度。

1.5 输出契约

一次调用返回四项(契约见 walkkcal/reply.py):

字段内容给谁
text结论句,三段式「结论 | 依据 | 下一步」人和 Agent
image480px 竖屏长图人
metatotal / shown / page / pages / keywordAgent(决定下一步)
next2~3 个建议的下一步参数用户(转成问句或按钮)

text 示例:

额度 450 卡。附近 47 家餐饮,24 家进入候选(麦当劳最多占 1/3),本批列 15 家(第 1/2 批)。 最合适的是「星巴克」——走 166 米,7 道在额度内。要换一批看看吗?

视觉编码(两套编码各管一件事):

  • 形状 = 业态(碗 = 餐馆 / 汉堡 = 麦当劳 / 咖啡杯 / 购物袋 / 火锅)
  • 颜色 = 热量(绿 = 低于缺口 / 黄 = 适配 / 红 = 高于缺口)

1.6 参数速查

参数默认说明
位置—必填。零参数时先走 locate.py;也可 --lat/--lon 直接给
基础额度450 卡一顿饭的预算基线。用户说「只吃 400 卡」→ --quota 400
步行折算4 卡 / 80 米走 80 米挣 4 卡
每店最多菜8 道不足则全列
档位阈值0.70× / 1.00× 额度低于 / 适配 / 高于缺口
industry不限业态限定,11 个可选(见 walkkcal/filter.py)
radius_m命令行 800 / 数据层 1500搜索半径。「别走太远」用 500
page0批次号;「换一批」加 1
每批家数15480px 竖屏的可读上限
麦当劳占比≤ 1/3避免结果退化为「麦当劳推荐器」

★ --radius 有两层默认值:shoot.py 命令行 800 米,数据层 nearby_dining() 1500 米。 需要收窄就显式传 --radius。


二、装与用(人)

2.1 安装:一个目录 + 这份文件

所有支持 Skill 的宿主都是同一个办法:建一个技能目录,把这份 SKILL.md 放进去。

只装你正在用的那一个,不需要全装:

宿主用户级技能目录
WorkBuddy~/.workbuddy/skills/
Trae CN~/.trae-cn/skills/
Codex CLI~/.codex/skills/
Claude Code~/.claude/skills/
千问 / Qwen Code~/.qwen/skills/
# 把 <宿主目录> 换成上表里你用那一个(示例:Trae CN)
mkdir -p ~/.trae-cn/skills/walk-kcal
cp SKILL.md ~/.trae-cn/skills/walk-kcal/
  • 也可以不走技能目录 —— 在会话里直接 @ 引用这份文件即可(技能就是一份 Markdown 说明书)。
  • 装好后新开一个会话,说「附近吃什么」就能触发。
  • 改了仓库里的 SKILL.md 后要重新复制一次(技能目录里是副本,不是软链)。
  • 本文件不含任何机器的绝对路径,副本放哪儿都不影响运行。 唯一的硬要求见下面第 1 条。

2.2 运行前提

#前提说明
1在仓库根目录运行所有命令都是相对根目录写的(python walkkcal/…)。如果你的工作目录不是这里,先 cd 过来
2麦当劳数据(可选,默认启用)MCD_MCP_TOKEN。不配也能用 —— 项目内置了一份官方数据快照(收录 demo 用到的 22 条,效果完整;官方全表 158 条,配 Token 走实时可取全);配了就走实时
3地图数据(可选,默认用高德)负责「周边有哪些店、分别多远」。默认高德,所以默认要 AMAP_KEY;换百度就设 BAIDU_AK + WALKKCAL_GEO=baidu;接其他家只需加一个 providers/geo_xxx.py。不配时只能用仓库里已存好的演示点位(那不是用户位置)
4出图的扩展包(可选,默认启用)默认输出 SVG 图,不装任何包也能出图。想额外得到 PNG(方便发微信 / 小红书),装一个小扩展包即可:pip install -r requirements.txt。除此之外所有功能只用 Python 自带库

这四项没有一项是"必须先有"的 —— 缺哪一项,项目都仍然能跑,只是能力范围相应收窄。

2.3 配置:Key 放在哪

不要把 Key 写进 .py 文件(那样会被一起提交上去)。统一从配置文件或环境变量读。

唯一入口是 walkkcal/config.py,取值顺序如下(环境变量优先级最高):

① 环境变量
② WALKKCAL_CONFIG 指向的 JSON(设了就用它,不再往下看)
③ <仓库>/config.local.json                    ← 项目配置文件(推荐放这里)
④ <仓库>/walkkcal/config.local.json
# 第一次用:从模板复制一份,填 Key
cp config.example.json config.local.json
# 查看配置从哪里读到(只报告有没有值,不打印值本身)
python walkkcal/config.py
  • 配置文件格式是 JSON(不是 YAML)—— 本项目坚持零第三方依赖,而读 YAML 需要额外装库。
  • config.local.json 已被 .gitignore 排除,永远不会进版本库。
  • 程序报错信息里的 Key 会被自动替换成 <已屏蔽 XXX>,不会顺着日志漏出去。
  • 若担心误提交,可以自查仓库里有没有形如 32 位十六进制串的密钥常量。

最小可用配置 = 一套「店 + 菜」的数据源。 麦当劳和地图是并列的两套来源, 配任意一套就能跑:

配了你会得到
麦当劳 Token麦当劳门店 + 官方精确营养值(国内唯一提供官方营养值的品牌)
地图 Key(默认高德)你周边任意餐厅 + 网友推荐菜 + 真实步行距离 + 热量估算
两个都配(推荐)两者合并成一份结果

一个都不配也能跑 —— 项目内置了数据快照(含麦当劳 158 条营养值 + 几个演示点位), 效果完整,只是不针对你的位置。

麦当劳的实时门店检索需要一个能翻译坐标的地图 Key(它的接口不接收经纬度), 细节见 3.2。

2.4 30 秒验证

cd <仓库根目录>
cp config.example.json config.local.json    # 首次:填 AMAP_KEY
python walkkcal/shoot.py "上海市浦东新区凌河路618弄95号"

⚠️ 上面这个地址只是示例,不是默认点位 —— 实际使用时换成用户给的位置。 命令会打印池子家数 / 请求次数 / 耗时,并生成一张图(约 16 秒)。 如果没装出图扩展包,产出的是同名 .svg —— 这是正常现象,不是故障。

也可以不输地址,直接自动定位(Windows 上基于 WiFi 指纹,实测精度 150~500 米):

python walkkcal/shoot.py

★ 想临时用环境变量覆盖 Key:AMAP_KEY=真实key python walkkcal/shoot.py "…" (别真写成 AMAP_KEY=xxx —— 那会把 Key 设成字面量 xxx,然后报无权限。)

看到「构建期自检通过」加一个图片文件,就说明整条链路是通的。

2.5 常用命令

# ★ 一步出图(推荐):给一个地址就行 —— 解析 → 抓数 → 渲染,约 16 秒
python walkkcal/shoot.py "上海市黄浦区南京东路100号"
# → out/上海市黄浦区南京东路100号_76fd57.png

python walkkcal/shoot.py "陆家嘴" --quota 400              # 这顿只吃 400 卡
python walkkcal/shoot.py "苏州市姑苏区观前街" --city 苏州   # 跨城市必须 --city
python walkkcal/shoot.py "陆家嘴" --radius 1000 --out out/ljz.png
python walkkcal/shoot.py "人民广场" --keep-scene           # 留下场景数据供排查

# 渲染已有场景(不联网,1~2 秒;适合换业态 / 翻页)
python walkkcal/render.py
WALKKCAL_DATA=examples/linghelu python walkkcal/render.py

# 只采集数据、不出图
python walkkcal/fetch_pois_at.py <lat> <lon> <radius> <场景名> --scene

两者的分工:shoot.py 用于只有地址、要联网抓一个新点位; render.py 用于已有场景数据、只想换业态或翻页。

⚠️ 不传 WALKKCAL_DATA 时,render.py 读的是 examples/default = 上海·静安虬江路 —— 那是演示点位,不是用户位置。

★ 抓取速度(134 秒 → 14 秒)来自把「逐店算距离」改成批量算距离 (高德 /v3/distance 一次能收 100 个目的地),请求数从 111 降到 9~12 次。 不要为了更快去调小请求间隔 —— 地图配额按并发计算,百度实测贴到 3 QPS 就会收到告警。

2.6 环境变量

render.py 没有命令行参数,离线渲染只能靠环境变量。

变量默认作用
AMAP_KEY—高德地图 Key(当前默认的地图源)。换百度 → BAIDU_AK + WALKKCAL_GEO=baidu
MCD_MCP_TOKEN—麦当劳官方营养值 / 实时找店。不设则读内置快照
BAIDU_AK—换百度地图时用
WALKKCAL_CONFIG—显式指定配置文件路径
WALKKCAL_DATAexamples/default场景目录(examples/k11、examples/jintie…)
WALKKCAL_QUOTA450额度基线(--quota 的离线等价物)
WALKKCAL_FILTER不限业态过滤(西餐 / 快餐 / 咖啡…)
WALKKCAL_PAGE0批次号;「换一批」加 1
WALKKCAL_RADIUS0 = 不限半径筛选(米)
WALKKCAL_DRINK0设 1 放出「只卖喝的」饮品店
WALKKCAL_OUTout/输出目录
WALKKCAL_MCD_LIVE关设 1 打开麦当劳实时找店(需 MCD_MCP_TOKEN + 地图 Key)
DISH_MODEB(离线)/ C(shoot.py)菜品配色方案

★ 每个 Key 都有两种放法:环境变量,或 config.local.json。 环境变量优先;配置文件的好处是不会跟着代码一起被提交。 查看当前实际读到哪个:python walkkcal/config.py。


三、技术说明

3.1 调用了哪些 MCP 能力

MCP ServerTool用途必要性
麦当劳 MCP https://mcp.mcd.cnlist-nutrition-foods官方 158 条营养数据麦当劳渠道的核心
麦当劳 MCPquery-nearby-stores门店与距离。入参只有 searchType/city/keyword/beType,不接收经纬度 → 传坐标前需先逆地理编码推荐
麦当劳 MCPdelivery-query-addresses用户在麦当劳 App 中存过的地址 —— 唯一「不需要地图 Key 也能拿到位置线索」的官方途径可选
高德 MCP https://mcp.amap.com周边检索 / 步行路径本地餐厅 + 真实步行距离推荐

配置见 mcp-config.example.json(只含 ${MCD_MCP_TOKEN} 占位符)。 不配 Token 也能跑离线演示:读 examples/default/ 快照 + packs/mcd_nutrition_snapshot.py。

3.2 麦当劳实时路径(默认关闭)

让麦当劳走实时需要两个 Key:因为它的门店接口不接收经纬度, 必须先用地图 Key 把坐标翻译成文字地标。

用户坐标 ──① 地图 Key 逆地理编码──▶ city + keyword ──② MCD_MCP_TOKEN──▶ 官方门店
缺什么结果
缺 MCD_MCP_TOKEN调不动 MCP → 读本地快照(默认路径:McdPack(offline=True))
缺地图 Key翻译不出 city/keyword → 连 MCP 都不会调,直接回退快照并说明原因
两个都有走官方实时;每行门店带 src='mcp'

自检命令(会打印每条渠道的门店来源与回退原因):

WALKKCAL_MCD_LIVE=1 python packs/registry.py
#   · src=mcp        → 走的是官方实时
#   · src=snapshot   → 回退到本地快照,同行写明原因

★ 通用接口一律用经纬度(search_stores(lon, lat, radius))—— 经纬度是普适的,接口不为任何一家的格式降级;格式翻译统一关在各自的适配层里。

★ 两条链各自的取数方式:

链怎么取数
文字链(MCP / Bot)有坐标 → 实时抓(provider.nearby_dining() + 批量算距离);无坐标或抓失败 → 退内置快照,并把原因写进 registry.notes()
图片链(render.py)读 examples/<场景>/*.json 快照

两者默认同源:shoot.py 先实时抓成场景,渲染再读它。 (图上的麦当劳来自地图 POI 的名称匹配,不来自 MCP —— 所以打开实时后文字与图片可能不一致。)

3.3 诚实边界(必须在输出中说明)

  1. 中餐热量由菜谱推算,平均误差 25-28%;麦当劳是 MCP 官方精确值
  2. 规则已预设:只数推荐菜,不含饮料甜品;多种做法取平均
  3. 算不出的不猜 —— 宁可显示「算不出」,不给假数字
  4. 数据为快照,不构成医疗建议

详细规则见仓库 README.md 与 docs/ 下的技术文档。

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

Report a problem with this skill