# 戒烟算法与 AI 策略说明 ## 1. 核心理念 采用**渐进式递减**策略,而非突然戒断: - 科学研究表明,逐步减少比冷火鸡戒断成功率更高 - 通过延长抽烟间隔,让身体逐渐适应尼古丁减少 - AI 分析个人模式,提供个性化的递减计划 --- ## 2. 默认递减算法 (staircase_delay_v1) ### 2.1 算法概述 ``` 下次建议时间 = 上次抽烟时间 + 基础间隔 + 奖励间隔 ``` ### 2.2 参数说明 | 参数 | 来源 | 默认值 | 说明 | |------|------|--------|------| | base_interval | profile.baseline_interval_minutes | 60 分钟 | 用户初始平均抽烟间隔(为空/0 时默认 60) | | resisted_7d | 近7天忍住次数 | 0 | level=0,num=0 的记录数 | | bonus_interval | 计算得出 | 0 | 奖励延长时间 | ### 2.3 奖励机制 每累计 **5 次忍住**,基础间隔 **+5 分钟**,最多 **+60 分钟**: ``` bonus_interval = min(floor(resisted_7d / 5) * 5, 60) final_interval = clamp(base_interval + bonus_interval, 5, 240) ``` **示例**: - 用户基础间隔 48 分钟,近 7 天忍住 12 次 - bonus = floor(12/5) * 5 = 10 分钟 - 最终间隔 = 48 + 10 = 58 分钟 ### 2.4 睡眠规避 若计算出的时间落在睡眠区间,顺延到**下一次**起床时间(可能是当天也可能是次日): ``` if suggested_time in [sleep_time, wake_up_time]: suggested_time = next_day_wake_up_time ``` ### 2.5 边界与兜底 - 若没有历史记录,则以“当前时间”作为 `last_smoke_at` 参与计算。 - 若生成未来日期计划(如明天),默认建议不早于该日起床时间;未配置作息时按 `07:00` 处理。 ### 2.6 算法流程图 ``` 获取上次抽烟时间 (last_smoke_at, 若无记录则取当前时间) ↓ 获取用户基础间隔 (base_interval_minutes) ↓ 统计近7天忍住次数 (resisted_7d) ↓ 计算奖励间隔: bonus = min(floor(resisted_7d / 5) * 5, 60) ↓ 计算建议时间: suggested = last_smoke_at + base + bonus (并限制在 5~240 分钟区间) ↓ 检查是否在睡眠时间? ├── 是 → 顺延到起床时间 └── 否 → 返回建议时间 ``` ### 2.7 过期/未来记录兜底 - 如果用户补录了“未来时间”的抽烟记录,为了避免前端出现负倒计时,服务端会把 `last_smoke_at` 限制在当前 `as_of` 时刻以内。 - 当 `plan_date` 是“今天”且 `last_smoke_at + interval` 早于当前时间时,会根据已过去的分钟数计算需要补齐的间隔,向前跳跃若干次直到结果落在未来。这样首页和 `GET /next_smoke_time` 都能返回一个“未来的下一次建议时间”,不会出现提示已经过期的时间点。 - 如果 `plan_date` 是将来日期(例如明天的日程),仍然按照指定日期的起床时间作为 `not_before_at`,不会使用上述补齐逻辑。 --- ## 3. AI 增强算法 ### 3.1 AI 时间节点规划 当用户解锁 AI 功能后,系统会: 1. **收集近 3 天数据**: - 每次抽烟的时间点 - 每次忍住的时间点 - 抽烟原因/场景标签 2. **分析抽烟模式**: - 高峰时段识别(如下午 2-4 点) - 触发场景识别(如压力、无聊、社交) - 间隔规律分析 3. **生成个性化时间节点**: - 避开高峰时段的前半小时 - 在用户通常能忍住的时段设置节点 - 逐日递增间隔 ### 3.2 AI 建议内容 AI 会生成以下内容(实际接口格式): ```json { "not_before_at": "2026-01-05T10:18:00+08:00", "suggested_at": "2026-01-05T10:28:00+08:00", "time_nodes": ["09:30", "12:30", "15:30", "19:00", "22:00"], "advice": "昨天你的吸烟量比限额少了2支,这是一个巨大的胜利!数据显示你的烟瘾在下午2点左右达到顶峰——今天试着那个时候去散散步。" } ``` ### 3.3 AI Prompt 设计 ``` 你是一位专业的戒烟辅导教练。基于用户的抽烟数据,提供个性化的戒烟建议。 用户档案: - 日均吸烟量:{baseline_cigs_per_day} 支 - 烟龄:{smoking_years} 年 - 抽烟动机:{smoke_motivations} - 戒烟动力:{quit_motivations} - 作息:{wake_up_time} - {sleep_time} 近3天数据: {recent_logs} 请分析: 1. 用户的抽烟规律和高峰时段 2. 主要的触发场景 3. 成功忍住的模式 然后生成: 1. 一段鼓励性的分析总结(2-3句话) 2. 明天的建议抽烟时间节点(比今天少1支) 3. 2-3条实用的应对建议 ``` --- ## 4. 阶段划分 ### 4.1 三阶段戒烟计划 | 阶段 | 时间 | 目标 | 策略 | |------|------|------|------| | 记录期 | Day 1-7 | 建立基线 | 正常抽烟,但每次都记录 | | 减量期 | Day 8-21 | 减少 50% | 每周目标递减,AI 指导 | | 巩固期 | Day 22-30 | 维持/归零 | 强化抵抗,心理建设 | ### 4.2 阶段进度计算 ```javascript // utils/stage.js function calculateStage(startDate) { const daysSinceStart = daysBetween(startDate, new Date()) if (daysSinceStart <= 7) { return { stage: 1, name: '记录期', progress: daysSinceStart / 7, daysLeft: 7 - daysSinceStart } } else if (daysSinceStart <= 21) { return { stage: 2, name: '减量期', progress: (daysSinceStart - 7) / 14, daysLeft: 21 - daysSinceStart } } else { return { stage: 3, name: '巩固期', progress: Math.min((daysSinceStart - 21) / 9, 1), daysLeft: Math.max(30 - daysSinceStart, 0) } } } ``` ### 4.3 每日目标计算(线性递减) 以 onboarding 完成日期为起点,到 `quit_date` 线性递减到 0: ```javascript // utils/target.js function calculateDailyTarget(baseline, startDate, quitDate, today) { if (!baseline || !startDate || !quitDate) return baseline if (today >= quitDate) return 0 const totalDays = daysBetween(startDate, quitDate) if (totalDays <= 0) return baseline const remainingDays = daysBetween(today, quitDate) const target = Math.round(baseline * (remainingDays / totalDays)) return remainingDays > 0 ? Math.max(target, 1) : 0 } ``` --- ## 5. 健康恢复计算(后端统一) 接口:`GET /api/v1/smoke/stats?range=week|month|year`(详见 `docs/smoke/API.md`) 基于医学研究的恢复时间线: | 时间点 | 恢复指标 | 计算方式 | |--------|----------|----------| | 20分钟 | 心率血压恢复正常 | 固定 | | 8小时 | 血氧水平恢复 | 固定 | | 24小时 | 心脏病风险开始下降 | 固定 | | 48小时 | 嗅觉味觉开始恢复 | 固定 | | 2周 | 肺功能提升 15% | 线性计算 | | 1月 | 肺功能提升 30% | 线性计算 | | 3月 | 肺功能提升 50% | 线性计算 | | 1年 | 心脏病风险降低 50% | 线性计算 | ```javascript // utils/health.js function calculateLungRecovery(smokeFreeMinutes) { const days = smokeFreeMinutes / (24 * 60) if (days < 14) { return (days / 14) * 15 } else if (days < 30) { return 15 + ((days - 14) / 16) * 15 } else if (days < 90) { return 30 + ((days - 30) / 60) * 20 } else { return Math.min(50 + ((days - 90) / 275) * 50, 100) } } ``` --- ## 6. 省钱计算(后端统一) 接口:`GET /api/v1/smoke/stats?range=week|month|year`(详见 `docs/smoke/API.md`) ```javascript // utils/money.js function calculateMoneySaved(packPriceCent, cigsPerPack, baselineCigsPerDay, actualCigsTotal, days) { const expectedTotal = baselineCigsPerDay * days const savedCigs = expectedTotal - actualCigsTotal const savedPacks = savedCigs / cigsPerPack return Math.round(savedPacks * packPriceCent) } ``` --- ## 7. 激励语生成(后端统一) 接口:`GET /api/v1/smoke/motivation`(详见 `docs/smoke/API.md`) 根据用户状态生成不同的激励语: ```javascript // utils/motivation.js function getMotivationMessage(context) { const { minutesSinceLast, todayCount, dailyTarget, resistedToday, quitMotivations } = context if (resistedToday > 0 && minutesSinceLast < 30) { return '太棒了!你刚刚成功抵抗了一次烟瘾' } if (todayCount < dailyTarget * 0.5) { return '今天的表现非常出色,继续保持!' } if (todayCount === dailyTarget - 1) { return '还剩最后一支配额,考虑把它留到睡前?' } if (todayCount > dailyTarget) { return `没关系,明天是新的一天。记住你为什么要戒烟:${quitMotivations[0]}` } return '保持连胜纪录!' } ``` --- ## 8. 数据分析指标 ### 8.1 关键指标 | 指标 | 计算方式 | 用途 | |------|----------|------| | 日均吸烟量 | 周期内总量 / 天数 | 趋势对比 | | 周同比变化 | (本周 - 上周) / 上周 | 进度评估 | | 忍住成功率 | 忍住次数 / (忍住+抽烟次数) | 意志力评估 | | 平均间隔 | 总时长 / 抽烟次数 | 递减效果 | | 最长无烟时长 | 最大间隔记录 | 成就激励 | | 较昨日减少 | 昨日支数 - 今日支数(可为负) | 若为负,表示今天超出昨日 | ### 8.2 周报数据结构 ```javascript // 周报数据结构 const weeklyReport = { period: { start: '2026-01-01', end: '2026-01-07' }, totalCigs: 35, dailyAverage: 5, comparedToLastWeek: -20, // 百分比变化 resistedCount: 12, longestGap: 180, // 分钟 peakHours: ['14:00', '21:00'], topTriggers: ['压力大', '无聊'], achievements: ['连续7天记录', '单日忍住5次'], nextWeekTarget: 4 } ```