news 2026/10/9 10:15:30

SparkyFitness Check-in 提示词工程解析:自定义测量类别匹配与 Mood Emoji 保真机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SparkyFitness Check-in 提示词工程解析:自定义测量类别匹配与 Mood Emoji 保真机制
  • 后端
  • 前端
  • 移动开发

【免费下载链接】SparkyFitness

SparkyFitness: Built for Families. Powered by AI. Track food, fitness, water, and health — together.

项目地址:https://gitcode.com/gh_mirrors/sp/SparkyFitness
点击查看免费下载

导读

本文围绕 SparkyFitnessServer/prompts/chatbot-core-checkin.md 这份系统提示词片段展开,剖析 SparkyFitness 的 AI 营养与健康教练(Sparky)在"每日打卡(check-in)"域的两条核心提示规则:将用户输入精确匹配到自定义测量类别,以及在确认/描述 mood 时必须保留关联 Emoji。阅读本文后,你将理解该提示词在服务端提示词组合架构中的加载位置、变量注入的数据来源、底层工具与共享数据结构的实现细节,以及测试用例如何锁定这些行为。

一、提示词文件在系统中的定位:按域动态拼接的架构

chatbot-core-checkin.md不是独立成篇的提示词,而是 Sparky 系统提示词按功能域拼接体系中的"check-in 片段"。完整的拼接逻辑位于 SparkyFitnessServer/services/chatService.ts 的getSystemPrompt():

if (categories.has('checkin')) { const checkinPath = path.join( __dirname, '../prompts', `chatbot-${suffix}-checkin.md` ); if (existsSync(checkinPath)) { content += '\n\n' + readFileSync(checkinPath, 'utf-8').trim(); } }

关键机制:

  • 后缀(suffix)决定版本:chatbot-core-checkin.md与chatbot-full-checkin.md同属 check-in 域,suffix由ChatToolProfile('full'或'core')决定,二者对应不同粒度的模型配置,提示词措辞随之切换。core 版强调"Compare inputs to the custom categories. If you find a match, use the exact category name.",full 版则扩展为"compare user inputs to the list above. If you find a match or variations (synonyms, capitalization), use the exact category name"。
  • 域由分类器决定:同一文件中的意图分类器会把用户消息归入checkin域,匹配关键词包括weight/height/waist/hips/neck/body fat/checkin/scale/bmi/mood/sleep/fasting/measurements/progress photo/sleep debt/chronotype等(见 chatService.ts 的category: 'checkin'正则),只有命中时才注入该片段。
  • 占位符运行时替换:拼接完成后统一执行content.replace(/\${customCategories}/g, customCategoriesList)与content.replace(/\${today}/g, todayInZone(chatTz)),将模板变量替换为用户级真实数据。

基座提示词 SparkyFitnessServer/prompts/chatbot-core.md 定义了整体人格与"直接执行工具、成功即确认"的对话策略,check-in 片段在其之上追加域专属约束。

二、${customCategories}变量注入:用户自定义测量类别的数据链路

提示词开头的${customCategories}是模板占位符,其数据来源是用户在应用中自行创建的自定义测量类别(如血压、腰围、自定义身体指标等)。

2.1 数据获取

SparkyFitnessServer/models/measurementRepository.ts 中的getCustomCategories(userId)从数据库读取该用户的全部自定义类别;SparkyFitnessServer/services/measurementService.ts 在getCustomCategories上封装了"代理用户(on-behalf-of)"语义:当targetUserId与当前认证用户不同时,优先返回目标用户的数据。

2.2 格式化为提示词列表

chatService.ts 使用带 60 秒 TTL 的缓存(chatContextInputsCache)并发加载类别与用户时区,随后把类别格式化为 Markdown 列表注入提示词:

customCategoriesList: customCategories.length > 0 ? customCategories .map( (cat: DatabaseCustomCategories) => `- ${cat.name} (${cat.measurement_type}, ${cat.frequency})` ) .join('\n') : 'None'

即最终注入形如:

- Blood Pressure (mmHg, Daily) - Waist Circumference (cm, Weekly)

每条包含name、measurement_type(单位)与frequency(频率),帮助 LLM 既知道类别名,也知道其语义单位与采集频次。

2.3 为什么需要这份上下文

sparky_manage_checkin工具的log_custom_metricaction 要求category_name与用户已存在的类别名精确一致。由于自定义类别完全由用户命名,LLM 若不被告知确切名称,很容易自创近似名称导致写入失败或产生重复类别。将完整清单注入提示词,是让"用户说 '血压' → 工具收到 'Blood Pressure'"这一对齐成为可能的前提。

三、精确类别名匹配的底层实现与测试锁定

提示词要求"if you find a match, use the exact category name"。这一约束并非只靠提示词约束,底层工具在匹配时也做了归一化兜底。查看 SparkyFitnessServer/ai/tools/checkinTools.ts 的log_custom_metric分支,服务端会基于用户类别列表做大小写不敏感的名称匹配,命中后使用类别内部 ID 写入。

测试 SparkyFitnessServer/tests/chatbotToolsCheckin.test.ts 明确锁定了这一行为:

describe('log_custom_metric', () => { it('logs against a case-insensitively matched category', async () => { vi.mocked(measurementService.getCustomCategories).mockResolvedValue([ { id: 'c1', name: 'Blood Pressure', measurement_type: 'mmHg', frequency: 'Daily' }, ]); // 用户输入 'blood pressure'(小写)也能命中类别 'Blood Pressure' await tools.sparky_manage_checkin.execute!({ action: 'log_custom_metric', category_name: 'blood pressure', value: 120, // ... }); }); });

由此可见:提示词层要求 LLM 输出"exact category name"(保证数据规范),工具层再以大小写不敏感匹配做工程兜底(容忍用户口语差异),full 版提示词中的 "variations (synonyms, capitalization)" 正是对这一兜底语义的提示级复述。

四、Mood Emoji 保真规则:从提示词到共享数据模型

提示词后半段是强约束(MUST):

When confirming a logged mood or describing the user's mood for today, you MUST always include the smileys/emojis associated with the mood tags (for example: "😰 Anxious", "Irritable 😤", "🥺 Sensitive", "😴 Tired"). Do not strip them out or write them as plain text only.

4.1 内置 mood 与 Emoji 的权威映射

mood 标签与 Emoji 的权威映射定义在共享包 shared/src/mood/index.ts 的BUILT_IN_MOODS常量中。它分为两类:

带数值区间(band)的 9 个强度型 mood——与遗留 0-100 MoodMeter 刻度一一对应,用于回填旧数据和从设备同步的数值 mood 推导标签:

namedisplayNameemojiband(0-100 上限)
sadSad😢15
angryAngry😠25
worriedWorried😟35
neutralNeutral😐45
thoughtfulThoughtful🤔55
calmCalm🙂65
confidentConfident😎75
happyHappy😀85
excitedExcited😍100

6 个纯描述型 tag(无 band)——与强度评分并列使用:

namedisplayNameemoji
energeticEnergetic⚡
sensitiveSensitive🥺
tiredTired😴
lowLow energy🔋
anxiousAnxious😰
irritableIrritable😤

提示词示例中的"😰 Anxious"、"Irritable 😤"、"🥺 Sensitive"、"😴 Tired"均可在上表中找到对应条目,属于anxious、irritable、sensitive、tired四个内置 tag。每条 MoodDef 还携带icon与color(如mood-calm/sky),供前端渲染使用。

4.2 服务端格式化:formatMoodTags是保真的工程底座

提示词要求 LLM 输出 Emoji,服务端则在两个出口用同一个formatMoodTags函数强制保真(checkinTools.ts):

function formatMoodTags(tags?: string[] | null): string { if (!tags || !tags.length) return ''; const formatted = tags.map((t) => { const matched = BUILT_IN_MOODS.find( (m) => m.name.toLowerCase() === t.toLowerCase() ); if (matched) { return `${matched.emoji} ${matched.displayName}`; } return t; // fallback if it is a custom tag or not found }); // ... }
  • 日志确认出口:log_moodaction 写入成功后返回Mood logged for ${date}: ${value}/10${tagsStr} — ${notes},tagsStr 即格式化结果。
  • 日记查询出口:list_checkin_diary渲染## Mood区块时同样调用该函数,保证历史回看也带 Emoji。

formatMoodTags对内置 tag 做大小写不敏感匹配并输出emoji + displayName,对用户自定义 tag 则原样回退,与提示词的保真要求互为表里。

4.3 测试如何锁定 Emoji 保真

chatbotToolsCheckin.test.ts 对两个出口都做了断言:

// 日记渲染出口 expect(result).toBe( '### Check-in Diary: 2026-06-01\n\n' + '## Mood\n' + '- 7/10 [😰 Anxious, 😟 Worried] — Feeling okay\n\n' ); // 日志确认出口 expect(result).toBe( '✅ Mood logged for 2026-06-01: 8/10 [😰 Anxious, 😴 Tired] — Feeling good.' );

两个断言精确到 Emoji 字符,意味着任何"剥离 Emoji、只写纯文本"的回归都会导致测试失败——提示词中的 MUST 由此获得可执行的工程保障。

五、提示词工程视角:为什么"不剥离 Emoji"是硬约束

从交互设计看,mood 是高度情绪化的数据:Emoji 是用户选择 tag 时看到的视觉符号(前端基于BUILT_IN_MOODS的 emoji/icon 渲染),若 LLM 确认回复里只写 "Anxious" 而不写 "😰",用户会感到确认与自身选择脱节。让确认文本与服务端渲染、前端展示三者使用同一套emoji + displayName格式,才能形成"看到即反馈、反馈即所见"的闭环。

这也解释了为什么提示词同时出现smileys/emojis与具体示例:LLM 需要既知道"必须输出 Emoji",又知道"以emoji 空格 名称的形式输出",二者缺一不可。

六、实践要点与配置小结

针对 SparkyFitness 服务端开发者或提示词维护者,可沉淀以下 checklist:

  1. 维护${customCategories}注入格式:chatService.ts中的格式化决定了 LLM 能看到什么。若调整measurement_type或frequency的展示语义,需同步确认getCustomCategories返回字段与DatabaseCustomCategories类型一致。
  2. 新增内置 mood 时同步三处:在shared/src/mood/index.ts的BUILT_IN_MOODS添加条目(含 emoji、displayName、icon、color、可选 band),formatMoodTags自动生效;若属于强度型(band),需确认moodValueToTag/representativeMoodValue的区间推导仍正确。
  3. 不要绕过工具直接拼文本:所有 mood 相关回复都应基于log_mood/list_checkin_diary返回的格式化结果,避免 LLM 自造 Emoji 与BUILT_IN_MOODS不一致。
  4. 回归保护:chatbotToolsCheckin.test.ts中的 Emoji 断言与log_custom_metric的大小写不敏感匹配测试,是这两条提示规则的守护网,修改提示词措辞不应破坏对应工具行为。
  5. core 与 full 版本保持语义一致:chatbot-core-checkin.md与chatbot-full-checkin.md措辞有简繁差异(full 版补充了 synonyms/capitalization 说明),但核心规则一致;修改其一需同步评估另一版本,避免同域两种模型行为漂移。

参考文件索引

  • 提示词片段:SparkyFitnessServer/prompts/chatbot-core-checkin.md、SparkyFitnessServer/prompts/chatbot-full-checkin.md
  • 拼接与变量注入:SparkyFitnessServer/services/chatService.ts
  • 工具实现:SparkyFitnessServer/ai/tools/checkinTools.ts
  • 共享 mood 数据模型:shared/src/mood/index.ts
  • 数据获取:SparkyFitnessServer/models/measurementRepository.ts、SparkyFitnessServer/services/measurementService.ts
  • 测试锁定:SparkyFitnessServer/tests/chatbotToolsCheckin.test.ts
  • 后端
  • 前端
  • 移动开发

【免费下载链接】SparkyFitness

SparkyFitness: Built for Families. Powered by AI. Track food, fitness, water, and health — together.

项目地址:https://gitcode.com/gh_mirrors/sp/SparkyFitness
点击查看免费下载

相关推荐

上一篇:D2DX源码剖析一:Microsoft Detours API钩子与内联汇编Naked Hook实战
下一篇:LargeImageModel完整API清单:largeImage4cj流式配置指南,一张表看懂全部接口

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/9 10:12:52

ZBF文件解析:光场复振幅数据的物理本质与Python读取

1. 项目概述:ZBF 文件不是“神秘黑盒”,而是物理光学传播的数字快照你有没有在光学设计软件里导出过一个后缀为.zbf的文件?它不像.zmx那样能直接打开编辑,也不像.png那样一眼就能看出内容,更不会被 Windows 资源管理器…

作者头像 李华
网站建设 2026/10/9 10:12:36

音视频修炼之编码器(四):并行架构

编码器并行架构4K 60fps 视频编码,如果单线程要 200ms / 帧,就只能跑到 5fps;想跑实时,必须靠帧级并行、片级并行、Tile / WPP、SIMD、GPU 和集群把吞吐堆起来。本文速览章节阅读重点一句话结论0. 三种并行方式先建立帧级、片级、…

作者头像 李华
网站建设 2026/10/9 10:12:26

Audacity 免费多轨音频编辑指南:5 步从录音到 MP3 成品

Audacity 免费多轨音频编辑指南:5 步从录音到 MP3 成品 【免费下载链接】audacity Audio Editor 项目地址: https://gitcode.com/GitHub_Trending/au/audacity 刚录完的播客一回放,空调嗡声和键盘声全混在人声里,想清理又不知道软件去…

作者头像 李华