从零构建 AI 驱动的 Flutter 应用:HabitGo 的 AIGC 全链路实践

为什么写这篇文章

这段时间我用 Flutter 做了个习惯追踪 App,一开始只是想练手,后来发现把 AI 真正用好、用深 这件事比我想象中有趣得多------不是简单地调一个 /chat/completions 就完了,而是要在 prompt 工程、用户体验、成本控制、个性化、容错兜底之间找到平衡。

这篇文章会把 HabitGo 的 AI 系统从头到尾拆解一遍,包含:

  • API 封装层怎么做
  • Prompt 工程如何面向场景设计
  • 用户反馈驱动的个性化 prompt 进化
  • 自研 Markdown 渲染器的实现
  • 多场景 AI 融合(问候语 / 每日一课 / 成就邮件 / 数据分析报告)
  • 免费版 AI 用量限制的设计

AI 系统全景

先看一张 AI 系统的全景架构图:

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                        UI Layer                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │ AI 对话  │  │ 首页卡片 │  │ 统计报告 │  │ 成就邮件 │   │
│  │ ChatPage │  │ 问候/挑战│  │ 周/月/年 │  │ Mailbox  │   │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘   │
└───────┼──────────────┼──────────────┼──────────────┼─────────┘
        │              │              │              │
        ▼              ▼              ▼              ▼
┌─────────────────────────────────────────────────────────────┐
│                     Service Layer                            │
│  ┌──────────────────────────────────────────────────────┐   │
│  │              DeepSeekService (Static)                 │   │
│  │  chat() │ analyzeHabits() │ dailyEncouragement()     │   │
│  │  generateAchievementMail() │ generateSuggestions()   │   │
│  └────────────────────┬─────────────────────────────────┘   │
│                       │                                      │
│  ┌────────────────────┴─────────────────────────────────┐   │
│  │           AiPersonalization (个性化)                   │   │
│  │  addFeedback() │ buildPersonalizedPrompt()            │   │
│  └──────────────────────────────────────────────────────┘   │
│                                                              │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐    │
│  │ DailyGreeting│ │ DailyLesson  │ │ DailyChallenge   │    │
│  │ Service      │ │ Service      │ │ Service           │    │
│  └──────┬───────┘ └──────┬───────┘ └───────┬──────────┘    │
└─────────┼────────────────┼─────────────────┼────────────────┘
          │                │                 │
          ▼                ▼                 ▼
┌─────────────────────────────────────────────────────────────┐
│                    Cache Layer (SharedPreferences)           │
│  每日一份,日期 → 内容映射,跨 App 生命周期持久化              │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│                     Monetization                            │
│  FreeTierLimits │ AiUsageTracker │ aiChatRemainingProvider  │
└─────────────────────────────────────────────────────────────┘

AI 不是作为一个独立模块存在,而是像血液一样渗透到产品的各个毛细血管。下面逐一拆解。


一、API 封装层:不只是包个 HTTP 请求

1.1 静态类 + 多语言自适应 System Prompt

dart 复制代码
class DeepSeekService {
  static const String _apiKey = 'sk-xxx';
  static const String _baseUrl = 'https://api.deepseek.com/v1';
  static const String _model = 'deepseek-chat';

  /// 当前用户语言 --- 在 main.dart 中启动时注入
  static String currentLanguage = '中文';

  static String get _localizedSystemPrompt {
    // 根据语言返回不同语言的 system prompt
    // 覆盖:中文 / English / 日本語 / 한국어 / Français / Español / Deutsch
    if (lang == '中文' || lang == 'zh') {
      return '你是 HabitGo 的习惯养成 AI 教练。'
             '你温暖、鼓励、务实。建议简洁具体...回复使用简体中文。';
    }
    if (lang == 'English' || lang == 'en') {
      return 'You are HabitGo\'s habit-building AI coach...';
    }
    // ... 其他 5 种语言
  }
}

为什么用静态方法而不是实例化?

AI 调用在 App 中是稀疏且低频的(每天顶多几十次),不需要连接池、不需要长生命周期。静态方法足够,同时避免了注入 boilerplate。唯一需要跨模块共享的状态是 currentLanguage,一个静态字符串即可。

1.2 通用 chat() 方法 --- 一次封装,七处复用

dart 复制代码
static Future<String> chat({
  required String userMessage,
  String systemPrompt = '',
  List<Map<String, String>>? extraHistory,
}) async {
  final sys = systemPrompt.isEmpty
      ? _localizedSystemPrompt  // 默认教练 prompt
      : systemPrompt;            // 调用方自定义(如成就邮件、报告生成)

  final messages = <Map<String, String>>[
    {'role': 'system', 'content': sys},
    if (extraHistory != null) ...extraHistory,
    {'role': 'user', 'content': userMessage},
  ];

  final response = await http.post(
    Uri.parse('$_baseUrl/chat/completions'),
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer $_apiKey',
    },
    body: jsonEncode({
      'model': _model,
      'messages': messages,
      'temperature': 0.7,
      'max_tokens': 800,
    }),
  );

  if (response.statusCode == 200) {
    final data = jsonDecode(response.body);
    return data['choices']?[0]?['message']?['content']
        ?? '抱歉,我暂时无法回复。';
  }
  return 'API 请求失败 (${response.statusCode})';
}

设计要点:

参数 设计意图
systemPrompt 为空 → 默认教练 prompt 对话场景最常用,减少调用方心智负担
systemPrompt 非空 → 覆盖默认 成就邮件、报告生成等专用场景
extraHistory 多轮对话上下文注入,不污染 userMessage
temperature: 0.7 在创意性和一致性之间取平衡,既不死板也不跑偏
max_tokens: 800 控制回复长度和成本,移动端用户不需要看论文

二、Prompt 工程:每个场景都是一次「编程」

调用 API 只是骨架,prompt 才是灵魂。HabitGo 为每个 AI 使用场景精心设计了专属 prompt。下面逐个拆解设计思路。

2.1 习惯数据分析 --- 结构化输入 + 格式约束

dart 复制代码
static Future<String> analyzeHabits({
  required List<Habit> habits,
  required int totalCheckins,
  required int activeDays,
  required int bestStreak,
  required int todayCheckins,
}) async {
  final prompt = '''
你是一个习惯养成教练。请分析以下用户的习惯数据,给出 3-5 条简洁的个性化建议(每条不超过 60 字):

📊 用户数据:
- 习惯列表:
${habits.map((h) => '- ${h.icon ?? '🎯'} ${h.name} (${h.isActive ? "进行中" : "已暂停"})').join('\n')}
- 总打卡次数:$totalCheckins
- 活跃天数:$activeDays
- 最佳连击:$bestStreak
- 今日已打卡:$todayCheckins

回复格式:每条建议前加 emoji 编号(1️⃣ 2️⃣ 3️⃣),语气温暖鼓励。
语言:$currentLanguage
''';
  return chat(userMessage: prompt);
}

设计思路

  • 把原始数据直接注入 prompt,让 AI「看到」用户的全部上下文。这不是幻觉风险,而是必要的领域知识注入。
  • 明确约束:3-5 条、每条 ≤60 字、emoji 编号 --- 防止 AI 啰嗦或跑偏。
  • 语气定调:「温暖鼓励」四个字决定了整段回复的情感基调。

2.2 每日问候语 --- 每天一次,不阻塞 UI

dart 复制代码
class DailyGreetingService {
  static Future<String> getGreeting() async {
    final prefs = await SharedPreferences.getInstance();
    final saved = prefs.getString('greeting_date');

    if (saved == _today()) {
      return prefs.getString('greeting_text')!;  // 缓存命中
    }

    // ⚡ 异步生成,不阻塞 UI:先返回旧值或默认值
    _generateAndCache(today);
    return prefs.getString('greeting_text')
        ?? '每一天都是新的开始,坚持就是胜利 ✨';
  }

  static Future<void> _generateAndCache(String today) async {
    final text = await DeepSeekService.chat(
      userMessage: '''
请生成一条今日问候语:
1. 一句温暖的问候(根据时间:早上/下午/晚上)
2. 一句名人名言、古诗词或哲理短句
3. 一句针对习惯养成的鼓励
格式:共 2-3 行,简洁有品位,风格每天不同。总字数不超过 120 字。
''',
      systemPrompt: '你是 HabitGo 的每日问候助手。风格温暖、励志、有文化底蕴。'
    );
    // 缓存到 SharedPreferences
    await prefs.setString('greeting_date', today);
    await prefs.setString('greeting_text', text);
  }
}

设计亮点

  1. 异步生成,先返回兜底值:首页打开后立即返回缓存或默认问候语,后台异步请求 AI。用户感知不到任何加载过程。
  2. 日期作为缓存键2026-07-17 格式的日期字符串作为 key,每天同一份,跨页面切换复用。
  3. 风格差异化指令:「风格每天不同」让 DeepSeek 的随机性(temperature 0.7)转化为产品需要的多样性。

2.3 每日一课 --- 日期驱动的主题轮换

dart 复制代码
class DailyLessonService {
  static const List<String> topics = [
    '宇宙天文', '地理奇观', '历史冷知识', '生物趣闻',
    '心理学效应', '经济学原理', '哲学思考', '文学典故',
    '艺术常识', '科技前沿', '民俗文化', '语言奥秘',
  ];

  static Future<void> _generateAndCache(String today) async {
    // 基于日期决定主题:一年 365 天,循环覆盖 12 个主题
    final dayOfYear = DateTime.now()
        .difference(DateTime(2024, 1, 1)).inDays.abs();
    final topic = topics[dayOfYear % topics.length];

    final prompt = '''
请讲一个关于「$topic」的有趣冷知识:
1. 真实可靠(基于科学/历史事实)
2. 3-5 句话,约 80-120 字
3. 开头用一个 emoji 表示主题
4. 结尾加一句"今日启发"
语言:${DeepSeekService.currentLanguage}
''';
    // 调用 + 缓存 ...
  }
}

核心设计dayOfYear % 12 让 365 天均匀覆盖 12 个领域,既保证了多样性,又确保了确定性(同一天不重复生成)。

2.4 成就邮件 --- 最长、最精细的 prompt

dart 复制代码
static Future<String> generateAchievementMail({
  required String userName,
  required String achievementName,
  required int currentStreak,
  required int totalCheckins,
}) async {
  final prompt = '''
给 $userName 写一封达成「$achievementName」成就的鼓励信:
- 当前连击:$currentStreak 天
- 累计打卡:$totalCheckins 次

要求:
- 3-4 段,200-300 字
- 称呼亲切、语气温暖
- 引用一句名人名言/古诗词
- 展望下一个目标
- 结尾鼓励继续

语言:$currentLanguage
''';
  return chat(userMessage: prompt, systemPrompt: '你是 HabitGo 的习惯养成教练,会给用户写温暖的鼓励信。');
}

这是整个系统中最长、约束最多的 prompt。为什么不直接用对话接口?因为邮件是一个独立的「文体」------它需要结构感、仪式感,而这些需要通过 prompt 显式传达。

2.5 AI 周报/月报/年报 --- 严格的格式约束

统计页面的「AI 报告」功能:

dart 复制代码
final fullPrompt = '''
你是一个数据分析师。请根据以下用户习惯数据写一份简洁的周报/月报/年报。

📊 数据摘要:
- 习惯:$habitList
- 累计打卡:$total 次
- 活跃天数:$activeDays
- 历史最佳连击:$bestStreak

报告格式要求(严格遵守):
1. 第一行:📅 习惯周报/月报/年报
2. 分 3 个板块:✅ 亮点 / ⚠️ 不足 / 💡 建议
3. 每个板块 2-3 句,每条用 • 开头(不是 - 也不是数字)
4. **绝对不要使用 markdown 格式**
5. 纯文本,100-200 字
''';

为什么不直接用 analyzeHabits? 数据分析是对话式的(「你最近坚持得不错,建议...」),而报告是结构化的(板块分明、格式固定)。两套 prompt 服务于两种不同的用户心智。

2.6 Follow-up 建议 --- Prompt 链式调用

对话结束时,自动生成 3 个 follow-up 问题:

dart 复制代码
static Future<List<String>> generateSuggestions({
  required String userMessage,
  required String aiReply,
}) async {
  final prompt = '''
基于以下对话,生成 3 个用户可能想问的后续问题(每个不超过 15 字):

用户:$userMessage
AI:$aiReply

输出格式(每行一个):
1. 第一个问题
2. 第二个问题
3. 第三个问题
语言:$currentLanguage
''';
  final reply = await chat(userMessage: prompt, systemPrompt: '...');
  // 解析:按行分割 → 去掉数字编号 → 最多取 3 条
  final lines = reply
      .split('\n')
      .where((l) => l.trim().isNotEmpty)
      .map((l) => l.replaceFirst(RegExp(r'^\d+[\.\)、]'), '').trim())
      .where((l) => l.isNotEmpty)
      .take(3)
      .toList();
  return lines.isEmpty ? ['继续说说?', '有什么建议?', '怎么改进?'] : lines;
}

这是 Prompt Chain 的实践:用第一次对话的输出作为第二次 prompt 的输入,不增加 UI 的等待时间(两次调用是串行的,但第二次很快)。


三、个性化进化:用用户的反馈训练 AI

这是我认为整个 AI 系统中最有意思的设计。

3.1 核心思路

大型语言模型支持 Few-shot Learning:在 system prompt 中放入几个「用户问 → AI 答」的示例,模型的回复风格会向示例靠拢。

HabitGo 把这个能力产品化为:「用户觉得某次回答有用 → 点 『让 AI 记住』→ 对话样本被持久化 → 后续所有 AI 调用自动注入这些样本」。

3.2 实现细节

dart 复制代码
class AiPersonalization {
  static const int maxPerDay = 3;
  static const int maxTotal = 30;

  /// 添加一条反馈
  static Future<bool> addFeedback({
    required String userMessage,
    required String aiReply,
  }) async {
    if (!await canFeedbackToday()) return false;

    final list = prefs.getStringList('ai_feedback_list') ?? [];
    list.add('$userMessage|||$aiReply');

    // 超限自动清理最旧的
    if (list.length > maxTotal) {
      list.removeRange(0, list.length - maxTotal);
    }

    await prefs.setStringList('ai_feedback_list', list);
    return true;
  }

  /// 构建追加到 system prompt 的个性化片段
  static Future<String> getAddendum() async {
    final list = prefs.getStringList('ai_feedback_list') ?? [];
    if (list.isEmpty) return '';

    final buf = StringBuffer();
    buf.writeln('\n\n[用户已确认对你有用的历史回答示例 --- 你应当延续这类风格与方向]');
    for (final line in list) {
      final parts = line.split('|||');
      buf.writeln('---');
      buf.writeln('用户问:${parts[0]}');
      buf.writeln('你答:${parts[1]}');
    }
    return buf.toString();
  }

  /// 最终拼装:基础 prompt + 个性化 addendum
  static Future<String> buildPersonalizedPrompt() async {
    final base = '你是 HabitGo 的习惯养成 AI 教练。';
    final addendum = await getAddendum();
    return '$base$addendum';
  }
}

3.3 限制机制的必要性

限制 原因
每日上限 3 次 防止用户在一天内塞入大量样本,导致 prompt 过长、费用爆炸
总上限 30 条 DeepSeek 的 context window 有限,且太多样本会稀释每条的影响力
FIFO 淘汰 超出删除最早 保证样本的时效性

3.4 在对话中的注入时机

dart 复制代码
// ai_chat_page.dart 中的 _send() 方法
if (isAnalysis) {
  reply = await DeepSeekService.analyzeHabits(/* ... */);
} else {
  // ⬇ 关键:对话场景使用个性化 prompt
  final sysPrompt = await AiPersonalization.buildPersonalizedPrompt();
  reply = await DeepSeekService.chat(
    userMessage: actualText,
    systemPrompt: sysPrompt,  // 基础 + addendum
  );
}

注意:数据分析场景不走个性化 prompt,因为分析建议需要客观,不宜被用户偏好带偏。这是一个有意识的区分。


四、自研 Markdown 渲染器:因为不需要引入重型依赖

AI 返回的内容天然带 Markdown。现有方案 flutter_markdown 很好,但它引入了一整套 HTML→Markdown→AST 的解析链,对只渲染加粗/斜体/标题/列表/行内代码的场景来说过于冗余。

4.1 设计原则:极简、够用、零依赖

markdown 复制代码
支持的语法:
✅ **加粗**         ✅ *斜体*           ✅ `行内代码`
✅ # ## ### 标题    ✅ - 无序列表        ✅ 1. 有序列表
✅ 空行 → 段落分隔   ❌ 链接/图片         ❌ 代码块
❌ 表格             ❌ 嵌套列表

不支持的语法就不解析(AI 也很少输出表格和嵌套列表),直接当普通文本渲染。

4.2 实现:两遍扫描

第一遍(行级解析):逐行正则匹配,生成对应的 Widget。

dart 复制代码
List<Widget> _parse(String input, TextStyle style, BuildContext ctx) {
  final widgets = <Widget>[];
  final lines = input.split('\n');
  int i = 0;

  while (i < lines.length) {
    final line = lines[i];

    // 标题:正则 ^#{1,3}\s+(.+)$
    final hMatch = RegExp(r'^(#{1,3})\s+(.+)$').firstMatch(line);
    if (hMatch != null) {
      final level = hMatch.group(1)!.length;
      widgets.add(_inline(hMatch.group(2)!,
          style.copyWith(fontSize: style.fontSize! + (3 - level) * 1.5,
                         fontWeight: FontWeight.w800)));
      i++; continue;
    }

    // 列表项:合并连续的列表行 → Row(bullet + content)
    final ulMatch = RegExp(r'^[\-\*]\s+(.+)$').firstMatch(line);
    final olMatch = RegExp(r'^\d+[\.\)]\s+(.+)$').firstMatch(line);
    if (ulMatch != null || olMatch != null) {
      // 向后扫描,合并连续列表
      int j = i; int idx = 1;
      while (j < lines.length) {
        // 用 Row(SizedBox(22) + Expanded(_inline(content))) 构建列表项
        j++;
      }
      i = j;
      widgets.addAll(items);
      continue;
    }

    // 空行 → SizedBox(height: 6)
    // 普通段落 → _inline(line)
    // ...
  }
  return widgets;
}

第二遍(行内解析) :单行内扫描 ** / * / `````,生成 TextSpan

dart 复制代码
List<InlineSpan> _toSpans(String text, TextStyle style, BuildContext ctx) {
  final spans = <InlineSpan>[];
  int i = 0;

  while (i < text.length) {
    final c = text[i];

    // 加粗:找到闭合的 **
    if (c == '*' && i + 1 < text.length && text[i + 1] == '*') {
      final end = text.indexOf('**', i + 2);
      if (end != -1) {
        spans.add(TextSpan(text: text.substring(i + 2, end),
            style: style.copyWith(fontWeight: FontWeight.w800)));
        i = end + 2; continue;
      }
    }

    // 行内代码:用 WidgetSpan 包裹带背景色的 Container
    if (c == '`') {
      final end = text.indexOf('`', i + 1);
      if (end != -1) {
        spans.add(WidgetSpan(
          child: Container(
            padding: EdgeInsets.symmetric(horizontal: 6, vertical: 1),
            decoration: BoxDecoration(
              color: codeBg ?? scheme.surfaceContainerHighest,
              borderRadius: BorderRadius.circular(4),
            ),
            child: Text(text.substring(i + 1, end),
                style: style.copyWith(fontFamily: 'monospace',
                    color: codeFg ?? scheme.primary)),
          ),
        ));
        i = end + 1; continue;
      }
    }

    // 普通字符:扫描到下一个 * 或 ` 再分段
    int next = i + 1;
    while (next < text.length && text[next] != '*' && text[next] != '`') next++;
    spans.add(TextSpan(text: text.substring(i, next), style: style));
    i = next;
  }
  return spans;
}

为什么行内代码用 WidgetSpan 而不是 TextSpan 因为代码需要背景色 + 圆角,纯 TextSpan 做不到。WidgetSpan 可以内嵌任意 Widget,代价是多了几个 Widget 节点,但对消息列表来说性能影响可以忽略。


五、用量控制:免费版的 AI 经济学

5.1 设计思路

DeepSeek 虽然便宜,但每 100 万 token 也要几分钱。App 免费版如果不做限制,成本会线性增长。

策略:

限制项 免费版 Premium
AI 对话 10 次/天 无限
AI 数据分析 5 次/天 无限
每日问候/挑战/一课 不限制 不限制

前两项是高频操作(用户主动触发),后三项是每天固定一次(系统自动触发,频率可控)。

5.2 技术实现

dart 复制代码
class AiUsageTracker {
  static Future<bool> tryConsumeChat() async =>
      _tryConsume('ai_chat_usage', 'ai_chat_date', FreeTierLimits.aiChatPerDay);

  static Future<bool> _tryConsume(
      String key, String dateKey, int maxPerDay) async {
    final prefs = await SharedPreferences.getInstance();
    final today = _todayKey();
    final savedDate = prefs.getString(dateKey);

    // 新的一天 → 重置计数
    if (savedDate != today) {
      await prefs.setString(dateKey, today);
      await prefs.setInt(key, 0);
    }

    final used = prefs.getInt(key) ?? 0;
    if (used >= maxPerDay) return false;  // 超限
    await prefs.setInt(key, used + 1);
    return true;  // 允许调用
  }
}

在 Riverpod 层的反应式绑定:

dart 复制代码
final aiChatRemainingProvider = FutureProvider<int>((ref) async {
  ref.watch(isPremiumProvider);  // 订阅状态变化时自动刷新
  if (ref.read(isPremiumProvider)) return -1;  // -1 表示无限
  return AiUsageTracker.remainingChat();
});

-1 的语义设计很巧妙:UI 层只需要判断 value == -1 就知道是会员,不需要额外传 isPremium


六、缓存策略:每天一份,永不过期(不请求)

每日问候、每日一课、每日挑战共用同一套缓存模式:

scss 复制代码
┌─────────────────────────────┐
│  getXxx()                   │
│   │                         │
│   ├── 缓存命中 (日期==今天)  │ → 立即返回
│   │                         │
│   ├── 缓存未命中             │
│   │   ├── 立即返回旧值/默认值 │ → UI 无等待
│   │   └── 后台 async 生成    │ → 成功后更新缓存
│   │                         │
│   └── 网络失败              │ → 返回 fallback 默认值
└─────────────────────────────┘

核心代码模式:

dart 复制代码
static Future<String> getXxx() async {
  final saved = prefs.getString(dateKey);
  if (saved == today) return prefs.getString(contentKey)!;  // 命中

  _generateAndCache(today);  // 🔥 不 await!后台异步执行
  return prefs.getString(contentKey) ?? fallbackDefault;    // 立即返回旧值
}

这个不 await 是精髓。如果 await,首页打开后要等 2-3 秒才有内容,用户体验极差。改为 fire-and-forget 后,首页秒开,2-3 秒后内容静默刷新。


七、AI 融入产品的七个触点

最后梳理一下 AI 和产品功能的一一对应关系:

# 触点 触发时机 AI 做的事 代码位置
1 首页问候 每次打开首页 生成名人名言 + 习惯鼓励 daily_greeting_service.dart
2 每日挑战 同上 推荐一个 5-30 分钟的小任务 daily_challenge_service.dart
3 每日一课 同上 讲一个冷知识(12 领域轮换) daily_lesson_service.dart
4 AI 教练对话 用户主动进入 AI Tab 自由对话 + 习惯数据分析 ai_chat_page.dart
5 AI 数据分析 对话中含「分析」关键词 读取习惯数据 + 给 3-5 条建议 analyzeHabits()
6 成就邮件 达成里程碑(3/7/14/30/100天) 写一封鼓励信 + 名人名言 generateAchievementMail()
7 AI 周/月/年报 统计页点击报告按钮 亮点/不足/建议三板斧 _AiReportPanel._generate()

再加上 AI 个性化进化 (反馈 → system prompt addendum)和 AI 成就名称生成 (心愿银行每次启动随机 6 个名称),一共 9 个 AI 触点,覆盖了 App 的首页、对话、统计、邮箱、心愿银行五个主要页面。


八、可以继续深挖的方向

8.1 Streaming 响应

目前所有 AI 调用都是一次性返回 ,用户需要等 2-5 秒才能看到完整内容。改成 SSE Streaming 后可以逐字显示 ,体验会提升一大截。DeepSeek API 支持 stream: true,技术上是可行的,只是 UI 层需要改造为流式接收 + 增量更新。

8.2 本地模型 / 端侧推理

把日问候/每日一课这种非实时、可预生成的内容做到端侧,进一步缩减 API 成本。Google 的 MediaPipe / ML Kit 正在往这个方向走。

8.3 A/B 测试 Prompt

目前所有 prompt 是基于个人经验手写的。如果能做 A/B 测试(两套 prompt → 对比用户行为数据),就可以数据驱动地优化 prompt,而不是靠感觉。

8.4 RAG(检索增强生成)

将用户的全部历史打卡数据做 embedding,查询时通过向量相似度检索相关上下文注入 prompt。这样 AI 能回答「我三个月前那个习惯后来怎么样了」之类的问题。


写在最后

做一个「AI 驱动的 App」真的不是接个 API 就完了。这篇文章涵盖了 HabitGo 从 prompt 设计、多场景集成、个性化进化、自研渲染器、用量控制到缓存策略的全流程,希望能给同样在探索 AI + 移动端的同学一些启发。

如果你也在做类似的事情,或者对某个技术细节有疑问,欢迎在评论区交流 👇

相关推荐
木西4 小时前
自动化工程实践:基于 DeepSeek 与 Hardhat 的 Web3 技术长文生成 Skill 实现
openai·agent·deepseek
AC赳赳老秦7 小时前
时间开销自动统计:OpenClaw 记录工作任务时长、分析时间分配、给出优化建议
java·大数据·开发语言·python·自动化·deepseek·openclaw
新AI佬1 天前
DeepSeek V4 峰谷计费上线:怎么把 AI 账单砍掉一半
费用·deepseek
赛联区块链1 天前
工厂使用AI降本增效实践与探索
人工智能·ai智能体·deepseek·ai赋能·工业ai·工厂ai
DigitalOcean1 天前
RadixArk 在 DigitalOcean 上实现 DeepSeek V4 吞吐量提升 10 倍
deepseek
AC赳赳老秦1 天前
GTD 工作法落地:用 OpenClaw 搭建收集-整理-执行-复盘全流程自动化工作流
大数据·运维·人工智能·信息可视化·自动化·deepseek·openclaw
开发者联盟league2 天前
vscode接入deepseek
vscode·deepseek
DS随心转小程序2 天前
deepseek 生成 word 文档指令搭配 AI 导出鸭高效完成各类文档批量输出
人工智能·word·豆包·deepseek·ai导出鸭
小李飞刀李寻欢3 天前
DeepSeek V3 版本模型结构分析
算法·大模型·deepseek